mirror of
https://github.com/bitwarden/web
synced 2025-12-06 00:03:28 +00:00
Compare commits
2 Commits
mono-repo
...
feature/ex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5d3caba65 | ||
|
|
5a2294fb4f |
@@ -21,6 +21,10 @@
|
||||
|
||||
<!--Required for any UI changes. Delete if not applicable-->
|
||||
|
||||
## Testing requirements
|
||||
|
||||
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
||||
|
||||
## Before you submit
|
||||
|
||||
- [ ] I have checked for **linting** errors (`npm run lint`) (required)
|
||||
@@ -11,10 +11,6 @@ on:
|
||||
branches-ignore:
|
||||
- "l10n_master"
|
||||
- "gh-pages"
|
||||
- "deploy"
|
||||
paths-ignore:
|
||||
- '.github/workflows/**'
|
||||
|
||||
|
||||
jobs:
|
||||
cloc:
|
||||
@@ -32,28 +28,6 @@ jobs:
|
||||
- name: Print lines of code
|
||||
run: cloc --include-lang TypeScript,JavaScript,HTML,Sass,CSS --vcs git
|
||||
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||
|
||||
- name: Cache npm
|
||||
id: npm-cache
|
||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||
with:
|
||||
path: "~/.npm"
|
||||
key: ${{ runner.os }}-npm-lint-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
|
||||
|
||||
setup:
|
||||
name: Setup
|
||||
runs-on: ubuntu-20.04
|
||||
@@ -67,26 +41,25 @@ jobs:
|
||||
id: version
|
||||
run: echo "::set-output name=value::${GITHUB_SHA:0:7}"
|
||||
|
||||
|
||||
build-oss-selfhost:
|
||||
name: Build OSS zip
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- lint
|
||||
needs: setup
|
||||
env:
|
||||
_VERSION: ${{ needs.setup.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
node-version: "16"
|
||||
|
||||
- name: Cache npm
|
||||
id: npm-cache
|
||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||
with:
|
||||
path: "~/.npm"
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
@@ -97,6 +70,9 @@ jobs:
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -112,26 +88,25 @@ jobs:
|
||||
path: ./web-${{ env._VERSION }}-selfhosted-open-source.zip
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
build-cloud:
|
||||
name: Build Cloud zip
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- lint
|
||||
needs: setup
|
||||
env:
|
||||
_VERSION: ${{ needs.setup.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
node-version: "16"
|
||||
|
||||
- name: Cache npm
|
||||
id: npm-cache
|
||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||
with:
|
||||
path: "~/.npm"
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
@@ -142,6 +117,9 @@ jobs:
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -157,26 +135,25 @@ jobs:
|
||||
path: ./web-${{ env._VERSION }}-cloud-COMMERCIAL.zip
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
build-commercial-selfhost:
|
||||
name: Build SelfHost Docker image
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- lint
|
||||
needs: setup
|
||||
env:
|
||||
_VERSION: ${{ needs.setup.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
node-version: "16"
|
||||
|
||||
- name: Cache npm
|
||||
id: npm-cache
|
||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||
with:
|
||||
path: "~/.npm"
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
@@ -188,13 +165,19 @@ jobs:
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Setup DCT
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
id: setup-dct
|
||||
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
|
||||
with:
|
||||
azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
azure-keyvault-name: "bitwarden-prod-kv"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Restore
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -229,11 +212,11 @@ jobs:
|
||||
run: docker tag bitwarden/web bitwarden/web:dev
|
||||
|
||||
- name: Tag hotfix branch
|
||||
if: github.ref == 'refs/heads/hotfix-rc'
|
||||
run: docker tag bitwarden/web bitwarden/web:hotfix-rc
|
||||
if: github.ref == 'refs/heads/hotfix'
|
||||
run: docker tag bitwarden/web bitwarden/web:hotfix
|
||||
|
||||
- name: List Docker images
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
run: docker images
|
||||
|
||||
- name: Push rc image
|
||||
@@ -251,59 +234,32 @@ jobs:
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
|
||||
|
||||
- name: Push hotfix image
|
||||
if: github.ref == 'refs/heads/hotfix-rc'
|
||||
run: docker push bitwarden/web:hotfix-rc
|
||||
if: github.ref == 'refs/heads/hotfix'
|
||||
run: docker push bitwarden/web:hotfix
|
||||
env:
|
||||
DOCKER_CONTENT_TRUST: 1
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
|
||||
|
||||
- name: Log out of Docker
|
||||
if: 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
|
||||
|
||||
- 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
|
||||
|
||||
- name: Tag and Push RC to Azure ACR QA registry
|
||||
env:
|
||||
REGISTRY: bitwardenqa.azurecr.io
|
||||
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 bitwarden/web \
|
||||
$REGISTRY/web-sh:$IMAGE_TAG
|
||||
docker push $REGISTRY/web-sh:$IMAGE_TAG
|
||||
|
||||
- name: Log out of Docker
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
run: docker logout
|
||||
|
||||
|
||||
build-qa:
|
||||
name: Build Docker images for QA environment
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- lint
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
node-version: "16"
|
||||
|
||||
- name: Cache npm
|
||||
id: npm-cache
|
||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||
with:
|
||||
path: "~/.npm"
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
@@ -322,6 +278,12 @@ jobs:
|
||||
- name: Log into container registry
|
||||
run: az acr login -n bitwardenqa
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Restore
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -377,29 +339,35 @@ jobs:
|
||||
- name: Log out of Docker
|
||||
run: docker logout
|
||||
|
||||
|
||||
windows:
|
||||
name: Test code on Windows
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Set up NuGet
|
||||
uses: nuget/setup-nuget@04b0c2b8d1b97922f67eca497d7cf0bf17b8ffe1
|
||||
with:
|
||||
nuget-version: "latest"
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
|
||||
|
||||
- name: Cache npm
|
||||
id: npm-cache
|
||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||
with:
|
||||
path: "~/.npm"
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
node-version: "16"
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
nuget help | grep Version
|
||||
msbuild -version
|
||||
dotnet --info
|
||||
node --version
|
||||
npm --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
@@ -408,13 +376,18 @@ jobs:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
GITHUB_EVENT: ${{ github.event_name }}
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
|
||||
- name: NPM build
|
||||
run: npm run build:bit:cloud
|
||||
|
||||
|
||||
crowdin-push:
|
||||
name: Crowdin Push
|
||||
if: github.ref == 'refs/heads/master'
|
||||
@@ -453,7 +426,6 @@ jobs:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
|
||||
|
||||
check-failures:
|
||||
name: Check for failures
|
||||
if: always()
|
||||
@@ -461,7 +433,6 @@ jobs:
|
||||
needs:
|
||||
- cloc
|
||||
- setup
|
||||
- lint
|
||||
- build-oss-selfhost
|
||||
- build-cloud
|
||||
- build-commercial-selfhost
|
||||
@@ -473,7 +444,6 @@ jobs:
|
||||
if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
|
||||
env:
|
||||
CLOC_STATUS: ${{ needs.cloc.result }}
|
||||
LINT_STATUS: ${{ needs.lint.result }}
|
||||
SETUP_STATUS: ${{ needs.setup.result }}
|
||||
BUILD_OSS_SELFHOST_STATUS: ${{ needs.build-oss-selfhost.result }}
|
||||
BUILD_CLOUD_STATUS: ${{ needs.build-cloud.result }}
|
||||
@@ -484,8 +454,6 @@ jobs:
|
||||
run: |
|
||||
if [ "$CLOC_STATUS" = "failure" ]; then
|
||||
exit 1
|
||||
elif [ "$LINT_STATUS" = "failure" ]; then
|
||||
exit 1
|
||||
elif [ "$SETUP_STATUS" = "failure" ]; then
|
||||
exit 1
|
||||
elif [ "$BUILD_OSS_SELFHOST_STATUS" = "failure" ]; then
|
||||
201
.github/workflows/release.yml
vendored
Normal file
201
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
---
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_type:
|
||||
description: 'Release Options'
|
||||
required: true
|
||||
default: 'Initial Release'
|
||||
type: choice
|
||||
options:
|
||||
- Initial Release
|
||||
- Redeploy
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
name: Setup
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
release_version: ${{ steps.version.outputs.package }}
|
||||
tag_version: ${{ steps.version.outputs.tag }}
|
||||
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||
steps:
|
||||
- name: Branch check
|
||||
run: |
|
||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix" ]]; then
|
||||
echo "==================================="
|
||||
echo "[!] Can only release from the 'rc' or 'hotfix' branches"
|
||||
echo "==================================="
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # 2.3.4
|
||||
|
||||
- name: Check Release Version
|
||||
id: version
|
||||
run: |
|
||||
version=$( jq -r ".version" package.json)
|
||||
previous_release_tag_version=$(
|
||||
curl -sL https://api.github.com/repos/$GITHUB_REPOSITORY/releases/latest | jq -r ".tag_name"
|
||||
)
|
||||
|
||||
if [ "v$version" == "$previous_release_tag_version" ] && \
|
||||
[ "${{ github.event.inputs.release_type }}" == "Initial Release" ]; then
|
||||
echo "[!] Already released v$version. Please bump version to continue"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "::set-output name=package::$version"
|
||||
echo "::set-output name=tag::v$version"
|
||||
|
||||
- name: Get branch name
|
||||
id: branch
|
||||
run: |
|
||||
BRANCH_NAME=$(basename ${{ github.ref }})
|
||||
echo "::set-output name=branch-name::$BRANCH_NAME"
|
||||
|
||||
self-host:
|
||||
name: Release self-host docker
|
||||
runs-on: ubuntu-20.04
|
||||
needs: setup
|
||||
env:
|
||||
_BRANCH_NAME: ${{ needs.setup.outputs.branch-name }}
|
||||
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
|
||||
steps:
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
docker --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Setup DCT
|
||||
id: setup-dct
|
||||
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
|
||||
with:
|
||||
azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
azure-keyvault-name: "bitwarden-prod-kv"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Pull latest selfhost image
|
||||
run: docker pull bitwarden/web:$_BRANCH_NAME
|
||||
|
||||
- name: Tag version and latest
|
||||
run: |
|
||||
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION
|
||||
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest
|
||||
|
||||
- name: List Docker images
|
||||
run: docker images
|
||||
|
||||
- name: Push version and latest image
|
||||
env:
|
||||
DOCKER_CONTENT_TRUST: 1
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
|
||||
run: |
|
||||
docker push bitwarden/web:$_RELEASE_VERSION
|
||||
docker push bitwarden/web:latest
|
||||
|
||||
- name: Log out of Docker
|
||||
run: docker logout
|
||||
|
||||
ghpages-deploy:
|
||||
name: Deploy Web Vault
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- self-host
|
||||
env:
|
||||
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
|
||||
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
with:
|
||||
ref: gh-pages
|
||||
|
||||
- name: Create deploy branch
|
||||
run: |
|
||||
git switch -c deploy-$_TAG_VERSION
|
||||
git push -u origin deploy-$_TAG_VERSION
|
||||
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Setup git config
|
||||
run: |
|
||||
git config user.name = "GitHub Action Bot"
|
||||
git config user.email = "<>"
|
||||
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
|
||||
git config --global url."https://".insteadOf ssh://
|
||||
|
||||
- name: Download latest cloud asset
|
||||
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.setup.outputs.branch-name }}
|
||||
artifacts: web-*-cloud-COMMERCIAL.zip
|
||||
|
||||
# This should result in a build directory in the current working directory
|
||||
- name: Unzip build asset
|
||||
run: unzip web-*-cloud-COMMERCIAL.zip
|
||||
|
||||
- name: Deploy GitHub Pages
|
||||
uses: crazy-max/ghaction-github-pages@db4476a01402e1a7ce05f41832040eef16d14925 # v2.5.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
target_branch: deploy-${{ needs.setup.outputs.tag_version }}
|
||||
build_dir: build
|
||||
keep_history: true
|
||||
commit_message: "Staging deploy ${{ needs.setup.outputs.release_version }}"
|
||||
|
||||
- name: Create Deploy PR
|
||||
env:
|
||||
PR_BRANCH: deploy-${{ env._TAG_VERSION }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh pr create --title "Deploy $_RELEASE_VERSION" \
|
||||
--body "Deploying $_RELEASE_VERSION" \
|
||||
--base gh-pages \
|
||||
--head "$PR_BRANCH"
|
||||
|
||||
release:
|
||||
name: Create GitHub Release
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- self-host
|
||||
- ghpages-deploy
|
||||
steps:
|
||||
- name: Download latest build artifacts
|
||||
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.setup.outputs.branch-name }}
|
||||
artifacts: "web-*-selfhosted-COMMERCIAL.zip,
|
||||
web-*-selfhosted-open-source.zip"
|
||||
|
||||
- name: Rename assets
|
||||
run: |
|
||||
mv web-*-selfhosted-COMMERCIAL.zip web-${{ needs.setup.outputs.release_version }}-selfhosted-COMMERCIAL.zip
|
||||
mv web-*-selfhosted-open-source.zip web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09
|
||||
with:
|
||||
name: "Version ${{ needs.setup.outputs.release_version }}"
|
||||
commit: ${{ github.sha }}
|
||||
tag: "${{ needs.setup.outputs.tag_version }}"
|
||||
body: "<insert release notes here>"
|
||||
artifacts: "web-${{ needs.setup.outputs.release_version }}-selfhosted-COMMERCIAL.zip,
|
||||
web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
0
apps/web/.gitignore → .gitignore
vendored
0
apps/web/.gitignore → .gitignore
vendored
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -0,0 +1,4 @@
|
||||
[submodule "jslib"]
|
||||
path = jslib
|
||||
url = https://github.com/bitwarden/jslib.git
|
||||
branch = master
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
build
|
||||
dist
|
||||
|
||||
#jslib
|
||||
jslib
|
||||
|
||||
# External libraries / auto synced locales
|
||||
src/locales
|
||||
@@ -10,7 +10,7 @@ Here is how you can get involved:
|
||||
- **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
||||
- **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
||||
- **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||
- **Help other users:** Go to the [Ask the Bitwarden Community category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||
- **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||
- **Translate:** See the localization (l10n) section below
|
||||
|
||||
## Contributor Agreement
|
||||
@@ -31,6 +31,6 @@ We use a translation tool called [Crowdin](https://crowdin.com) to help manage o
|
||||
|
||||
If you are interested in helping translate the Bitwarden web vault into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-web
|
||||
|
||||
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/dwbit).
|
||||
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/kspearrin).
|
||||
|
||||
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM bitwarden/server
|
||||
FROM bitwarden/server:dev
|
||||
|
||||
LABEL com.bitwarden.product="bitwarden"
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
> **Repository Reorganization in Progress**
|
||||
>
|
||||
> We are currently migrating some projects over to a mono repository. For existing PR's we will be providing documentation on how to move/migrate them. To minimize the overhead we are actively reviewing open PRs. If possible please ensure any pending comments are resolved as soon as possible.
|
||||
>
|
||||
> New pull requests created during this transition period may not get addressed —if needed, please create a new PR after the reorganization is complete.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/web-vault-macbook.png" alt="" width="600" height="358" />
|
||||
</p>
|
||||
@@ -67,10 +61,6 @@ You can also manually adjusting your API endpoint settings by adding `config/loc
|
||||
|
||||
Where the `urls` object is defined by the [Urls type in jslib](https://github.com/bitwarden/jslib/blob/master/common/src/abstractions/environment.service.ts).
|
||||
|
||||
## We're Hiring!
|
||||
|
||||
Interested in contributing in a big way? Consider joining our team! We're hiring for many positions. Please take a look at our [Careers page](https://bitwarden.com/careers/) to see what opportunities are currently open as well as what it's like to work at Bitwarden.
|
||||
|
||||
## Contribute
|
||||
|
||||
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
|
||||
45
SECURITY.md
Normal file
45
SECURITY.md
Normal file
@@ -0,0 +1,45 @@
|
||||
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
|
||||
users safe. If you believe you've found a security issue in our product or service, we encourage you to
|
||||
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
|
||||
|
||||
# Disclosure Policy
|
||||
|
||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
|
||||
effort to quickly resolve the issue.
|
||||
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
|
||||
third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
|
||||
degradation of our service. Only interact with accounts you own or with explicit permission of the
|
||||
account holder.
|
||||
- If you would like to encrypt your report, please use the PGP key with long ID
|
||||
`0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
||||
|
||||
# In-scope
|
||||
|
||||
- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
|
||||
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
|
||||
code is available at https://github.com/bitwarden.
|
||||
|
||||
# Exclusions
|
||||
|
||||
The following bug classes are out-of scope:
|
||||
|
||||
- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
|
||||
or that we already know of. Note that some of our issue tracking is private.
|
||||
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
|
||||
upstream maintainer.
|
||||
- Attacks requiring physical access to a user's device.
|
||||
- Self-XSS
|
||||
- Issues related to software or protocols not under Bitwarden's control
|
||||
- Vulnerabilities in outdated versions of Bitwarden
|
||||
- Missing security best practices that do not directly lead to a vulnerability
|
||||
- Issues that do not have any impact on the general public
|
||||
|
||||
While researching, we'd like to ask you to refrain from:
|
||||
|
||||
- Denial of service
|
||||
- Spamming
|
||||
- Social engineering (including phishing) of Bitwarden staff or contractors
|
||||
- Any physical attempts against Bitwarden property or data centers
|
||||
|
||||
Thank you for helping keep Bitwarden and our users safe!
|
||||
@@ -1,8 +0,0 @@
|
||||
**/dist
|
||||
**/build
|
||||
jslib
|
||||
webpack.config.js
|
||||
scripts/optimize.js
|
||||
config.js
|
||||
|
||||
**/node_modules
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"extends": ["./jslib/shared/eslintrc.json"],
|
||||
"rules": {
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"alphabetize": {
|
||||
"order": "asc"
|
||||
},
|
||||
"newlines-between": "always",
|
||||
"pathGroups": [
|
||||
{
|
||||
"pattern": "jslib-*/**",
|
||||
"group": "external",
|
||||
"position": "after"
|
||||
},
|
||||
{
|
||||
"pattern": "src/**/*",
|
||||
"group": "parent",
|
||||
"position": "before"
|
||||
}
|
||||
],
|
||||
"pathGroupsExcludedImportTypes": ["builtin"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
16
apps/web/.github/workflows/enforce-labels.yml
vendored
16
apps/web/.github/workflows/enforce-labels.yml
vendored
@@ -1,16 +0,0 @@
|
||||
---
|
||||
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"
|
||||
334
apps/web/.github/workflows/release.yml
vendored
334
apps/web/.github/workflows/release.yml
vendored
@@ -1,334 +0,0 @@
|
||||
---
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_type:
|
||||
description: 'Release Options'
|
||||
required: true
|
||||
default: 'Initial Release'
|
||||
type: choice
|
||||
options:
|
||||
- Initial Release
|
||||
- Redeploy
|
||||
- Dry Run
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
name: Setup
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
release_version: ${{ steps.version.outputs.version }}
|
||||
tag_version: ${{ steps.version.outputs.version }}
|
||||
branch_name: ${{ steps.branch.outputs.branch_name }}
|
||||
steps:
|
||||
- name: Branch check
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
run: |
|
||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||
echo "==================================="
|
||||
echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
|
||||
echo "==================================="
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # 2.4.0
|
||||
|
||||
- name: Check Release Version
|
||||
id: version
|
||||
uses: bitwarden/gh-actions/release-version-check@ea9fab01d76940267b4147cc1c4542431246b9f6
|
||||
with:
|
||||
release-type: ${{ github.event.inputs.release_type }}
|
||||
project-type: ts
|
||||
file: package.json
|
||||
|
||||
- name: Get branch name
|
||||
id: branch
|
||||
run: |
|
||||
BRANCH_NAME=$(basename ${{ github.ref }})
|
||||
echo "::set-output name=branch_name::$BRANCH_NAME"
|
||||
|
||||
|
||||
self-host:
|
||||
name: Release self-host docker
|
||||
runs-on: ubuntu-20.04
|
||||
needs: setup
|
||||
env:
|
||||
_BRANCH_NAME: ${{ needs.setup.outputs.branch_name }}
|
||||
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
|
||||
_RELEASE_OPTION: ${{ github.event.inputs.release_type }}
|
||||
steps:
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
docker --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
echo "Github Release Option: $_RELEASE_OPTION"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||
|
||||
########## DockerHub ##########
|
||||
- name: Setup DCT
|
||||
id: setup-dct
|
||||
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
|
||||
with:
|
||||
azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
azure-keyvault-name: "bitwarden-prod-kv"
|
||||
|
||||
- name: Pull latest selfhost image
|
||||
run: |
|
||||
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
||||
docker pull bitwarden/web:latest
|
||||
else
|
||||
docker pull bitwarden/web:$_BRANCH_NAME
|
||||
fi
|
||||
|
||||
- name: Tag version and latest
|
||||
run: |
|
||||
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
||||
docker tag bitwarden/web:latest bitwarden/web:dryrun
|
||||
else
|
||||
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION
|
||||
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest
|
||||
fi
|
||||
|
||||
- name: Push version and latest image
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
env:
|
||||
DOCKER_CONTENT_TRUST: 1
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
|
||||
run: |
|
||||
docker push bitwarden/web:$_RELEASE_VERSION
|
||||
docker push bitwarden/web:latest
|
||||
|
||||
- name: Log out of Docker and disable Docker Notary
|
||||
run: |
|
||||
docker logout
|
||||
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
||||
|
||||
########## 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
|
||||
|
||||
- name: Tag version and latest
|
||||
env:
|
||||
REGISTRY: bitwardenqa.azurecr.io
|
||||
run: |
|
||||
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
||||
docker tag bitwarden/web:latest $REGISTRY/web:dryrun
|
||||
else
|
||||
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web:$_RELEASE_VERSION
|
||||
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web:latest
|
||||
|
||||
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web-sh:$_RELEASE_VERSION
|
||||
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web-sh:latest
|
||||
fi
|
||||
|
||||
- name: Push version and latest image
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
env:
|
||||
REGISTRY: bitwardenqa.azurecr.io
|
||||
run: |
|
||||
docker push $REGISTRY/web:$_RELEASE_VERSION
|
||||
docker push $REGISTRY/web:latest
|
||||
|
||||
docker push $REGISTRY/web-sh:$_RELEASE_VERSION
|
||||
docker push $REGISTRY/web-sh:latest
|
||||
|
||||
- name: Log out of Docker
|
||||
run: docker logout
|
||||
|
||||
|
||||
ghpages-deploy:
|
||||
name: Deploy Web Vault to GitHub Pages
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- self-host
|
||||
env:
|
||||
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
|
||||
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
with:
|
||||
ref: gh-pages
|
||||
|
||||
- name: Create gh-pages-deploy branch
|
||||
run: |
|
||||
git switch -c gh-pages-deploy-$_TAG_VERSION
|
||||
git push -u origin gh-pages-deploy-$_TAG_VERSION
|
||||
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
|
||||
- name: Setup git config
|
||||
run: |
|
||||
git config user.name = "GitHub Action Bot"
|
||||
git config user.email = "<>"
|
||||
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
|
||||
git config --global url."https://".insteadOf ssh://
|
||||
|
||||
- name: Download latest cloud asset
|
||||
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.setup.outputs.branch_name }}
|
||||
artifacts: web-*-cloud-COMMERCIAL.zip
|
||||
|
||||
# This should result in a build directory in the current working directory
|
||||
- name: Unzip build asset
|
||||
run: unzip web-*-cloud-COMMERCIAL.zip
|
||||
|
||||
- name: Deploy GitHub Pages
|
||||
uses: crazy-max/ghaction-github-pages@a117e4aa1fb4854d021546d2abdfac95be568a3a # v2.6.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
target_branch: gh-pages-deploy-${{ needs.setup.outputs.tag_version }}
|
||||
build_dir: build
|
||||
keep_history: true
|
||||
commit_message: "Staging deploy ${{ needs.setup.outputs.release_version }}"
|
||||
dry_run: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||
|
||||
- name: Create GitHub Pages Deploy PR
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
env:
|
||||
PR_BRANCH: gh-pages-deploy-${{ env._TAG_VERSION }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh pr create --title "Deploy $_RELEASE_VERSION to GitHub Pages" \
|
||||
--body "Deploying $_RELEASE_VERSION" \
|
||||
--base gh-pages \
|
||||
--head "$PR_BRANCH"
|
||||
|
||||
|
||||
cfpages-deploy:
|
||||
name: Deploy Web Vault to CloudFlare Pages branch
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- self-host
|
||||
env:
|
||||
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
|
||||
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
|
||||
- name: Download latest cloud asset
|
||||
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.setup.outputs.branch_name }}
|
||||
artifacts: web-*-cloud-COMMERCIAL.zip
|
||||
|
||||
# This should result in a build directory in the current working directory
|
||||
- name: Unzip build asset
|
||||
run: unzip web-*-cloud-COMMERCIAL.zip
|
||||
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
with:
|
||||
ref: deploy
|
||||
path: deployment
|
||||
|
||||
- name: Setup git config
|
||||
run: |
|
||||
git config --global user.name = "GitHub Action Bot"
|
||||
git config --global user.email = "<>"
|
||||
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
|
||||
git config --global url."https://".insteadOf ssh://
|
||||
|
||||
- name: Deploy CloudFlare Pages
|
||||
run: |
|
||||
rm -rf ./*
|
||||
cp -R ../build/* .
|
||||
working-directory: deployment
|
||||
|
||||
- name: Create cf-pages-deploy branch
|
||||
run: |
|
||||
git switch -c cf-pages-deploy-$_TAG_VERSION
|
||||
git add .
|
||||
git commit -m "Staging deploy ${{ needs.setup.outputs.release_version }}"
|
||||
git push -u origin cf-pages-deploy-$_TAG_VERSION
|
||||
working-directory: deployment
|
||||
|
||||
- name: Create CloudFlare Pages Deploy PR
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
env:
|
||||
PR_BRANCH: cf-pages-deploy-${{ env._TAG_VERSION }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh pr create --title "Deploy $_RELEASE_VERSION to CloudFlare Pages" \
|
||||
--body "Deploying $_RELEASE_VERSION" \
|
||||
--base deploy \
|
||||
--head "$PR_BRANCH"
|
||||
|
||||
|
||||
release:
|
||||
name: Create GitHub Release
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- self-host
|
||||
- ghpages-deploy
|
||||
- cfpages-deploy
|
||||
steps:
|
||||
- name: Download latest build artifacts
|
||||
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.setup.outputs.branch_name }}
|
||||
artifacts: "web-*-selfhosted-COMMERCIAL.zip,
|
||||
web-*-selfhosted-open-source.zip"
|
||||
|
||||
- name: Rename assets
|
||||
run: |
|
||||
mv web-*-selfhosted-COMMERCIAL.zip web-${{ needs.setup.outputs.release_version }}-selfhosted-COMMERCIAL.zip
|
||||
mv web-*-selfhosted-open-source.zip web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip
|
||||
|
||||
- name: Create release
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01
|
||||
with:
|
||||
name: "Version ${{ needs.setup.outputs.release_version }}"
|
||||
commit: ${{ github.sha }}
|
||||
tag: "${{ needs.setup.outputs.tag_version }}"
|
||||
body: "<insert release notes here>"
|
||||
artifacts: "web-${{ needs.setup.outputs.release_version }}-selfhosted-COMMERCIAL.zip,
|
||||
web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
|
||||
|
||||
dry-run:
|
||||
name: Dry Run Cleanup
|
||||
runs-on: ubuntu-20.04
|
||||
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||
env:
|
||||
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
|
||||
needs:
|
||||
- setup
|
||||
- release
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # 2.4.0
|
||||
|
||||
- name: Remove gh-pages-deploy branch
|
||||
run: git push origin --delete gh-pages-deploy-$_TAG_VERSION
|
||||
|
||||
- name: Remove cf-pages-deploy branch
|
||||
run: git push origin --delete cf-pages-deploy-$_TAG_VERSION
|
||||
11
apps/web/.github/workflows/workflow-linter.yml
vendored
11
apps/web/.github/workflows/workflow-linter.yml
vendored
@@ -1,11 +0,0 @@
|
||||
---
|
||||
name: Workflow Linter
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/**
|
||||
|
||||
jobs:
|
||||
call-workflow:
|
||||
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master
|
||||
@@ -1,21 +0,0 @@
|
||||
Bitwarden believes that working with security researchers across the globe is crucial to keeping our users safe. If you believe you've found a security issue in our product or service, we encourage you to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We welcome working with you to resolve the issue promptly. Thanks in advance!
|
||||
|
||||
# Disclosure Policy
|
||||
|
||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue.
|
||||
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder.
|
||||
- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
||||
|
||||
While researching, we'd like to ask you to refrain from:
|
||||
|
||||
- Denial of service
|
||||
- Spamming
|
||||
- Social engineering (including phishing) of Bitwarden staff or contractors
|
||||
- Any physical attempts against Bitwarden property or data centers
|
||||
|
||||
# We want to help you!
|
||||
|
||||
If you have something that you feel is close to exploitation, or if you'd like some information regarding the internal API, or generally have any questions regarding the app that would help in your efforts, please email us at https://bitwarden.com/contact and ask for that information. As stated above, Bitwarden wants to help you find issues, and is more than willing to help.
|
||||
|
||||
Thank you for helping keep Bitwarden and our users safe!
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"dev": {
|
||||
"proxyApi": "http://localhost:4001",
|
||||
"proxyIdentity": "http://localhost:33657",
|
||||
"proxyEvents": "http://localhost:46274",
|
||||
"proxyNotifications": "http://localhost:61841",
|
||||
"port": 8081
|
||||
}
|
||||
}
|
||||
13568
apps/web/package-lock.json
generated
13568
apps/web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
module.exports = {
|
||||
plugins: [require("tailwindcss"), require("autoprefixer"), require("postcss-nested")],
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import { StateService as BaseStateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageOptions } from "jslib-common/models/domain/storageOptions";
|
||||
|
||||
import { Account } from "src/models/account";
|
||||
|
||||
export abstract class StateService extends BaseStateService<Account> {
|
||||
getRememberEmail: (options?: StorageOptions) => Promise<boolean>;
|
||||
setRememberEmail: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
import { Component, NgZone } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { PolicyData } from "jslib-common/models/data/policyData";
|
||||
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
|
||||
import { Policy } from "jslib-common/models/domain/policy";
|
||||
import { ListResponse } from "jslib-common/models/response/listResponse";
|
||||
import { PolicyResponse } from "jslib-common/models/response/policyResponse";
|
||||
|
||||
import { StateService } from "../../abstractions/state.service";
|
||||
import { RouterService } from "../services/router.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-login",
|
||||
templateUrl: "login.component.html",
|
||||
})
|
||||
export class LoginComponent extends BaseLoginComponent {
|
||||
showResetPasswordAutoEnrollWarning = false;
|
||||
enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions;
|
||||
policies: ListResponse<PolicyResponse>;
|
||||
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
private route: ActivatedRoute,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
private apiService: ApiService,
|
||||
private policyService: PolicyService,
|
||||
logService: LogService,
|
||||
ngZone: NgZone,
|
||||
protected stateService: StateService,
|
||||
private messagingService: MessagingService,
|
||||
private routerService: RouterService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
platformUtilsService,
|
||||
i18nService,
|
||||
stateService,
|
||||
environmentService,
|
||||
passwordGenerationService,
|
||||
cryptoFunctionService,
|
||||
logService,
|
||||
ngZone
|
||||
);
|
||||
this.onSuccessfulLogin = async () => {
|
||||
this.messagingService.send("setFullWidth");
|
||||
};
|
||||
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||
if (qParams.email != null && qParams.email.indexOf("@") > -1) {
|
||||
this.email = qParams.email;
|
||||
}
|
||||
if (qParams.premium != null) {
|
||||
this.routerService.setPreviousUrl("/settings/premium");
|
||||
} else if (qParams.org != null) {
|
||||
const route = this.router.createUrlTree(["create-organization"], {
|
||||
queryParams: { plan: qParams.org },
|
||||
});
|
||||
this.routerService.setPreviousUrl(route.toString());
|
||||
}
|
||||
|
||||
// Are they coming from an email for sponsoring a families organization
|
||||
if (qParams.sponsorshipToken != null) {
|
||||
const route = this.router.createUrlTree(["setup/families-for-enterprise"], {
|
||||
queryParams: { token: qParams.sponsorshipToken },
|
||||
});
|
||||
this.routerService.setPreviousUrl(route.toString());
|
||||
}
|
||||
await super.ngOnInit();
|
||||
this.rememberEmail = await this.stateService.getRememberEmail();
|
||||
});
|
||||
|
||||
const invite = await this.stateService.getOrganizationInvitation();
|
||||
if (invite != null) {
|
||||
let policyList: Policy[] = null;
|
||||
try {
|
||||
this.policies = await this.apiService.getPoliciesByToken(
|
||||
invite.organizationId,
|
||||
invite.token,
|
||||
invite.email,
|
||||
invite.organizationUserId
|
||||
);
|
||||
policyList = this.policyService.mapPoliciesFromToken(this.policies);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
|
||||
if (policyList != null) {
|
||||
const resetPasswordPolicy = this.policyService.getResetPasswordPolicyOptions(
|
||||
policyList,
|
||||
invite.organizationId
|
||||
);
|
||||
// Set to true if policy enabled and auto-enroll enabled
|
||||
this.showResetPasswordAutoEnrollWarning =
|
||||
resetPasswordPolicy[1] && resetPasswordPolicy[0].autoEnrollEnabled;
|
||||
|
||||
this.enforcedPasswordPolicyOptions =
|
||||
await this.policyService.getMasterPasswordPolicyOptions(policyList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async goAfterLogIn() {
|
||||
// Check master password against policy
|
||||
if (this.enforcedPasswordPolicyOptions != null) {
|
||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
||||
this.masterPassword,
|
||||
this.getPasswordStrengthUserInput()
|
||||
);
|
||||
const masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
||||
|
||||
// If invalid, save policies and require update
|
||||
if (
|
||||
!this.policyService.evaluateMasterPassword(
|
||||
masterPasswordScore,
|
||||
this.masterPassword,
|
||||
this.enforcedPasswordPolicyOptions
|
||||
)
|
||||
) {
|
||||
const policiesData: { [id: string]: PolicyData } = {};
|
||||
this.policies.data.map((p) => (policiesData[p.id] = new PolicyData(p)));
|
||||
await this.policyService.replace(policiesData);
|
||||
this.router.navigate(["update-password"]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const previousUrl = this.routerService.getPreviousUrl();
|
||||
if (previousUrl) {
|
||||
this.router.navigateByUrl(previousUrl);
|
||||
} else {
|
||||
this.router.navigate([this.successRoute]);
|
||||
}
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await this.stateService.setRememberEmail(this.rememberEmail);
|
||||
if (!this.rememberEmail) {
|
||||
await this.stateService.setRememberedEmail(null);
|
||||
}
|
||||
await super.submit();
|
||||
}
|
||||
|
||||
private getPasswordStrengthUserInput() {
|
||||
let userInput: string[] = [];
|
||||
const atPosition = this.email.indexOf("@");
|
||||
if (atPosition > -1) {
|
||||
userInput = userInput.concat(
|
||||
this.email
|
||||
.substr(0, atPosition)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/)
|
||||
);
|
||||
}
|
||||
return userInput;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off">
|
||||
<div class="row justify-content-md-center mt-5">
|
||||
<div class="col-4">
|
||||
<p class="lead text-center mb-4">{{ "updateMasterPassword" | i18n }}</p>
|
||||
<div class="card d-block">
|
||||
<div class="card-body">
|
||||
<app-callout type="warning">{{ "masterPasswordInvalidWarning" | i18n }} </app-callout>
|
||||
<app-callout
|
||||
type="info"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
*ngIf="enforcedPolicyOptions"
|
||||
></app-callout>
|
||||
|
||||
<form
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
ngNativeValidate
|
||||
autocomplete="off"
|
||||
>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label for="currentMasterPassword">{{ "currentMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="currentMasterPassword"
|
||||
type="password"
|
||||
name="MasterPasswordHash"
|
||||
class="form-control"
|
||||
[(ngModel)]="currentMasterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label for="newMasterPassword">{{ "newMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="newMasterPassword"
|
||||
type="password"
|
||||
name="NewMasterPasswordHash"
|
||||
class="form-control mb-1"
|
||||
[(ngModel)]="masterPassword"
|
||||
(input)="updatePasswordStrength()"
|
||||
required
|
||||
appInputVerbatim
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
<app-password-strength
|
||||
[score]="masterPasswordScore"
|
||||
[showText]="true"
|
||||
></app-password-strength>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label for="masterPasswordRetype">{{ "confirmNewMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPasswordRetype"
|
||||
type="password"
|
||||
name="MasterPasswordRetype"
|
||||
class="form-control"
|
||||
[(ngModel)]="masterPasswordRetype"
|
||||
required
|
||||
appInputVerbatim
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i
|
||||
class="fa fa-spinner fa-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span>{{ "changeMasterPassword" | i18n }}</span>
|
||||
</button>
|
||||
<button (click)="cancel()" type="button" class="btn btn-outline-secondary">
|
||||
<span>{{ "cancel" | i18n }}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "jslib-angular/components/update-password.component";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-update-password",
|
||||
templateUrl: "update-password.component.html",
|
||||
})
|
||||
export class UpdatePasswordComponent extends BaseUpdatePasswordComponent {
|
||||
constructor(
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
policyService: PolicyService,
|
||||
cryptoService: CryptoService,
|
||||
messagingService: MessagingService,
|
||||
apiService: ApiService,
|
||||
logService: LogService,
|
||||
stateService: StateService,
|
||||
userVerificationService: UserVerificationService
|
||||
) {
|
||||
super(
|
||||
router,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
passwordGenerationService,
|
||||
policyService,
|
||||
cryptoService,
|
||||
messagingService,
|
||||
apiService,
|
||||
stateService,
|
||||
userVerificationService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<div *ngIf="loaded && activeOrganization != null" class="tw-flex">
|
||||
<button
|
||||
class="tw-flex tw-items-center tw-bg-background-alt tw-border-none"
|
||||
type="button"
|
||||
id="pickerButton"
|
||||
[appA11yTitle]="'organizationPicker' | i18n"
|
||||
[bitMenuTriggerFor]="orgPickerMenu"
|
||||
>
|
||||
<app-avatar
|
||||
[data]="activeOrganization.name"
|
||||
size="45"
|
||||
[circle]="true"
|
||||
[dynamic]="true"
|
||||
></app-avatar>
|
||||
<div class="tw-flex">
|
||||
<div class="org-name tw-ml-3">
|
||||
<span>{{ activeOrganization.name }}</span>
|
||||
<small class="tw-text-muted">{{ "organization" | i18n }}</small>
|
||||
</div>
|
||||
<div class="tw-ml-3">
|
||||
<i class="bwi bwi-angle-down tw-text-main" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div>
|
||||
<div
|
||||
class="tw-ml-3 tw-border tw-border-solid tw-rounded tw-border-danger-500 tw-text-danger"
|
||||
*ngIf="!activeOrganization.enabled"
|
||||
>
|
||||
<div class="tw-py-2 tw-px-5">
|
||||
<i class="bwi bwi-exclamation-triangle" aria-hidden="true"></i>
|
||||
{{ "organizationIsDisabled" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="tw-ml-3 tw-border tw-border-solid tw-rounded tw-border-info-500 tw-text-info"
|
||||
*ngIf="activeOrganization.isProviderUser"
|
||||
>
|
||||
<div class="tw-py-2 tw-px-5">
|
||||
<i class="bwi bwi-exclamation-triangle" aria-hidden="true"></i>
|
||||
{{ "accessingUsingProvider" | i18n: activeOrganization.providerName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<bit-menu #orgPickerMenu>
|
||||
<ul aria-labelledby="pickerButton" class="tw-p-0 tw-m-0">
|
||||
<li *ngFor="let org of organizations" class="tw-list-none tw-flex tw-flex-col" role="none">
|
||||
<a bitMenuItem [routerLink]="['/organizations', org.id]">
|
||||
<i
|
||||
class="bwi bwi-check mr-2"
|
||||
[ngClass]="org.id === activeOrganization.id ? 'visible' : 'invisible'"
|
||||
>
|
||||
<span class="tw-sr-only">{{ "currentOrganization" | i18n }}</span>
|
||||
</i>
|
||||
{{ org.name }}
|
||||
</a>
|
||||
</li>
|
||||
<bit-menu-divider></bit-menu-divider>
|
||||
<li class="tw-list-none" role="none">
|
||||
<a bitMenuItem routerLink="/create-organization">
|
||||
<i class="bwi bwi-plus mr-2"></i>
|
||||
{{ "newOrganization" | i18n }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</bit-menu>
|
||||
</div>
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
|
||||
import { NavigationPermissionsService } from "../organizations/services/navigation-permissions.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-organization-switcher",
|
||||
templateUrl: "organization-switcher.component.html",
|
||||
})
|
||||
export class OrganizationSwitcherComponent implements OnInit {
|
||||
constructor(private organizationService: OrganizationService, private i18nService: I18nService) {}
|
||||
|
||||
@Input() activeOrganization: Organization = null;
|
||||
organizations: Organization[] = [];
|
||||
|
||||
loaded = false;
|
||||
|
||||
async ngOnInit() {
|
||||
await this.load();
|
||||
}
|
||||
|
||||
async load() {
|
||||
const orgs = await this.organizationService.getAll();
|
||||
this.organizations = orgs
|
||||
.filter((org) => NavigationPermissionsService.canAccessAdmin(org))
|
||||
.sort(Utils.getSortFunction(this.i18nService, "name"));
|
||||
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-premium-badge",
|
||||
template: `
|
||||
<button *appNotPremium bitBadge badgeType="success" (click)="premiumRequired()">
|
||||
{{ "premium" | i18n }}
|
||||
</button>
|
||||
`,
|
||||
})
|
||||
export class PremiumBadgeComponent {
|
||||
constructor(private messagingService: MessagingService) {}
|
||||
|
||||
premiumRequired() {
|
||||
this.messagingService.send("premiumRequired");
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
|
||||
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "jslib-common/enums/authenticationStatus";
|
||||
|
||||
@Injectable()
|
||||
export class HomeGuard implements CanActivate {
|
||||
constructor(private router: Router, private authService: AuthService) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot) {
|
||||
const authStatus = await this.authService.getAuthStatus();
|
||||
|
||||
if (authStatus === AuthenticationStatus.LoggedOut) {
|
||||
return this.router.createUrlTree(["/login"], { queryParams: route.queryParams });
|
||||
}
|
||||
if (authStatus === AuthenticationStatus.Locked) {
|
||||
return this.router.createUrlTree(["/lock"], { queryParams: route.queryParams });
|
||||
}
|
||||
return this.router.createUrlTree(["/vault"], { queryParams: route.queryParams });
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
<nav class="navbar navbar-expand navbar-dark" [ngClass]="{ 'nav-background-alt': selfHosted }">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" routerLink="/" appA11yTitle="{{ 'pageTitle' | i18n: 'Bitwarden' }}">
|
||||
<i class="bwi bwi-shield" aria-hidden="true"></i>
|
||||
</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" routerLink="/vault">{{ "vaults" | i18n }}</a>
|
||||
</li>
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" routerLink="/sends">{{ "send" | i18n }}</a>
|
||||
</li>
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" routerLink="/tools">{{ "tools" | i18n }}</a>
|
||||
</li>
|
||||
<li class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" routerLink="/reports">{{ "reports" | i18n }}</a>
|
||||
</li>
|
||||
<li *ngIf="organizations.length >= 1" class="nav-item" routerLinkActive="active">
|
||||
<a class="nav-link" [routerLink]="['/organizations', organizations[0].id]">{{
|
||||
"organizations" | i18n
|
||||
}}</a>
|
||||
</li>
|
||||
<ng-container *ngIf="providers.length >= 1">
|
||||
<li class="nav-item" routerLinkActive="active" *ngIf="providers.length == 1">
|
||||
<a class="nav-link" [routerLink]="['/providers', providers[0].id]">{{
|
||||
"provider" | i18n
|
||||
}}</a>
|
||||
</li>
|
||||
<li class="nav-item" routerLinkActive="active" *ngIf="providers.length > 1">
|
||||
<a class="nav-link" routerLink="/providers">{{ "provider" | i18n }}</a>
|
||||
</li>
|
||||
</ng-container>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
|
||||
<li>
|
||||
<button
|
||||
[bitMenuTriggerFor]="accountMenu"
|
||||
class="tw-border-0 tw-bg-transparent tw-text-alt2 tw-opacity-70 hover:tw-opacity-90"
|
||||
>
|
||||
<i class="bwi bwi-user-circle bwi-lg" aria-hidden="true"></i>
|
||||
<i class="bwi bwi-caret-down bwi-sm" aria-hidden="true"></i>
|
||||
</button>
|
||||
<bit-menu class="dropdown-menu" #accountMenu>
|
||||
<div class="tw-max-w-[300px] tw-min-w-[200px] tw-flex tw-flex-col">
|
||||
<div
|
||||
class="tw-flex tw-items-center tw-leading-tight tw-text-info tw-py-1 tw-px-4"
|
||||
*ngIf="name"
|
||||
appStopProp
|
||||
>
|
||||
<app-avatar
|
||||
[data]="name"
|
||||
[email]="email"
|
||||
size="25"
|
||||
fontSize="14"
|
||||
[circle]="true"
|
||||
></app-avatar>
|
||||
<div class="tw-ml-2 tw-block tw-overflow-hidden tw-whitespace-nowrap">
|
||||
<span>{{ "loggedInAs" | i18n }}</span>
|
||||
<small class="tw-text-muted tw-block tw-overflow-hidden tw-whitespace-nowrap">{{
|
||||
name
|
||||
}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<bit-menu-divider></bit-menu-divider>
|
||||
<a bitMenuItem routerLink="/settings/account">
|
||||
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
|
||||
{{ "accountSettings" | i18n }}
|
||||
</a>
|
||||
<a bitMenuItem href="https://bitwarden.com/help/" target="_blank" rel="noopener">
|
||||
<i class="bwi bwi-fw bwi-question-circle" aria-hidden="true"></i>
|
||||
{{ "getHelp" | i18n }}
|
||||
</a>
|
||||
<a bitMenuItem href="https://bitwarden.com/download/" target="_blank" rel="noopener">
|
||||
<i class="bwi bwi-fw bwi-download" aria-hidden="true"></i>
|
||||
{{ "getApps" | i18n }}
|
||||
</a>
|
||||
<bit-menu-divider></bit-menu-divider>
|
||||
<button bitMenuItem type="button" (click)="lock()">
|
||||
<i class="bwi bwi-fw bwi-lock" aria-hidden="true"></i>
|
||||
{{ "lockNow" | i18n }}
|
||||
</button>
|
||||
<button bitMenuItem type="button" (click)="logOut()">
|
||||
<i class="bwi bwi-fw bwi-sign-out" aria-hidden="true"></i>
|
||||
{{ "logOut" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</bit-menu>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -1,84 +0,0 @@
|
||||
import { Component, NgZone, OnInit } from "@angular/core";
|
||||
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { TokenService } from "jslib-common/abstractions/token.service";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
import { Provider } from "jslib-common/models/domain/provider";
|
||||
|
||||
import { NavigationPermissionsService as OrgNavigationPermissionsService } from "../organizations/services/navigation-permissions.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-navbar",
|
||||
templateUrl: "navbar.component.html",
|
||||
})
|
||||
export class NavbarComponent implements OnInit {
|
||||
selfHosted = false;
|
||||
name: string;
|
||||
email: string;
|
||||
providers: Provider[] = [];
|
||||
organizations: Organization[] = [];
|
||||
|
||||
constructor(
|
||||
private messagingService: MessagingService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private tokenService: TokenService,
|
||||
private providerService: ProviderService,
|
||||
private syncService: SyncService,
|
||||
private organizationService: OrganizationService,
|
||||
private i18nService: I18nService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone
|
||||
) {
|
||||
this.selfHosted = this.platformUtilsService.isSelfHost();
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.name = await this.tokenService.getName();
|
||||
this.email = await this.tokenService.getEmail();
|
||||
if (this.name == null || this.name.trim() === "") {
|
||||
this.name = this.email;
|
||||
}
|
||||
|
||||
// Ensure providers and organizations are loaded
|
||||
if ((await this.syncService.getLastSync()) == null) {
|
||||
await this.syncService.fullSync(false);
|
||||
}
|
||||
this.providers = await this.providerService.getAll();
|
||||
|
||||
this.organizations = await this.buildOrganizations();
|
||||
|
||||
this.broadcasterService.subscribe(this.constructor.name, async (message: any) => {
|
||||
this.ngZone.run(async () => {
|
||||
switch (message.command) {
|
||||
case "organizationCreated":
|
||||
if (this.organizations.length < 1) {
|
||||
this.organizations = await this.buildOrganizations();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async buildOrganizations() {
|
||||
const allOrgs = await this.organizationService.getAll();
|
||||
return allOrgs
|
||||
.filter((org) => OrgNavigationPermissionsService.canAccessAdmin(org))
|
||||
.sort(Utils.getSortFunction(this.i18nService, "name"));
|
||||
}
|
||||
|
||||
lock() {
|
||||
this.messagingService.send("lockVault");
|
||||
}
|
||||
|
||||
logOut() {
|
||||
this.messagingService.send("logout");
|
||||
}
|
||||
}
|
||||
@@ -1,493 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { UserVerificationComponent } from "jslib-angular/components/user-verification.component";
|
||||
|
||||
import { AcceptEmergencyComponent } from "../accounts/accept-emergency.component";
|
||||
import { AcceptOrganizationComponent } from "../accounts/accept-organization.component";
|
||||
import { HintComponent } from "../accounts/hint.component";
|
||||
import { LockComponent } from "../accounts/lock.component";
|
||||
import { LoginComponent } from "../accounts/login.component";
|
||||
import { RecoverDeleteComponent } from "../accounts/recover-delete.component";
|
||||
import { RecoverTwoFactorComponent } from "../accounts/recover-two-factor.component";
|
||||
import { RegisterComponent } from "../accounts/register.component";
|
||||
import { RemovePasswordComponent } from "../accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "../accounts/set-password.component";
|
||||
import { SsoComponent } from "../accounts/sso.component";
|
||||
import { TwoFactorOptionsComponent } from "../accounts/two-factor-options.component";
|
||||
import { TwoFactorComponent } from "../accounts/two-factor.component";
|
||||
import { UpdatePasswordComponent } from "../accounts/update-password.component";
|
||||
import { UpdateTempPasswordComponent } from "../accounts/update-temp-password.component";
|
||||
import { VerifyEmailTokenComponent } from "../accounts/verify-email-token.component";
|
||||
import { VerifyRecoverDeleteComponent } from "../accounts/verify-recover-delete.component";
|
||||
import { NestedCheckboxComponent } from "../components/nested-checkbox.component";
|
||||
import { OrganizationSwitcherComponent } from "../components/organization-switcher.component";
|
||||
import { PasswordRepromptComponent } from "../components/password-reprompt.component";
|
||||
import { PasswordStrengthComponent } from "../components/password-strength.component";
|
||||
import { PremiumBadgeComponent } from "../components/premium-badge.component";
|
||||
import { FooterComponent } from "../layouts/footer.component";
|
||||
import { FrontendLayoutComponent } from "../layouts/frontend-layout.component";
|
||||
import { NavbarComponent } from "../layouts/navbar.component";
|
||||
import { UserLayoutComponent } from "../layouts/user-layout.component";
|
||||
import { OrganizationLayoutComponent } from "../organizations/layouts/organization-layout.component";
|
||||
import { BulkConfirmComponent as OrgBulkConfirmComponent } from "../organizations/manage/bulk/bulk-confirm.component";
|
||||
import { BulkRemoveComponent as OrgBulkRemoveComponent } from "../organizations/manage/bulk/bulk-remove.component";
|
||||
import { BulkStatusComponent as OrgBulkStatusComponent } from "../organizations/manage/bulk/bulk-status.component";
|
||||
import { CollectionAddEditComponent as OrgCollectionAddEditComponent } from "../organizations/manage/collection-add-edit.component";
|
||||
import { CollectionsComponent as OrgManageCollectionsComponent } from "../organizations/manage/collections.component";
|
||||
import { EntityEventsComponent as OrgEntityEventsComponent } from "../organizations/manage/entity-events.component";
|
||||
import { EventsComponent as OrgEventsComponent } from "../organizations/manage/events.component";
|
||||
import { GroupAddEditComponent as OrgGroupAddEditComponent } from "../organizations/manage/group-add-edit.component";
|
||||
import { GroupsComponent as OrgGroupsComponent } from "../organizations/manage/groups.component";
|
||||
import { ManageComponent as OrgManageComponent } from "../organizations/manage/manage.component";
|
||||
import { PeopleComponent as OrgPeopleComponent } from "../organizations/manage/people.component";
|
||||
import { PoliciesComponent as OrgPoliciesComponent } from "../organizations/manage/policies.component";
|
||||
import { PolicyEditComponent as OrgPolicyEditComponent } from "../organizations/manage/policy-edit.component";
|
||||
import { ResetPasswordComponent as OrgResetPasswordComponent } from "../organizations/manage/reset-password.component";
|
||||
import { UserAddEditComponent as OrgUserAddEditComponent } from "../organizations/manage/user-add-edit.component";
|
||||
import { UserConfirmComponent as OrgUserConfirmComponent } from "../organizations/manage/user-confirm.component";
|
||||
import { UserGroupsComponent as OrgUserGroupsComponent } from "../organizations/manage/user-groups.component";
|
||||
import { DisableSendPolicyComponent } from "../organizations/policies/disable-send.component";
|
||||
import { MasterPasswordPolicyComponent } from "../organizations/policies/master-password.component";
|
||||
import { PasswordGeneratorPolicyComponent } from "../organizations/policies/password-generator.component";
|
||||
import { PersonalOwnershipPolicyComponent } from "../organizations/policies/personal-ownership.component";
|
||||
import { RequireSsoPolicyComponent } from "../organizations/policies/require-sso.component";
|
||||
import { ResetPasswordPolicyComponent } from "../organizations/policies/reset-password.component";
|
||||
import { SendOptionsPolicyComponent } from "../organizations/policies/send-options.component";
|
||||
import { SingleOrgPolicyComponent } from "../organizations/policies/single-org.component";
|
||||
import { TwoFactorAuthenticationPolicyComponent } from "../organizations/policies/two-factor-authentication.component";
|
||||
import { AccountComponent as OrgAccountComponent } from "../organizations/settings/account.component";
|
||||
import { AdjustSubscription } from "../organizations/settings/adjust-subscription.component";
|
||||
import { BillingSyncApiKeyComponent } from "../organizations/settings/billing-sync-api-key.component";
|
||||
import { ChangePlanComponent } from "../organizations/settings/change-plan.component";
|
||||
import { DeleteOrganizationComponent } from "../organizations/settings/delete-organization.component";
|
||||
import { DownloadLicenseComponent } from "../organizations/settings/download-license.component";
|
||||
import { ImageSubscriptionHiddenComponent as OrgSubscriptionHiddenComponent } from "../organizations/settings/image-subscription-hidden.component";
|
||||
import { OrganizationBillingComponent } from "../organizations/settings/organization-billing.component";
|
||||
import { OrganizationSubscriptionComponent } from "../organizations/settings/organization-subscription.component";
|
||||
import { SettingsComponent as OrgSettingComponent } from "../organizations/settings/settings.component";
|
||||
import { TwoFactorSetupComponent as OrgTwoFactorSetupComponent } from "../organizations/settings/two-factor-setup.component";
|
||||
import { AcceptFamilySponsorshipComponent } from "../organizations/sponsorships/accept-family-sponsorship.component";
|
||||
import { FamiliesForEnterpriseSetupComponent } from "../organizations/sponsorships/families-for-enterprise-setup.component";
|
||||
import { ExportComponent as OrgExportComponent } from "../organizations/tools/export.component";
|
||||
import { ExposedPasswordsReportComponent as OrgExposedPasswordsReportComponent } from "../organizations/tools/exposed-passwords-report.component";
|
||||
import { ImportComponent as OrgImportComponent } from "../organizations/tools/import.component";
|
||||
import { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent } from "../organizations/tools/inactive-two-factor-report.component";
|
||||
import { ReusedPasswordsReportComponent as OrgReusedPasswordsReportComponent } from "../organizations/tools/reused-passwords-report.component";
|
||||
import { ToolsComponent as OrgToolsComponent } from "../organizations/tools/tools.component";
|
||||
import { UnsecuredWebsitesReportComponent as OrgUnsecuredWebsitesReportComponent } from "../organizations/tools/unsecured-websites-report.component";
|
||||
import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from "../organizations/tools/weak-passwords-report.component";
|
||||
import { AddEditComponent as OrgAddEditComponent } from "../organizations/vault/add-edit.component";
|
||||
import { AttachmentsComponent as OrgAttachmentsComponent } from "../organizations/vault/attachments.component";
|
||||
import { CiphersComponent as OrgCiphersComponent } from "../organizations/vault/ciphers.component";
|
||||
import { CollectionsComponent as OrgCollectionsComponent } from "../organizations/vault/collections.component";
|
||||
import { ProvidersComponent } from "../providers/providers.component";
|
||||
import { BreachReportComponent } from "../reports/breach-report.component";
|
||||
import { ExposedPasswordsReportComponent } from "../reports/exposed-passwords-report.component";
|
||||
import { InactiveTwoFactorReportComponent } from "../reports/inactive-two-factor-report.component";
|
||||
import { ReportCardComponent } from "../reports/report-card.component";
|
||||
import { ReportListComponent } from "../reports/report-list.component";
|
||||
import { ReportsComponent } from "../reports/reports.component";
|
||||
import { ReusedPasswordsReportComponent } from "../reports/reused-passwords-report.component";
|
||||
import { UnsecuredWebsitesReportComponent } from "../reports/unsecured-websites-report.component";
|
||||
import { WeakPasswordsReportComponent } from "../reports/weak-passwords-report.component";
|
||||
import { AccessComponent } from "../send/access.component";
|
||||
import { AddEditComponent as SendAddEditComponent } from "../send/add-edit.component";
|
||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from "../send/efflux-dates.component";
|
||||
import { SendComponent } from "../send/send.component";
|
||||
import { AccountComponent } from "../settings/account.component";
|
||||
import { AddCreditComponent } from "../settings/add-credit.component";
|
||||
import { AdjustPaymentComponent } from "../settings/adjust-payment.component";
|
||||
import { AdjustStorageComponent } from "../settings/adjust-storage.component";
|
||||
import { ApiKeyComponent } from "../settings/api-key.component";
|
||||
import { BillingSyncKeyComponent } from "../settings/billing-sync-key.component";
|
||||
import { ChangeEmailComponent } from "../settings/change-email.component";
|
||||
import { ChangeKdfComponent } from "../settings/change-kdf.component";
|
||||
import { ChangePasswordComponent } from "../settings/change-password.component";
|
||||
import { CreateOrganizationComponent } from "../settings/create-organization.component";
|
||||
import { DeauthorizeSessionsComponent } from "../settings/deauthorize-sessions.component";
|
||||
import { DeleteAccountComponent } from "../settings/delete-account.component";
|
||||
import { DomainRulesComponent } from "../settings/domain-rules.component";
|
||||
import { EmergencyAccessAddEditComponent } from "../settings/emergency-access-add-edit.component";
|
||||
import { EmergencyAccessAttachmentsComponent } from "../settings/emergency-access-attachments.component";
|
||||
import { EmergencyAccessConfirmComponent } from "../settings/emergency-access-confirm.component";
|
||||
import { EmergencyAccessTakeoverComponent } from "../settings/emergency-access-takeover.component";
|
||||
import { EmergencyAccessViewComponent } from "../settings/emergency-access-view.component";
|
||||
import { EmergencyAccessComponent } from "../settings/emergency-access.component";
|
||||
import { EmergencyAddEditComponent } from "../settings/emergency-add-edit.component";
|
||||
import { OrganizationPlansComponent } from "../settings/organization-plans.component";
|
||||
import { PaymentMethodComponent } from "../settings/payment-method.component";
|
||||
import { PaymentComponent } from "../settings/payment.component";
|
||||
import { PreferencesComponent } from "../settings/preferences.component";
|
||||
import { PremiumComponent } from "../settings/premium.component";
|
||||
import { ProfileComponent } from "../settings/profile.component";
|
||||
import { PurgeVaultComponent } from "../settings/purge-vault.component";
|
||||
import { SecurityKeysComponent } from "../settings/security-keys.component";
|
||||
import { SecurityComponent } from "../settings/security.component";
|
||||
import { SettingsComponent } from "../settings/settings.component";
|
||||
import { SponsoredFamiliesComponent } from "../settings/sponsored-families.component";
|
||||
import { SponsoringOrgRowComponent } from "../settings/sponsoring-org-row.component";
|
||||
import { SubscriptionComponent } from "../settings/subscription.component";
|
||||
import { TaxInfoComponent } from "../settings/tax-info.component";
|
||||
import { TwoFactorAuthenticatorComponent } from "../settings/two-factor-authenticator.component";
|
||||
import { TwoFactorDuoComponent } from "../settings/two-factor-duo.component";
|
||||
import { TwoFactorEmailComponent } from "../settings/two-factor-email.component";
|
||||
import { TwoFactorRecoveryComponent } from "../settings/two-factor-recovery.component";
|
||||
import { TwoFactorSetupComponent } from "../settings/two-factor-setup.component";
|
||||
import { TwoFactorVerifyComponent } from "../settings/two-factor-verify.component";
|
||||
import { TwoFactorWebAuthnComponent } from "../settings/two-factor-webauthn.component";
|
||||
import { TwoFactorYubiKeyComponent } from "../settings/two-factor-yubikey.component";
|
||||
import { UpdateKeyComponent } from "../settings/update-key.component";
|
||||
import { UpdateLicenseComponent } from "../settings/update-license.component";
|
||||
import { UserBillingHistoryComponent } from "../settings/user-billing-history.component";
|
||||
import { UserSubscriptionComponent } from "../settings/user-subscription.component";
|
||||
import { VaultTimeoutInputComponent } from "../settings/vault-timeout-input.component";
|
||||
import { VerifyEmailComponent } from "../settings/verify-email.component";
|
||||
import { ExportComponent } from "../tools/export.component";
|
||||
import { GeneratorComponent } from "../tools/generator.component";
|
||||
import { ImportComponent } from "../tools/import.component";
|
||||
import { PasswordGeneratorHistoryComponent } from "../tools/password-generator-history.component";
|
||||
import { ToolsComponent } from "../tools/tools.component";
|
||||
import { AddEditCustomFieldsComponent } from "../vault/add-edit-custom-fields.component";
|
||||
import { AddEditComponent } from "../vault/add-edit.component";
|
||||
import { AttachmentsComponent } from "../vault/attachments.component";
|
||||
import { BulkActionsComponent } from "../vault/bulk-actions.component";
|
||||
import { BulkDeleteComponent } from "../vault/bulk-delete.component";
|
||||
import { BulkMoveComponent } from "../vault/bulk-move.component";
|
||||
import { BulkRestoreComponent } from "../vault/bulk-restore.component";
|
||||
import { BulkShareComponent } from "../vault/bulk-share.component";
|
||||
import { CiphersComponent } from "../vault/ciphers.component";
|
||||
import { CollectionsComponent } from "../vault/collections.component";
|
||||
import { FolderAddEditComponent } from "../vault/folder-add-edit.component";
|
||||
import { ShareComponent } from "../vault/share.component";
|
||||
|
||||
import { PipesModule } from "./pipes/pipes.module";
|
||||
import { SharedModule } from "./shared.module";
|
||||
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
|
||||
import { OrganizationBadgeModule } from "./vault/modules/organization-badge/organization-badge.module";
|
||||
|
||||
// Please do not add to this list of declarations - we should refactor these into modules when doing so makes sense until there are none left.
|
||||
// If you are building new functionality, please create or extend a feature module instead.
|
||||
@NgModule({
|
||||
imports: [SharedModule, VaultFilterModule, OrganizationBadgeModule, PipesModule],
|
||||
declarations: [
|
||||
PremiumBadgeComponent,
|
||||
AcceptEmergencyComponent,
|
||||
AcceptFamilySponsorshipComponent,
|
||||
AcceptOrganizationComponent,
|
||||
AccessComponent,
|
||||
AccountComponent,
|
||||
AddCreditComponent,
|
||||
AddEditComponent,
|
||||
AddEditCustomFieldsComponent,
|
||||
AddEditCustomFieldsComponent,
|
||||
AdjustPaymentComponent,
|
||||
AdjustStorageComponent,
|
||||
AdjustSubscription,
|
||||
ApiKeyComponent,
|
||||
AttachmentsComponent,
|
||||
BillingSyncApiKeyComponent,
|
||||
BillingSyncKeyComponent,
|
||||
BreachReportComponent,
|
||||
BulkActionsComponent,
|
||||
BulkDeleteComponent,
|
||||
BulkMoveComponent,
|
||||
BulkRestoreComponent,
|
||||
BulkShareComponent,
|
||||
ChangeEmailComponent,
|
||||
ChangeKdfComponent,
|
||||
ChangePasswordComponent,
|
||||
ChangePlanComponent,
|
||||
CiphersComponent,
|
||||
CollectionsComponent,
|
||||
CreateOrganizationComponent,
|
||||
DeauthorizeSessionsComponent,
|
||||
DeleteAccountComponent,
|
||||
DeleteOrganizationComponent,
|
||||
DisableSendPolicyComponent,
|
||||
DomainRulesComponent,
|
||||
DownloadLicenseComponent,
|
||||
EmergencyAccessAddEditComponent,
|
||||
EmergencyAccessAttachmentsComponent,
|
||||
EmergencyAccessComponent,
|
||||
EmergencyAccessConfirmComponent,
|
||||
EmergencyAccessTakeoverComponent,
|
||||
EmergencyAccessViewComponent,
|
||||
EmergencyAddEditComponent,
|
||||
ExportComponent,
|
||||
ExposedPasswordsReportComponent,
|
||||
FamiliesForEnterpriseSetupComponent,
|
||||
FolderAddEditComponent,
|
||||
FooterComponent,
|
||||
FrontendLayoutComponent,
|
||||
HintComponent,
|
||||
ImportComponent,
|
||||
InactiveTwoFactorReportComponent,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
MasterPasswordPolicyComponent,
|
||||
NavbarComponent,
|
||||
NestedCheckboxComponent,
|
||||
OrganizationSwitcherComponent,
|
||||
OrgAccountComponent,
|
||||
OrgAddEditComponent,
|
||||
OrganizationBillingComponent,
|
||||
OrganizationLayoutComponent,
|
||||
OrganizationPlansComponent,
|
||||
OrganizationSubscriptionComponent,
|
||||
OrgAttachmentsComponent,
|
||||
OrgBulkConfirmComponent,
|
||||
OrgBulkRemoveComponent,
|
||||
OrgBulkStatusComponent,
|
||||
OrgCiphersComponent,
|
||||
OrgCollectionAddEditComponent,
|
||||
OrgCollectionsComponent,
|
||||
OrgEntityEventsComponent,
|
||||
OrgEventsComponent,
|
||||
OrgExportComponent,
|
||||
OrgExposedPasswordsReportComponent,
|
||||
OrgGroupAddEditComponent,
|
||||
OrgGroupsComponent,
|
||||
OrgImportComponent,
|
||||
OrgInactiveTwoFactorReportComponent,
|
||||
OrgManageCollectionsComponent,
|
||||
OrgManageComponent,
|
||||
OrgPeopleComponent,
|
||||
OrgPoliciesComponent,
|
||||
OrgPolicyEditComponent,
|
||||
OrgResetPasswordComponent,
|
||||
OrgReusedPasswordsReportComponent,
|
||||
OrgSettingComponent,
|
||||
OrgToolsComponent,
|
||||
OrgTwoFactorSetupComponent,
|
||||
OrgSubscriptionHiddenComponent,
|
||||
OrgUnsecuredWebsitesReportComponent,
|
||||
OrgUserAddEditComponent,
|
||||
OrgUserConfirmComponent,
|
||||
OrgUserGroupsComponent,
|
||||
OrgWeakPasswordsReportComponent,
|
||||
GeneratorComponent,
|
||||
PasswordGeneratorHistoryComponent,
|
||||
PasswordGeneratorPolicyComponent,
|
||||
PasswordRepromptComponent,
|
||||
PasswordStrengthComponent,
|
||||
PaymentComponent,
|
||||
PaymentMethodComponent,
|
||||
PersonalOwnershipPolicyComponent,
|
||||
PreferencesComponent,
|
||||
PremiumBadgeComponent,
|
||||
PremiumComponent,
|
||||
ProfileComponent,
|
||||
ProvidersComponent,
|
||||
PurgeVaultComponent,
|
||||
RecoverDeleteComponent,
|
||||
RecoverTwoFactorComponent,
|
||||
RegisterComponent,
|
||||
RemovePasswordComponent,
|
||||
ReportCardComponent,
|
||||
ReportListComponent,
|
||||
ReportsComponent,
|
||||
RequireSsoPolicyComponent,
|
||||
ResetPasswordPolicyComponent,
|
||||
ReusedPasswordsReportComponent,
|
||||
SecurityComponent,
|
||||
SecurityKeysComponent,
|
||||
SendAddEditComponent,
|
||||
SendComponent,
|
||||
SendEffluxDatesComponent,
|
||||
SendOptionsPolicyComponent,
|
||||
SetPasswordComponent,
|
||||
SettingsComponent,
|
||||
ShareComponent,
|
||||
SingleOrgPolicyComponent,
|
||||
SponsoredFamiliesComponent,
|
||||
SponsoringOrgRowComponent,
|
||||
SsoComponent,
|
||||
SubscriptionComponent,
|
||||
TaxInfoComponent,
|
||||
ToolsComponent,
|
||||
TwoFactorAuthenticationPolicyComponent,
|
||||
TwoFactorAuthenticatorComponent,
|
||||
TwoFactorComponent,
|
||||
TwoFactorDuoComponent,
|
||||
TwoFactorEmailComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
TwoFactorRecoveryComponent,
|
||||
TwoFactorSetupComponent,
|
||||
TwoFactorVerifyComponent,
|
||||
TwoFactorWebAuthnComponent,
|
||||
TwoFactorYubiKeyComponent,
|
||||
UnsecuredWebsitesReportComponent,
|
||||
UpdateKeyComponent,
|
||||
UpdateLicenseComponent,
|
||||
UpdatePasswordComponent,
|
||||
UpdateTempPasswordComponent,
|
||||
UserBillingHistoryComponent,
|
||||
UserLayoutComponent,
|
||||
UserSubscriptionComponent,
|
||||
UserVerificationComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
VerifyEmailComponent,
|
||||
VerifyEmailTokenComponent,
|
||||
VerifyRecoverDeleteComponent,
|
||||
WeakPasswordsReportComponent,
|
||||
],
|
||||
exports: [
|
||||
PremiumBadgeComponent,
|
||||
AcceptEmergencyComponent,
|
||||
AcceptOrganizationComponent,
|
||||
AccessComponent,
|
||||
AccountComponent,
|
||||
AddCreditComponent,
|
||||
AddEditComponent,
|
||||
AddEditCustomFieldsComponent,
|
||||
AddEditCustomFieldsComponent,
|
||||
AdjustPaymentComponent,
|
||||
AdjustStorageComponent,
|
||||
AdjustSubscription,
|
||||
ApiKeyComponent,
|
||||
AttachmentsComponent,
|
||||
BreachReportComponent,
|
||||
BulkActionsComponent,
|
||||
BulkDeleteComponent,
|
||||
BulkMoveComponent,
|
||||
BulkRestoreComponent,
|
||||
BulkShareComponent,
|
||||
ChangeEmailComponent,
|
||||
ChangeKdfComponent,
|
||||
ChangePasswordComponent,
|
||||
ChangePlanComponent,
|
||||
CiphersComponent,
|
||||
CollectionsComponent,
|
||||
CreateOrganizationComponent,
|
||||
DeauthorizeSessionsComponent,
|
||||
DeleteAccountComponent,
|
||||
DeleteOrganizationComponent,
|
||||
DisableSendPolicyComponent,
|
||||
DomainRulesComponent,
|
||||
DownloadLicenseComponent,
|
||||
EmergencyAccessAddEditComponent,
|
||||
EmergencyAccessAttachmentsComponent,
|
||||
EmergencyAccessComponent,
|
||||
EmergencyAccessConfirmComponent,
|
||||
EmergencyAccessTakeoverComponent,
|
||||
EmergencyAccessViewComponent,
|
||||
EmergencyAddEditComponent,
|
||||
ExportComponent,
|
||||
ExposedPasswordsReportComponent,
|
||||
FamiliesForEnterpriseSetupComponent,
|
||||
FolderAddEditComponent,
|
||||
FooterComponent,
|
||||
FrontendLayoutComponent,
|
||||
HintComponent,
|
||||
ImportComponent,
|
||||
InactiveTwoFactorReportComponent,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
MasterPasswordPolicyComponent,
|
||||
NavbarComponent,
|
||||
NestedCheckboxComponent,
|
||||
OrganizationSwitcherComponent,
|
||||
OrgAccountComponent,
|
||||
OrgAddEditComponent,
|
||||
OrganizationBillingComponent,
|
||||
OrganizationLayoutComponent,
|
||||
OrganizationPlansComponent,
|
||||
OrganizationSubscriptionComponent,
|
||||
OrgAttachmentsComponent,
|
||||
OrgBulkConfirmComponent,
|
||||
OrgBulkRemoveComponent,
|
||||
OrgBulkStatusComponent,
|
||||
OrgCiphersComponent,
|
||||
OrgCollectionAddEditComponent,
|
||||
OrgCollectionsComponent,
|
||||
OrgEntityEventsComponent,
|
||||
OrgEventsComponent,
|
||||
OrgExportComponent,
|
||||
OrgExposedPasswordsReportComponent,
|
||||
OrgGroupAddEditComponent,
|
||||
OrgGroupsComponent,
|
||||
OrgImportComponent,
|
||||
OrgInactiveTwoFactorReportComponent,
|
||||
OrgManageCollectionsComponent,
|
||||
OrgManageComponent,
|
||||
OrgPeopleComponent,
|
||||
OrgPoliciesComponent,
|
||||
OrgPolicyEditComponent,
|
||||
OrgResetPasswordComponent,
|
||||
OrgReusedPasswordsReportComponent,
|
||||
OrgSettingComponent,
|
||||
OrgToolsComponent,
|
||||
OrgTwoFactorSetupComponent,
|
||||
OrgUnsecuredWebsitesReportComponent,
|
||||
OrgUserAddEditComponent,
|
||||
OrgUserConfirmComponent,
|
||||
OrgUserGroupsComponent,
|
||||
OrgWeakPasswordsReportComponent,
|
||||
GeneratorComponent,
|
||||
PasswordGeneratorHistoryComponent,
|
||||
PasswordGeneratorPolicyComponent,
|
||||
PasswordRepromptComponent,
|
||||
PasswordStrengthComponent,
|
||||
PaymentComponent,
|
||||
PaymentMethodComponent,
|
||||
PersonalOwnershipPolicyComponent,
|
||||
PreferencesComponent,
|
||||
PremiumBadgeComponent,
|
||||
PremiumComponent,
|
||||
ProfileComponent,
|
||||
ProvidersComponent,
|
||||
PurgeVaultComponent,
|
||||
RecoverDeleteComponent,
|
||||
RecoverTwoFactorComponent,
|
||||
RegisterComponent,
|
||||
RemovePasswordComponent,
|
||||
ReportCardComponent,
|
||||
ReportListComponent,
|
||||
ReportsComponent,
|
||||
RequireSsoPolicyComponent,
|
||||
ResetPasswordPolicyComponent,
|
||||
ReusedPasswordsReportComponent,
|
||||
SecurityComponent,
|
||||
SecurityKeysComponent,
|
||||
SendAddEditComponent,
|
||||
SendComponent,
|
||||
SendEffluxDatesComponent,
|
||||
SendOptionsPolicyComponent,
|
||||
SetPasswordComponent,
|
||||
SettingsComponent,
|
||||
ShareComponent,
|
||||
SingleOrgPolicyComponent,
|
||||
SponsoredFamiliesComponent,
|
||||
SponsoringOrgRowComponent,
|
||||
SsoComponent,
|
||||
SubscriptionComponent,
|
||||
TaxInfoComponent,
|
||||
ToolsComponent,
|
||||
TwoFactorAuthenticationPolicyComponent,
|
||||
TwoFactorAuthenticatorComponent,
|
||||
TwoFactorComponent,
|
||||
TwoFactorDuoComponent,
|
||||
TwoFactorEmailComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
TwoFactorRecoveryComponent,
|
||||
TwoFactorSetupComponent,
|
||||
TwoFactorVerifyComponent,
|
||||
TwoFactorWebAuthnComponent,
|
||||
TwoFactorYubiKeyComponent,
|
||||
UnsecuredWebsitesReportComponent,
|
||||
UpdateKeyComponent,
|
||||
UpdateLicenseComponent,
|
||||
UpdatePasswordComponent,
|
||||
UpdateTempPasswordComponent,
|
||||
UserBillingHistoryComponent,
|
||||
UserLayoutComponent,
|
||||
UserSubscriptionComponent,
|
||||
UserVerificationComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
VerifyEmailComponent,
|
||||
VerifyEmailTokenComponent,
|
||||
VerifyRecoverDeleteComponent,
|
||||
WeakPasswordsReportComponent,
|
||||
],
|
||||
})
|
||||
export class LooseComponentsModule {}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { ScrollingModule } from "@angular/cdk/scrolling";
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { SharedModule } from "../../shared.module";
|
||||
|
||||
import { EntityUsersComponent } from "./entity-users.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule, ScrollingModule],
|
||||
declarations: [EntityUsersComponent],
|
||||
exports: [EntityUsersComponent],
|
||||
})
|
||||
export class OrganizationManageModule {}
|
||||
@@ -1,59 +0,0 @@
|
||||
<div
|
||||
class="modal fade"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="enrollMasterPasswordResetTitle"
|
||||
>
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form
|
||||
class="modal-content"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
ngNativeValidate
|
||||
>
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="enrollMasterPasswordResetTitle">
|
||||
{{ (isEnrolled ? "withdrawPasswordReset" : "enrollPasswordReset") | i18n }}
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
appA11yTitle="{{ 'close' | i18n }}"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<app-callout type="warning" *ngIf="!isEnrolled">
|
||||
{{ "resetPasswordEnrollmentWarning" | i18n }}
|
||||
</app-callout>
|
||||
<app-user-verification [(ngModel)]="verification" name="secret"> </app-user-verification>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button bitButton buttonType="primary" type="submit" [disabled]="form.loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
*ngIf="form.loading"
|
||||
></i>
|
||||
<span>
|
||||
{{ "submit" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
type="button"
|
||||
data-dismiss="modal"
|
||||
appA11yTitle="{{ 'close' | i18n }}"
|
||||
>
|
||||
<span>
|
||||
{{ "cancel" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,97 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ModalRef } from "jslib-angular/components/modal/modal.ref";
|
||||
import { ModalConfig } from "jslib-angular/services/modal.service";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
import { OrganizationUserResetPasswordEnrollmentRequest } from "jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest";
|
||||
import { Verification } from "jslib-common/types/verification";
|
||||
|
||||
@Component({
|
||||
selector: "app-enroll-master-password-reset",
|
||||
templateUrl: "enroll-master-password-reset.component.html",
|
||||
})
|
||||
export class EnrollMasterPasswordReset {
|
||||
organization: Organization;
|
||||
|
||||
verification: Verification;
|
||||
formPromise: Promise<any>;
|
||||
|
||||
constructor(
|
||||
private userVerificationService: UserVerificationService,
|
||||
private apiService: ApiService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
private cryptoService: CryptoService,
|
||||
private syncService: SyncService,
|
||||
private logService: LogService,
|
||||
private modalRef: ModalRef,
|
||||
config: ModalConfig
|
||||
) {
|
||||
this.organization = config.data.organization;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
let toastStringRef = "withdrawPasswordResetSuccess";
|
||||
|
||||
this.formPromise = this.userVerificationService
|
||||
.buildRequest(this.verification, OrganizationUserResetPasswordEnrollmentRequest)
|
||||
.then(async (request) => {
|
||||
// Set variables
|
||||
let keyString: string = null;
|
||||
|
||||
// Enrolling
|
||||
if (!this.organization.resetPasswordEnrolled) {
|
||||
// Retrieve Public Key
|
||||
const orgKeys = await this.apiService.getOrganizationKeys(this.organization.id);
|
||||
if (orgKeys == null) {
|
||||
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
||||
}
|
||||
|
||||
const publicKey = Utils.fromB64ToArray(orgKeys.publicKey);
|
||||
|
||||
// RSA Encrypt user's encKey.key with organization public key
|
||||
const encKey = await this.cryptoService.getEncKey();
|
||||
const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer);
|
||||
keyString = encryptedKey.encryptedString;
|
||||
toastStringRef = "enrollPasswordResetSuccess";
|
||||
|
||||
// Create request and execute enrollment
|
||||
request.resetPasswordKey = keyString;
|
||||
await this.apiService.putOrganizationUserResetPasswordEnrollment(
|
||||
this.organization.id,
|
||||
this.organization.userId,
|
||||
request
|
||||
);
|
||||
} else {
|
||||
// Withdrawal
|
||||
request.resetPasswordKey = keyString;
|
||||
await this.apiService.putOrganizationUserResetPasswordEnrollment(
|
||||
this.organization.id,
|
||||
this.organization.userId,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
await this.syncService.fullSync(true);
|
||||
});
|
||||
try {
|
||||
await this.formPromise;
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t(toastStringRef));
|
||||
this.modalRef.close();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
get isEnrolled(): boolean {
|
||||
return this.organization.resetPasswordEnrolled;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ScrollingModule } from "@angular/cdk/scrolling";
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { LooseComponentsModule } from "../../loose-components.module";
|
||||
import { SharedModule } from "../../shared.module";
|
||||
|
||||
import { EnrollMasterPasswordReset } from "./enroll-master-password-reset.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule, ScrollingModule, LooseComponentsModule],
|
||||
declarations: [EnrollMasterPasswordReset],
|
||||
exports: [EnrollMasterPasswordReset],
|
||||
})
|
||||
export class OrganizationUserModule {}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { Pipe, PipeTransform } from "@angular/core";
|
||||
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
|
||||
@Pipe({
|
||||
name: "orgNameFromId",
|
||||
pure: true,
|
||||
})
|
||||
export class GetOrgNameFromIdPipe implements PipeTransform {
|
||||
transform(value: string, organizations: Organization[]) {
|
||||
const orgName = organizations.find((o) => o.id === value)?.name;
|
||||
return orgName;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { GetOrgNameFromIdPipe } from "./get-organization-name.pipe";
|
||||
|
||||
@NgModule({
|
||||
imports: [],
|
||||
declarations: [GetOrgNameFromIdPipe],
|
||||
exports: [GetOrgNameFromIdPipe],
|
||||
})
|
||||
export class PipesModule {}
|
||||
@@ -1,149 +0,0 @@
|
||||
import { DragDropModule } from "@angular/cdk/drag-drop";
|
||||
import { DatePipe, registerLocaleData, CommonModule } from "@angular/common";
|
||||
import localeAf from "@angular/common/locales/af";
|
||||
import localeAz from "@angular/common/locales/az";
|
||||
import localeBe from "@angular/common/locales/be";
|
||||
import localeBg from "@angular/common/locales/bg";
|
||||
import localeBn from "@angular/common/locales/bn";
|
||||
import localeBs from "@angular/common/locales/bs";
|
||||
import localeCa from "@angular/common/locales/ca";
|
||||
import localeCs from "@angular/common/locales/cs";
|
||||
import localeDa from "@angular/common/locales/da";
|
||||
import localeDe from "@angular/common/locales/de";
|
||||
import localeEl from "@angular/common/locales/el";
|
||||
import localeEnGb from "@angular/common/locales/en-GB";
|
||||
import localeEnIn from "@angular/common/locales/en-IN";
|
||||
import localeEo from "@angular/common/locales/eo";
|
||||
import localeEs from "@angular/common/locales/es";
|
||||
import localeEt from "@angular/common/locales/et";
|
||||
import localeFi from "@angular/common/locales/fi";
|
||||
import localeFil from "@angular/common/locales/fil";
|
||||
import localeFr from "@angular/common/locales/fr";
|
||||
import localeHe from "@angular/common/locales/he";
|
||||
import localeHi from "@angular/common/locales/hi";
|
||||
import localeHr from "@angular/common/locales/hr";
|
||||
import localeHu from "@angular/common/locales/hu";
|
||||
import localeId from "@angular/common/locales/id";
|
||||
import localeIt from "@angular/common/locales/it";
|
||||
import localeJa from "@angular/common/locales/ja";
|
||||
import localeKa from "@angular/common/locales/ka";
|
||||
import localeKm from "@angular/common/locales/km";
|
||||
import localeKn from "@angular/common/locales/kn";
|
||||
import localeKo from "@angular/common/locales/ko";
|
||||
import localeLv from "@angular/common/locales/lv";
|
||||
import localeMl from "@angular/common/locales/ml";
|
||||
import localeNb from "@angular/common/locales/nb";
|
||||
import localeNl from "@angular/common/locales/nl";
|
||||
import localeNn from "@angular/common/locales/nn";
|
||||
import localePl from "@angular/common/locales/pl";
|
||||
import localePtBr from "@angular/common/locales/pt";
|
||||
import localePtPt from "@angular/common/locales/pt-PT";
|
||||
import localeRo from "@angular/common/locales/ro";
|
||||
import localeRu from "@angular/common/locales/ru";
|
||||
import localeSi from "@angular/common/locales/si";
|
||||
import localeSk from "@angular/common/locales/sk";
|
||||
import localeSl from "@angular/common/locales/sl";
|
||||
import localeSr from "@angular/common/locales/sr";
|
||||
import localeSv from "@angular/common/locales/sv";
|
||||
import localeTr from "@angular/common/locales/tr";
|
||||
import localeUk from "@angular/common/locales/uk";
|
||||
import localeVi from "@angular/common/locales/vi";
|
||||
import localeZhCn from "@angular/common/locales/zh-Hans";
|
||||
import localeZhTw from "@angular/common/locales/zh-Hant";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { BadgeModule, ButtonModule, CalloutModule, MenuModule } from "@bitwarden/components";
|
||||
import { InfiniteScrollModule } from "ngx-infinite-scroll";
|
||||
import { ToastrModule } from "ngx-toastr";
|
||||
|
||||
import { JslibModule } from "jslib-angular/jslib.module";
|
||||
|
||||
registerLocaleData(localeAf, "af");
|
||||
registerLocaleData(localeAz, "az");
|
||||
registerLocaleData(localeBe, "be");
|
||||
registerLocaleData(localeBg, "bg");
|
||||
registerLocaleData(localeBn, "bn");
|
||||
registerLocaleData(localeBs, "bs");
|
||||
registerLocaleData(localeCa, "ca");
|
||||
registerLocaleData(localeCs, "cs");
|
||||
registerLocaleData(localeDa, "da");
|
||||
registerLocaleData(localeDe, "de");
|
||||
registerLocaleData(localeEl, "el");
|
||||
registerLocaleData(localeEnGb, "en-GB");
|
||||
registerLocaleData(localeEnIn, "en-IN");
|
||||
registerLocaleData(localeEo, "eo");
|
||||
registerLocaleData(localeEs, "es");
|
||||
registerLocaleData(localeEt, "et");
|
||||
registerLocaleData(localeFi, "fi");
|
||||
registerLocaleData(localeFil, "fil");
|
||||
registerLocaleData(localeFr, "fr");
|
||||
registerLocaleData(localeHe, "he");
|
||||
registerLocaleData(localeHi, "hi");
|
||||
registerLocaleData(localeHr, "hr");
|
||||
registerLocaleData(localeHu, "hu");
|
||||
registerLocaleData(localeId, "id");
|
||||
registerLocaleData(localeIt, "it");
|
||||
registerLocaleData(localeJa, "ja");
|
||||
registerLocaleData(localeKa, "ka");
|
||||
registerLocaleData(localeKm, "km");
|
||||
registerLocaleData(localeKn, "kn");
|
||||
registerLocaleData(localeKo, "ko");
|
||||
registerLocaleData(localeLv, "lv");
|
||||
registerLocaleData(localeMl, "ml");
|
||||
registerLocaleData(localeNb, "nb");
|
||||
registerLocaleData(localeNl, "nl");
|
||||
registerLocaleData(localeNn, "nn");
|
||||
registerLocaleData(localePl, "pl");
|
||||
registerLocaleData(localePtBr, "pt-BR");
|
||||
registerLocaleData(localePtPt, "pt-PT");
|
||||
registerLocaleData(localeRo, "ro");
|
||||
registerLocaleData(localeRu, "ru");
|
||||
registerLocaleData(localeSi, "si");
|
||||
registerLocaleData(localeSk, "sk");
|
||||
registerLocaleData(localeSl, "sl");
|
||||
registerLocaleData(localeSr, "sr");
|
||||
registerLocaleData(localeSv, "sv");
|
||||
registerLocaleData(localeTr, "tr");
|
||||
registerLocaleData(localeUk, "uk");
|
||||
registerLocaleData(localeVi, "vi");
|
||||
registerLocaleData(localeZhCn, "zh-CN");
|
||||
registerLocaleData(localeZhTw, "zh-TW");
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
DragDropModule,
|
||||
FormsModule,
|
||||
InfiniteScrollModule,
|
||||
JslibModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule,
|
||||
BadgeModule,
|
||||
ButtonModule,
|
||||
CalloutModule,
|
||||
ToastrModule,
|
||||
BadgeModule,
|
||||
ButtonModule,
|
||||
MenuModule,
|
||||
],
|
||||
exports: [
|
||||
CommonModule,
|
||||
DragDropModule,
|
||||
FormsModule,
|
||||
InfiniteScrollModule,
|
||||
JslibModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule,
|
||||
BadgeModule,
|
||||
ButtonModule,
|
||||
CalloutModule,
|
||||
ToastrModule,
|
||||
BadgeModule,
|
||||
ButtonModule,
|
||||
MenuModule,
|
||||
],
|
||||
providers: [DatePipe],
|
||||
bootstrap: [],
|
||||
})
|
||||
export class SharedModule {}
|
||||
@@ -1,74 +0,0 @@
|
||||
<ng-container *ngIf="show">
|
||||
<div class="filter-heading">
|
||||
<button
|
||||
(click)="toggleCollapse(collectionsGrouping)"
|
||||
[attr.aria-expanded]="!isCollapsed(collectionsGrouping)"
|
||||
aria-controls="collection-filters"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
class="toggle-button"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw"
|
||||
[ngClass]="{
|
||||
'bwi-angle-right': isCollapsed(collectionsGrouping),
|
||||
'bwi-angle-down': !isCollapsed(collectionsGrouping)
|
||||
}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<h3 class="filter-title"> {{ collectionsGrouping.name | i18n }}</h3>
|
||||
</div>
|
||||
<ul id="collection-filters" *ngIf="!isCollapsed(collectionsGrouping)" class="filter-options">
|
||||
<ng-template #recursiveCollections let-collections>
|
||||
<li
|
||||
*ngFor="let c of collections"
|
||||
[ngClass]="{
|
||||
active: c.node.id === activeFilter.selectedCollectionId
|
||||
}"
|
||||
class="filter-option"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button
|
||||
class="toggle-button"
|
||||
*ngIf="c.children.length"
|
||||
(click)="toggleCollapse(c.node)"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
[attr.aria-expanded]="!isCollapsed(c.node)"
|
||||
[attr.aria-controls]="c.node.name + '_children'"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw"
|
||||
[ngClass]="{
|
||||
'bwi-angle-right': isCollapsed(c.node),
|
||||
'bwi-angle-down': !isCollapsed(c.node)
|
||||
}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<button class="filter-button" (click)="applyFilter(c.node)">
|
||||
<i
|
||||
*ngIf="c.children.length === 0"
|
||||
class="bwi bwi-collection bwi-fw"
|
||||
aria-hidden="true"
|
||||
></i
|
||||
> {{ c.node.name }}
|
||||
</button>
|
||||
</span>
|
||||
<ul
|
||||
[id]="c.node.name + '_children'"
|
||||
class="nested-filter-options"
|
||||
*ngIf="c.children.length && !isCollapsed(c.node)"
|
||||
>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveCollections; context: { $implicit: c.children }"
|
||||
>
|
||||
</ng-container>
|
||||
</ul>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveCollections; context: { $implicit: nestedCollections }"
|
||||
>
|
||||
</ng-container>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { CollectionFilterComponent as BaseCollectionFilterComponent } from "jslib-angular/modules/vault-filter/components/collection-filter.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-collection-filter",
|
||||
templateUrl: "collection-filter.component.html",
|
||||
})
|
||||
export class CollectionFilterComponent extends BaseCollectionFilterComponent {}
|
||||
@@ -1,82 +0,0 @@
|
||||
<ng-container *ngIf="!hide">
|
||||
<div class="filter-heading">
|
||||
<button
|
||||
class="toggle-button"
|
||||
(click)="toggleCollapse(foldersGrouping)"
|
||||
[attr.aria-expanded]="!isCollapsed(foldersGrouping)"
|
||||
aria-controls="folder-filters"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'bwi-angle-right': isCollapsed(foldersGrouping),
|
||||
'bwi-angle-down': !isCollapsed(foldersGrouping)
|
||||
}"
|
||||
></i>
|
||||
</button>
|
||||
<h3 class="filter-title"> {{ "folders" | i18n }}</h3>
|
||||
<button
|
||||
class="text-muted ml-auto add-button"
|
||||
(click)="addFolder()"
|
||||
appA11yTitle="{{ 'addFolder' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<ul id="folder-filters" *ngIf="!isCollapsed(foldersGrouping)" class="filter-options">
|
||||
<ng-template #recursiveFolders let-folders>
|
||||
<li
|
||||
*ngFor="let f of folders"
|
||||
[ngClass]="{
|
||||
active: f.node.id === activeFilter.selectedFolderId && activeFilter.selectedFolder
|
||||
}"
|
||||
class="filter-option"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button
|
||||
*ngIf="f.children.length"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
(click)="toggleCollapse(f.node)"
|
||||
[attr.aria-expanded]="!isCollapsed(f.node)"
|
||||
[attr.aria-controls]="f.node.name + '_children'"
|
||||
class="toggle-button"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw"
|
||||
[ngClass]="{
|
||||
'bwi-angle-right': isCollapsed(f.node),
|
||||
'bwi-angle-down': !isCollapsed(f.node)
|
||||
}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<button class="filter-button" (click)="applyFilter(f.node)">
|
||||
<i *ngIf="f.children.length === 0" class="bwi bwi-fw bwi-folder" aria-hidden="true"></i
|
||||
> {{ f.node.name }}
|
||||
</button>
|
||||
<button
|
||||
class="edit-button"
|
||||
(click)="editFolder(f.node)"
|
||||
appA11yTitle="{{ 'editFolder' | i18n }}"
|
||||
*ngIf="f.node.id"
|
||||
>
|
||||
<i class="bwi bwi-pencil bwi-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</span>
|
||||
<ul
|
||||
[id]="f.node.name + '_children'"
|
||||
class="nested-filter-options"
|
||||
*ngIf="f.children.length && !isCollapsed(f.node)"
|
||||
>
|
||||
<ng-container *ngTemplateOutlet="recursiveFolders; context: { $implicit: f.children }">
|
||||
</ng-container>
|
||||
</ul>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveFolders; context: { $implicit: nestedFolders }"
|
||||
></ng-container>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { FolderFilterComponent as BaseFolderFilterComponent } from "jslib-angular/modules/vault-filter/components/folder-filter.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-folder-filter",
|
||||
templateUrl: "folder-filter.component.html",
|
||||
})
|
||||
export class FolderFilterComponent extends BaseFolderFilterComponent {}
|
||||
@@ -1,155 +0,0 @@
|
||||
<ng-container *ngIf="!hide">
|
||||
<ng-container [ngSwitch]="displayMode">
|
||||
<ng-container *ngSwitchCase="'noOrganizations'">
|
||||
<ul class="filter-options">
|
||||
<li class="filter-option active">
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button">
|
||||
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
|
||||
{{ "myVault" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="filter-option">
|
||||
<span class="filter-buttons">
|
||||
<a href="#" routerLink="/create-organization" class="filter-button">
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
{{ "newOrganization" | i18n }}
|
||||
</a>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'personalOwnershipPolicy'">
|
||||
<div class="filter-heading">
|
||||
<button
|
||||
(click)="toggleCollapse()"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
class="toggle-button"
|
||||
[attr.aria-expanded]="!isCollapsed"
|
||||
aria-controls="organization-filters"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'bwi-angle-right': isCollapsed,
|
||||
'bwi-angle-down': !isCollapsed
|
||||
}"
|
||||
></i>
|
||||
</button>
|
||||
<button
|
||||
class="filter-button"
|
||||
(click)="clearFilter()"
|
||||
[ngClass]="{ active: !hasActiveFilter }"
|
||||
>
|
||||
{{ organizationGrouping.name | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<ul id="organization-filters" *ngIf="!isCollapsed" class="filter-options">
|
||||
<li
|
||||
class="filter-option"
|
||||
*ngFor="let organization of organizations"
|
||||
[ngClass]="{ active: organization.id === activeFilter.selectedOrganizationId }"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyOrganizationFilter(organization)">
|
||||
<i class="bwi bwi-fw bwi-business" aria-hidden="true"></i>
|
||||
{{ organization.name }}
|
||||
</button>
|
||||
<ng-container *ngIf="organization.id === activeFilter.selectedOrganizationId">
|
||||
<button [bitMenuTriggerFor]="orgMenu" class="org-options ml-auto">
|
||||
<i class="bwi bwi-ellipsis-v" aria-hidden="true"></i>
|
||||
</button>
|
||||
<bit-menu class="filter-organization-options" #orgMenu>
|
||||
<app-organization-options [organization]="organization"></app-organization-options>
|
||||
</bit-menu>
|
||||
</ng-container>
|
||||
</span>
|
||||
</li>
|
||||
<li class="filter-option">
|
||||
<span class="filter-buttons">
|
||||
<a href="#" routerLink="/create-organization" class="filter-button">
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
{{ "newOrganization" | i18n }}
|
||||
</a>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'singleOrganizationAndPersonalOwnershipPolicies'">
|
||||
<div class="filter-heading">
|
||||
<button class="filter-button active">
|
||||
<i class="bwi bwi-fw bwi-business" aria-hidden="true"></i>
|
||||
{{ organizations[0].name }}
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<div class="filter-heading">
|
||||
<button
|
||||
class="toggle-button"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
(click)="toggleCollapse()"
|
||||
[attr.aria-expanded]="!isCollapsed"
|
||||
aria-controls="organization-filters"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'bwi-angle-right': isCollapsed,
|
||||
'bwi-angle-down': !isCollapsed
|
||||
}"
|
||||
></i>
|
||||
</button>
|
||||
<button
|
||||
class="filter-button"
|
||||
(click)="clearFilter()"
|
||||
[ngClass]="{ active: !hasActiveFilter }"
|
||||
>
|
||||
{{ organizationGrouping.name | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<ul id="organization-filters" *ngIf="!isCollapsed" class="filter-options">
|
||||
<li class="filter-option" [ngClass]="{ active: activeFilter.myVaultOnly }">
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyMyVaultFilter()">
|
||||
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
|
||||
{{ "myVault" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="filter-option"
|
||||
*ngFor="let organization of organizations"
|
||||
[ngClass]="{ active: organization.id === activeFilter.selectedOrganizationId }"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyOrganizationFilter(organization)">
|
||||
<i class="bwi bwi-fw bwi-business" aria-hidden="true"></i>
|
||||
{{ organization.name }}
|
||||
</button>
|
||||
<ng-container>
|
||||
<button [bitMenuTriggerFor]="orgMenu" class="org-options ml-auto">
|
||||
<i class="bwi bwi-ellipsis-v" aria-hidden="true"></i>
|
||||
</button>
|
||||
<bit-menu class="filter-organization-options" #orgMenu>
|
||||
<app-organization-options [organization]="organization"></app-organization-options>
|
||||
</bit-menu>
|
||||
</ng-container>
|
||||
</span>
|
||||
</li>
|
||||
<li class="filter-option" *ngIf="!(displayMode === 'singleOrganizationPolicy')">
|
||||
<span class="filter-buttons">
|
||||
<a href="#" routerLink="/create-organization" class="filter-button">
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
{{ "newOrganization" | i18n }}
|
||||
</a>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<hr />
|
||||
</ng-container>
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { OrganizationFilterComponent as BaseOrganizationFilterComponent } from "jslib-angular/modules/vault-filter/components/organization-filter.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-organization-filter",
|
||||
templateUrl: "organization-filter.component.html",
|
||||
})
|
||||
export class OrganizationFilterComponent extends BaseOrganizationFilterComponent {
|
||||
displayText = "allVaults";
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<ng-container *ngIf="!loaded">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted tw-m-2"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<div *ngIf="loaded" class="tw-max-w-[300px] tw-min-w-[200px] tw-flex tw-flex-col">
|
||||
<button
|
||||
*ngIf="allowEnrollmentChanges(organization) && !organization.resetPasswordEnrolled"
|
||||
class="dropdown-item"
|
||||
(click)="toggleResetPasswordEnrollment(organization)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-key" aria-hidden="true"></i>
|
||||
{{ "enrollPasswordReset" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
*ngIf="allowEnrollmentChanges(organization) && organization.resetPasswordEnrolled"
|
||||
class="dropdown-item"
|
||||
(click)="toggleResetPasswordEnrollment(organization)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-undo" aria-hidden="true"></i>
|
||||
{{ "withdrawPasswordReset" | i18n }}
|
||||
</button>
|
||||
<ng-container *ngIf="organization.useSso && organization.identifier">
|
||||
<button
|
||||
*ngIf="organization.ssoBound; else linkSso"
|
||||
class="dropdown-item"
|
||||
(click)="unlinkSso(organization)"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-chain-broken" aria-hidden="true"></i>
|
||||
{{ "unlinkSso" | i18n }}
|
||||
</button>
|
||||
<ng-template #linkSso>
|
||||
<app-link-sso [organization]="organization"> </app-link-sso>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<button class="dropdown-item text-danger" (click)="leave(organization)">
|
||||
<i class="bwi bwi-fw bwi-sign-out" aria-hidden="true"></i>
|
||||
{{ "leave" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,123 +0,0 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { PolicyType } from "jslib-common/enums/policyType";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
import { Policy } from "jslib-common/models/domain/policy";
|
||||
|
||||
import { EnrollMasterPasswordReset } from "../../organizations/users/enroll-master-password-reset.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-organization-options",
|
||||
templateUrl: "organization-options.component.html",
|
||||
})
|
||||
export class OrganizationOptionsComponent {
|
||||
actionPromise: Promise<any>;
|
||||
policies: Policy[];
|
||||
loaded = false;
|
||||
|
||||
@Input() organization: Organization;
|
||||
|
||||
constructor(
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
private apiService: ApiService,
|
||||
private syncService: SyncService,
|
||||
private policyService: PolicyService,
|
||||
private modalService: ModalService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.load();
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.policies = await this.policyService.getAll(PolicyType.ResetPassword);
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
allowEnrollmentChanges(org: Organization): boolean {
|
||||
if (org.usePolicies && org.useResetPassword && org.hasPublicAndPrivateKeys) {
|
||||
const policy = this.policies.find((p) => p.organizationId === org.id);
|
||||
if (policy != null && policy.enabled) {
|
||||
return org.resetPasswordEnrolled && policy.data.autoEnrollEnabled ? false : true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
showEnrolledStatus(org: Organization): boolean {
|
||||
return (
|
||||
org.useResetPassword &&
|
||||
org.resetPasswordEnrolled &&
|
||||
this.policies.some((p) => p.organizationId === org.id && p.enabled)
|
||||
);
|
||||
}
|
||||
|
||||
async unlinkSso(org: Organization) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("unlinkSsoConfirmation"),
|
||||
org.name,
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("no"),
|
||||
"warning"
|
||||
);
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
this.actionPromise = this.apiService.deleteSsoUser(org.id).then(() => {
|
||||
return this.syncService.fullSync(true);
|
||||
});
|
||||
await this.actionPromise;
|
||||
this.platformUtilsService.showToast("success", null, "Unlinked SSO");
|
||||
await this.load();
|
||||
} catch (e) {
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), e.message);
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async leave(org: Organization) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("leaveOrganizationConfirmation"),
|
||||
org.name,
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("no"),
|
||||
"warning"
|
||||
);
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
this.actionPromise = this.apiService.postLeaveOrganization(org.id).then(() => {
|
||||
return this.syncService.fullSync(true);
|
||||
});
|
||||
await this.actionPromise;
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("leftOrganization"));
|
||||
await this.load();
|
||||
} catch (e) {
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), e.message);
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async toggleResetPasswordEnrollment(org: Organization) {
|
||||
this.modalService.open(EnrollMasterPasswordReset, {
|
||||
allowMultipleModals: true,
|
||||
data: {
|
||||
organization: org,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<ng-container *ngIf="show">
|
||||
<ul class="filter-options">
|
||||
<li class="filter-option" [ngClass]="{ active: activeFilter.status === 'all' }">
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyFilter('all')">
|
||||
<i class="bwi bwi-fw bwi-filter" aria-hidden="true"></i> {{ "allItems" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
*ngIf="!hideFavorites"
|
||||
class="filter-option"
|
||||
[ngClass]="{ active: activeFilter.status === 'favorites' }"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyFilter('favorites')">
|
||||
<i class="bwi bwi-fw bwi-star" aria-hidden="true"></i> {{ "favorites" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
*ngIf="!hideTrash"
|
||||
class="filter-option"
|
||||
[ngClass]="{ active: activeFilter.status === 'trash' }"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyFilter('trash')">
|
||||
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i> {{ "trash" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { StatusFilterComponent as BaseStatusFilterComponent } from "jslib-angular/modules/vault-filter/components/status-filter.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-status-filter",
|
||||
templateUrl: "status-filter.component.html",
|
||||
})
|
||||
export class StatusFilterComponent extends BaseStatusFilterComponent {}
|
||||
@@ -1,60 +0,0 @@
|
||||
<div class="filter-heading">
|
||||
<button
|
||||
class="toggle-button"
|
||||
[attr.aria-expanded]="!isCollapsed"
|
||||
aria-controls="type-filters"
|
||||
(click)="toggleCollapse()"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'bwi-angle-right': isCollapsed,
|
||||
'bwi-angle-down': !isCollapsed
|
||||
}"
|
||||
></i>
|
||||
</button>
|
||||
<h3> {{ "types" | i18n }}</h3>
|
||||
</div>
|
||||
<ul id="type-filters" *ngIf="!isCollapsed" class="filter-options">
|
||||
<li
|
||||
class="filter-option"
|
||||
[ngClass]="{ active: activeFilter.cipherType === cipherTypeEnum.Login }"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyFilter(cipherTypeEnum.Login)">
|
||||
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i> {{ "typeLogin" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li class="filter-option" [ngClass]="{ active: activeFilter.cipherType === cipherTypeEnum.Card }">
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyFilter(cipherTypeEnum.Card)">
|
||||
<i class="bwi bwi-fw bwi-credit-card" aria-hidden="true"></i> {{ "typeCard" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="filter-option"
|
||||
[ngClass]="{ active: activeFilter.cipherType === cipherTypeEnum.Identity }"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyFilter(cipherTypeEnum.Identity)">
|
||||
<i class="bwi bwi-fw bwi-id-card" aria-hidden="true"></i> {{ "typeIdentity" | i18n }}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="filter-option"
|
||||
[ngClass]="{ active: activeFilter.cipherType === cipherTypeEnum.SecureNote }"
|
||||
>
|
||||
<span class="filter-buttons">
|
||||
<button class="filter-button" (click)="applyFilter(cipherTypeEnum.SecureNote)">
|
||||
<i class="bwi bwi-fw bwi-sticky-note" aria-hidden="true"></i> {{
|
||||
"typeSecureNote" | i18n
|
||||
}}
|
||||
</button>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { TypeFilterComponent as BaseTypeFilterComponent } from "jslib-angular/modules/vault-filter/components/type-filter.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-type-filter",
|
||||
templateUrl: "type-filter.component.html",
|
||||
})
|
||||
export class TypeFilterComponent extends BaseTypeFilterComponent {}
|
||||
@@ -1,80 +0,0 @@
|
||||
<div class="card vault-filters">
|
||||
<div class="container loading-spinner" *ngIf="!isLoaded">
|
||||
<i class="bwi bwi-spinner bwi-spin bwi-3x" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div *ngIf="isLoaded">
|
||||
<div class="card-header d-flex">
|
||||
{{ "filters" | i18n }}
|
||||
<a
|
||||
class="ml-auto"
|
||||
href="https://bitwarden.com/help/searching-vault/"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<input
|
||||
type="search"
|
||||
placeholder="{{ (searchPlaceholder | i18n) || ('searchVault' | i18n) }}"
|
||||
id="search"
|
||||
class="form-control"
|
||||
[(ngModel)]="searchText"
|
||||
(input)="searchTextChanged()"
|
||||
autocomplete="off"
|
||||
appAutofocus
|
||||
/>
|
||||
<app-organization-filter
|
||||
*ngIf="showOrgFilter"
|
||||
[hide]="hideOrganizations"
|
||||
[activeFilter]="activeFilter"
|
||||
[collapsedFilterNodes]="collapsedFilterNodes"
|
||||
[organizations]="organizations"
|
||||
[activePersonalOwnershipPolicy]="activePersonalOwnershipPolicy"
|
||||
[activeSingleOrganizationPolicy]="activeSingleOrganizationPolicy"
|
||||
(onNodeCollapseStateChange)="toggleFilterNodeCollapseState($event)"
|
||||
(onFilterChange)="applyFilter($event)"
|
||||
></app-organization-filter>
|
||||
<div class="filter">
|
||||
<app-status-filter
|
||||
[hideFavorites]="!showFavorites"
|
||||
[hideTrash]="hideTrash"
|
||||
[activeFilter]="activeFilter"
|
||||
(onFilterChange)="applyFilter($event)"
|
||||
></app-status-filter>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<app-type-filter
|
||||
[activeFilter]="activeFilter"
|
||||
[collapsedFilterNodes]="collapsedFilterNodes"
|
||||
(onNodeCollapseStateChange)="toggleFilterNodeCollapseState($event)"
|
||||
(onFilterChange)="applyFilter($event)"
|
||||
></app-type-filter>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<app-folder-filter
|
||||
[hide]="!showFolders"
|
||||
[activeFilter]="activeFilter"
|
||||
[collapsedFilterNodes]="collapsedFilterNodes"
|
||||
[folderNodes]="folders"
|
||||
(onNodeCollapseStateChange)="toggleFilterNodeCollapseState($event)"
|
||||
(onFilterChange)="applyFilter($event)"
|
||||
(onAddFolder)="addFolder()"
|
||||
(onEditFolder)="editFolder($event)"
|
||||
></app-folder-filter>
|
||||
</div>
|
||||
<div class="filter">
|
||||
<app-collection-filter
|
||||
[hide]="hideCollections"
|
||||
[activeFilter]="activeFilter"
|
||||
[collapsedFilterNodes]="collapsedFilterNodes"
|
||||
[collectionNodes]="collections"
|
||||
(onNodeCollapseStateChange)="toggleFilterNodeCollapseState($event)"
|
||||
(onFilterChange)="applyFilter($event)"
|
||||
></app-collection-filter>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { VaultFilterComponent as BaseVaultFilterComponent } from "jslib-angular/modules/vault-filter/vault-filter.component";
|
||||
import { VaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
|
||||
@Component({
|
||||
selector: "app-vault-filter",
|
||||
templateUrl: "vault-filter.component.html",
|
||||
})
|
||||
export class VaultFilterComponent extends BaseVaultFilterComponent {
|
||||
@Input() showOrgFilter = true;
|
||||
@Input() showFolders = true;
|
||||
@Input() showFavorites = true;
|
||||
|
||||
@Output() onSearchTextChanged = new EventEmitter<string>();
|
||||
|
||||
searchPlaceholder: string;
|
||||
searchText = "";
|
||||
|
||||
organization: Organization;
|
||||
|
||||
constructor(vaultFilterService: VaultFilterService) {
|
||||
super(vaultFilterService);
|
||||
}
|
||||
|
||||
searchTextChanged() {
|
||||
this.onSearchTextChanged.emit(this.searchText);
|
||||
}
|
||||
|
||||
async initCollections() {
|
||||
return await this.vaultFilterService.buildCollections(this.organization?.id);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { VaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service";
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { SharedModule } from "../shared.module";
|
||||
|
||||
import { CollectionFilterComponent } from "./components/collection-filter.component";
|
||||
import { FolderFilterComponent } from "./components/folder-filter.component";
|
||||
import { LinkSsoComponent } from "./components/link-sso.component";
|
||||
import { OrganizationFilterComponent } from "./components/organization-filter.component";
|
||||
import { OrganizationOptionsComponent } from "./components/organization-options.component";
|
||||
import { StatusFilterComponent } from "./components/status-filter.component";
|
||||
import { TypeFilterComponent } from "./components/type-filter.component";
|
||||
import { VaultFilterComponent } from "./vault-filter.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule],
|
||||
declarations: [
|
||||
VaultFilterComponent,
|
||||
CollectionFilterComponent,
|
||||
FolderFilterComponent,
|
||||
OrganizationFilterComponent,
|
||||
OrganizationOptionsComponent,
|
||||
StatusFilterComponent,
|
||||
TypeFilterComponent,
|
||||
LinkSsoComponent,
|
||||
],
|
||||
exports: [VaultFilterComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: VaultFilterService,
|
||||
useClass: VaultFilterService,
|
||||
deps: [
|
||||
StateService,
|
||||
OrganizationService,
|
||||
FolderService,
|
||||
CipherService,
|
||||
CollectionService,
|
||||
PolicyService,
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class VaultFilterModule {}
|
||||
@@ -1,3 +0,0 @@
|
||||
import { VaultFilterService as BaseVaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service";
|
||||
|
||||
export class VaultFilterService extends BaseVaultFilterService {}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { IndividualVaultComponent } from "./individual-vault.component";
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: "",
|
||||
component: IndividualVaultComponent,
|
||||
data: { titleId: "vaults" },
|
||||
},
|
||||
];
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class IndividualVaultRoutingModule {}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { VaultModule } from "../../vault.module";
|
||||
|
||||
import { IndividualVaultRoutingModule } from "./individual-vault-routing.module";
|
||||
import { IndividualVaultComponent } from "./individual-vault.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [VaultModule, IndividualVaultRoutingModule],
|
||||
declarations: [IndividualVaultComponent],
|
||||
exports: [IndividualVaultComponent],
|
||||
})
|
||||
export class IndividualVaultModule {}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { SharedModule } from "../../../shared.module";
|
||||
|
||||
import { OrganizationNameBadgeComponent } from "./organization-name-badge.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule],
|
||||
declarations: [OrganizationNameBadgeComponent],
|
||||
exports: [OrganizationNameBadgeComponent],
|
||||
})
|
||||
export class OrganizationBadgeModule {}
|
||||
@@ -1,9 +0,0 @@
|
||||
<button
|
||||
bitBadge
|
||||
[style.color]="textColor"
|
||||
[style.background-color]="color"
|
||||
appA11yTitle="{{ organizationName }}"
|
||||
(click)="emitOnOrganizationClicked()"
|
||||
>
|
||||
{{ organizationName | ellipsis: 13 }}
|
||||
</button>
|
||||
@@ -1,59 +0,0 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-badge",
|
||||
templateUrl: "organization-name-badge.component.html",
|
||||
})
|
||||
export class OrganizationNameBadgeComponent implements OnInit {
|
||||
@Input() organizationName: string;
|
||||
@Input() profileName: string;
|
||||
|
||||
@Output() onOrganizationClicked = new EventEmitter<string>();
|
||||
|
||||
color: string;
|
||||
textColor: string;
|
||||
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.organizationName == null || this.organizationName === "") {
|
||||
this.organizationName = this.i18nService.t("me");
|
||||
this.color = this.stringToColor(this.profileName.toUpperCase());
|
||||
}
|
||||
if (this.color == null) {
|
||||
this.color = this.stringToColor(this.organizationName.toUpperCase());
|
||||
}
|
||||
this.textColor = this.pickTextColorBasedOnBgColor();
|
||||
}
|
||||
|
||||
// This value currently isn't stored anywhere, only calculated in the app-avatar component
|
||||
// Once we are allowing org colors to be changed and saved, change this out
|
||||
private stringToColor(str: string): string {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
let color = "#";
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const value = (hash >> (i * 8)) & 0xff;
|
||||
color += ("00" + value.toString(16)).substr(-2);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
// There are a few ways to calculate text color for contrast, this one seems to fit accessibility guidelines best.
|
||||
// https://stackoverflow.com/a/3943023/6869691
|
||||
private pickTextColorBasedOnBgColor() {
|
||||
const color = this.color.charAt(0) === "#" ? this.color.substring(1, 7) : this.color;
|
||||
const r = parseInt(color.substring(0, 2), 16); // hexToR
|
||||
const g = parseInt(color.substring(2, 4), 16); // hexToG
|
||||
const b = parseInt(color.substring(4, 6), 16); // hexToB
|
||||
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? "black !important" : "white !important";
|
||||
}
|
||||
|
||||
emitOnOrganizationClicked() {
|
||||
this.onOrganizationClicked.emit();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { OrganizationVaultComponent } from "./organization-vault.component";
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: "",
|
||||
component: OrganizationVaultComponent,
|
||||
data: { titleId: "vaults" },
|
||||
},
|
||||
];
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class OrganizationVaultRoutingModule {}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { VaultModule } from "../../vault.module";
|
||||
|
||||
import { OrganizationVaultRoutingModule } from "./organization-vault-routing.module";
|
||||
import { OrganizationVaultComponent } from "./organization-vault.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [VaultModule, OrganizationVaultRoutingModule],
|
||||
declarations: [OrganizationVaultComponent],
|
||||
exports: [OrganizationVaultComponent],
|
||||
})
|
||||
export class OrganizationVaultModule {}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { LooseComponentsModule } from "../loose-components.module";
|
||||
import { SharedModule } from "../shared.module";
|
||||
import { VaultFilterModule } from "../vault-filter/vault-filter.module";
|
||||
|
||||
import { VaultService } from "./vault.service";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule, VaultFilterModule, LooseComponentsModule],
|
||||
exports: [SharedModule, VaultFilterModule, LooseComponentsModule],
|
||||
providers: [
|
||||
{
|
||||
provide: VaultService,
|
||||
useClass: VaultService,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class VaultModule {}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { VaultFilter } from "jslib-angular/modules/vault-filter/models/vault-filter.model";
|
||||
|
||||
export class VaultService {
|
||||
calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string {
|
||||
if (vaultFilter.status === "favorites") {
|
||||
return "searchFavorites";
|
||||
}
|
||||
if (vaultFilter.status === "trash") {
|
||||
return "searchTrash";
|
||||
}
|
||||
if (vaultFilter.cipherType != null) {
|
||||
return "searchType";
|
||||
}
|
||||
if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId != "none") {
|
||||
return "searchFolder";
|
||||
}
|
||||
if (vaultFilter.selectedCollectionId != null) {
|
||||
return "searchCollection";
|
||||
}
|
||||
if (vaultFilter.selectedOrganizationId != null) {
|
||||
return "searchOrganization";
|
||||
}
|
||||
if (vaultFilter.myVaultOnly) {
|
||||
return "searchMyVault";
|
||||
}
|
||||
|
||||
return "searchVault";
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { Permissions } from "jslib-common/enums/permissions";
|
||||
|
||||
@Injectable()
|
||||
export class PermissionsGuard implements CanActivate {
|
||||
constructor(
|
||||
private router: Router,
|
||||
private organizationService: OrganizationService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
private syncService: SyncService
|
||||
) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
// TODO: We need to fix this issue once and for all.
|
||||
if ((await this.syncService.getLastSync()) == null) {
|
||||
await this.syncService.fullSync(false);
|
||||
}
|
||||
|
||||
const org = await this.organizationService.get(route.params.organizationId);
|
||||
if (org == null) {
|
||||
return this.router.createUrlTree(["/"]);
|
||||
}
|
||||
|
||||
if (!org.isOwner && !org.enabled) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("organizationIsDisabled")
|
||||
);
|
||||
return this.router.createUrlTree(["/"]);
|
||||
}
|
||||
|
||||
const permissions = route.data == null ? [] : (route.data.permissions as Permissions[]);
|
||||
if (permissions != null && !org.hasAnyPermission(permissions)) {
|
||||
// Handle linkable ciphers for organizations the user only has view access to
|
||||
// https://bitwarden.atlassian.net/browse/EC-203
|
||||
if (state.root.queryParamMap.has("cipherId")) {
|
||||
return this.router.createUrlTree(["/vault"], {
|
||||
queryParams: {
|
||||
cipherId: state.root.queryParamMap.get("cipherId"),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("accessDenied"));
|
||||
return this.router.createUrlTree(["/"]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuard } from "jslib-angular/guards/auth.guard";
|
||||
import { Permissions } from "jslib-common/enums/permissions";
|
||||
|
||||
import { PermissionsGuard } from "./guards/permissions.guard";
|
||||
import { OrganizationLayoutComponent } from "./layouts/organization-layout.component";
|
||||
import { CollectionsComponent } from "./manage/collections.component";
|
||||
import { EventsComponent } from "./manage/events.component";
|
||||
import { GroupsComponent } from "./manage/groups.component";
|
||||
import { ManageComponent } from "./manage/manage.component";
|
||||
import { PeopleComponent } from "./manage/people.component";
|
||||
import { PoliciesComponent } from "./manage/policies.component";
|
||||
import { NavigationPermissionsService } from "./services/navigation-permissions.service";
|
||||
import { AccountComponent } from "./settings/account.component";
|
||||
import { OrganizationBillingComponent } from "./settings/organization-billing.component";
|
||||
import { OrganizationSubscriptionComponent } from "./settings/organization-subscription.component";
|
||||
import { SettingsComponent } from "./settings/settings.component";
|
||||
import { TwoFactorSetupComponent } from "./settings/two-factor-setup.component";
|
||||
import { ExportComponent } from "./tools/export.component";
|
||||
import { ExposedPasswordsReportComponent } from "./tools/exposed-passwords-report.component";
|
||||
import { ImportComponent } from "./tools/import.component";
|
||||
import { InactiveTwoFactorReportComponent } from "./tools/inactive-two-factor-report.component";
|
||||
import { ReusedPasswordsReportComponent } from "./tools/reused-passwords-report.component";
|
||||
import { ToolsComponent } from "./tools/tools.component";
|
||||
import { UnsecuredWebsitesReportComponent } from "./tools/unsecured-websites-report.component";
|
||||
import { WeakPasswordsReportComponent } from "./tools/weak-passwords-report.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: ":organizationId",
|
||||
component: OrganizationLayoutComponent,
|
||||
canActivate: [AuthGuard, PermissionsGuard],
|
||||
data: {
|
||||
permissions: NavigationPermissionsService.getPermissions("admin"),
|
||||
},
|
||||
children: [
|
||||
{ path: "", pathMatch: "full", redirectTo: "vault" },
|
||||
{
|
||||
path: "vault",
|
||||
loadChildren: async () =>
|
||||
(await import("../modules/vault/modules/organization-vault/organization-vault.module"))
|
||||
.OrganizationVaultModule,
|
||||
},
|
||||
{
|
||||
path: "tools",
|
||||
component: ToolsComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: { permissions: NavigationPermissionsService.getPermissions("tools") },
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
pathMatch: "full",
|
||||
redirectTo: "import",
|
||||
},
|
||||
{
|
||||
path: "import",
|
||||
component: ImportComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "importData",
|
||||
permissions: [Permissions.AccessImportExport],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "export",
|
||||
component: ExportComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "exportVault",
|
||||
permissions: [Permissions.AccessImportExport],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "exposed-passwords-report",
|
||||
component: ExposedPasswordsReportComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "exposedPasswordsReport",
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "inactive-two-factor-report",
|
||||
component: InactiveTwoFactorReportComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "inactive2faReport",
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "reused-passwords-report",
|
||||
component: ReusedPasswordsReportComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "reusedPasswordsReport",
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "unsecured-websites-report",
|
||||
component: UnsecuredWebsitesReportComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "unsecuredWebsitesReport",
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "weak-passwords-report",
|
||||
component: WeakPasswordsReportComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "weakPasswordsReport",
|
||||
permissions: [Permissions.AccessReports],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "manage",
|
||||
component: ManageComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
permissions: NavigationPermissionsService.getPermissions("manage"),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
pathMatch: "full",
|
||||
redirectTo: "people",
|
||||
},
|
||||
{
|
||||
path: "collections",
|
||||
component: CollectionsComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "collections",
|
||||
permissions: [
|
||||
Permissions.CreateNewCollections,
|
||||
Permissions.EditAnyCollection,
|
||||
Permissions.DeleteAnyCollection,
|
||||
Permissions.EditAssignedCollections,
|
||||
Permissions.DeleteAssignedCollections,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "events",
|
||||
component: EventsComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "eventLogs",
|
||||
permissions: [Permissions.AccessEventLogs],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "groups",
|
||||
component: GroupsComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "groups",
|
||||
permissions: [Permissions.ManageGroups],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "people",
|
||||
component: PeopleComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "people",
|
||||
permissions: [Permissions.ManageUsers, Permissions.ManageUsersPassword],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "policies",
|
||||
component: PoliciesComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
titleId: "policies",
|
||||
permissions: [Permissions.ManagePolicies],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "settings",
|
||||
component: SettingsComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: { permissions: NavigationPermissionsService.getPermissions("settings") },
|
||||
children: [
|
||||
{ path: "", pathMatch: "full", redirectTo: "account" },
|
||||
{ path: "account", component: AccountComponent, data: { titleId: "myOrganization" } },
|
||||
{
|
||||
path: "two-factor",
|
||||
component: TwoFactorSetupComponent,
|
||||
data: { titleId: "twoStepLogin" },
|
||||
},
|
||||
{
|
||||
path: "billing",
|
||||
component: OrganizationBillingComponent,
|
||||
canActivate: [PermissionsGuard],
|
||||
data: { titleId: "billing", permissions: [Permissions.ManageBilling] },
|
||||
},
|
||||
{
|
||||
path: "subscription",
|
||||
component: OrganizationSubscriptionComponent,
|
||||
data: { titleId: "subscription" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class OrganizationsRoutingModule {}
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Permissions } from "jslib-common/enums/permissions";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
|
||||
const permissions = {
|
||||
manage: [
|
||||
Permissions.CreateNewCollections,
|
||||
Permissions.EditAnyCollection,
|
||||
Permissions.DeleteAnyCollection,
|
||||
Permissions.EditAssignedCollections,
|
||||
Permissions.DeleteAssignedCollections,
|
||||
Permissions.AccessEventLogs,
|
||||
Permissions.ManageGroups,
|
||||
Permissions.ManageUsers,
|
||||
Permissions.ManagePolicies,
|
||||
],
|
||||
tools: [Permissions.AccessImportExport, Permissions.AccessReports],
|
||||
settings: [Permissions.ManageOrganization],
|
||||
};
|
||||
|
||||
export class NavigationPermissionsService {
|
||||
static getPermissions(route: keyof typeof permissions | "admin") {
|
||||
if (route === "admin") {
|
||||
return Object.values(permissions).reduce((previous, current) => previous.concat(current), []);
|
||||
}
|
||||
|
||||
return permissions[route];
|
||||
}
|
||||
|
||||
static canAccessAdmin(organization: Organization): boolean {
|
||||
return (
|
||||
this.canAccessTools(organization) ||
|
||||
this.canAccessSettings(organization) ||
|
||||
this.canAccessManage(organization)
|
||||
);
|
||||
}
|
||||
|
||||
static canAccessTools(organization: Organization): boolean {
|
||||
return organization.hasAnyPermission(NavigationPermissionsService.getPermissions("tools"));
|
||||
}
|
||||
|
||||
static canAccessSettings(organization: Organization): boolean {
|
||||
return organization.hasAnyPermission(NavigationPermissionsService.getPermissions("settings"));
|
||||
}
|
||||
|
||||
static canAccessManage(organization: Organization): boolean {
|
||||
return organization.hasAnyPermission(NavigationPermissionsService.getPermissions("manage"));
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="billingSyncApiKeyTitle">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form
|
||||
class="modal-content"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
ngNativeValidate
|
||||
>
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="billingSyncApiKeyTitle">
|
||||
{{ (hasBillingToken ? "viewBillingSyncToken" : "generateBillingSyncToken") | i18n }}
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
appA11yTitle="{{ 'close' | i18n }}"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<app-user-verification
|
||||
[(ngModel)]="masterPassword"
|
||||
ngDefaultControl
|
||||
name="secret"
|
||||
*ngIf="!clientSecret"
|
||||
>
|
||||
</app-user-verification>
|
||||
<ng-container *ngIf="clientSecret && showRotateScreen">
|
||||
<p>{{ "rotateBillingSyncTokenTitle" | i18n }}</p>
|
||||
<app-callout type="warning">
|
||||
{{ "rotateBillingSyncTokenWarning" | i18n }}
|
||||
</app-callout>
|
||||
</ng-container>
|
||||
|
||||
<div *ngIf="clientSecret && !showRotateScreen">
|
||||
<p>{{ "copyPasteBillingSync" | i18n }}</p>
|
||||
<label for="clientSecret">Billing Sync Key</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
id="clientSecret"
|
||||
class="form-control text-monospace"
|
||||
type="text"
|
||||
[(ngModel)]="clientSecret"
|
||||
name="clientSecret"
|
||||
disabled
|
||||
/>
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="copy()"
|
||||
[appA11yTitle]="'copy' | i18n"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small text-muted mt-2" *ngIf="showLastSyncText">
|
||||
<b class="font-weight-semibold">{{ "lastSync" | i18n }}:</b>
|
||||
{{ lastSyncDate | date: "medium" }}
|
||||
</div>
|
||||
<div class="small text-danger mt-2" *ngIf="showAwaitingSyncText">
|
||||
<i class="bwi bwi-error"></i>
|
||||
{{
|
||||
(daysBetween === 1 ? "awaitingSyncSingular" : "awaitingSyncPlural")
|
||||
| i18n: daysBetween
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-submit"
|
||||
[disabled]="form.loading"
|
||||
*ngIf="!clientSecret || showRotateScreen"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
*ngIf="form.loading"
|
||||
></i>
|
||||
<span>
|
||||
{{ submitButtonText }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
data-dismiss="modal"
|
||||
*ngIf="!showRotateScreen"
|
||||
>
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
*ngIf="showRotateScreen"
|
||||
(click)="cancelRotate()"
|
||||
>
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
*ngIf="clientSecret && !showRotateScreen"
|
||||
(click)="rotateToken()"
|
||||
>
|
||||
{{ "rotateToken" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,108 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||
import { OrganizationApiKeyType } from "jslib-common/enums/organizationApiKeyType";
|
||||
import { OrganizationApiKeyRequest } from "jslib-common/models/request/organizationApiKeyRequest";
|
||||
import { ApiKeyResponse } from "jslib-common/models/response/apiKeyResponse";
|
||||
import { Verification } from "jslib-common/types/verification";
|
||||
|
||||
@Component({
|
||||
selector: "app-billing-sync-api-key",
|
||||
templateUrl: "billing-sync-api-key.component.html",
|
||||
})
|
||||
export class BillingSyncApiKeyComponent {
|
||||
organizationId: string;
|
||||
hasBillingToken: boolean;
|
||||
|
||||
showRotateScreen: boolean;
|
||||
masterPassword: Verification;
|
||||
formPromise: Promise<ApiKeyResponse>;
|
||||
clientSecret?: string;
|
||||
keyRevisionDate?: Date;
|
||||
lastSyncDate?: Date = null;
|
||||
|
||||
constructor(
|
||||
private userVerificationService: UserVerificationService,
|
||||
private apiService: ApiService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService
|
||||
) {}
|
||||
|
||||
copy() {
|
||||
this.platformUtilsService.copyToClipboard(this.clientSecret);
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (this.showRotateScreen) {
|
||||
this.formPromise = this.userVerificationService
|
||||
.buildRequest(this.masterPassword, OrganizationApiKeyRequest)
|
||||
.then((request) => {
|
||||
request.type = OrganizationApiKeyType.BillingSync;
|
||||
return this.apiService.postOrganizationRotateApiKey(this.organizationId, request);
|
||||
});
|
||||
const response = await this.formPromise;
|
||||
await this.load(response);
|
||||
this.showRotateScreen = false;
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("billingSyncApiKeyRotated")
|
||||
);
|
||||
} else {
|
||||
this.formPromise = this.userVerificationService
|
||||
.buildRequest(this.masterPassword, OrganizationApiKeyRequest)
|
||||
.then((request) => {
|
||||
request.type = OrganizationApiKeyType.BillingSync;
|
||||
return this.apiService.postOrganizationApiKey(this.organizationId, request);
|
||||
});
|
||||
const response = await this.formPromise;
|
||||
await this.load(response);
|
||||
}
|
||||
}
|
||||
|
||||
async load(response: ApiKeyResponse) {
|
||||
this.clientSecret = response.apiKey;
|
||||
this.keyRevisionDate = response.revisionDate;
|
||||
this.hasBillingToken = true;
|
||||
const syncStatus = await this.apiService.getSponsorshipSyncStatus(this.organizationId);
|
||||
this.lastSyncDate = syncStatus.lastSyncDate;
|
||||
}
|
||||
|
||||
cancelRotate() {
|
||||
this.showRotateScreen = false;
|
||||
}
|
||||
|
||||
rotateToken() {
|
||||
this.showRotateScreen = true;
|
||||
}
|
||||
|
||||
private dayDiff(date1: Date, date2: Date): number {
|
||||
const diffTime = Math.abs(date2.getTime() - date1.getTime());
|
||||
return Math.round(diffTime / (1000 * 60 * 60 * 24));
|
||||
}
|
||||
|
||||
get submitButtonText(): string {
|
||||
if (this.showRotateScreen) {
|
||||
return this.i18nService.t("rotateToken");
|
||||
}
|
||||
|
||||
return this.i18nService.t(this.hasBillingToken ? "continue" : "generateToken");
|
||||
}
|
||||
|
||||
get showLastSyncText(): boolean {
|
||||
// If the keyRevisionDate is later than the lastSyncDate we need to show
|
||||
// a warning that they need to put the billing sync key in their self hosted install
|
||||
return this.lastSyncDate && this.lastSyncDate > this.keyRevisionDate;
|
||||
}
|
||||
|
||||
get showAwaitingSyncText(): boolean {
|
||||
return this.lastSyncDate && this.lastSyncDate <= this.keyRevisionDate;
|
||||
}
|
||||
|
||||
get daysBetween(): number {
|
||||
return this.dayDiff(this.keyRevisionDate, new Date());
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
import { Component, EventEmitter, OnInit, Output } from "@angular/core";
|
||||
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||
import { CipherType } from "jslib-common/enums/cipherType";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
import { Verification } from "jslib-common/types/verification";
|
||||
|
||||
class CountBasedLocalizationKey {
|
||||
singular: string;
|
||||
plural: string;
|
||||
|
||||
getKey(count: number) {
|
||||
return count == 1 ? this.singular : this.plural;
|
||||
}
|
||||
|
||||
constructor(singular: string, plural: string) {
|
||||
this.singular = singular;
|
||||
this.plural = plural;
|
||||
}
|
||||
}
|
||||
|
||||
class OrganizationContentSummaryItem {
|
||||
count: number;
|
||||
get localizationKey(): string {
|
||||
return this.localizationKeyOptions.getKey(this.count);
|
||||
}
|
||||
private localizationKeyOptions: CountBasedLocalizationKey;
|
||||
constructor(count: number, localizationKeyOptions: CountBasedLocalizationKey) {
|
||||
this.count = count;
|
||||
this.localizationKeyOptions = localizationKeyOptions;
|
||||
}
|
||||
}
|
||||
|
||||
class OrganizationContentSummary {
|
||||
totalItemCount = 0;
|
||||
itemCountByType: OrganizationContentSummaryItem[] = [];
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-delete-organization",
|
||||
templateUrl: "delete-organization.component.html",
|
||||
})
|
||||
export class DeleteOrganizationComponent implements OnInit {
|
||||
organizationId: string;
|
||||
loaded: boolean;
|
||||
deleteOrganizationRequestType: "InvalidFamiliesForEnterprise" | "RegularDelete" = "RegularDelete";
|
||||
organizationName: string;
|
||||
organizationContentSummary: OrganizationContentSummary = new OrganizationContentSummary();
|
||||
@Output() onSuccess: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
masterPassword: Verification;
|
||||
formPromise: Promise<any>;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
private logService: LogService,
|
||||
private cipherService: CipherService,
|
||||
private organizationService: OrganizationService
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
await this.load();
|
||||
}
|
||||
|
||||
async submit() {
|
||||
try {
|
||||
this.formPromise = this.userVerificationService
|
||||
.buildRequest(this.masterPassword)
|
||||
.then((request) => this.apiService.deleteOrganization(this.organizationId, request));
|
||||
await this.formPromise;
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
this.i18nService.t("organizationDeleted"),
|
||||
this.i18nService.t("organizationDeletedDesc")
|
||||
);
|
||||
this.onSuccess.emit();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async load() {
|
||||
this.organizationName = (await this.organizationService.get(this.organizationId)).name;
|
||||
this.organizationContentSummary = await this.buildOrganizationContentSummary();
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
private async buildOrganizationContentSummary(): Promise<OrganizationContentSummary> {
|
||||
const organizationContentSummary = new OrganizationContentSummary();
|
||||
const organizationItems = (
|
||||
await this.cipherService.getAllFromApiForOrganization(this.organizationId)
|
||||
).filter((item) => item.deletedDate == null);
|
||||
|
||||
if (organizationItems.length < 1) {
|
||||
return organizationContentSummary;
|
||||
}
|
||||
|
||||
organizationContentSummary.totalItemCount = organizationItems.length;
|
||||
for (const cipherType of Utils.iterateEnum(CipherType)) {
|
||||
const count = this.getOrganizationItemCountByType(organizationItems, cipherType);
|
||||
if (count > 0) {
|
||||
organizationContentSummary.itemCountByType.push(
|
||||
new OrganizationContentSummaryItem(
|
||||
count,
|
||||
this.getOrganizationItemLocalizationKeysByType(CipherType[cipherType])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return organizationContentSummary;
|
||||
}
|
||||
|
||||
private getOrganizationItemCountByType(items: CipherView[], type: CipherType) {
|
||||
return items.filter((item) => item.type == type).length;
|
||||
}
|
||||
|
||||
private getOrganizationItemLocalizationKeysByType(type: string): CountBasedLocalizationKey {
|
||||
return new CountBasedLocalizationKey(`type${type}`, `type${type}Plural`);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<svg width="216" height="231" viewBox="0 0 216 231" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M133.356 85.6618C133.136 85.43 132.871 85.2457 132.577 85.1198C132.283 84.9939 131.968 84.93 131.648 84.9318H87.8482C87.5289 84.93 87.2113 84.9939 86.9175 85.1198C86.6237 85.2457 86.359 85.43 86.14 85.6618C85.9083 85.8808 85.7239 86.1473 85.598 86.4411C85.4721 86.7349 85.4082 87.0506 85.41 87.37V116.57C85.4192 118.793 85.8499 120.994 86.6802 123.056C87.4705 125.091 88.5326 127.011 89.8375 128.761C91.1789 130.515 92.6808 132.137 94.3233 133.612C95.8472 135.01 97.4532 136.318 99.1304 137.528C100.59 138.565 102.123 139.547 103.729 140.474C105.335 141.401 106.469 142.027 107.131 142.354C107.799 142.682 108.339 142.941 108.741 143.113C109.055 143.264 109.4 143.339 109.748 143.332C110.091 143.337 110.431 143.257 110.737 143.102C111.146 142.923 111.679 142.671 112.354 142.343C113.03 142.014 114.179 141.386 115.756 140.463C117.333 139.539 118.884 138.554 120.355 137.517C122.034 136.306 123.642 134.999 125.169 133.601C126.814 132.128 128.316 130.504 129.655 128.75C130.958 126.998 132.021 125.08 132.813 123.045C133.645 120.983 134.075 118.782 134.083 116.559V87.3591C134.085 87.0415 134.021 86.7276 133.895 86.4356C133.769 86.1436 133.586 85.8808 133.356 85.6618ZM127.71 116.836C127.71 127.421 109.748 136.514 109.748 136.514V91.1879H127.71V116.836Z" fill="var(--color-secondary-700)"/>
|
||||
<path d="M24.6216 122.3C24.7144 123.4 25.6819 124.217 26.7825 124.125C27.8832 124.032 28.7002 123.064 28.6074 121.964L24.6216 122.3ZM151.501 45.7445C152.57 45.4679 153.213 44.3768 152.936 43.3074L148.429 25.8809C148.152 24.8115 147.061 24.1688 145.992 24.4454C144.922 24.722 144.28 25.8131 144.556 26.8825L148.563 42.3728L133.073 46.3793C132.003 46.6559 131.361 47.747 131.637 48.8164C131.914 49.8858 133.005 50.5285 134.074 50.2519L151.501 45.7445ZM28.6074 121.964C26.6788 99.0874 34.4658 75.5543 51.9661 58.054L49.1377 55.2256C30.7695 73.5938 22.5982 98.2999 24.6216 122.3L28.6074 121.964ZM51.9661 58.054C78.5404 31.4797 119.036 27.3026 149.985 45.5315L152.015 42.0849C119.534 22.9534 77.0327 27.3306 49.1377 55.2256L51.9661 58.054Z" fill="var(--color-secondary-700)"/>
|
||||
<path d="M67.4747 185.062C66.4089 185.352 65.7801 186.451 66.0701 187.517L70.797 204.885C71.0871 205.951 72.1862 206.58 73.252 206.29C74.3179 205.999 74.9467 204.9 74.6567 203.834L70.455 188.396L85.8934 184.194C86.9592 183.904 87.5881 182.805 87.298 181.739C87.008 180.674 85.9088 180.045 84.843 180.335L67.4747 185.062ZM192.478 100.283C192.286 99.1952 191.248 98.4697 190.16 98.6625C189.072 98.8552 188.347 99.8931 188.54 100.981L192.478 100.283ZM166.138 172.225C139.813 198.55 99.8271 202.897 68.9932 185.255L67.0068 188.727C99.3669 207.242 141.333 202.687 168.966 175.054L166.138 172.225ZM188.54 100.981C192.985 126.07 185.516 152.847 166.138 172.225L168.966 175.054C189.307 154.713 197.142 126.603 192.478 100.283L188.54 100.981Z" fill="var(--color-secondary-700)"/>
|
||||
<path d="M37.726 108.132C39.283 92.1931 46.1655 76.6765 58.3734 64.4686C75.2893 47.5527 98.5583 40.8618 120.5 44.396" stroke="var(--color-secondary-700)" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M180.595 119.132C179.653 136.119 172.693 152.834 159.717 165.811C144.44 181.088 123.982 188.025 104 186.623" stroke="var(--color-secondary-700)" stroke-width="2" stroke-linecap="round"/>
|
||||
<rect x="153.5" y="56.1317" width="49" height="34" rx="2.5" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
<path d="M153.5 63.6317V63.6317C153.5 66.9454 156.186 69.6317 159.5 69.6317H172.509M202.5 63.6317V63.6317C202.5 66.9454 199.814 69.6317 196.5 69.6317H183.491" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
<mask id="path-8-inside-1_1000_26057" fill="white">
|
||||
<rect x="172" y="65.6317" width="12" height="9" rx="1.25"/>
|
||||
</mask>
|
||||
<rect x="172" y="65.6317" width="12" height="9" rx="1.25" stroke="var(--color-secondary-700)" stroke-width="6" mask="url(#path-8-inside-1_1000_26057)"/>
|
||||
<path d="M187 54.6317C187 54.6317 187 53.6317 187 52.6317C187 51.6317 186.053 50.6317 185.105 50.6317C184.158 50.6317 171.842 50.6317 170.895 50.6317C169.947 50.6317 169 51.6317 169 52.6317C169 53.6317 169 54.6317 169 54.6317" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
<circle cx="48" cy="141" r="10.5" fill="var(--color-background)" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
<path d="M64.9935 168.5H64.9126H64.8318H64.7512H64.6708H64.5905H64.5104H64.4304H64.3506H64.2709H64.1914H64.1121H64.0329H63.9538H63.8749H63.7962H63.7176H63.6392H63.5609H63.4828H63.4048H63.3269H63.2492H63.1717H63.0943H63.017H62.9399H62.8629H62.7861H62.7094H62.6329H62.5565H62.4802H62.4041H62.3281H62.2523H62.1766H62.101H62.0256H61.9503H61.8751H61.8001H61.7252H61.6504H61.5758H61.5013H61.4269H61.3527H61.2786H61.2046H61.1308H61.0571H60.9835H60.91H60.8367H60.7635H60.6904H60.6175H60.5446H60.4719H60.3993H60.3269H60.2545H60.1823H60.1102H60.0382H59.9664H59.8946H59.823H59.7515H59.6801H59.6088H59.5376H59.4666H59.3956H59.3248H59.2541H59.1835H59.113H59.0426H58.9724H58.9022H58.8322H58.7622H58.6924H58.6226H58.553H58.4835H58.4141H58.3448H58.2756H58.2065H58.1375H58.0686H57.9998H57.9311H57.8625H57.794H57.7256H57.6572H57.589H57.5209H57.4529H57.385H57.3172H57.2494H57.1818H57.1142H57.0468H56.9794H56.9121H56.8449H56.7779H56.7108H56.6439H56.5771H56.5103H56.4437H56.3771H56.3106H56.2442H56.1779H56.1117H56.0455H55.9794H55.9134H55.8475H55.7817H55.7159H55.6502H55.5846H55.5191H55.4537H55.3883H55.323H55.2578H55.1926H55.1275H55.0625H54.9976H54.9328H54.868H54.8032H54.7386H54.674H54.6095H54.5451H54.4807H54.4164H54.3521H54.2879H54.2238H54.1598H54.0958H54.0318H53.968H53.9042H53.8404H53.7767H53.7131H53.6495H53.586H53.5226H53.4592H53.3958H53.3325H53.2693H53.2061H53.143H53.0799H53.0169H52.9539H52.891H52.8281H52.7653H52.7025H52.6398H52.5771H52.5145H52.4519H52.3894H52.3269H52.2645H52.202H52.1397H52.0774H52.0151H51.9528H51.8907H51.8285H51.7664H51.7043H51.6423H51.5803H51.5183H51.4564H51.3945H51.3326H51.2708H51.209H51.1472H51.0855H51.0238H50.9621H50.9005H50.8389H50.7773H50.7157H50.6542H50.5927H50.5312H50.4698H50.4084H50.347H50.2856H50.2243H50.1629H50.1016H50.0404H49.9791H49.9178H49.8566H49.7954H49.7342H49.6731H49.6119H49.5508H49.4896H49.4285H49.3674H49.3064H49.2453H49.1842H49.1232H49.0622H49.0011H48.9401H48.8791H48.8181H48.7571H48.6962H48.6352H48.5742H48.5133H48.4523H48.3913H48.3304H48.2694H48.2085H48.1475H48.0866H48.0257H47.9647H47.9038H47.8428H47.7819H47.7209H47.6599H47.599H47.538H47.477H47.416H47.3551H47.2941H47.2331H47.172H47.111H47.05H46.9889H46.9279H46.8668H46.8057H46.7446H46.6835H46.6224H46.5612H46.5001H46.4389H46.3777H46.3165H46.2553H46.194H46.1328H46.0715H46.0102H45.9489H45.8875H45.8261H45.7647H45.7033H45.6418H45.5804H45.5189H45.4573H45.3958H45.3342H45.2726H45.2109H45.1493H45.0876H45.0258H44.9641H44.9023H44.8404H44.7786H44.7166H44.6547H44.5927H44.5307H44.4687H44.4066H44.3445H44.2823H44.2201H44.1578H44.0956H44.0332H43.9709H43.9084H43.846H43.7835H43.7209H43.6583H43.5957H43.533H43.4703H43.4075H43.3447H43.2818H43.2189H43.1559H43.0929H43.0298H42.9666H42.9034H42.8402H42.7769H42.7135H42.6501H42.5867H42.5231H42.4596H42.3959H42.3322H42.2685H42.2046H42.1408H42.0768H42.0128H41.9487H41.8846H41.8204H41.7562H41.6918H41.6274H41.563H41.4985H41.4339H41.3692H41.3045H41.2397H41.1748H41.1098H41.0448H40.9797H40.9146H40.8493H40.784H40.7186H40.6532H40.5876H40.522H40.4563H40.3905H40.3247H40.2588H40.1928H40.1267H40.0605H39.9942H39.9279H39.8615H39.795H39.7284H39.6617H39.5949H39.5281H39.4611H39.3941H39.327H39.2598H39.1925H39.1251H39.0576H38.9901H38.9224H38.8547H38.7868H38.7189H38.6508H38.5827H38.5145H38.4461H38.3777H38.3092H38.2406H38.1719H38.103H38.0341H37.9651H37.896H37.8267H37.7574H37.688H37.6184H37.5488H37.479H37.4092H37.3392H37.2691H37.199H37.1287H37.0583H36.9878H36.9171H36.8464H36.7755H36.7046H36.6335H36.5623H36.491H36.4196H36.3481H36.2764H36.2046H36.1328H36.0607H35.9886H35.9164H35.844H35.7715H35.6989H35.6262H35.5533H35.4803H35.4072H35.334H35.2606H35.1872H35.1135H35.0398H34.9659H34.8919H34.8178H34.7436H34.6692H34.5947H34.52H34.4452H34.3703H34.2952H34.2201H34.1447H34.0693H33.9937H33.9179H33.8421H33.7661H33.6899H33.6136H33.5372H33.4606H33.3839H33.3071H33.2301H33.1529H33.0756H32.9982H32.9206H32.8429H32.765H32.687H32.6088H32.5305H32.452H32.3734H32.2946H32.2157H32.1367H32.0574H31.978H31.8985H31.8188H31.739H31.659H31.5788H31.4985H31.418H31.3374H31.2566H31.1757H31.0946H31.0133H30.9318H30.8503H30.7685H30.6866H30.6045H30.5222H30.4398H30.3572H30.2745H30.1915C30.0489 168.5 29.9693 168.466 29.9164 168.433C29.8553 168.394 29.7779 168.322 29.702 168.192C29.5361 167.906 29.4608 167.48 29.5197 167.111C30.9235 158.322 38.6474 151.574 47.9925 151.574C57.3375 151.574 65.0615 158.322 66.4652 167.111C66.5751 167.799 66.4037 168.054 66.3124 168.149C66.1888 168.278 65.8364 168.5 64.9935 168.5Z" fill="var(--color-background)" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
<circle cx="20" cy="141" r="10.5" fill="var(--color-background)" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
<path d="M36.9935 168.5H36.9126H36.8318H36.7512H36.6708H36.5905H36.5104H36.4304H36.3506H36.2709H36.1914H36.1121H36.0329H35.9538H35.8749H35.7962H35.7176H35.6392H35.5609H35.4828H35.4048H35.3269H35.2492H35.1717H35.0943H35.017H34.9399H34.8629H34.7861H34.7094H34.6329H34.5565H34.4802H34.4041H34.3281H34.2523H34.1766H34.101H34.0256H33.9503H33.8751H33.8001H33.7252H33.6504H33.5758H33.5013H33.4269H33.3527H33.2786H33.2046H33.1308H33.0571H32.9835H32.91H32.8367H32.7635H32.6904H32.6175H32.5446H32.4719H32.3993H32.3269H32.2545H32.1823H32.1102H32.0382H31.9664H31.8946H31.823H31.7515H31.6801H31.6088H31.5376H31.4666H31.3956H31.3248H31.2541H31.1835H31.113H31.0426H30.9724H30.9022H30.8322H30.7622H30.6924H30.6226H30.553H30.4835H30.4141H30.3448H30.2756H30.2065H30.1375H30.0686H29.9998H29.9311H29.8625H29.794H29.7256H29.6572H29.589H29.5209H29.4529H29.385H29.3172H29.2494H29.1818H29.1142H29.0468H28.9794H28.9121H28.8449H28.7779H28.7108H28.6439H28.5771H28.5103H28.4437H28.3771H28.3106H28.2442H28.1779H28.1117H28.0455H27.9794H27.9134H27.8475H27.7817H27.7159H27.6502H27.5846H27.5191H27.4537H27.3883H27.323H27.2578H27.1926H27.1275H27.0625H26.9976H26.9328H26.868H26.8032H26.7386H26.674H26.6095H26.5451H26.4807H26.4164H26.3521H26.2879H26.2238H26.1598H26.0958H26.0318H25.968H25.9042H25.8404H25.7767H25.7131H25.6495H25.586H25.5226H25.4592H25.3958H25.3325H25.2693H25.2061H25.143H25.0799H25.0169H24.9539H24.891H24.8281H24.7653H24.7025H24.6398H24.5771H24.5145H24.4519H24.3894H24.3269H24.2645H24.202H24.1397H24.0774H24.0151H23.9528H23.8907H23.8285H23.7664H23.7043H23.6423H23.5803H23.5183H23.4564H23.3945H23.3326H23.2708H23.209H23.1472H23.0855H23.0238H22.9621H22.9005H22.8389H22.7773H22.7157H22.6542H22.5927H22.5312H22.4698H22.4084H22.347H22.2856H22.2243H22.1629H22.1016H22.0404H21.9791H21.9178H21.8566H21.7954H21.7342H21.6731H21.6119H21.5508H21.4896H21.4285H21.3674H21.3064H21.2453H21.1842H21.1232H21.0622H21.0011H20.9401H20.8791H20.8181H20.7571H20.6962H20.6352H20.5742H20.5133H20.4523H20.3913H20.3304H20.2694H20.2085H20.1475H20.0866H20.0257H19.9647H19.9038H19.8428H19.7819H19.7209H19.6599H19.599H19.538H19.477H19.416H19.3551H19.2941H19.2331H19.172H19.111H19.05H18.9889H18.9279H18.8668H18.8057H18.7446H18.6835H18.6224H18.5612H18.5001H18.4389H18.3777H18.3165H18.2553H18.194H18.1328H18.0715H18.0102H17.9489H17.8875H17.8261H17.7647H17.7033H17.6418H17.5804H17.5189H17.4573H17.3958H17.3342H17.2726H17.2109H17.1493H17.0876H17.0258H16.9641H16.9023H16.8404H16.7786H16.7166H16.6547H16.5927H16.5307H16.4687H16.4066H16.3445H16.2823H16.2201H16.1578H16.0956H16.0332H15.9709H15.9084H15.846H15.7835H15.7209H15.6583H15.5957H15.533H15.4703H15.4075H15.3447H15.2818H15.2189H15.1559H15.0929H15.0298H14.9666H14.9034H14.8402H14.7769H14.7135H14.6501H14.5867H14.5231H14.4596H14.3959H14.3322H14.2685H14.2046H14.1408H14.0768H14.0128H13.9487H13.8846H13.8204H13.7562H13.6918H13.6274H13.563H13.4985H13.4339H13.3692H13.3045H13.2397H13.1748H13.1098H13.0448H12.9797H12.9146H12.8493H12.784H12.7186H12.6532H12.5876H12.522H12.4563H12.3905H12.3247H12.2588H12.1928H12.1267H12.0605H11.9942H11.9279H11.8615H11.795H11.7284H11.6617H11.5949H11.5281H11.4611H11.3941H11.327H11.2598H11.1925H11.1251H11.0576H10.9901H10.9224H10.8547H10.7868H10.7189H10.6508H10.5827H10.5145H10.4461H10.3777H10.3092H10.2406H10.1719H10.103H10.0341H9.9651H9.89597H9.82674H9.75741H9.68798H9.61843H9.54879H9.47904H9.40918H9.33921H9.26914H9.19896H9.12867H9.05826H8.98775H8.91713H8.8464H8.77555H8.70459H8.63351H8.56232H8.49102H8.4196H8.34807H8.27641H8.20464H8.13276H8.06075H7.98862H7.91638H7.84401H7.77152H7.69891H7.62617H7.55332H7.48034H7.40723H7.334H7.26064H7.18716H7.11355H7.03981H6.96594H6.89195H6.81782H6.74356H6.66918H6.59466H6.52H6.44522H6.3703H6.29525H6.22006H6.14474H6.06928H5.99368H5.91795H5.84208H5.76607H5.68992H5.61363H5.5372H5.46062H5.38391H5.30705H5.23005H5.15291H5.07562H4.99819H4.92061H4.84288H4.76501H4.68699H4.60882H4.5305H4.45203H4.37342H4.29465H4.21573H4.13665H4.05743H3.97805H3.89851H3.81882H3.73898H3.65898H3.57882H3.49851H3.41804H3.33741H3.25662H3.17566H3.09455H3.01328H2.93185H2.85025H2.76849H2.68657H2.60448H2.52223H2.43981H2.35722H2.27447H2.19155C2.04893 168.5 1.96927 168.466 1.91645 168.433C1.85532 168.394 1.77792 168.322 1.702 168.192C1.53613 167.906 1.46078 167.48 1.51975 167.111C2.92347 158.322 10.6474 151.574 19.9925 151.574C29.3375 151.574 37.0615 158.322 38.4652 167.111C38.5751 167.799 38.4037 168.054 38.3124 168.149C38.1888 168.278 37.8364 168.5 36.9935 168.5Z" fill="var(--color-background)" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
<circle cx="34" cy="154" r="10.5" fill="var(--color-background)" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
<path d="M50.9935 181.5H50.9126H50.8318H50.7512H50.6708H50.5905H50.5104H50.4304H50.3506H50.2709H50.1914H50.1121H50.0329H49.9538H49.8749H49.7962H49.7176H49.6392H49.5609H49.4828H49.4048H49.3269H49.2492H49.1717H49.0943H49.017H48.9399H48.8629H48.7861H48.7094H48.6329H48.5565H48.4802H48.4041H48.3281H48.2523H48.1766H48.101H48.0256H47.9503H47.8751H47.8001H47.7252H47.6504H47.5758H47.5013H47.4269H47.3527H47.2786H47.2046H47.1308H47.0571H46.9835H46.91H46.8367H46.7635H46.6904H46.6175H46.5446H46.4719H46.3993H46.3269H46.2545H46.1823H46.1102H46.0382H45.9664H45.8946H45.823H45.7515H45.6801H45.6088H45.5376H45.4666H45.3956H45.3248H45.2541H45.1835H45.113H45.0426H44.9724H44.9022H44.8322H44.7622H44.6924H44.6226H44.553H44.4835H44.4141H44.3448H44.2756H44.2065H44.1375H44.0686H43.9998H43.9311H43.8625H43.794H43.7256H43.6572H43.589H43.5209H43.4529H43.385H43.3172H43.2494H43.1818H43.1142H43.0468H42.9794H42.9121H42.8449H42.7779H42.7108H42.6439H42.5771H42.5103H42.4437H42.3771H42.3106H42.2442H42.1779H42.1117H42.0455H41.9794H41.9134H41.8475H41.7817H41.7159H41.6502H41.5846H41.5191H41.4537H41.3883H41.323H41.2578H41.1926H41.1275H41.0625H40.9976H40.9328H40.868H40.8032H40.7386H40.674H40.6095H40.5451H40.4807H40.4164H40.3521H40.2879H40.2238H40.1598H40.0958H40.0318H39.968H39.9042H39.8404H39.7767H39.7131H39.6495H39.586H39.5226H39.4592H39.3958H39.3325H39.2693H39.2061H39.143H39.0799H39.0169H38.9539H38.891H38.8281H38.7653H38.7025H38.6398H38.5771H38.5145H38.4519H38.3894H38.3269H38.2645H38.202H38.1397H38.0774H38.0151H37.9528H37.8907H37.8285H37.7664H37.7043H37.6423H37.5803H37.5183H37.4564H37.3945H37.3326H37.2708H37.209H37.1472H37.0855H37.0238H36.9621H36.9005H36.8389H36.7773H36.7157H36.6542H36.5927H36.5312H36.4698H36.4084H36.347H36.2856H36.2243H36.1629H36.1016H36.0404H35.9791H35.9178H35.8566H35.7954H35.7342H35.6731H35.6119H35.5508H35.4896H35.4285H35.3674H35.3064H35.2453H35.1842H35.1232H35.0622H35.0011H34.9401H34.8791H34.8181H34.7571H34.6962H34.6352H34.5742H34.5133H34.4523H34.3913H34.3304H34.2694H34.2085H34.1475H34.0866H34.0257H33.9647H33.9038H33.8428H33.7819H33.7209H33.6599H33.599H33.538H33.477H33.416H33.3551H33.2941H33.2331H33.172H33.111H33.05H32.9889H32.9279H32.8668H32.8057H32.7446H32.6835H32.6224H32.5612H32.5001H32.4389H32.3777H32.3165H32.2553H32.194H32.1328H32.0715H32.0102H31.9489H31.8875H31.8261H31.7647H31.7033H31.6418H31.5804H31.5189H31.4573H31.3958H31.3342H31.2726H31.2109H31.1493H31.0876H31.0258H30.9641H30.9023H30.8404H30.7786H30.7166H30.6547H30.5927H30.5307H30.4687H30.4066H30.3445H30.2823H30.2201H30.1578H30.0956H30.0332H29.9709H29.9084H29.846H29.7835H29.7209H29.6583H29.5957H29.533H29.4703H29.4075H29.3447H29.2818H29.2189H29.1559H29.0929H29.0298H28.9666H28.9034H28.8402H28.7769H28.7135H28.6501H28.5867H28.5231H28.4596H28.3959H28.3322H28.2685H28.2046H28.1408H28.0768H28.0128H27.9487H27.8846H27.8204H27.7562H27.6918H27.6274H27.563H27.4985H27.4339H27.3692H27.3045H27.2397H27.1748H27.1098H27.0448H26.9797H26.9146H26.8493H26.784H26.7186H26.6532H26.5876H26.522H26.4563H26.3905H26.3247H26.2588H26.1928H26.1267H26.0605H25.9942H25.9279H25.8615H25.795H25.7284H25.6617H25.5949H25.5281H25.4611H25.3941H25.327H25.2598H25.1925H25.1251H25.0576H24.9901H24.9224H24.8547H24.7868H24.7189H24.6508H24.5827H24.5145H24.4461H24.3777H24.3092H24.2406H24.1719H24.103H24.0341H23.9651H23.896H23.8267H23.7574H23.688H23.6184H23.5488H23.479H23.4092H23.3392H23.2691H23.199H23.1287H23.0583H22.9878H22.9171H22.8464H22.7755H22.7046H22.6335H22.5623H22.491H22.4196H22.3481H22.2764H22.2046H22.1328H22.0607H21.9886H21.9164H21.844H21.7715H21.6989H21.6262H21.5533H21.4803H21.4072H21.334H21.2606H21.1872H21.1135H21.0398H20.9659H20.8919H20.8178H20.7436H20.6692H20.5947H20.52H20.4452H20.3703H20.2952H20.2201H20.1447H20.0693H19.9937H19.9179H19.8421H19.7661H19.6899H19.6136H19.5372H19.4606H19.3839H19.3071H19.2301H19.1529H19.0756H18.9982H18.9206H18.8429H18.765H18.687H18.6088H18.5305H18.452H18.3734H18.2946H18.2157H18.1367H18.0574H17.978H17.8985H17.8188H17.739H17.659H17.5788H17.4985H17.418H17.3374H17.2566H17.1757H17.0946H17.0133H16.9318H16.8503H16.7685H16.6866H16.6045H16.5222H16.4398H16.3572H16.2745H16.1915C16.045 181.5 15.9628 181.465 15.9092 181.432C15.8479 181.394 15.772 181.324 15.6978 181.198C15.5354 180.922 15.4617 180.509 15.5193 180.153C16.9196 171.496 24.6325 164.823 33.9925 164.823C43.3524 164.823 51.0654 171.496 52.4657 180.153C52.574 180.823 52.4052 181.064 52.319 181.152C52.1962 181.279 51.8413 181.5 50.9935 181.5Z" fill="var(--color-background)" stroke="var(--color-secondary-700)" stroke-width="3"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,8 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
// Component is used so that the SVG can embed CSS color variables
|
||||
@Component({
|
||||
selector: "app-image-org-subscription-hidden",
|
||||
templateUrl: "image-subscription-hidden.component.svg",
|
||||
})
|
||||
export class ImageSubscriptionHiddenComponent {}
|
||||
@@ -1,13 +0,0 @@
|
||||
<div class="mt-5 d-flex justify-content-center" *ngIf="loading">
|
||||
<div>
|
||||
<img class="mb-4 logo logo-themed" alt="Bitwarden" />
|
||||
<p class="text-center">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-2x text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-accept-family-sponsorship",
|
||||
templateUrl: "accept-family-sponsorship.component.html",
|
||||
})
|
||||
export class AcceptFamilySponsorshipComponent extends BaseAcceptComponent {
|
||||
failedShortMessage = "inviteAcceptFailedShort";
|
||||
failedMessage = "inviteAcceptFailed";
|
||||
|
||||
requiredParameters = ["email", "token"];
|
||||
|
||||
async authedHandler(qParams: any) {
|
||||
this.router.navigate(["/setup/families-for-enterprise"], { queryParams: qParams });
|
||||
}
|
||||
|
||||
async unauthedHandler(qParams: any) {
|
||||
if (!qParams.register) {
|
||||
this.router.navigate(["/login"], { queryParams: { email: qParams.email } });
|
||||
} else {
|
||||
this.router.navigate(["/register"], { queryParams: { email: qParams.email } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuard } from "jslib-angular/guards/auth.guard";
|
||||
import { LockGuard } from "jslib-angular/guards/lock.guard";
|
||||
import { UnauthGuard } from "jslib-angular/guards/unauth.guard";
|
||||
|
||||
import { AcceptEmergencyComponent } from "./accounts/accept-emergency.component";
|
||||
import { AcceptOrganizationComponent } from "./accounts/accept-organization.component";
|
||||
import { HintComponent } from "./accounts/hint.component";
|
||||
import { LockComponent } from "./accounts/lock.component";
|
||||
import { LoginComponent } from "./accounts/login.component";
|
||||
import { RecoverDeleteComponent } from "./accounts/recover-delete.component";
|
||||
import { RecoverTwoFactorComponent } from "./accounts/recover-two-factor.component";
|
||||
import { RegisterComponent } from "./accounts/register.component";
|
||||
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||
import { SsoComponent } from "./accounts/sso.component";
|
||||
import { TwoFactorComponent } from "./accounts/two-factor.component";
|
||||
import { UpdatePasswordComponent } from "./accounts/update-password.component";
|
||||
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
|
||||
import { VerifyEmailTokenComponent } from "./accounts/verify-email-token.component";
|
||||
import { VerifyRecoverDeleteComponent } from "./accounts/verify-recover-delete.component";
|
||||
import { HomeGuard } from "./guards/home.guard";
|
||||
import { FrontendLayoutComponent } from "./layouts/frontend-layout.component";
|
||||
import { UserLayoutComponent } from "./layouts/user-layout.component";
|
||||
import { AcceptFamilySponsorshipComponent } from "./organizations/sponsorships/accept-family-sponsorship.component";
|
||||
import { FamiliesForEnterpriseSetupComponent } from "./organizations/sponsorships/families-for-enterprise-setup.component";
|
||||
import { AccessComponent } from "./send/access.component";
|
||||
import { SendComponent } from "./send/send.component";
|
||||
import { AccountComponent } from "./settings/account.component";
|
||||
import { CreateOrganizationComponent } from "./settings/create-organization.component";
|
||||
import { DomainRulesComponent } from "./settings/domain-rules.component";
|
||||
import { EmergencyAccessViewComponent } from "./settings/emergency-access-view.component";
|
||||
import { EmergencyAccessComponent } from "./settings/emergency-access.component";
|
||||
import { PreferencesComponent } from "./settings/preferences.component";
|
||||
import { SettingsComponent } from "./settings/settings.component";
|
||||
import { SponsoredFamiliesComponent } from "./settings/sponsored-families.component";
|
||||
import { ExportComponent } from "./tools/export.component";
|
||||
import { GeneratorComponent } from "./tools/generator.component";
|
||||
import { ImportComponent } from "./tools/import.component";
|
||||
import { ToolsComponent } from "./tools/tools.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: "",
|
||||
component: FrontendLayoutComponent,
|
||||
data: { doNotSaveUrl: true },
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
pathMatch: "full",
|
||||
children: [], // Children lets us have an empty component.
|
||||
canActivate: [HomeGuard], // Redirects either to vault, login or lock page.
|
||||
},
|
||||
{ path: "login", component: LoginComponent, canActivate: [UnauthGuard] },
|
||||
{ path: "2fa", component: TwoFactorComponent, canActivate: [UnauthGuard] },
|
||||
{
|
||||
path: "register",
|
||||
component: RegisterComponent,
|
||||
canActivate: [UnauthGuard],
|
||||
data: { titleId: "createAccount" },
|
||||
},
|
||||
{
|
||||
path: "sso",
|
||||
component: SsoComponent,
|
||||
canActivate: [UnauthGuard],
|
||||
data: { titleId: "enterpriseSingleSignOn" },
|
||||
},
|
||||
{
|
||||
path: "set-password",
|
||||
component: SetPasswordComponent,
|
||||
data: { titleId: "setMasterPassword" },
|
||||
},
|
||||
{
|
||||
path: "hint",
|
||||
component: HintComponent,
|
||||
canActivate: [UnauthGuard],
|
||||
data: { titleId: "passwordHint" },
|
||||
},
|
||||
{
|
||||
path: "lock",
|
||||
component: LockComponent,
|
||||
canActivate: [LockGuard],
|
||||
},
|
||||
{ path: "verify-email", component: VerifyEmailTokenComponent },
|
||||
{
|
||||
path: "accept-organization",
|
||||
component: AcceptOrganizationComponent,
|
||||
data: { titleId: "joinOrganization", doNotSaveUrl: false },
|
||||
},
|
||||
{
|
||||
path: "accept-emergency",
|
||||
component: AcceptEmergencyComponent,
|
||||
data: { titleId: "acceptEmergency", doNotSaveUrl: false },
|
||||
},
|
||||
{
|
||||
path: "accept-families-for-enterprise",
|
||||
component: AcceptFamilySponsorshipComponent,
|
||||
data: { titleId: "acceptFamilySponsorship", doNotSaveUrl: false },
|
||||
},
|
||||
{ path: "recover", pathMatch: "full", redirectTo: "recover-2fa" },
|
||||
{
|
||||
path: "recover-2fa",
|
||||
component: RecoverTwoFactorComponent,
|
||||
canActivate: [UnauthGuard],
|
||||
data: { titleId: "recoverAccountTwoStep" },
|
||||
},
|
||||
{
|
||||
path: "recover-delete",
|
||||
component: RecoverDeleteComponent,
|
||||
canActivate: [UnauthGuard],
|
||||
data: { titleId: "deleteAccount" },
|
||||
},
|
||||
{
|
||||
path: "verify-recover-delete",
|
||||
component: VerifyRecoverDeleteComponent,
|
||||
canActivate: [UnauthGuard],
|
||||
data: { titleId: "deleteAccount" },
|
||||
},
|
||||
{
|
||||
path: "send/:sendId/:key",
|
||||
component: AccessComponent,
|
||||
data: { title: "Bitwarden Send" },
|
||||
},
|
||||
{
|
||||
path: "update-temp-password",
|
||||
component: UpdateTempPasswordComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: { titleId: "updateTempPassword" },
|
||||
},
|
||||
{
|
||||
path: "update-password",
|
||||
component: UpdatePasswordComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: { titleId: "updatePassword" },
|
||||
},
|
||||
{
|
||||
path: "remove-password",
|
||||
component: RemovePasswordComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: { titleId: "removeMasterPassword" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
component: UserLayoutComponent,
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{
|
||||
path: "vault",
|
||||
loadChildren: async () =>
|
||||
(await import("./modules/vault/modules/individual-vault/individual-vault.module"))
|
||||
.IndividualVaultModule,
|
||||
},
|
||||
{ path: "sends", component: SendComponent, data: { title: "Send" } },
|
||||
{
|
||||
path: "create-organization",
|
||||
component: CreateOrganizationComponent,
|
||||
data: { titleId: "newOrganization" },
|
||||
},
|
||||
{
|
||||
path: "settings",
|
||||
component: SettingsComponent,
|
||||
children: [
|
||||
{ path: "", pathMatch: "full", redirectTo: "account" },
|
||||
{ path: "account", component: AccountComponent, data: { titleId: "myAccount" } },
|
||||
{
|
||||
path: "preferences",
|
||||
component: PreferencesComponent,
|
||||
data: { titleId: "preferences" },
|
||||
},
|
||||
{
|
||||
path: "security",
|
||||
loadChildren: async () =>
|
||||
(await import("./settings/security-routing.module")).SecurityRoutingModule,
|
||||
},
|
||||
{
|
||||
path: "domain-rules",
|
||||
component: DomainRulesComponent,
|
||||
data: { titleId: "domainRules" },
|
||||
},
|
||||
{
|
||||
path: "subscription",
|
||||
loadChildren: async () =>
|
||||
(await import("./settings/subscription-routing.module")).SubscriptionRoutingModule,
|
||||
},
|
||||
{
|
||||
path: "emergency-access",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
component: EmergencyAccessComponent,
|
||||
data: { titleId: "emergencyAccess" },
|
||||
},
|
||||
{
|
||||
path: ":id",
|
||||
component: EmergencyAccessViewComponent,
|
||||
data: { titleId: "emergencyAccess" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "sponsored-families",
|
||||
component: SponsoredFamiliesComponent,
|
||||
data: { titleId: "sponsoredFamilies" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "tools",
|
||||
component: ToolsComponent,
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{ path: "", pathMatch: "full", redirectTo: "generator" },
|
||||
{ path: "import", component: ImportComponent, data: { titleId: "importData" } },
|
||||
{ path: "export", component: ExportComponent, data: { titleId: "exportVault" } },
|
||||
{
|
||||
path: "generator",
|
||||
component: GeneratorComponent,
|
||||
data: { titleId: "generator" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "reports",
|
||||
loadChildren: async () =>
|
||||
(await import("./reports/reports-routing.module")).ReportsRoutingModule,
|
||||
},
|
||||
{ path: "setup/families-for-enterprise", component: FamiliesForEnterpriseSetupComponent },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "organizations",
|
||||
loadChildren: () =>
|
||||
import("./organizations/organization-routing.module").then(
|
||||
(m) => m.OrganizationsRoutingModule
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, {
|
||||
useHash: true,
|
||||
paramsInheritanceStrategy: "always",
|
||||
// enableTracing: true,
|
||||
}),
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class OssRoutingModule {}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { LooseComponentsModule } from "./modules/loose-components.module";
|
||||
import { OrganizationManageModule } from "./modules/organizations/manage/organization-manage.module";
|
||||
import { OrganizationUserModule } from "./modules/organizations/users/organization-user.module";
|
||||
import { PipesModule } from "./modules/pipes/pipes.module";
|
||||
import { SharedModule } from "./modules/shared.module";
|
||||
import { VaultFilterModule } from "./modules/vault-filter/vault-filter.module";
|
||||
import { OrganizationBadgeModule } from "./modules/vault/modules/organization-badge/organization-badge.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
LooseComponentsModule,
|
||||
VaultFilterModule,
|
||||
OrganizationBadgeModule,
|
||||
PipesModule,
|
||||
OrganizationManageModule,
|
||||
OrganizationUserModule,
|
||||
],
|
||||
exports: [LooseComponentsModule, VaultFilterModule, OrganizationBadgeModule, PipesModule],
|
||||
bootstrap: [],
|
||||
})
|
||||
export class OssModule {}
|
||||
@@ -1,33 +0,0 @@
|
||||
<app-navbar></app-navbar>
|
||||
<div class="container page-content">
|
||||
<div class="page-header d-flex">
|
||||
<h1>{{ "providers" | i18n }}</h1>
|
||||
</div>
|
||||
<p *ngIf="!loaded" class="text-muted">
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</p>
|
||||
<ng-container *ngIf="loaded">
|
||||
<table class="table table-hover table-list" *ngIf="providers && providers.length">
|
||||
<tbody>
|
||||
<tr *ngFor="let p of providers">
|
||||
<td width="30">
|
||||
<app-avatar [data]="p.name" size="25" [circle]="true" [fontSize]="14"></app-avatar>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" [routerLink]="['/providers', p.id]">{{ p.name }}</a>
|
||||
<ng-container *ngIf="!p.enabled">
|
||||
<i
|
||||
class="bwi bwi-exclamation-triangle text-danger"
|
||||
title="{{ 'providerIsDisabled' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "providerIsDisabled" | i18n }}</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<app-footer></app-footer>
|
||||
@@ -1,25 +0,0 @@
|
||||
<a
|
||||
class="tw-border tw-border-solid tw-border-secondary-300 tw-rounded tw-overflow-hidden tw-h-full tw-w-72 tw-block !tw-text-main hover:tw-no-underline hover:tw-scale-105 tw-transition-all focus:tw-outline-none focus:tw-ring focus:tw-ring-offset-2 focus:tw-ring-primary-700"
|
||||
[routerLink]="route"
|
||||
(click)="click()"
|
||||
>
|
||||
<div class="tw-relative">
|
||||
<div
|
||||
class="tw-text-center tw-h-28 tw-flex tw-bg-background-alt2 tw-text-primary-300"
|
||||
[ngClass]="{ 'tw-grayscale': premium }"
|
||||
>
|
||||
<div class="tw-m-auto" [innerHtml]="icon"></div>
|
||||
</div>
|
||||
<div class="tw-p-5" [ngClass]="{ 'tw-grayscale': report.requiresPremium }">
|
||||
<h3 class="tw-text-xl tw-font-bold tw-mb-4">{{ report.title | i18n }}</h3>
|
||||
<p class="tw-mb-0">{{ report.description | i18n }}</p>
|
||||
</div>
|
||||
<span
|
||||
bitBadge
|
||||
badgeType="success"
|
||||
class="tw-absolute tw-left-2 tw-top-2 tw-leading-none"
|
||||
*ngIf="premium"
|
||||
>{{ "premium" | i18n }}</span
|
||||
>
|
||||
</div>
|
||||
</a>
|
||||
@@ -1,166 +0,0 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
export enum ReportTypes {
|
||||
"exposedPasswords" = "exposedPasswords",
|
||||
"reusedPasswords" = "reusedPasswords",
|
||||
"weakPasswords" = "weakPasswords",
|
||||
"unsecuredWebsites" = "unsecuredWebsites",
|
||||
"inactive2fa" = "inactive2fa",
|
||||
"dataBreach" = "dataBreach",
|
||||
}
|
||||
|
||||
type ReportEntry = {
|
||||
title: string;
|
||||
description: string;
|
||||
route: string;
|
||||
icon: string;
|
||||
requiresPremium: boolean;
|
||||
};
|
||||
|
||||
const reports: Record<ReportTypes, ReportEntry> = {
|
||||
exposedPasswords: {
|
||||
title: "exposedPasswordsReport",
|
||||
description: "exposedPasswordsReportDesc",
|
||||
route: "exposed-passwords-report",
|
||||
icon: `
|
||||
<svg width="101" height="77" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.374 50.192a26.42 26.42 0 0 0 9.111 1.608c14.34 0 25.965-11.372 25.965-25.4 0-.337-.007-.673-.02-1.008h25.299v34.85H32.374v-10.05Z" fill="currentColor" />
|
||||
<path d="M15.805 26.4c0 14.028 11.625 25.4 25.965 25.4s25.964-11.372 25.964-25.4C67.734 12.372 56.11 1 41.77 1 27.43 1 15.805 12.372 15.805 26.4Z" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M27.914 47.849a1 1 0 0 0-2 0h2Zm68.288-26.792a2.12 2.12 0 0 1 2.14 2.11h2c0-2.253-1.83-4.11-4.14-4.11v2Zm2.14 2.11v40.552h2V23.167h-2Zm0 40.552c0 1.172-.958 2.11-2.14 2.11v2c2.25 0 4.14-1.798 4.14-4.11h-2Zm-2.14 2.11H30.054v2h66.148v-2Zm-66.148 0a2.12 2.12 0 0 1-2.14-2.11h-2a4.12 4.12 0 0 0 4.14 4.11v-2Zm-2.14-2.11V47.85h-2v15.87h2Zm39.254-42.662h29.034v-2H67.168v2Z" fill="#fff" />
|
||||
<path d="M67.203 25.56h25.64v34.85H32.487V50.011" stroke="#fff" stroke-width="2" stroke-linejoin="round" />
|
||||
<path d="M47.343 76h31.571" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M57.557 66.83V76M67.771 66.83V76" stroke="#fff" stroke-width="2" stroke-linejoin="round" />
|
||||
<path d="m20.995 42.873-3.972 3.972-14.61 14.61a3.413 3.413 0 0 0 0 4.826v0a3.413 3.413 0 0 0 4.827 0l14.61-14.61 3.972-3.972" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M86.037 32.488H71.845M86.037 37.81H76.28M71.845 37.81h-6.652M86.037 43.132h-6.209M74.95 43.132H61.2M86.037 48.454H71.845M66.967 48.454h-7.54M86.037 53.776H66.08M61.201 53.776h-11.53M44.793 53.776h-7.096" stroke="#fff" stroke-width="2" stroke-linecap="round" />
|
||||
<rect width="40.801" height="9.757" rx="4" transform="matrix(-1 0 0 1 61.201 14.748)" stroke="#fff" stroke-width="2" />
|
||||
<path d="M16.852 33.375h28.375a4 4 0 0 1 4 4v1.757a4 4 0 0 1-4 4H22.174M66.523 33.375h-3.539a4 4 0 0 0-4 4v3.761c0 1.102.894 1.996 1.996 1.996v0" stroke="#fff" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
reusedPasswords: {
|
||||
title: "reusedPasswordsReport",
|
||||
description: "reusedPasswordsReportDesc",
|
||||
route: "reused-passwords-report",
|
||||
icon: `
|
||||
<svg width="102" height="102" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M57.983 15.06a35.664 35.664 0 0 1 14.531 6.27c16.164 11.78 19.585 34.613 7.643 51a37.227 37.227 0 0 1-6.81 7.138m-32.842 6.697a35.708 35.708 0 0 1-11.239-5.495c-16.163-11.78-19.585-34.613-7.642-51a37.55 37.55 0 0 1 3.295-3.929" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M93.909 64.598H7.72c-.708 0-1.275-.662-1.275-1.49V40.273c0-.828.567-1.49 1.275-1.49H93.91c.708 0 1.275.663 1.275 1.49v22.837c.047.827-.567 1.49-1.275 1.49Z" fill="currentColor" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M21.532 52.186v-5.965M21.532 52.187l5.748-1.844M21.532 52.186l3.524 4.881M21.531 52.186l-3.47 4.881M21.532 52.187l-5.694-1.844M40.944 52.186v-5.965M40.944 52.187l5.694-1.844M40.944 52.187l3.525 4.88M40.944 52.187l-3.525 4.88M40.944 52.187l-5.694-1.844M54.849 57.337h11.294M74.21 57.337h11.295M41.75 83l.71 4.75-4.75.71M58.664 18.66 56 14.665 59.996 12" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
weakPasswords: {
|
||||
title: "weakPasswordsReport",
|
||||
description: "weakPasswordsReportDesc",
|
||||
route: "weak-passwords-report",
|
||||
icon: `
|
||||
<svg width="78" height="78" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M66.493 64.415V77H9.979V64.324M9.979 44.065V32.106h56.514v12.148" stroke="#fff" stroke-width="2" stroke-linejoin="round" />
|
||||
<path d="M75.44 64.852H2.085c-.603 0-1.085-.555-1.085-1.25V44.448c0-.694.482-1.25 1.085-1.25H75.44c.603 0 1.085.556 1.085 1.25v19.156c.04.694-.482 1.25-1.085 1.25Z" fill="currentColor" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M13.84 54.56v-5.077M13.84 54.56l4.893-1.57M13.84 54.56l3 4.153M13.84 54.56l-2.954 4.153M13.84 54.56l-4.846-1.57M30.363 54.56v-5.077M30.363 54.56l4.846-1.57M30.363 54.56l3 4.153M30.363 54.56l-3 4.153M30.363 54.56l-4.846-1.57M42.197 59.042h9.506M58.57 59.042h9.507" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M20.863 31.364c-.274-5.285 0-15.817 1.093-18.863 1.276-3.554 6.233-10.826 15.856-11.482 4.83-.273 15.2 2.296 18.043 14.763" stroke="#fff" stroke-width="2" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
unsecuredWebsites: {
|
||||
title: "unsecuredWebsitesReport",
|
||||
description: "unsecuredWebsitesReportDesc",
|
||||
route: "unsecured-websites-report",
|
||||
icon: `
|
||||
<svg width="113" height="76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.71 12.983h110.362v55.11a6 6 0 0 1-6 6H7.711a6 6 0 0 1-6-6v-55.11Z" fill="currentColor" />
|
||||
<rect x="1" y="1.073" width="110.5" height="73.454" rx="9" stroke="#fff" stroke-width="2" />
|
||||
<path d="M89.48 8.048V7.47M96.363 8.048V7.47M103.246 8.048V7.47" stroke="#fff" stroke-width="4" stroke-linecap="round" />
|
||||
<path d="M0 12.983h111.217" stroke="#fff" stroke-width="2" />
|
||||
<path d="m93.236 44.384-18.42-11.026 2.93 21.266 5.582-5.237 4.27 6.46 2.98-1.971-4.26-6.446 6.918-3.046Z" fill="#175DDC" stroke="#fff" stroke-width="2" stroke-linejoin="round" />
|
||||
<rect width="96.673" height="6.886" rx="3.443" transform="matrix(-1 0 0 1 104.373 18.721)" stroke="#fff" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
inactive2fa: {
|
||||
title: "inactive2faReport",
|
||||
description: "inactive2faReportDesc",
|
||||
route: "inactive-two-factor-report",
|
||||
icon: `
|
||||
<svg width="42" height="75" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" stroke="#fff" stroke-width="2" d="M1 13.121h39.595v48.758H1z" />
|
||||
<rect x="1" y="1" width="39.595" height="73" rx="8" stroke="#fff" stroke-width="2" />
|
||||
<path stroke="#fff" stroke-width="2" stroke-linecap="round" d="M12.344 8.091h16.907M18.907 67.424h3.025M31.503 32.515c-2.047-4.337-6.717-7.061-11.73-6.414a11.356 11.356 0 0 0-9.125 7.126M10.816 42.016c2.047 4.337 6.718 7.062 11.73 6.414 4.346-.562 7.8-3.51 9.213-7.358" />
|
||||
<path d="m33.584 29.293-1.295 4.625-4.625-1.295M8.523 44.725l1.441-4.581 4.582 1.441" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
dataBreach: {
|
||||
title: "dataBreachReport",
|
||||
description: "breachDesc",
|
||||
route: "breach-report",
|
||||
icon: `
|
||||
<svg width="58" height="75" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M39.569 74H13.007a7 7 0 0 1-7-7V31.077a7 7 0 0 1 7-7h19.101a7 7 0 0 1 4.988 2.088l7.46 7.576a7 7 0 0 1 2.013 4.912V67a7 7 0 0 1-7 7Z" fill="#175DDC" stroke="#fff" stroke-width="2" />
|
||||
<path d="M44.576 69.055H18.015a7 7 0 0 1-7-7V26.132a7 7 0 0 1 7-7h19.1a7 7 0 0 1 4.988 2.088l7.46 7.576a7 7 0 0 1 2.013 4.911v28.348a7 7 0 0 1-7 7Z" fill="#175DDC" stroke="#fff" stroke-width="2" />
|
||||
<path d="M50 63.698H23.439a7 7 0 0 1-7-7V20.775a7 7 0 0 1 7-7h19.1a7 7 0 0 1 4.988 2.088l7.46 7.575A7 7 0 0 1 57 28.35v28.348a7 7 0 0 1-7 7Z" fill="#175DDC" stroke="#fff" stroke-width="2" />
|
||||
<path d="M44.648 13.599v3.95a8 8 0 0 0 8 8h4.518" stroke="#fff" stroke-width="2" />
|
||||
<path stroke="#fff" stroke-width="2" stroke-linecap="round" d="M23.533 37.736H49.49M23.533 46.802H49.49M23.533 42.269H49.49M23.533 55.456H49.49M23.533 50.923H49.49" />
|
||||
<path d="M1 16.483C1 7.944 8.013 1 16.69 1c8.678 0 15.691 6.944 15.691 15.483 0 8.54-7.013 15.484-15.69 15.484C8.012 31.967 1 25.023 1 16.484Z" fill="#518FFF" stroke="#fff" stroke-width="2" />
|
||||
<path d="m16.562 7.979.1 11.538" stroke="#fff" stroke-width="2" stroke-linecap="round" />
|
||||
<ellipse rx="1.252" ry="1.236" transform="rotate(-.479 2802.219 -1964.476) skewX(.012)" fill="#fff" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: false,
|
||||
},
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-report-card",
|
||||
templateUrl: "report-card.component.html",
|
||||
})
|
||||
export class ReportCardComponent implements OnInit {
|
||||
@Input() type: ReportTypes;
|
||||
|
||||
report: ReportEntry;
|
||||
|
||||
hasPremium: boolean;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private messagingService: MessagingService,
|
||||
private sanitizer: DomSanitizer
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.report = reports[this.type];
|
||||
|
||||
this.hasPremium = await this.stateService.getCanAccessPremium();
|
||||
}
|
||||
|
||||
get premium() {
|
||||
return this.report.requiresPremium && !this.hasPremium;
|
||||
}
|
||||
|
||||
get route() {
|
||||
if (this.premium) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.report.route;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.sanitizer.bypassSecurityTrustHtml(this.report.icon);
|
||||
}
|
||||
|
||||
click() {
|
||||
if (this.premium) {
|
||||
this.messagingService.send("premiumRequired");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "reports" | i18n }}</h1>
|
||||
</div>
|
||||
|
||||
<p>{{ "reportsDesc" | i18n }}</p>
|
||||
|
||||
<div class="tw-inline-grid tw-grid-cols-3 tw-gap-4">
|
||||
<div *ngFor="let report of reports">
|
||||
<app-report-card [type]="report"></app-report-card>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ReportTypes } from "./report-card.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-report-list",
|
||||
templateUrl: "report-list.component.html",
|
||||
})
|
||||
export class ReportListComponent {
|
||||
reports = [
|
||||
ReportTypes.exposedPasswords,
|
||||
ReportTypes.reusedPasswords,
|
||||
ReportTypes.weakPasswords,
|
||||
ReportTypes.unsecuredWebsites,
|
||||
ReportTypes.inactive2fa,
|
||||
ReportTypes.dataBreach,
|
||||
];
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuard } from "jslib-angular/guards/auth.guard";
|
||||
|
||||
import { BreachReportComponent } from "./breach-report.component";
|
||||
import { ExposedPasswordsReportComponent } from "./exposed-passwords-report.component";
|
||||
import { InactiveTwoFactorReportComponent } from "./inactive-two-factor-report.component";
|
||||
import { ReportListComponent } from "./report-list.component";
|
||||
import { ReportsComponent } from "./reports.component";
|
||||
import { ReusedPasswordsReportComponent } from "./reused-passwords-report.component";
|
||||
import { UnsecuredWebsitesReportComponent } from "./unsecured-websites-report.component";
|
||||
import { WeakPasswordsReportComponent } from "./weak-passwords-report.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: "",
|
||||
component: ReportsComponent,
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{ path: "", pathMatch: "full", component: ReportListComponent, data: { homepage: true } },
|
||||
{
|
||||
path: "breach-report",
|
||||
component: BreachReportComponent,
|
||||
data: { titleId: "dataBreachReport" },
|
||||
},
|
||||
{
|
||||
path: "reused-passwords-report",
|
||||
component: ReusedPasswordsReportComponent,
|
||||
data: { titleId: "reusedPasswordsReport" },
|
||||
},
|
||||
{
|
||||
path: "unsecured-websites-report",
|
||||
component: UnsecuredWebsitesReportComponent,
|
||||
data: { titleId: "unsecuredWebsitesReport" },
|
||||
},
|
||||
{
|
||||
path: "weak-passwords-report",
|
||||
component: WeakPasswordsReportComponent,
|
||||
data: { titleId: "weakPasswordsReport" },
|
||||
},
|
||||
{
|
||||
path: "exposed-passwords-report",
|
||||
component: ExposedPasswordsReportComponent,
|
||||
data: { titleId: "exposedPasswordsReport" },
|
||||
},
|
||||
{
|
||||
path: "inactive-two-factor-report",
|
||||
component: InactiveTwoFactorReportComponent,
|
||||
data: { titleId: "inactive2faReport" },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class ReportsRoutingModule {}
|
||||
@@ -1,12 +0,0 @@
|
||||
<div class="container page-content">
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col">
|
||||
<a bitButton routerLink="./" *ngIf="!homepage">
|
||||
<i class="bwi bwi-angle-left" aria-hidden="true"></i>
|
||||
{{ "backToReports" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Component, OnDestroy } from "@angular/core";
|
||||
import { NavigationEnd, Router } from "@angular/router";
|
||||
import { Subscription } from "rxjs";
|
||||
import { filter } from "rxjs/operators";
|
||||
|
||||
@Component({
|
||||
selector: "app-reports",
|
||||
templateUrl: "reports.component.html",
|
||||
})
|
||||
export class ReportsComponent implements OnDestroy {
|
||||
homepage = true;
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(router: Router) {
|
||||
this.subscription = router.events
|
||||
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||
.subscribe((event) => {
|
||||
this.homepage = (event as NavigationEnd).url == "/reports";
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscription?.unsubscribe();
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import { Inject, Injectable } from "@angular/core";
|
||||
|
||||
import { WINDOW } from "jslib-angular/services/jslib-services.module";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||
import {
|
||||
EnvironmentService as EnvironmentServiceAbstraction,
|
||||
Urls,
|
||||
} from "jslib-common/abstractions/environment.service";
|
||||
import { EventService as EventLoggingServiceAbstraction } from "jslib-common/abstractions/event.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
import { ThemeType } from "jslib-common/enums/themeType";
|
||||
import { ContainerService } from "jslib-common/services/container.service";
|
||||
import { EventService as EventLoggingService } from "jslib-common/services/event.service";
|
||||
import { VaultTimeoutService as VaultTimeoutService } from "jslib-common/services/vaultTimeout.service";
|
||||
|
||||
import { I18nService as I18nService } from "../../services/i18n.service";
|
||||
|
||||
@Injectable()
|
||||
export class InitService {
|
||||
constructor(
|
||||
@Inject(WINDOW) private win: Window,
|
||||
private environmentService: EnvironmentServiceAbstraction,
|
||||
private notificationsService: NotificationsServiceAbstraction,
|
||||
private vaultTimeoutService: VaultTimeoutServiceAbstraction,
|
||||
private i18nService: I18nServiceAbstraction,
|
||||
private eventLoggingService: EventLoggingServiceAbstraction,
|
||||
private twoFactorService: TwoFactorServiceAbstraction,
|
||||
private stateService: StateServiceAbstraction,
|
||||
private platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
private cryptoService: CryptoServiceAbstraction
|
||||
) {}
|
||||
|
||||
init() {
|
||||
return async () => {
|
||||
await this.stateService.init();
|
||||
|
||||
const urls = process.env.URLS as Urls;
|
||||
urls.base ??= this.win.location.origin;
|
||||
this.environmentService.setUrls(urls);
|
||||
|
||||
setTimeout(() => this.notificationsService.init(), 3000);
|
||||
|
||||
(this.vaultTimeoutService as VaultTimeoutService).init(true);
|
||||
const locale = await this.stateService.getLocale();
|
||||
await (this.i18nService as I18nService).init(locale);
|
||||
(this.eventLoggingService as EventLoggingService).init(true);
|
||||
this.twoFactorService.init();
|
||||
const htmlEl = this.win.document.documentElement;
|
||||
htmlEl.classList.add("locale_" + this.i18nService.translationLocale);
|
||||
|
||||
// Initial theme is set in index.html which must be updated if there are any changes to theming logic
|
||||
this.platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => {
|
||||
const bwTheme = await this.stateService.getTheme();
|
||||
if (bwTheme === ThemeType.System) {
|
||||
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
|
||||
htmlEl.classList.add("theme_" + sysTheme);
|
||||
}
|
||||
});
|
||||
|
||||
const containerService = new ContainerService(this.cryptoService);
|
||||
containerService.attachToWindow(this.win);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
import { APP_INITIALIZER, NgModule } from "@angular/core";
|
||||
import { ToastrModule } from "ngx-toastr";
|
||||
|
||||
import {
|
||||
JslibServicesModule,
|
||||
SECURE_STORAGE,
|
||||
STATE_FACTORY,
|
||||
STATE_SERVICE_USE_CACHE,
|
||||
LOCALES_DIRECTORY,
|
||||
SYSTEM_LANGUAGE,
|
||||
} from "jslib-angular/services/jslib-services.module";
|
||||
import { ModalService as ModalServiceAbstraction } from "jslib-angular/services/modal.service";
|
||||
import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service";
|
||||
import { CipherService as CipherServiceAbstraction } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService as CollectionServiceAbstraction } from "jslib-common/abstractions/collection.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||
import { FolderService as FolderServiceAbstraction } from "jslib-common/abstractions/folder.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||
import { ImportService as ImportServiceAbstraction } from "jslib-common/abstractions/import.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService as BaseStateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service";
|
||||
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
|
||||
import { StateFactory } from "jslib-common/factories/stateFactory";
|
||||
import { ImportService } from "jslib-common/services/import.service";
|
||||
|
||||
import { StateService as StateServiceAbstraction } from "../../abstractions/state.service";
|
||||
import { Account } from "../../models/account";
|
||||
import { GlobalState } from "../../models/globalState";
|
||||
import { BroadcasterMessagingService } from "../../services/broadcasterMessaging.service";
|
||||
import { HtmlStorageService } from "../../services/htmlStorage.service";
|
||||
import { I18nService } from "../../services/i18n.service";
|
||||
import { MemoryStorageService } from "../../services/memoryStorage.service";
|
||||
import { PasswordRepromptService } from "../../services/passwordReprompt.service";
|
||||
import { StateService } from "../../services/state.service";
|
||||
import { StateMigrationService } from "../../services/stateMigration.service";
|
||||
import { WebPlatformUtilsService } from "../../services/webPlatformUtils.service";
|
||||
import { HomeGuard } from "../guards/home.guard";
|
||||
import { PermissionsGuard as OrgPermissionsGuard } from "../organizations/guards/permissions.guard";
|
||||
import { NavigationPermissionsService as OrgPermissionsService } from "../organizations/services/navigation-permissions.service";
|
||||
|
||||
import { EventService } from "./event.service";
|
||||
import { InitService } from "./init.service";
|
||||
import { ModalService } from "./modal.service";
|
||||
import { PolicyListService } from "./policy-list.service";
|
||||
import { RouterService } from "./router.service";
|
||||
|
||||
@NgModule({
|
||||
imports: [ToastrModule, JslibServicesModule],
|
||||
declarations: [],
|
||||
providers: [
|
||||
OrgPermissionsService,
|
||||
OrgPermissionsGuard,
|
||||
InitService,
|
||||
RouterService,
|
||||
EventService,
|
||||
PolicyListService,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: (initService: InitService) => initService.init(),
|
||||
deps: [InitService],
|
||||
multi: true,
|
||||
},
|
||||
{
|
||||
provide: STATE_FACTORY,
|
||||
useValue: new StateFactory(GlobalState, Account),
|
||||
},
|
||||
{
|
||||
provide: STATE_SERVICE_USE_CACHE,
|
||||
useValue: false,
|
||||
},
|
||||
{
|
||||
provide: I18nServiceAbstraction,
|
||||
useClass: I18nService,
|
||||
deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY],
|
||||
},
|
||||
{ provide: StorageServiceAbstraction, useClass: HtmlStorageService },
|
||||
{
|
||||
provide: SECURE_STORAGE,
|
||||
// TODO: platformUtilsService.isDev has a helper for this, but using that service here results in a circular dependency.
|
||||
// We have a tech debt item in the backlog to break up platformUtilsService, but in the meantime simply checking the environement here is less cumbersome.
|
||||
useClass: process.env.NODE_ENV === "development" ? HtmlStorageService : MemoryStorageService,
|
||||
},
|
||||
{
|
||||
provide: PlatformUtilsServiceAbstraction,
|
||||
useClass: WebPlatformUtilsService,
|
||||
},
|
||||
{ provide: MessagingServiceAbstraction, useClass: BroadcasterMessagingService },
|
||||
{ provide: ModalServiceAbstraction, useClass: ModalService },
|
||||
{
|
||||
provide: ImportServiceAbstraction,
|
||||
useClass: ImportService,
|
||||
deps: [
|
||||
CipherServiceAbstraction,
|
||||
FolderServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
CollectionServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: StateMigrationServiceAbstraction,
|
||||
useClass: StateMigrationService,
|
||||
deps: [StorageServiceAbstraction, SECURE_STORAGE, STATE_FACTORY],
|
||||
},
|
||||
{
|
||||
provide: StateServiceAbstraction,
|
||||
useClass: StateService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
SECURE_STORAGE,
|
||||
LogService,
|
||||
StateMigrationServiceAbstraction,
|
||||
STATE_FACTORY,
|
||||
STATE_SERVICE_USE_CACHE,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: BaseStateServiceAbstraction,
|
||||
useExisting: StateServiceAbstraction,
|
||||
},
|
||||
{
|
||||
provide: PasswordRepromptServiceAbstraction,
|
||||
useClass: PasswordRepromptService,
|
||||
},
|
||||
HomeGuard,
|
||||
],
|
||||
})
|
||||
export class ServicesModule {}
|
||||
@@ -1,32 +0,0 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "myAccount" | i18n }}</h1>
|
||||
</div>
|
||||
<app-profile></app-profile>
|
||||
<ng-container *ngIf="showChangeEmail">
|
||||
<div class="secondary-header">
|
||||
<h1>{{ "changeEmail" | i18n }}</h1>
|
||||
</div>
|
||||
<app-change-email></app-change-email>
|
||||
</ng-container>
|
||||
<div class="secondary-header text-danger border-0 mb-0">
|
||||
<h1>{{ "dangerZone" | i18n }}</h1>
|
||||
</div>
|
||||
<div class="card border-danger">
|
||||
<div class="card-body">
|
||||
<p>{{ "dangerZoneDesc" | i18n }}</p>
|
||||
<button bitButton buttonType="danger" (click)="deauthorizeSessions()">
|
||||
{{ "deauthorizeSessions" | i18n }}
|
||||
</button>
|
||||
<button bitButton buttonType="danger" (click)="purgeVault()">
|
||||
{{ "purgeVault" | i18n }}
|
||||
</button>
|
||||
<button bitButton buttonType="danger" (click)="deleteAccount()">
|
||||
{{ "deleteAccount" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #deauthorizeSessionsTemplate></ng-template>
|
||||
<ng-template #purgeVaultTemplate></ng-template>
|
||||
<ng-template #deleteAccountTemplate></ng-template>
|
||||
<ng-template #viewUserApiKeyTemplate></ng-template>
|
||||
<ng-template #rotateUserApiKeyTemplate></ng-template>
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { DeauthorizeSessionsComponent } from "./deauthorize-sessions.component";
|
||||
import { DeleteAccountComponent } from "./delete-account.component";
|
||||
import { PurgeVaultComponent } from "./purge-vault.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-account",
|
||||
templateUrl: "account.component.html",
|
||||
})
|
||||
export class AccountComponent {
|
||||
@ViewChild("deauthorizeSessionsTemplate", { read: ViewContainerRef, static: true })
|
||||
deauthModalRef: ViewContainerRef;
|
||||
@ViewChild("purgeVaultTemplate", { read: ViewContainerRef, static: true })
|
||||
purgeModalRef: ViewContainerRef;
|
||||
@ViewChild("deleteAccountTemplate", { read: ViewContainerRef, static: true })
|
||||
deleteModalRef: ViewContainerRef;
|
||||
|
||||
showChangeEmail = true;
|
||||
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
private apiService: ApiService,
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private stateService: StateService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.showChangeEmail = !(await this.keyConnectorService.getUsesKeyConnector());
|
||||
}
|
||||
|
||||
async deauthorizeSessions() {
|
||||
await this.modalService.openViewRef(DeauthorizeSessionsComponent, this.deauthModalRef);
|
||||
}
|
||||
|
||||
async purgeVault() {
|
||||
await this.modalService.openViewRef(PurgeVaultComponent, this.purgeModalRef);
|
||||
}
|
||||
|
||||
async deleteAccount() {
|
||||
await this.modalService.openViewRef(DeleteAccountComponent, this.deleteModalRef);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user