mirror of
https://github.com/bitwarden/web
synced 2025-12-06 00:03:28 +00:00
Compare commits
76 Commits
beeep/deco
...
v2.27.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05a17629a5 | ||
|
|
a1bd0b7eec | ||
|
|
f263156484 | ||
|
|
c174751b9c | ||
|
|
970d3162ac | ||
|
|
18f1929f65 | ||
|
|
5cb3941190 | ||
|
|
0e515bc6c1 | ||
|
|
e103ddf02f | ||
|
|
8242989b9d | ||
|
|
5e7d94efb8 | ||
|
|
3bc8955dd5 | ||
|
|
bc05d27082 | ||
|
|
e93c155885 | ||
|
|
1076749635 | ||
|
|
06e1af6d48 | ||
|
|
cf9a90d10e | ||
|
|
6e8c15bccd | ||
|
|
7d018e4b59 | ||
|
|
f832cb4138 | ||
|
|
b8a23cf014 | ||
|
|
d0c0e80b6c | ||
|
|
98fb71fcb6 | ||
|
|
1b52b5a98a | ||
|
|
c3e5c74253 | ||
|
|
df5b175cdf | ||
|
|
1c495e87c9 | ||
|
|
01f128a4a9 | ||
|
|
a4d5b145ac | ||
|
|
d944e0e25c | ||
|
|
d141ccca52 | ||
|
|
9e872bed2c | ||
|
|
c071b692f2 | ||
|
|
041bb1bf0a | ||
|
|
0b5e1eb256 | ||
|
|
8c39fdb21e | ||
|
|
ca3efc8fee | ||
|
|
c323f38f16 | ||
|
|
9df4eb4c0d | ||
|
|
1712ed53be | ||
|
|
45a39f6200 | ||
|
|
a2d241263b | ||
|
|
5987d3deda | ||
|
|
080a3c655e | ||
|
|
dac48242b7 | ||
|
|
e4d9ab52a0 | ||
|
|
aee8a2661e | ||
|
|
ff6bb236c0 | ||
|
|
f79b20294a | ||
|
|
3a0c34b934 | ||
|
|
e09df347f4 | ||
|
|
e68ab0031d | ||
|
|
64416c9406 | ||
|
|
6779adb064 | ||
|
|
1b28a4b954 | ||
|
|
6320498fb3 | ||
|
|
bfd5f3e564 | ||
|
|
c755443735 | ||
|
|
0e5f2530a9 | ||
|
|
5105633fa4 | ||
|
|
e975056c21 | ||
|
|
be21167ef8 | ||
|
|
e09898e4d8 | ||
|
|
868d235faa | ||
|
|
5c764a95f4 | ||
|
|
596c3e86e9 | ||
|
|
8030da2ed5 | ||
|
|
8910430dfb | ||
|
|
6bf6d4b47f | ||
|
|
ca199a398e | ||
|
|
61ab2fbda3 | ||
|
|
d79f074825 | ||
|
|
e3b962a779 | ||
|
|
cc657eb853 | ||
|
|
e14a266ee0 | ||
|
|
e1732cfa10 |
@@ -1,4 +1,3 @@
|
||||
*
|
||||
!docker/*
|
||||
!build/*
|
||||
!entrypoint.sh
|
||||
|
||||
8
.eslintignore
Normal file
8
.eslintignore
Normal file
@@ -0,0 +1,8 @@
|
||||
**/dist
|
||||
**/build
|
||||
jslib
|
||||
webpack.config.js
|
||||
scripts/optimize.js
|
||||
config.js
|
||||
|
||||
**/node_modules
|
||||
31
.eslintrc.json
Normal file
31
.eslintrc.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
65
.github/workflows/build.yml
vendored
65
.github/workflows/build.yml
vendored
@@ -11,6 +11,8 @@ on:
|
||||
branches-ignore:
|
||||
- "l10n_master"
|
||||
- "gh-pages"
|
||||
paths-ignore:
|
||||
- '.github/workflows/**'
|
||||
|
||||
jobs:
|
||||
cloc:
|
||||
@@ -28,6 +30,26 @@ 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
|
||||
@@ -44,7 +66,9 @@ jobs:
|
||||
build-oss-selfhost:
|
||||
name: Build OSS zip
|
||||
runs-on: ubuntu-20.04
|
||||
needs: setup
|
||||
needs:
|
||||
- setup
|
||||
- lint
|
||||
env:
|
||||
_VERSION: ${{ needs.setup.outputs.version }}
|
||||
steps:
|
||||
@@ -91,7 +115,9 @@ jobs:
|
||||
build-cloud:
|
||||
name: Build Cloud zip
|
||||
runs-on: ubuntu-20.04
|
||||
needs: setup
|
||||
needs:
|
||||
- setup
|
||||
- lint
|
||||
env:
|
||||
_VERSION: ${{ needs.setup.outputs.version }}
|
||||
steps:
|
||||
@@ -138,7 +164,9 @@ jobs:
|
||||
build-commercial-selfhost:
|
||||
name: Build SelfHost Docker image
|
||||
runs-on: ubuntu-20.04
|
||||
needs: setup
|
||||
needs:
|
||||
- setup
|
||||
- lint
|
||||
env:
|
||||
_VERSION: ${{ needs.setup.outputs.version }}
|
||||
steps:
|
||||
@@ -165,7 +193,7 @@ 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'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||
id: setup-dct
|
||||
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
|
||||
with:
|
||||
@@ -175,9 +203,6 @@ jobs:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Restore
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -212,11 +237,11 @@ jobs:
|
||||
run: docker tag bitwarden/web bitwarden/web:dev
|
||||
|
||||
- name: Tag hotfix branch
|
||||
if: github.ref == 'refs/heads/hotfix'
|
||||
run: docker tag bitwarden/web bitwarden/web:hotfix
|
||||
if: github.ref == 'refs/heads/hotfix-rc'
|
||||
run: docker tag bitwarden/web bitwarden/web:hotfix-rc
|
||||
|
||||
- name: List Docker images
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||
run: docker images
|
||||
|
||||
- name: Push rc image
|
||||
@@ -234,19 +259,22 @@ jobs:
|
||||
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
|
||||
|
||||
- name: Push hotfix image
|
||||
if: github.ref == 'refs/heads/hotfix'
|
||||
run: docker push bitwarden/web:hotfix
|
||||
if: github.ref == 'refs/heads/hotfix-rc'
|
||||
run: docker push bitwarden/web:hotfix-rc
|
||||
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'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||
run: docker logout
|
||||
|
||||
build-qa:
|
||||
name: Build Docker images for QA environment
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- setup
|
||||
- lint
|
||||
steps:
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||
@@ -281,9 +309,6 @@ jobs:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Restore
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -367,7 +392,6 @@ jobs:
|
||||
run: |
|
||||
nuget help | grep Version
|
||||
msbuild -version
|
||||
dotnet --info
|
||||
node --version
|
||||
npm --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
@@ -382,9 +406,6 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
|
||||
- name: NPM build
|
||||
run: npm run build:bit:cloud
|
||||
|
||||
@@ -433,6 +454,7 @@ jobs:
|
||||
needs:
|
||||
- cloc
|
||||
- setup
|
||||
- lint
|
||||
- build-oss-selfhost
|
||||
- build-cloud
|
||||
- build-commercial-selfhost
|
||||
@@ -444,6 +466,7 @@ 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 }}
|
||||
@@ -454,6 +477,8 @@ 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
|
||||
|
||||
16
.github/workflows/enforce-labels.yml
vendored
Normal file
16
.github/workflows/enforce-labels.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Enforce PR labels
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, unlabeled, opened, edited, synchronize]
|
||||
jobs:
|
||||
enforce-label:
|
||||
name: EnforceLabel
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Enforce Label
|
||||
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
|
||||
with:
|
||||
BANNED_LABELS: "hold"
|
||||
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"
|
||||
110
.github/workflows/release.yml
vendored
110
.github/workflows/release.yml
vendored
@@ -12,6 +12,7 @@ on:
|
||||
options:
|
||||
- Initial Release
|
||||
- Redeploy
|
||||
- Dry Run
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
@@ -20,19 +21,20 @@ jobs:
|
||||
outputs:
|
||||
release_version: ${{ steps.version.outputs.package }}
|
||||
tag_version: ${{ steps.version.outputs.tag }}
|
||||
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||
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" ]]; then
|
||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||
echo "==================================="
|
||||
echo "[!] Can only release from the 'rc' or 'hotfix' branches"
|
||||
echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
|
||||
echo "==================================="
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # 2.3.4
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # 2.4.0
|
||||
|
||||
- name: Check Release Version
|
||||
id: version
|
||||
@@ -55,15 +57,17 @@ jobs:
|
||||
id: branch
|
||||
run: |
|
||||
BRANCH_NAME=$(basename ${{ github.ref }})
|
||||
echo "::set-output name=branch-name::$BRANCH_NAME"
|
||||
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 }}
|
||||
_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: |
|
||||
@@ -71,7 +75,12 @@ jobs:
|
||||
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
|
||||
@@ -79,21 +88,25 @@ jobs:
|
||||
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
|
||||
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: |
|
||||
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
|
||||
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 }}
|
||||
@@ -101,9 +114,43 @@ jobs:
|
||||
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
|
||||
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
|
||||
|
||||
- name: Log out of Docker
|
||||
run: docker logout
|
||||
|
||||
|
||||
ghpages-deploy:
|
||||
name: Deploy Web Vault
|
||||
runs-on: ubuntu-20.04
|
||||
@@ -115,7 +162,7 @@ jobs:
|
||||
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
with:
|
||||
ref: gh-pages
|
||||
|
||||
@@ -125,7 +172,7 @@ jobs:
|
||||
git push -u origin deploy-$_TAG_VERSION
|
||||
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
|
||||
- name: Setup git config
|
||||
run: |
|
||||
@@ -139,7 +186,7 @@ jobs:
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.setup.outputs.branch-name }}
|
||||
branch: ${{ needs.setup.outputs.branch_name }}
|
||||
artifacts: web-*-cloud-COMMERCIAL.zip
|
||||
|
||||
# This should result in a build directory in the current working directory
|
||||
@@ -147,7 +194,7 @@ jobs:
|
||||
run: unzip web-*-cloud-COMMERCIAL.zip
|
||||
|
||||
- name: Deploy GitHub Pages
|
||||
uses: crazy-max/ghaction-github-pages@db4476a01402e1a7ce05f41832040eef16d14925 # v2.5.0
|
||||
uses: crazy-max/ghaction-github-pages@a117e4aa1fb4854d021546d2abdfac95be568a3a # v2.6.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
@@ -155,8 +202,10 @@ jobs:
|
||||
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 Deploy PR
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
env:
|
||||
PR_BRANCH: deploy-${{ env._TAG_VERSION }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -166,6 +215,7 @@ jobs:
|
||||
--base gh-pages \
|
||||
--head "$PR_BRANCH"
|
||||
|
||||
|
||||
release:
|
||||
name: Create GitHub Release
|
||||
runs-on: ubuntu-20.04
|
||||
@@ -179,7 +229,7 @@ jobs:
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.setup.outputs.branch-name }}
|
||||
branch: ${{ needs.setup.outputs.branch_name }}
|
||||
artifacts: "web-*-selfhosted-COMMERCIAL.zip,
|
||||
web-*-selfhosted-open-source.zip"
|
||||
|
||||
@@ -189,7 +239,8 @@ jobs:
|
||||
mv web-*-selfhosted-open-source.zip web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01
|
||||
with:
|
||||
name: "Version ${{ needs.setup.outputs.release_version }}"
|
||||
commit: ${{ github.sha }}
|
||||
@@ -199,3 +250,20 @@ jobs:
|
||||
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 deploy branch
|
||||
run: git push origin --delete deploy-$_TAG_VERSION
|
||||
|
||||
11
.github/workflows/workflow-linter.yml
vendored
Normal file
11
.github/workflows/workflow-linter.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Workflow Linter
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/**
|
||||
|
||||
jobs:
|
||||
call-workflow:
|
||||
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master
|
||||
0
.husky/pre-commit
Normal file → Executable file
0
.husky/pre-commit
Normal file → Executable file
28
Dockerfile
28
Dockerfile
@@ -1,4 +1,4 @@
|
||||
FROM nginx:stable
|
||||
FROM bitwarden/server
|
||||
|
||||
LABEL com.bitwarden.product="bitwarden"
|
||||
|
||||
@@ -8,29 +8,13 @@ RUN apt-get update \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY docker/nginx.conf /etc/nginx
|
||||
COPY docker/nginx-web.conf /etc/nginx
|
||||
COPY docker/mime.types /etc/nginx
|
||||
COPY docker/security-headers.conf /etc/nginx
|
||||
|
||||
ENV ASPNETCORE_URLS http://+:5000
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
COPY ./build .
|
||||
COPY docker/entrypoint.sh /
|
||||
COPY entrypoint.sh /
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
RUN bash /entrypoint.sh
|
||||
RUN chown -R bitwarden:bitwarden /app && chmod -R 755 /app && \
|
||||
chown -R bitwarden:bitwarden /var/cache/nginx && \
|
||||
chown -R bitwarden:bitwarden /var/log/nginx && \
|
||||
chown -R bitwarden:bitwarden /etc/nginx/conf.d
|
||||
RUN touch /var/run/nginx.pid && \
|
||||
chown -R bitwarden:bitwarden /var/run/nginx.pid
|
||||
HEALTHCHECK CMD curl -f http://localhost:5000 || exit 1
|
||||
|
||||
USER bitwarden
|
||||
|
||||
EXPOSE 8080
|
||||
HEALTHCHECK CMD curl -f http://localhost:8080 || exit 1
|
||||
|
||||
#ENTRYPOINT ["/entrypoint.sh"]
|
||||
#CMD ["tail", "-f", "/dev/null"]
|
||||
CMD nginx -g 'daemon off;'
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
@@ -61,6 +61,10 @@ 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.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { AppComponent as BaseAppComponent } from "src/app/app.component";
|
||||
|
||||
import { DisablePersonalVaultExportPolicy } from "./policies/disable-personal-vault-export.component";
|
||||
import { MaximumVaultTimeoutPolicy } from "./policies/maximum-vault-timeout.component";
|
||||
|
||||
|
||||
@@ -7,17 +7,17 @@ import { InfiniteScrollModule } from "ngx-infinite-scroll";
|
||||
|
||||
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
|
||||
|
||||
import { OssRoutingModule } from "src/app/oss-routing.module";
|
||||
import { OssModule } from "src/app/oss.module";
|
||||
import { ServicesModule } from "src/app/services/services.module";
|
||||
import { WildcardRoutingModule } from "src/app/wildcard-routing.module";
|
||||
|
||||
import { AppRoutingModule } from "./app-routing.module";
|
||||
import { AppComponent } from "./app.component";
|
||||
import { OrganizationsModule } from "./organizations/organizations.module";
|
||||
import { DisablePersonalVaultExportPolicyComponent } from "./policies/disable-personal-vault-export.component";
|
||||
import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-timeout.component";
|
||||
|
||||
import { OssRoutingModule } from "src/app/oss-routing.module";
|
||||
import { OssModule } from "src/app/oss.module";
|
||||
import { ServicesModule } from "src/app/services/services.module";
|
||||
import { WildcardRoutingModule } from "src/app/wildcard-routing.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
OssModule,
|
||||
|
||||
@@ -5,7 +5,6 @@ import "bootstrap";
|
||||
import "jquery";
|
||||
import "popper.js";
|
||||
|
||||
// tslint:disable-next-line
|
||||
require("src/scss/styles.scss");
|
||||
|
||||
import { AppModule } from "./app.module";
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Directive, Input, OnInit, Self } from "@angular/core";
|
||||
import { ControlValueAccessor, FormControl, NgControl, Validators } from "@angular/forms";
|
||||
|
||||
import { dirtyRequired } from "jslib-angular/validators/dirty.validator";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Directive()
|
||||
export abstract class BaseCvaComponent implements ControlValueAccessor, OnInit {
|
||||
get describedById() {
|
||||
return this.showDescribedBy ? this.controlId + "Desc" : null;
|
||||
}
|
||||
|
||||
get showDescribedBy() {
|
||||
return this.helperText != null || this.controlDir.control.hasError("required");
|
||||
}
|
||||
|
||||
get isRequired() {
|
||||
return (
|
||||
this.controlDir.control.hasValidator(Validators.required) ||
|
||||
this.controlDir.control.hasValidator(dirtyRequired)
|
||||
);
|
||||
}
|
||||
|
||||
@Input() label: string;
|
||||
@Input() controlId: string;
|
||||
@Input() helperText: string;
|
||||
|
||||
internalControl = new FormControl("");
|
||||
|
||||
protected onChange: any;
|
||||
protected onTouched: any;
|
||||
|
||||
constructor(@Self() public controlDir: NgControl) {
|
||||
this.controlDir.valueAccessor = this;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.internalControl.valueChanges.subscribe(this.onValueChangesInternal);
|
||||
}
|
||||
|
||||
onBlurInternal() {
|
||||
this.onTouched();
|
||||
}
|
||||
|
||||
// CVA interfaces
|
||||
writeValue(value: string) {
|
||||
this.internalControl.setValue(value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: any) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any) {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
if (isDisabled) {
|
||||
this.internalControl.disable();
|
||||
} else {
|
||||
this.internalControl.enable();
|
||||
}
|
||||
}
|
||||
|
||||
protected onValueChangesInternal: any = (value: string) => this.onChange(value);
|
||||
// End CVA interfaces
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
[attr.id]="controlId"
|
||||
[attr.aria-describedby]="describedById"
|
||||
[formControl]="internalControl"
|
||||
(blur)="onBlurInternal()"
|
||||
/>
|
||||
<label class="form-check-label" [attr.for]="controlId">{{ label }}</label>
|
||||
</div>
|
||||
<small *ngIf="showDescribedBy" [attr.id]="describedById" class="form-text text-muted">{{
|
||||
helperText
|
||||
}}</small>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { BaseCvaComponent } from "./base-cva.component";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Component({
|
||||
selector: "app-input-checkbox",
|
||||
templateUrl: "input-checkbox.component.html",
|
||||
})
|
||||
export class InputCheckboxComponent extends BaseCvaComponent {}
|
||||
@@ -0,0 +1,26 @@
|
||||
<div class="form-group">
|
||||
<label>{{ label }}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="controlValue" />
|
||||
<div class="input-group-append" *ngIf="showLaunch">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'launch' | i18n }}"
|
||||
(click)="launchUri(controlValue)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-external-link" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="input-group-append" *ngIf="showCopy">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(controlValue)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Component({
|
||||
selector: "app-input-text-readonly",
|
||||
templateUrl: "input-text-readonly.component.html",
|
||||
})
|
||||
export class InputTextReadOnlyComponent {
|
||||
@Input() controlValue: string;
|
||||
@Input() label: string;
|
||||
@Input() showCopy = true;
|
||||
@Input() showLaunch = false;
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService) {}
|
||||
|
||||
copy(value: string) {
|
||||
this.platformUtilsService.copyToClipboard(value);
|
||||
}
|
||||
|
||||
launchUri(url: string) {
|
||||
this.platformUtilsService.launchUri(url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<div class="form-group">
|
||||
<label [attr.for]="controlId">
|
||||
{{ label }}
|
||||
<small *ngIf="isRequired" class="text-muted form-text d-inline"
|
||||
>({{ "required" | i18n }})</small
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
[formControl]="internalControl"
|
||||
class="form-control"
|
||||
[attr.id]="controlId"
|
||||
[attr.aria-describedby]="describedById"
|
||||
[attr.aria-invalid]="controlDir.control.invalid"
|
||||
(blur)="onBlurInternal()"
|
||||
/>
|
||||
<div *ngIf="showDescribedBy" [attr.id]="describedById">
|
||||
<small
|
||||
*ngIf="helperText != null && !controlDir.control.hasError(helperTextSameAsError)"
|
||||
class="form-text text-muted"
|
||||
>
|
||||
{{ helperText }}
|
||||
</small>
|
||||
<small class="error-inline" *ngIf="controlDir.control.hasError('required')" role="alert">
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||
{{
|
||||
controlDir.control.hasError(helperTextSameAsError)
|
||||
? helperText
|
||||
: ("fieldRequiredError" | i18n: label)
|
||||
}}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { BaseCvaComponent } from "./base-cva.component";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Component({
|
||||
selector: "app-input-text[label][controlId]",
|
||||
templateUrl: "input-text.component.html",
|
||||
})
|
||||
export class InputTextComponent extends BaseCvaComponent implements OnInit {
|
||||
@Input() helperTextSameAsError: string;
|
||||
@Input() requiredErrorMessage: string;
|
||||
@Input() stripSpaces = false;
|
||||
|
||||
transformValue: (value: string) => string = null;
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
if (this.stripSpaces) {
|
||||
this.transformValue = this.doStripSpaces;
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: string) {
|
||||
this.internalControl.setValue(value == null ? "" : value);
|
||||
}
|
||||
|
||||
protected onValueChangesInternal: any = (value: string) => {
|
||||
let newValue = value;
|
||||
if (this.transformValue != null) {
|
||||
newValue = this.transformValue(value);
|
||||
this.internalControl.setValue(newValue, { emitEvent: false });
|
||||
}
|
||||
this.onChange(newValue);
|
||||
};
|
||||
|
||||
protected onValueChangeInternal(value: string) {
|
||||
let newValue = value;
|
||||
if (this.transformValue != null) {
|
||||
newValue = this.transformValue(value);
|
||||
this.internalControl.setValue(newValue, { emitEvent: false });
|
||||
}
|
||||
}
|
||||
|
||||
private doStripSpaces(value: string) {
|
||||
return value.replace(/ /g, "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<div class="form-group">
|
||||
<label [attr.for]="controlId">
|
||||
{{ label }}
|
||||
<small *ngIf="isRequired" class="text-muted form-text d-inline"
|
||||
>({{ "required" | i18n }})</small
|
||||
>
|
||||
</label>
|
||||
<select
|
||||
class="form-control"
|
||||
[attr.id]="controlId"
|
||||
[attr.aria-invalid]="controlDir.control.invalid"
|
||||
[formControl]="internalControl"
|
||||
(blur)="onBlurInternal()"
|
||||
>
|
||||
<option *ngFor="let o of selectOptions" [ngValue]="o.value" disabled="{{ o.disabled }}">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { SelectOptions } from "jslib-angular/interfaces/selectOptions";
|
||||
|
||||
import { BaseCvaComponent } from "./base-cva.component";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Component({
|
||||
selector: "app-select",
|
||||
templateUrl: "select.component.html",
|
||||
})
|
||||
export class SelectComponent extends BaseCvaComponent {
|
||||
@Input() selectOptions: SelectOptions[];
|
||||
}
|
||||
@@ -14,10 +14,9 @@
|
||||
<form
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[formGroup]="data"
|
||||
[formGroup]="ssoConfigForm"
|
||||
[appApiAction]="formPromise"
|
||||
*ngIf="!loading"
|
||||
ngNativeValidate
|
||||
>
|
||||
<p>
|
||||
{{ "ssoPolicyHelpStart" | i18n }}
|
||||
@@ -27,461 +26,407 @@
|
||||
{{ "ssoPolicyHelpKeyConnector" | i18n }}
|
||||
</p>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="enabled"
|
||||
[formControl]="enabled"
|
||||
name="Enabled"
|
||||
/>
|
||||
<label class="form-check-label" for="enabled">{{ "allowSso" | i18n }}</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">{{ "allowSsoDesc" | i18n }}</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ "memberDecryptionOption" | i18n }}</label>
|
||||
<div class="form-check form-check-block">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
id="memberDecryptionPass"
|
||||
[value]="false"
|
||||
formControlName="keyConnectorEnabled"
|
||||
/>
|
||||
<label class="form-check-label" for="memberDecryptionPass">
|
||||
{{ "masterPass" | i18n }}
|
||||
<small>{{ "memberDecryptionPassDesc" | i18n }}</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mt-2 form-check-block">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
id="memberDecryptionKey"
|
||||
[value]="true"
|
||||
formControlName="keyConnectorEnabled"
|
||||
[attr.disabled]="!organization.useKeyConnector || null"
|
||||
/>
|
||||
<label class="form-check-label" for="memberDecryptionKey">
|
||||
{{ "keyConnector" | i18n }}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
href="https://bitwarden.com/help/article/about-key-connector/"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
<small>{{ "memberDecryptionKeyConnectorDesc" | i18n }}</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="data.value.keyConnectorEnabled">
|
||||
<app-callout type="warning" [useAlertRole]="true">
|
||||
{{ "keyConnectorWarning" | i18n }}
|
||||
</app-callout>
|
||||
<!-- Root form -->
|
||||
<ng-container>
|
||||
<app-input-checkbox
|
||||
controlId="enabled"
|
||||
[formControl]="enabled"
|
||||
[label]="'allowSso' | i18n"
|
||||
[helperText]="'allowSsoDesc' | i18n"
|
||||
></app-input-checkbox>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="keyConnectorUrl">{{ "keyConnectorUrl" | i18n }}</label>
|
||||
<div class="input-group">
|
||||
<label>{{ "memberDecryptionOption" | i18n }}</label>
|
||||
<div class="form-check form-check-block">
|
||||
<input
|
||||
class="form-control"
|
||||
formControlName="keyConnectorUrl"
|
||||
id="keyConnectorUrl"
|
||||
required
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
id="memberDecryptionPass"
|
||||
[value]="false"
|
||||
formControlName="keyConnectorEnabled"
|
||||
/>
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="validateKeyConnectorUrl()"
|
||||
[disabled]="!enableTestKeyConnector"
|
||||
<label class="form-check-label" for="memberDecryptionPass">
|
||||
{{ "masterPass" | i18n }}
|
||||
<small>{{ "memberDecryptionPassDesc" | i18n }}</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mt-2 form-check-block">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
id="memberDecryptionKey"
|
||||
[value]="true"
|
||||
formControlName="keyConnectorEnabled"
|
||||
[attr.disabled]="!organization.useKeyConnector || null"
|
||||
/>
|
||||
<label class="form-check-label" for="memberDecryptionKey">
|
||||
{{ "keyConnector" | i18n }}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
href="https://bitwarden.com/help/about-key-connector/"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
*ngIf="keyConnectorUrl.pending"
|
||||
></i>
|
||||
<span *ngIf="!keyConnectorUrl.pending">
|
||||
{{ "keyConnectorTest" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
<small>{{ "memberDecryptionKeyConnectorDesc" | i18n }}</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Key Connector -->
|
||||
<ng-container *ngIf="ssoConfigForm.get('keyConnectorEnabled').value">
|
||||
<app-callout type="warning" [useAlertRole]="true">
|
||||
{{ "keyConnectorWarning" | i18n }}
|
||||
</app-callout>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="keyConnectorUrl">
|
||||
{{ "keyConnectorUrl" | i18n }}
|
||||
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
class="form-control"
|
||||
formControlName="keyConnectorUrl"
|
||||
id="keyConnectorUrl"
|
||||
aria-describedby="keyConnectorUrlDesc"
|
||||
(change)="haveTestedKeyConnector = false"
|
||||
appInputStripSpaces
|
||||
appA11yInvalid
|
||||
/>
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="validateKeyConnectorUrl()"
|
||||
[disabled]="!enableTestKeyConnector"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
*ngIf="keyConnectorUrl.pending"
|
||||
></i>
|
||||
<span *ngIf="!keyConnectorUrl.pending">
|
||||
{{ "keyConnectorTest" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="haveTestedKeyConnector" id="keyConnectorUrlDesc" aria-live="polite">
|
||||
<small
|
||||
class="error-inline"
|
||||
*ngIf="keyConnectorUrl.hasError('invalidUrl'); else keyConnectorSuccess"
|
||||
>
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||
{{ "keyConnectorTestFail" | i18n }}
|
||||
</small>
|
||||
<ng-template #keyConnectorSuccess>
|
||||
<small class="text-success">
|
||||
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||
{{ "keyConnectorTestSuccess" | i18n }}
|
||||
</small>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="keyConnectorUrl.pristine && !keyConnectorUrl.pending">
|
||||
<div class="text-danger" *ngIf="keyConnectorUrl.hasError('invalidUrl')" role="alert">
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
{{ "keyConnectorTestFail" | i18n }}
|
||||
</div>
|
||||
<div class="text-success" *ngIf="!keyConnectorUrl.hasError('invalidUrl')" role="alert">
|
||||
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||
{{ "keyConnectorTestSuccess" | i18n }}
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<app-select
|
||||
controlId="type"
|
||||
[label]="'type' | i18n"
|
||||
[selectOptions]="ssoTypeOptions"
|
||||
formControlName="configType"
|
||||
>
|
||||
</app-select>
|
||||
</ng-container>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type">{{ "type" | i18n }}</label>
|
||||
<select class="form-control" id="type" formControlName="configType">
|
||||
<option [ngValue]="0" disabled>{{ "selectType" | i18n }}</option>
|
||||
<option [ngValue]="1">OpenID Connect</option>
|
||||
<option [ngValue]="2">SAML 2.0</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- OIDC -->
|
||||
<div *ngIf="data.value.configType == 1">
|
||||
<div
|
||||
*ngIf="ssoConfigForm.get('configType').value === ssoType.OpenIdConnect"
|
||||
[formGroup]="openIdForm"
|
||||
>
|
||||
<div class="config-section">
|
||||
<h2>{{ "openIdConnectConfig" | i18n }}</h2>
|
||||
<div class="form-group">
|
||||
<label>{{ "callbackPath" | i18n }}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="callbackPath" />
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(callbackPath)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="secondary-header">{{ "openIdConnectConfig" | i18n }}</h2>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'callbackPath' | i18n"
|
||||
[controlValue]="callbackPath"
|
||||
></app-input-text-readonly>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'signedOutCallbackPath' | i18n"
|
||||
[controlValue]="signedOutCallbackPath"
|
||||
></app-input-text-readonly>
|
||||
|
||||
<app-input-text
|
||||
[label]="'authority' | i18n"
|
||||
controlId="authority"
|
||||
[stripSpaces]="true"
|
||||
formControlName="authority"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'clientId' | i18n"
|
||||
controlId="clientId"
|
||||
[stripSpaces]="true"
|
||||
formControlName="clientId"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'clientSecret' | i18n"
|
||||
controlId="clientSecret"
|
||||
[stripSpaces]="true"
|
||||
formControlName="clientSecret"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'metadataAddress' | i18n"
|
||||
controlId="metadataAddress"
|
||||
[stripSpaces]="true"
|
||||
[helperText]="'openIdAuthorityRequired' | i18n"
|
||||
formControlName="metadataAddress"
|
||||
></app-input-text>
|
||||
|
||||
<app-select
|
||||
controlId="redirectBehavior"
|
||||
[label]="'oidcRedirectBehavior' | i18n"
|
||||
[selectOptions]="connectRedirectOptions"
|
||||
formControlName="redirectBehavior"
|
||||
>
|
||||
</app-select>
|
||||
|
||||
<app-input-checkbox
|
||||
controlId="getClaimsFromUserInfoEndpoint"
|
||||
formControlName="getClaimsFromUserInfoEndpoint"
|
||||
[label]="'getClaimsFromUserInfoEndpoint' | i18n"
|
||||
></app-input-checkbox>
|
||||
|
||||
<!-- Optional customizations -->
|
||||
<div
|
||||
class="section-header d-flex flex-row align-items-center mt-3 mb-3"
|
||||
(click)="toggleOpenIdCustomizations()"
|
||||
>
|
||||
<h3 class="mb-0 mr-2" id="customizations-header">
|
||||
{{ "openIdOptionalCustomizations" | i18n }}
|
||||
</h3>
|
||||
<button
|
||||
class="mb-1 btn btn-link"
|
||||
type="button"
|
||||
appStopClick
|
||||
role="button"
|
||||
aria-controls="customizations"
|
||||
[attr.aria-expanded]="showOpenIdCustomizations"
|
||||
aria-labelledby="customizations-header"
|
||||
>
|
||||
<i
|
||||
class="bwi"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'bwi-angle-down': !showOpenIdCustomizations,
|
||||
'bwi-chevron-up': showOpenIdCustomizations
|
||||
}"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ "signedOutCallbackPath" | i18n }}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="signedOutCallbackPath" />
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(signedOutCallbackPath)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="authority">{{ "authority" | i18n }}</label>
|
||||
<input class="form-control" formControlName="authority" id="authority" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="clientId">{{ "clientId" | i18n }}</label>
|
||||
<input class="form-control" formControlName="clientId" id="clientId" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="clientSecret">{{ "clientSecret" | i18n }}</label>
|
||||
<input class="form-control" formControlName="clientSecret" id="clientSecret" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="metadataAddress">{{ "metadataAddress" | i18n }}</label>
|
||||
<input class="form-control" formControlName="metadataAddress" id="metadataAddress" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="redirectBehavior">{{ "oidcRedirectBehavior" | i18n }}</label>
|
||||
<select class="form-control" formControlName="redirectBehavior" id="redirectBehavior">
|
||||
<option [ngValue]="0">Redirect GET</option>
|
||||
<option [ngValue]="1">Form POST</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="getClaimsFromUserInfoEndpoint"
|
||||
formControlName="getClaimsFromUserInfoEndpoint"
|
||||
/>
|
||||
<label class="form-check-label" for="getClaimsFromUserInfoEndpoint">
|
||||
{{ "getClaimsFromUserInfoEndpoint" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="additionalScopes">{{ "additionalScopes" | i18n }}</label>
|
||||
<input class="form-control" formControlName="additionalScopes" id="additionalScopes" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="additionalUserIdClaimTypes">{{ "additionalUserIdClaimTypes" | i18n }}</label>
|
||||
<input
|
||||
class="form-control"
|
||||
<div id="customizations" [hidden]="!showOpenIdCustomizations">
|
||||
<app-input-text
|
||||
[label]="'additionalScopes' | i18n"
|
||||
controlId="additionalScopes"
|
||||
[helperText]="'separateMultipleWithComma' | i18n"
|
||||
formControlName="additionalScopes"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'additionalUserIdClaimTypes' | i18n"
|
||||
controlId="additionalUserIdClaimTypes"
|
||||
[helperText]="'separateMultipleWithComma' | i18n"
|
||||
formControlName="additionalUserIdClaimTypes"
|
||||
id="additionalUserIdClaimTypes"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="additionalEmailClaimTypes">{{ "additionalEmailClaimTypes" | i18n }}</label>
|
||||
<input
|
||||
class="form-control"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'additionalEmailClaimTypes' | i18n"
|
||||
controlId="additionalEmailClaimTypes"
|
||||
[helperText]="'separateMultipleWithComma' | i18n"
|
||||
formControlName="additionalEmailClaimTypes"
|
||||
id="additionalEmailClaimTypes"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="additionalNameClaimTypes">{{ "additionalNameClaimTypes" | i18n }}</label>
|
||||
<input
|
||||
class="form-control"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'additionalNameClaimTypes' | i18n"
|
||||
controlId="additionalNameClaimTypes"
|
||||
[helperText]="'separateMultipleWithComma' | i18n"
|
||||
formControlName="additionalNameClaimTypes"
|
||||
id="additionalNameClaimTypes"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="acrValues">{{ "acrValues" | i18n }}</label>
|
||||
<input class="form-control" formControlName="acrValues" id="acrValues" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="expectedReturnAcrValue">{{ "expectedReturnAcrValue" | i18n }}</label>
|
||||
<input
|
||||
class="form-control"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'acrValues' | i18n"
|
||||
controlId="acrValues"
|
||||
helperText="acr_values"
|
||||
formControlName="acrValues"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'expectedReturnAcrValue' | i18n"
|
||||
controlId="expectedReturnAcrValue"
|
||||
helperText="acr_validation"
|
||||
formControlName="expectedReturnAcrValue"
|
||||
id="expectedReturnAcrValue"
|
||||
/>
|
||||
></app-input-text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="data.value.configType == 2">
|
||||
<!-- SAML2 SP -->
|
||||
<div *ngIf="ssoConfigForm.get('configType').value === ssoType.Saml2" [formGroup]="samlForm">
|
||||
<!-- SAML2 SP -->
|
||||
<div class="config-section">
|
||||
<h2>{{ "samlSpConfig" | i18n }}</h2>
|
||||
<div class="form-group">
|
||||
<label>{{ "spEntityId" | i18n }}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="spEntityId" />
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(spEntityId)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ "spMetadataUrl" | i18n }}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="spMetadataUrl" />
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'launch' | i18n }}"
|
||||
(click)="launchUri(spMetadataUrl)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-external-link" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(spMetadataUrl)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ "spAcsUrl" | i18n }}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="spAcsUrl" />
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(spAcsUrl)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spNameIdFormat">{{ "spNameIdFormat" | i18n }}</label>
|
||||
<select class="form-control" formControlName="spNameIdFormat" id="spNameIdFormat">
|
||||
<option value="0">Not Configured</option>
|
||||
<option value="1">Unspecified</option>
|
||||
<option value="2">Email Address</option>
|
||||
<option value="3">X.509 Subject Name</option>
|
||||
<option value="4">Windows Domain Qualified Name</option>
|
||||
<option value="5">Kerberos Principal Name</option>
|
||||
<option value="6">Entity Identifier</option>
|
||||
<option value="7">Persistent</option>
|
||||
<option value="8">Transient</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spOutboundSigningAlgorithm">{{ "spOutboundSigningAlgorithm" | i18n }}</label>
|
||||
<select
|
||||
class="form-control"
|
||||
formControlName="spOutboundSigningAlgorithm"
|
||||
id="spOutboundSigningAlgorithm"
|
||||
>
|
||||
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spSigningBehavior">{{ "spSigningBehavior" | i18n }}</label>
|
||||
<select class="form-control" formControlName="spSigningBehavior" id="spSigningBehavior">
|
||||
<option value="0">If IdP Wants Authn Requests Signed</option>
|
||||
<option value="1">Always</option>
|
||||
<option value="3">Never</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spMinIncomingSigningAlgorithm">{{
|
||||
"spMinIncomingSigningAlgorithm" | i18n
|
||||
}}</label>
|
||||
<select
|
||||
class="form-control"
|
||||
formControlName="spMinIncomingSigningAlgorithm"
|
||||
id="spMinIncomingSigningAlgorithm"
|
||||
>
|
||||
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="spWantAssertionsSigned"
|
||||
formControlName="spWantAssertionsSigned"
|
||||
/>
|
||||
<label class="form-check-label" for="spWantAssertionsSigned">
|
||||
{{ "spWantAssertionsSigned" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="spValidateCertificates"
|
||||
formControlName="spValidateCertificates"
|
||||
/>
|
||||
<label class="form-check-label" for="spValidateCertificates">
|
||||
{{ "spValidateCertificates" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="secondary-header">{{ "samlSpConfig" | i18n }}</h2>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'spEntityId' | i18n"
|
||||
[controlValue]="spEntityId"
|
||||
></app-input-text-readonly>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'spMetadataUrl' | i18n"
|
||||
[controlValue]="spMetadataUrl"
|
||||
[showLaunch]="true"
|
||||
></app-input-text-readonly>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'spAcsUrl' | i18n"
|
||||
[controlValue]="spAcsUrl"
|
||||
></app-input-text-readonly>
|
||||
|
||||
<app-select
|
||||
controlId="spNameIdFormat"
|
||||
[label]="'spNameIdFormat' | i18n"
|
||||
[selectOptions]="saml2NameIdFormatOptions"
|
||||
formControlName="spNameIdFormat"
|
||||
>
|
||||
</app-select>
|
||||
|
||||
<app-select
|
||||
controlId="spOutboundSigningAlgorithm"
|
||||
[label]="'spOutboundSigningAlgorithm' | i18n"
|
||||
[selectOptions]="samlSigningAlgorithmOptions"
|
||||
formControlName="spOutboundSigningAlgorithm"
|
||||
>
|
||||
</app-select>
|
||||
|
||||
<app-select
|
||||
controlId="spSigningBehavior"
|
||||
[label]="'spSigningBehavior' | i18n"
|
||||
[selectOptions]="saml2SigningBehaviourOptions"
|
||||
formControlName="spSigningBehavior"
|
||||
>
|
||||
</app-select>
|
||||
|
||||
<app-select
|
||||
controlId="spMinIncomingSigningAlgorithm"
|
||||
[label]="'spMinIncomingSigningAlgorithm' | i18n"
|
||||
[selectOptions]="samlSigningAlgorithmOptions"
|
||||
formControlName="spMinIncomingSigningAlgorithm"
|
||||
>
|
||||
</app-select>
|
||||
|
||||
<app-input-checkbox
|
||||
controlId="spWantAssertionsSigned"
|
||||
formControlName="spWantAssertionsSigned"
|
||||
[label]="'spWantAssertionsSigned' | i18n"
|
||||
></app-input-checkbox>
|
||||
|
||||
<app-input-checkbox
|
||||
controlId="spValidateCertificates"
|
||||
formControlName="spValidateCertificates"
|
||||
[label]="'spValidateCertificates' | i18n"
|
||||
></app-input-checkbox>
|
||||
</div>
|
||||
|
||||
<!-- SAML2 IDP -->
|
||||
<div class="config-section">
|
||||
<h2>{{ "samlIdpConfig" | i18n }}</h2>
|
||||
<h2 class="secondary-header">{{ "samlIdpConfig" | i18n }}</h2>
|
||||
|
||||
<app-input-text
|
||||
[label]="'idpEntityId' | i18n"
|
||||
controlId="idpEntityId"
|
||||
formControlName="idpEntityId"
|
||||
></app-input-text>
|
||||
|
||||
<app-select
|
||||
controlId="idpBindingType"
|
||||
[label]="'idpBindingType' | i18n"
|
||||
[selectOptions]="saml2BindingTypeOptions"
|
||||
formControlName="idpBindingType"
|
||||
>
|
||||
</app-select>
|
||||
|
||||
<app-input-text
|
||||
[label]="'idpSingleSignOnServiceUrl' | i18n"
|
||||
controlId="idpSingleSignOnServiceUrl"
|
||||
[helperText]="'idpSingleSignOnServiceUrlRequired' | i18n"
|
||||
[stripSpaces]="true"
|
||||
formControlName="idpSingleSignOnServiceUrl"
|
||||
></app-input-text>
|
||||
|
||||
<app-input-text
|
||||
[label]="'idpSingleLogoutServiceUrl' | i18n"
|
||||
controlId="idpSingleLogoutServiceUrl"
|
||||
[stripSpaces]="true"
|
||||
formControlName="idpSingleLogoutServiceUrl"
|
||||
></app-input-text>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="idpEntityId">{{ "idpEntityId" | i18n }}</label>
|
||||
<input class="form-control" formControlName="idpEntityId" id="idpEntityId" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="idpBindingType">{{ "idpBindingType" | i18n }}</label>
|
||||
<select class="form-control" formControlName="idpBindingType" id="idpBindingType">
|
||||
<option value="1">Redirect</option>
|
||||
<option value="2">HTTP POST</option>
|
||||
<option value="4">Artifact</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="idpSingleSignOnServiceUrl">{{ "idpSingleSignOnServiceUrl" | i18n }}</label>
|
||||
<input
|
||||
class="form-control"
|
||||
formControlName="idpSingleSignOnServiceUrl"
|
||||
id="idpSingleSignOnServiceUrl"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="idpSingleLogoutServiceUrl">{{ "idpSingleLogoutServiceUrl" | i18n }}</label>
|
||||
<input
|
||||
class="form-control"
|
||||
formControlName="idpSingleLogoutServiceUrl"
|
||||
id="idpSingleLogoutServiceUrl"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="idpArtifactResolutionServiceUrl">{{
|
||||
"idpArtifactResolutionServiceUrl" | i18n
|
||||
}}</label>
|
||||
<input
|
||||
class="form-control"
|
||||
formControlName="idpArtifactResolutionServiceUrl"
|
||||
id="idpArtifactResolutionServiceUrl"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="idpX509PublicCert">{{ "idpX509PublicCert" | i18n }}</label>
|
||||
<label for="idpX509PublicCert">
|
||||
{{ "idpX509PublicCert" | i18n }}
|
||||
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
|
||||
</label>
|
||||
<textarea
|
||||
formControlName="idpX509PublicCert"
|
||||
class="form-control form-control-sm text-monospace"
|
||||
rows="6"
|
||||
id="idpX509PublicCert"
|
||||
appA11yInvalid
|
||||
aria-describedby="idpX509PublicCertDesc"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="idpOutboundSigningAlgorithm">{{ "idpOutboundSigningAlgorithm" | i18n }}</label>
|
||||
<select
|
||||
class="form-control"
|
||||
formControlName="idpOutboundSigningAlgorithm"
|
||||
id="idpOutboundSigningAlgorithm"
|
||||
<small
|
||||
id="idpX509PublicCertDesc"
|
||||
class="error-inline"
|
||||
role="alert"
|
||||
*ngIf="samlForm.get('idpX509PublicCert').hasError('required')"
|
||||
>
|
||||
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="idpAllowUnsolicitedAuthnResponse"
|
||||
formControlName="idpAllowUnsolicitedAuthnResponse"
|
||||
/>
|
||||
<label class="form-check-label" for="idpAllowUnsolicitedAuthnResponse">
|
||||
{{ "idpAllowUnsolicitedAuthnResponse" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="idpDisableOutboundLogoutRequests"
|
||||
formControlName="idpDisableOutboundLogoutRequests"
|
||||
/>
|
||||
<label class="form-check-label" for="idpDisableOutboundLogoutRequests">
|
||||
{{ "idpDisableOutboundLogoutRequests" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="idpWantAuthnRequestsSigned"
|
||||
formControlName="idpWantAuthnRequestsSigned"
|
||||
/>
|
||||
<label class="form-check-label" for="idpWantAuthnRequestsSigned">
|
||||
{{ "idpWantAuthnRequestsSigned" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||
{{ "fieldRequiredError" | i18n: ("idpX509PublicCert" | i18n) }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<app-select
|
||||
controlId="idpOutboundSigningAlgorithm"
|
||||
[label]="'idpOutboundSigningAlgorithm' | i18n"
|
||||
[selectOptions]="samlSigningAlgorithmOptions"
|
||||
formControlName="idpOutboundSigningAlgorithm"
|
||||
>
|
||||
</app-select>
|
||||
|
||||
<!--TODO: Uncomment once Unsolicited IdP Response is supported-->
|
||||
<!-- <app-input-checkbox
|
||||
controlId="idpAllowUnsolicitedAuthnResponse"
|
||||
formControlName="idpAllowUnsolicitedAuthnResponse"
|
||||
[label]="'idpAllowUnsolicitedAuthnResponse' | i18n"
|
||||
></app-input-checkbox> -->
|
||||
|
||||
<app-input-checkbox
|
||||
controlId="idpAllowOutboundLogoutRequests"
|
||||
formControlName="idpAllowOutboundLogoutRequests"
|
||||
[label]="'idpAllowOutboundLogoutRequests' | i18n"
|
||||
></app-input-checkbox>
|
||||
|
||||
<app-input-checkbox
|
||||
controlId="idpWantAuthnRequestsSigned"
|
||||
formControlName="idpWantAuthnRequestsSigned"
|
||||
[label]="'idpSignAuthenticationRequests' | i18n"
|
||||
></app-input-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -489,4 +434,15 @@
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "save" | i18n }}</span>
|
||||
</button>
|
||||
<div
|
||||
id="errorSummary"
|
||||
class="error-summary text-danger"
|
||||
*ngIf="this.getErrorCount(ssoConfigForm) as errorCount"
|
||||
>
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||
{{
|
||||
(errorCount === 1 ? "formErrorSummarySingle" : "formErrorSummaryPlural") | i18n: errorCount
|
||||
}}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,29 +1,82 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { AbstractControl, FormBuilder, FormGroup } from "@angular/forms";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { SelectOptions } from "jslib-angular/interfaces/selectOptions";
|
||||
import { dirtyRequired } from "jslib-angular/validators/dirty.validator";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
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 {
|
||||
OpenIdConnectRedirectBehavior,
|
||||
Saml2BindingType,
|
||||
Saml2NameIdFormat,
|
||||
Saml2SigningBehavior,
|
||||
SsoType,
|
||||
} from "jslib-common/enums/ssoEnums";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { SsoConfigApi } from "jslib-common/models/api/ssoConfigApi";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
|
||||
import { OrganizationSsoRequest } from "jslib-common/models/request/organization/organizationSsoRequest";
|
||||
import { OrganizationSsoResponse } from "jslib-common/models/response/organization/organizationSsoResponse";
|
||||
import { SsoConfigView } from "jslib-common/models/view/ssoConfigView";
|
||||
|
||||
const defaultSigningAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-manage-sso",
|
||||
templateUrl: "sso.component.html",
|
||||
})
|
||||
export class SsoComponent implements OnInit {
|
||||
samlSigningAlgorithms = [
|
||||
readonly ssoType = SsoType;
|
||||
|
||||
readonly ssoTypeOptions: SelectOptions[] = [
|
||||
{ name: this.i18nService.t("selectType"), value: SsoType.None, disabled: true },
|
||||
{ name: "OpenID Connect", value: SsoType.OpenIdConnect },
|
||||
{ name: "SAML 2.0", value: SsoType.Saml2 },
|
||||
];
|
||||
|
||||
readonly samlSigningAlgorithms = [
|
||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
||||
"http://www.w3.org/2000/09/xmldsig#rsa-sha384",
|
||||
"http://www.w3.org/2000/09/xmldsig#rsa-sha512",
|
||||
"http://www.w3.org/2000/09/xmldsig#rsa-sha1",
|
||||
];
|
||||
|
||||
readonly saml2SigningBehaviourOptions: SelectOptions[] = [
|
||||
{
|
||||
name: "If IdP Wants Authn Requests Signed",
|
||||
value: Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned,
|
||||
},
|
||||
{ name: "Always", value: Saml2SigningBehavior.Always },
|
||||
{ name: "Never", value: Saml2SigningBehavior.Never },
|
||||
];
|
||||
readonly saml2BindingTypeOptions: SelectOptions[] = [
|
||||
{ name: "Redirect", value: Saml2BindingType.HttpRedirect },
|
||||
{ name: "HTTP POST", value: Saml2BindingType.HttpPost },
|
||||
];
|
||||
readonly saml2NameIdFormatOptions: SelectOptions[] = [
|
||||
{ name: "Not Configured", value: Saml2NameIdFormat.NotConfigured },
|
||||
{ name: "Unspecified", value: Saml2NameIdFormat.Unspecified },
|
||||
{ name: "Email Address", value: Saml2NameIdFormat.EmailAddress },
|
||||
{ name: "X.509 Subject Name", value: Saml2NameIdFormat.X509SubjectName },
|
||||
{ name: "Windows Domain Qualified Name", value: Saml2NameIdFormat.WindowsDomainQualifiedName },
|
||||
{ name: "Kerberos Principal Name", value: Saml2NameIdFormat.KerberosPrincipalName },
|
||||
{ name: "Entity Identifier", value: Saml2NameIdFormat.EntityIdentifier },
|
||||
{ name: "Persistent", value: Saml2NameIdFormat.Persistent },
|
||||
{ name: "Transient", value: Saml2NameIdFormat.Transient },
|
||||
];
|
||||
|
||||
readonly connectRedirectOptions: SelectOptions[] = [
|
||||
{ name: "Redirect GET", value: OpenIdConnectRedirectBehavior.RedirectGet },
|
||||
{ name: "Form POST", value: OpenIdConnectRedirectBehavior.FormPost },
|
||||
];
|
||||
|
||||
showOpenIdCustomizations = false;
|
||||
|
||||
loading = true;
|
||||
haveTestedKeyConnector = false;
|
||||
organizationId: string;
|
||||
organization: Organization;
|
||||
formPromise: Promise<any>;
|
||||
@@ -35,44 +88,57 @@ export class SsoComponent implements OnInit {
|
||||
spAcsUrl: string;
|
||||
|
||||
enabled = this.formBuilder.control(false);
|
||||
data = this.formBuilder.group({
|
||||
configType: [],
|
||||
|
||||
keyConnectorEnabled: [],
|
||||
keyConnectorUrl: [],
|
||||
openIdForm = this.formBuilder.group(
|
||||
{
|
||||
authority: ["", dirtyRequired],
|
||||
clientId: ["", dirtyRequired],
|
||||
clientSecret: ["", dirtyRequired],
|
||||
metadataAddress: [],
|
||||
redirectBehavior: [OpenIdConnectRedirectBehavior.RedirectGet, dirtyRequired],
|
||||
getClaimsFromUserInfoEndpoint: [],
|
||||
additionalScopes: [],
|
||||
additionalUserIdClaimTypes: [],
|
||||
additionalEmailClaimTypes: [],
|
||||
additionalNameClaimTypes: [],
|
||||
acrValues: [],
|
||||
expectedReturnAcrValue: [],
|
||||
},
|
||||
{
|
||||
updateOn: "blur",
|
||||
}
|
||||
);
|
||||
|
||||
// OpenId
|
||||
authority: [],
|
||||
clientId: [],
|
||||
clientSecret: [],
|
||||
metadataAddress: [],
|
||||
redirectBehavior: [],
|
||||
getClaimsFromUserInfoEndpoint: [],
|
||||
additionalScopes: [],
|
||||
additionalUserIdClaimTypes: [],
|
||||
additionalEmailClaimTypes: [],
|
||||
additionalNameClaimTypes: [],
|
||||
acrValues: [],
|
||||
expectedReturnAcrValue: [],
|
||||
samlForm = this.formBuilder.group(
|
||||
{
|
||||
spNameIdFormat: [Saml2NameIdFormat.NotConfigured],
|
||||
spOutboundSigningAlgorithm: [defaultSigningAlgorithm],
|
||||
spSigningBehavior: [Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned],
|
||||
spMinIncomingSigningAlgorithm: [defaultSigningAlgorithm],
|
||||
spWantAssertionsSigned: [],
|
||||
spValidateCertificates: [],
|
||||
|
||||
// SAML
|
||||
spNameIdFormat: [],
|
||||
spOutboundSigningAlgorithm: [],
|
||||
spSigningBehavior: [],
|
||||
spMinIncomingSigningAlgorithm: [],
|
||||
spWantAssertionsSigned: [],
|
||||
spValidateCertificates: [],
|
||||
idpEntityId: ["", dirtyRequired],
|
||||
idpBindingType: [Saml2BindingType.HttpRedirect],
|
||||
idpSingleSignOnServiceUrl: [],
|
||||
idpSingleLogoutServiceUrl: [],
|
||||
idpX509PublicCert: ["", dirtyRequired],
|
||||
idpOutboundSigningAlgorithm: [defaultSigningAlgorithm],
|
||||
idpAllowUnsolicitedAuthnResponse: [],
|
||||
idpAllowOutboundLogoutRequests: [true],
|
||||
idpWantAuthnRequestsSigned: [],
|
||||
},
|
||||
{
|
||||
updateOn: "blur",
|
||||
}
|
||||
);
|
||||
|
||||
idpEntityId: [],
|
||||
idpBindingType: [],
|
||||
idpSingleSignOnServiceUrl: [],
|
||||
idpSingleLogoutServiceUrl: [],
|
||||
idpArtifactResolutionServiceUrl: [],
|
||||
idpX509PublicCert: [],
|
||||
idpOutboundSigningAlgorithm: [],
|
||||
idpAllowUnsolicitedAuthnResponse: [],
|
||||
idpDisableOutboundLogoutRequests: [],
|
||||
idpWantAuthnRequestsSigned: [],
|
||||
ssoConfigForm = this.formBuilder.group({
|
||||
configType: [SsoType.None],
|
||||
keyConnectorEnabled: [false],
|
||||
keyConnectorUrl: [""],
|
||||
openId: this.openIdForm,
|
||||
saml: this.samlForm,
|
||||
});
|
||||
|
||||
constructor(
|
||||
@@ -85,6 +151,25 @@ export class SsoComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.ssoConfigForm.get("configType").valueChanges.subscribe((newType: SsoType) => {
|
||||
if (newType === SsoType.OpenIdConnect) {
|
||||
this.openIdForm.enable();
|
||||
this.samlForm.disable();
|
||||
} else if (newType === SsoType.Saml2) {
|
||||
this.openIdForm.disable();
|
||||
this.samlForm.enable();
|
||||
} else {
|
||||
this.openIdForm.disable();
|
||||
this.samlForm.disable();
|
||||
}
|
||||
});
|
||||
|
||||
this.samlForm
|
||||
.get("spSigningBehavior")
|
||||
.valueChanges.subscribe(() =>
|
||||
this.samlForm.get("idpX509PublicCert").updateValueAndValidity()
|
||||
);
|
||||
|
||||
this.route.parent.parent.params.subscribe(async (params) => {
|
||||
this.organizationId = params.organizationId;
|
||||
await this.load();
|
||||
@@ -94,9 +179,7 @@ export class SsoComponent implements OnInit {
|
||||
async load() {
|
||||
this.organization = await this.organizationService.get(this.organizationId);
|
||||
const ssoSettings = await this.apiService.getOrganizationSso(this.organizationId);
|
||||
|
||||
this.data.patchValue(ssoSettings.data);
|
||||
this.enabled.setValue(ssoSettings.enabled);
|
||||
this.populateForm(ssoSettings);
|
||||
|
||||
this.callbackPath = ssoSettings.urls.callbackPath;
|
||||
this.signedOutCallbackPath = ssoSettings.urls.signedOutCallbackPath;
|
||||
@@ -104,28 +187,30 @@ export class SsoComponent implements OnInit {
|
||||
this.spMetadataUrl = ssoSettings.urls.spMetadataUrl;
|
||||
this.spAcsUrl = ssoSettings.urls.spAcsUrl;
|
||||
|
||||
this.keyConnectorUrl.markAsDirty();
|
||||
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
copy(value: string) {
|
||||
this.platformUtilsService.copyToClipboard(value);
|
||||
}
|
||||
|
||||
launchUri(url: string) {
|
||||
this.platformUtilsService.launchUri(url);
|
||||
}
|
||||
|
||||
async submit() {
|
||||
this.formPromise = this.postData();
|
||||
this.validateForm(this.ssoConfigForm);
|
||||
|
||||
if (this.ssoConfigForm.get("keyConnectorEnabled").value) {
|
||||
await this.validateKeyConnectorUrl();
|
||||
}
|
||||
|
||||
if (!this.ssoConfigForm.valid) {
|
||||
this.readOutErrors();
|
||||
return;
|
||||
}
|
||||
|
||||
const request = new OrganizationSsoRequest();
|
||||
request.enabled = this.enabled.value;
|
||||
request.data = SsoConfigApi.fromView(this.ssoConfigForm.value as SsoConfigView);
|
||||
|
||||
this.formPromise = this.apiService.postOrganizationSso(this.organizationId, request);
|
||||
|
||||
try {
|
||||
const response = await this.formPromise;
|
||||
|
||||
this.data.patchValue(response.data);
|
||||
this.enabled.setValue(response.enabled);
|
||||
|
||||
this.populateForm(response);
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved"));
|
||||
} catch {
|
||||
// Logged by appApiAction, do nothing
|
||||
@@ -134,24 +219,8 @@ export class SsoComponent implements OnInit {
|
||||
this.formPromise = null;
|
||||
}
|
||||
|
||||
async postData() {
|
||||
if (this.data.get("keyConnectorEnabled").value) {
|
||||
await this.validateKeyConnectorUrl();
|
||||
|
||||
if (this.keyConnectorUrl.hasError("invalidUrl")) {
|
||||
throw new Error(this.i18nService.t("keyConnectorTestFail"));
|
||||
}
|
||||
}
|
||||
|
||||
const request = new OrganizationSsoRequest();
|
||||
request.enabled = this.enabled.value;
|
||||
request.data = this.data.value;
|
||||
|
||||
return this.apiService.postOrganizationSso(this.organizationId, request);
|
||||
}
|
||||
|
||||
async validateKeyConnectorUrl() {
|
||||
if (this.keyConnectorUrl.pristine) {
|
||||
if (this.haveTestedKeyConnector) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,18 +235,84 @@ export class SsoComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
this.keyConnectorUrl.markAsPristine();
|
||||
this.haveTestedKeyConnector = true;
|
||||
}
|
||||
|
||||
toggleOpenIdCustomizations() {
|
||||
this.showOpenIdCustomizations = !this.showOpenIdCustomizations;
|
||||
}
|
||||
|
||||
getErrorCount(form: FormGroup): number {
|
||||
return Object.values(form.controls).reduce((acc: number, control: AbstractControl) => {
|
||||
if (control instanceof FormGroup) {
|
||||
return acc + this.getErrorCount(control);
|
||||
}
|
||||
|
||||
if (control.errors == null) {
|
||||
return acc;
|
||||
}
|
||||
return acc + Object.keys(control.errors).length;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
get enableTestKeyConnector() {
|
||||
return (
|
||||
this.data.get("keyConnectorEnabled").value &&
|
||||
this.keyConnectorUrl != null &&
|
||||
this.keyConnectorUrl.value !== ""
|
||||
this.ssoConfigForm.get("keyConnectorEnabled").value &&
|
||||
!Utils.isNullOrWhitespace(this.keyConnectorUrl?.value)
|
||||
);
|
||||
}
|
||||
|
||||
get keyConnectorUrl() {
|
||||
return this.data.get("keyConnectorUrl");
|
||||
return this.ssoConfigForm.get("keyConnectorUrl");
|
||||
}
|
||||
|
||||
get samlSigningAlgorithmOptions(): SelectOptions[] {
|
||||
return this.samlSigningAlgorithms.map((algorithm) => ({ name: algorithm, value: algorithm }));
|
||||
}
|
||||
|
||||
private validateForm(form: FormGroup) {
|
||||
Object.values(form.controls).forEach((control: AbstractControl) => {
|
||||
if (control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (control instanceof FormGroup) {
|
||||
this.validateForm(control);
|
||||
} else {
|
||||
control.markAsDirty();
|
||||
control.markAsTouched();
|
||||
control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private populateForm(ssoSettings: OrganizationSsoResponse) {
|
||||
this.enabled.setValue(ssoSettings.enabled);
|
||||
if (ssoSettings.data != null) {
|
||||
const ssoConfigView = new SsoConfigView(ssoSettings.data);
|
||||
this.ssoConfigForm.patchValue(ssoConfigView);
|
||||
}
|
||||
}
|
||||
|
||||
private readOutErrors() {
|
||||
const errorText = this.i18nService.t("error");
|
||||
const errorCount = this.getErrorCount(this.ssoConfigForm);
|
||||
const errorCountText = this.i18nService.t(
|
||||
errorCount === 1 ? "formErrorSummarySingle" : "formErrorSummaryPlural",
|
||||
errorCount.toString()
|
||||
);
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.className = "sr-only";
|
||||
div.id = "srErrorCount";
|
||||
div.setAttribute("aria-live", "polite");
|
||||
div.innerText = errorText + ": " + errorCountText;
|
||||
|
||||
const existing = document.getElementById("srErrorCount");
|
||||
if (existing != null) {
|
||||
existing.remove();
|
||||
}
|
||||
|
||||
document.body.append(div);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
|
||||
|
||||
import { Permissions } from "jslib-common/enums/permissions";
|
||||
|
||||
import { OrganizationLayoutComponent } from "src/app/layouts/organization-layout.component";
|
||||
|
||||
@@ -4,11 +4,23 @@ import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
|
||||
import { OssModule } from "src/app/oss.module";
|
||||
|
||||
import { InputCheckboxComponent } from "./components/input-checkbox.component";
|
||||
import { InputTextReadOnlyComponent } from "./components/input-text-readonly.component";
|
||||
import { InputTextComponent } from "./components/input-text.component";
|
||||
import { SelectComponent } from "./components/select.component";
|
||||
import { SsoComponent } from "./manage/sso.component";
|
||||
import { OrganizationsRoutingModule } from "./organizations-routing.module";
|
||||
|
||||
// Form components are for use in the SSO Configuration Form only and should not be exported for use elsewhere.
|
||||
// They will be deprecated by the Component Library.
|
||||
@NgModule({
|
||||
imports: [CommonModule, FormsModule, ReactiveFormsModule, OssModule, OrganizationsRoutingModule],
|
||||
declarations: [SsoComponent],
|
||||
declarations: [
|
||||
InputCheckboxComponent,
|
||||
InputTextComponent,
|
||||
InputTextReadOnlyComponent,
|
||||
SelectComponent,
|
||||
SsoComponent,
|
||||
],
|
||||
})
|
||||
export class OrganizationsModule {}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
import { PolicyType } from "jslib-common/enums/policyType";
|
||||
|
||||
import { PolicyRequest } from "jslib-common/models/request/policyRequest";
|
||||
|
||||
import {
|
||||
BasePolicy,
|
||||
BasePolicyComponent,
|
||||
|
||||
@@ -2,9 +2,7 @@ import { Component } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
import { PolicyType } from "jslib-common/enums/policyType";
|
||||
|
||||
import { PolicyRequest } from "jslib-common/models/request/policyRequest";
|
||||
|
||||
import {
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
|
||||
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
|
||||
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||
|
||||
import { WebProviderService } from "../services/webProvider.service";
|
||||
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
import { Provider } from "jslib-common/models/domain/provider";
|
||||
|
||||
import { WebProviderService } from "../services/webProvider.service";
|
||||
|
||||
@Component({
|
||||
selector: "provider-add-organization",
|
||||
templateUrl: "add-organization.component.html",
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ValidationService } from "jslib-angular/services/validation.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";
|
||||
@@ -10,13 +11,8 @@ import { OrganizationService } from "jslib-common/abstractions/organization.serv
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||
|
||||
import { PlanType } from "jslib-common/enums/planType";
|
||||
import { ProviderUserType } from "jslib-common/enums/providerUserType";
|
||||
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
import { ProviderOrganizationOrganizationDetailsResponse } from "jslib-common/models/response/provider/providerOrganizationResponse";
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
|
||||
|
||||
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 { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { ProviderUserAcceptRequest } from "jslib-common/models/request/provider/providerUserAcceptRequest";
|
||||
|
||||
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-accept-provider",
|
||||
templateUrl: "accept-provider.component.html",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
||||
import { ProviderUserBulkConfirmRequest } from "jslib-common/models/request/provider/providerUserBulkConfirmRequest";
|
||||
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
|
||||
|
||||
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
||||
|
||||
import { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "src/app/organizations/manage/bulk/bulk-confirm.component";
|
||||
import { BulkUserDetails } from "src/app/organizations/manage/bulk/bulk-status.component";
|
||||
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { ExportService } from "jslib-common/abstractions/export.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 { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
|
||||
import { EventResponse } from "jslib-common/models/response/eventResponse";
|
||||
|
||||
import { EventService } from "src/app/services/event.service";
|
||||
|
||||
import { BaseEventsComponent } from "src/app/common/base.events.component";
|
||||
import { EventService } from "src/app/services/event.service";
|
||||
|
||||
@Component({
|
||||
selector: "provider-events",
|
||||
templateUrl: "events.component.html",
|
||||
})
|
||||
export class EventsComponent extends BaseEventsComponent implements OnInit {
|
||||
exportFileName: string = "provider-events";
|
||||
exportFileName = "provider-events";
|
||||
providerId: string;
|
||||
|
||||
private providerUsersUserIdMap = new Map<string, any>();
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
|
||||
import { Provider } from "jslib-common/models/domain/provider";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
{{ "eventLogs" | i18n }}
|
||||
</a>
|
||||
<a class="dropdown-item text-danger" href="#" appStopClick (click)="remove(u)">
|
||||
<i class="bwi bwi-fw bwi-remove" aria-hidden="true"></i>
|
||||
<i class="bwi bwi-fw bwi-close" aria-hidden="true"></i>
|
||||
{{ "remove" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ValidationService } from "jslib-angular/services/validation.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";
|
||||
@@ -11,26 +14,18 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
|
||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||
|
||||
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
||||
import { ProviderUserType } from "jslib-common/enums/providerUserType";
|
||||
|
||||
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
|
||||
import { ListResponse } from "jslib-common/models/response/listResponse";
|
||||
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
|
||||
|
||||
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
|
||||
import { ProviderUserConfirmRequest } from "jslib-common/models/request/provider/providerUserConfirmRequest";
|
||||
import { ListResponse } from "jslib-common/models/response/listResponse";
|
||||
import { ProviderUserBulkResponse } from "jslib-common/models/response/provider/providerUserBulkResponse";
|
||||
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
|
||||
|
||||
import { BasePeopleComponent } from "src/app/common/base.people.component";
|
||||
import { BulkStatusComponent } from "src/app/organizations/manage/bulk/bulk-status.component";
|
||||
import { EntityEventsComponent } from "src/app/organizations/manage/entity-events.component";
|
||||
|
||||
import { BulkConfirmComponent } from "./bulk/bulk-confirm.component";
|
||||
import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
|
||||
import { UserAddEditComponent } from "./user-add-edit.component";
|
||||
@@ -158,17 +153,13 @@ export class PeopleComponent
|
||||
}
|
||||
|
||||
async events(user: ProviderUserUserDetailsResponse) {
|
||||
const [modal] = await this.modalService.openViewRef(
|
||||
EntityEventsComponent,
|
||||
this.eventsModalRef,
|
||||
(comp) => {
|
||||
comp.name = this.userNamePipe.transform(user);
|
||||
comp.providerId = this.providerId;
|
||||
comp.entityId = user.id;
|
||||
comp.showUser = false;
|
||||
comp.entity = "user";
|
||||
}
|
||||
);
|
||||
await this.modalService.openViewRef(EntityEventsComponent, this.eventsModalRef, (comp) => {
|
||||
comp.name = this.userNamePipe.transform(user);
|
||||
comp.providerId = this.providerId;
|
||||
comp.entityId = user.id;
|
||||
comp.showUser = false;
|
||||
comp.entity = "user";
|
||||
});
|
||||
}
|
||||
|
||||
async bulkRemove() {
|
||||
@@ -272,13 +263,14 @@ export class PeopleComponent
|
||||
|
||||
childComponent.users = users.map((user) => {
|
||||
let message = keyedErrors[user.id] ?? successfullMessage;
|
||||
// eslint-disable-next-line
|
||||
if (!keyedFilteredUsers.hasOwnProperty(user.id)) {
|
||||
message = this.i18nService.t("bulkFilteredMessage");
|
||||
}
|
||||
|
||||
return {
|
||||
user: user,
|
||||
error: keyedErrors.hasOwnProperty(user.id),
|
||||
error: keyedErrors.hasOwnProperty(user.id), // eslint-disable-line
|
||||
message: message,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
href="https://bitwarden.com/help/article/user-types-access-control/#user-types"
|
||||
href="https://bitwarden.com/help/provider-users/"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
|
||||
@@ -4,12 +4,9 @@ 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 { ProviderUserInviteRequest } from "jslib-common/models/request/provider/providerUserInviteRequest";
|
||||
|
||||
import { PermissionsApi } from "jslib-common/models/api/permissionsApi";
|
||||
|
||||
import { ProviderUserType } from "jslib-common/enums/providerUserType";
|
||||
import { PermissionsApi } from "jslib-common/models/api/permissionsApi";
|
||||
import { ProviderUserInviteRequest } from "jslib-common/models/request/provider/providerUserInviteRequest";
|
||||
import { ProviderUserUpdateRequest } from "jslib-common/models/request/provider/providerUserUpdateRequest";
|
||||
|
||||
@Component({
|
||||
@@ -24,7 +21,7 @@ export class UserAddEditComponent implements OnInit {
|
||||
@Output() onDeletedUser = new EventEmitter();
|
||||
|
||||
loading = true;
|
||||
editMode: boolean = false;
|
||||
editMode = false;
|
||||
title: string;
|
||||
emails: string;
|
||||
type: ProviderUserType = ProviderUserType.ServiceUser;
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Component } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
|
||||
import { Provider } from "jslib-common/models/domain/provider";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -4,7 +4,9 @@ import { RouterModule, Routes } from "@angular/router";
|
||||
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
|
||||
import { Permissions } from "jslib-common/enums/permissions";
|
||||
|
||||
import { AddOrganizationComponent } from "./clients/add-organization.component";
|
||||
import { FrontendLayoutComponent } from "src/app/layouts/frontend-layout.component";
|
||||
import { ProvidersComponent } from "src/app/providers/providers.component";
|
||||
|
||||
import { ClientsComponent } from "./clients/clients.component";
|
||||
import { CreateOrganizationComponent } from "./clients/create-organization.component";
|
||||
import { AcceptProviderComponent } from "./manage/accept-provider.component";
|
||||
@@ -12,16 +14,12 @@ import { EventsComponent } from "./manage/events.component";
|
||||
import { ManageComponent } from "./manage/manage.component";
|
||||
import { PeopleComponent } from "./manage/people.component";
|
||||
import { ProvidersLayoutComponent } from "./providers-layout.component";
|
||||
import { SettingsComponent } from "./settings/settings.component";
|
||||
import { SetupProviderComponent } from "./setup/setup-provider.component";
|
||||
import { SetupComponent } from "./setup/setup.component";
|
||||
|
||||
import { FrontendLayoutComponent } from "src/app/layouts/frontend-layout.component";
|
||||
|
||||
import { ProvidersComponent } from "src/app/providers/providers.component";
|
||||
import { ProviderGuardService } from "./services/provider-guard.service";
|
||||
import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
|
||||
import { AccountComponent } from "./settings/account.component";
|
||||
import { SettingsComponent } from "./settings/settings.component";
|
||||
import { SetupProviderComponent } from "./setup/setup-provider.component";
|
||||
import { SetupComponent } from "./setup/setup.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { ComponentFactoryResolver } from "@angular/core";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { ComponentFactoryResolver, NgModule } from "@angular/core";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
|
||||
import { ProviderGuardService } from "./services/provider-guard.service";
|
||||
import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
|
||||
import { WebProviderService } from "./services/webProvider.service";
|
||||
|
||||
import { ProvidersLayoutComponent } from "./providers-layout.component";
|
||||
import { ProvidersRoutingModule } from "./providers-routing.module";
|
||||
import { OssModule } from "src/app/oss.module";
|
||||
|
||||
import { AddOrganizationComponent } from "./clients/add-organization.component";
|
||||
import { ClientsComponent } from "./clients/clients.component";
|
||||
import { CreateOrganizationComponent } from "./clients/create-organization.component";
|
||||
|
||||
import { AcceptProviderComponent } from "./manage/accept-provider.component";
|
||||
import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component";
|
||||
import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.component";
|
||||
@@ -23,15 +16,16 @@ import { EventsComponent } from "./manage/events.component";
|
||||
import { ManageComponent } from "./manage/manage.component";
|
||||
import { PeopleComponent } from "./manage/people.component";
|
||||
import { UserAddEditComponent } from "./manage/user-add-edit.component";
|
||||
|
||||
import { ProvidersLayoutComponent } from "./providers-layout.component";
|
||||
import { ProvidersRoutingModule } from "./providers-routing.module";
|
||||
import { ProviderGuardService } from "./services/provider-guard.service";
|
||||
import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
|
||||
import { WebProviderService } from "./services/webProvider.service";
|
||||
import { AccountComponent } from "./settings/account.component";
|
||||
import { SettingsComponent } from "./settings/settings.component";
|
||||
|
||||
import { SetupProviderComponent } from "./setup/setup-provider.component";
|
||||
import { SetupComponent } from "./setup/setup.component";
|
||||
|
||||
import { OssModule } from "src/app/oss.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, FormsModule, OssModule, ProvidersRoutingModule],
|
||||
declarations: [
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Injectable } from "@angular/core";
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
|
||||
|
||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
|
||||
import { Permissions } from "jslib-common/enums/permissions";
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Injectable } from "@angular/core";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { ProviderAddOrganizationRequest } from "jslib-common/models/request/provider/providerAddOrganizationRequest";
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -6,9 +6,7 @@ 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 { ProviderUpdateRequest } from "jslib-common/models/request/provider/providerUpdateRequest";
|
||||
|
||||
import { ProviderResponse } from "jslib-common/models/response/provider/providerResponse";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
|
||||
@Component({
|
||||
@@ -9,15 +8,11 @@ import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
templateUrl: "settings.component.html",
|
||||
})
|
||||
export class SettingsComponent {
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private providerService: ProviderService,
|
||||
private platformUtilsService: PlatformUtilsService
|
||||
) {}
|
||||
constructor(private route: ActivatedRoute, private providerService: ProviderService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.parent.params.subscribe(async (params) => {
|
||||
const provider = await this.providerService.get(params.providerId);
|
||||
await this.providerService.get(params.providerId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export class SetupProviderComponent extends BaseAcceptComponent {
|
||||
this.router.navigate(["/providers/setup"], { queryParams: qParams });
|
||||
}
|
||||
|
||||
// tslint:disable-next-line
|
||||
async unauthedHandler(qParams: any) {}
|
||||
async unauthedHandler(qParams: any) {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
|
||||
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
types {
|
||||
|
||||
# Data interchange
|
||||
|
||||
application/atom+xml atom;
|
||||
application/json json map topojson;
|
||||
application/ld+json jsonld;
|
||||
application/rss+xml rss;
|
||||
application/vnd.geo+json geojson;
|
||||
application/xml rdf xml;
|
||||
|
||||
|
||||
# JavaScript
|
||||
|
||||
# Normalize to standard type.
|
||||
# https://tools.ietf.org/html/rfc4329#section-7.2
|
||||
application/javascript js;
|
||||
|
||||
|
||||
# Manifest files
|
||||
|
||||
application/manifest+json webmanifest;
|
||||
application/x-web-app-manifest+json webapp;
|
||||
text/cache-manifest appcache;
|
||||
|
||||
|
||||
# Media files
|
||||
|
||||
audio/midi mid midi kar;
|
||||
audio/mp4 aac f4a f4b m4a;
|
||||
audio/mpeg mp3;
|
||||
audio/ogg oga ogg opus;
|
||||
audio/x-realaudio ra;
|
||||
audio/x-wav wav;
|
||||
image/bmp bmp;
|
||||
image/gif gif;
|
||||
image/jpeg jpeg jpg;
|
||||
image/jxr jxr hdp wdp;
|
||||
image/png png;
|
||||
image/svg+xml svg svgz;
|
||||
image/tiff tif tiff;
|
||||
image/vnd.wap.wbmp wbmp;
|
||||
image/webp webp;
|
||||
image/x-jng jng;
|
||||
video/3gpp 3gp 3gpp;
|
||||
video/mp4 f4p f4v m4v mp4;
|
||||
video/mpeg mpeg mpg;
|
||||
video/ogg ogv;
|
||||
video/quicktime mov;
|
||||
video/webm webm;
|
||||
video/x-flv flv;
|
||||
video/x-mng mng;
|
||||
video/x-ms-asf asf asx;
|
||||
video/x-ms-wmv wmv;
|
||||
video/x-msvideo avi;
|
||||
|
||||
# Serving `.ico` image files with a different media type
|
||||
# prevents Internet Explorer from displaying then as images:
|
||||
# https://github.com/h5bp/html5-boilerplate/commit/37b5fec090d00f38de64b591bcddcb205aadf8ee
|
||||
|
||||
image/x-icon cur ico;
|
||||
|
||||
|
||||
# Microsoft Office
|
||||
|
||||
application/msword doc;
|
||||
application/vnd.ms-excel xls;
|
||||
application/vnd.ms-powerpoint ppt;
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
|
||||
|
||||
|
||||
# Web fonts
|
||||
|
||||
application/font-woff woff;
|
||||
application/font-woff2 woff2;
|
||||
application/vnd.ms-fontobject eot;
|
||||
|
||||
# Browsers usually ignore the font media types and simply sniff
|
||||
# the bytes to figure out the font type.
|
||||
# https://mimesniff.spec.whatwg.org/#matching-a-font-type-pattern
|
||||
#
|
||||
# However, Blink and WebKit based browsers will show a warning
|
||||
# in the console if the following font types are served with any
|
||||
# other media types.
|
||||
|
||||
application/x-font-ttf ttc ttf;
|
||||
font/opentype otf;
|
||||
|
||||
|
||||
# Other
|
||||
|
||||
application/java-archive ear jar war;
|
||||
application/mac-binhex40 hqx;
|
||||
application/octet-stream bin deb dll dmg exe img iso msi msm msp safariextz;
|
||||
application/pdf pdf;
|
||||
application/postscript ai eps ps;
|
||||
application/rtf rtf;
|
||||
application/vnd.google-earth.kml+xml kml;
|
||||
application/vnd.google-earth.kmz kmz;
|
||||
application/vnd.wap.wmlc wmlc;
|
||||
application/x-7z-compressed 7z;
|
||||
application/x-bb-appworld bbaw;
|
||||
application/x-bittorrent torrent;
|
||||
application/x-chrome-extension crx;
|
||||
application/x-cocoa cco;
|
||||
application/x-java-archive-diff jardiff;
|
||||
application/x-java-jnlp-file jnlp;
|
||||
application/x-makeself run;
|
||||
application/x-opera-extension oex;
|
||||
application/x-perl pl pm;
|
||||
application/x-pilot pdb prc;
|
||||
application/x-rar-compressed rar;
|
||||
application/x-redhat-package-manager rpm;
|
||||
application/x-sea sea;
|
||||
application/x-shockwave-flash swf;
|
||||
application/x-stuffit sit;
|
||||
application/x-tcl tcl tk;
|
||||
application/x-x509-ca-cert crt der pem;
|
||||
application/x-xpinstall xpi;
|
||||
application/xhtml+xml xhtml;
|
||||
application/xslt+xml xsl;
|
||||
application/zip zip;
|
||||
text/css css;
|
||||
text/csv csv;
|
||||
text/html htm html shtml;
|
||||
text/markdown md;
|
||||
text/mathml mml;
|
||||
text/plain txt;
|
||||
text/vcard vcard vcf;
|
||||
text/vnd.rim.location.xloc xloc;
|
||||
text/vnd.sun.j2me.app-descriptor jad;
|
||||
text/vnd.wap.wml wml;
|
||||
text/vtt vtt;
|
||||
text/x-component htc;
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#######################################################################
|
||||
# WARNING: This file is generated. Do not make changes to this file. #
|
||||
# They will be overwritten on update. You can manage various settings #
|
||||
# used in this file from the ./bwdata/config.yml file for your #
|
||||
# installation. #
|
||||
#######################################################################
|
||||
|
||||
server {
|
||||
listen 8080 default_server;
|
||||
listen [::]:8080 default_server;
|
||||
include /etc/nginx/security-headers.conf;
|
||||
|
||||
location / {
|
||||
root /app;
|
||||
index index.html index.htm;
|
||||
include /etc/nginx/security-headers.conf;
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
add_header X-Robots-Tag "noindex, nofollow";
|
||||
}
|
||||
|
||||
location /alive {
|
||||
return 200 'alive';
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
# nginx Configuration File
|
||||
# http://wiki.nginx.org/Configuration
|
||||
|
||||
# Run as a less privileged user for security reasons.
|
||||
# user www www;
|
||||
|
||||
# How many worker threads to run;
|
||||
# "auto" sets it to the number of CPU cores available in the system, and
|
||||
# offers the best performance. Don't set it higher than the number of CPU
|
||||
# cores if changing this parameter.
|
||||
|
||||
# The maximum number of connections for Nginx is calculated by:
|
||||
# max_clients = worker_processes * worker_connections
|
||||
worker_processes auto;
|
||||
|
||||
# Maximum open file descriptors per process;
|
||||
# should be > worker_connections.
|
||||
worker_rlimit_nofile 8192;
|
||||
|
||||
events {
|
||||
# When you need > 8000 * cpu_cores connections, you start optimizing your OS,
|
||||
# and this is probably the point at which you hire people who are smarter than
|
||||
# you, as this is *a lot* of requests.
|
||||
worker_connections 8000;
|
||||
}
|
||||
|
||||
# Default error log file
|
||||
# (this is only used when you don't override error_log on a server{} level)
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
http {
|
||||
# Hide nginx version information.
|
||||
server_tokens off;
|
||||
|
||||
# Define the MIME types for files.
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Update charset_types to match updated mime.types.
|
||||
# text/html is always included by charset module.
|
||||
# Default: text/html text/xml text/plain text/vnd.wap.wml application/javascript application/rss+xml
|
||||
charset_types
|
||||
text/css
|
||||
text/plain
|
||||
text/vnd.wap.wml
|
||||
application/javascript
|
||||
application/json
|
||||
application/rss+xml
|
||||
application/xml;
|
||||
|
||||
# Format to use in log files
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
# Default log file
|
||||
# (this is only used when you don't override access_log on a server{} level)
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# How long to allow each connection to stay idle; longer values are better
|
||||
# for each individual client, particularly for SSL, but means that worker
|
||||
# connections are tied up longer. (Default: 65)
|
||||
keepalive_timeout 20;
|
||||
|
||||
# Speed up file transfers by using sendfile() to copy directly
|
||||
# between descriptors rather than using read()/write().
|
||||
# For performance reasons, on FreeBSD systems w/ ZFS
|
||||
# this option should be disabled as ZFS's ARC caches
|
||||
# frequently used files in RAM by default.
|
||||
sendfile on;
|
||||
|
||||
# Tell Nginx not to send out partial frames; this increases throughput
|
||||
# since TCP frames are filled up before being sent out. (adds TCP_CORK)
|
||||
tcp_nopush on;
|
||||
|
||||
|
||||
# Compression
|
||||
|
||||
# Enable Gzip compressed.
|
||||
gzip on;
|
||||
|
||||
# Compression level (1-9).
|
||||
# 5 is a perfect compromise between size and cpu usage, offering about
|
||||
# 75% reduction for most ascii files (almost identical to level 9).
|
||||
gzip_comp_level 5;
|
||||
|
||||
# Don't compress anything that's already small and unlikely to shrink much
|
||||
# if at all (the default is 20 bytes, which is bad as that usually leads to
|
||||
# larger files after gzipping).
|
||||
gzip_min_length 256;
|
||||
|
||||
# Compress data even for clients that are connecting to us via proxies,
|
||||
# identified by the "Via" header (required for CloudFront).
|
||||
gzip_proxied any;
|
||||
|
||||
# Tell proxies to cache both the gzipped and regular version of a resource
|
||||
# whenever the client's Accept-Encoding capabilities header varies;
|
||||
# Avoids the issue where a non-gzip capable client (which is extremely rare
|
||||
# today) would display gibberish if their proxy gave them the gzipped version.
|
||||
gzip_vary on;
|
||||
|
||||
# Compress all output labeled with one of the following MIME-types.
|
||||
gzip_types
|
||||
application/atom+xml
|
||||
application/javascript
|
||||
application/json
|
||||
application/ld+json
|
||||
application/manifest+json
|
||||
application/rss+xml
|
||||
application/vnd.geo+json
|
||||
application/vnd.ms-fontobject
|
||||
application/x-font-ttf
|
||||
application/x-web-app-manifest+json
|
||||
application/xhtml+xml
|
||||
application/xml
|
||||
font/opentype
|
||||
image/bmp
|
||||
image/svg+xml
|
||||
image/x-icon
|
||||
text/cache-manifest
|
||||
text/css
|
||||
text/plain
|
||||
text/vcard
|
||||
text/vnd.rim.location.xloc
|
||||
text/vtt
|
||||
text/x-component
|
||||
text/x-cross-domain-policy;
|
||||
# text/html is always compressed by HttpGzipModule
|
||||
|
||||
# This should be turned on if you are going to have pre-compressed copies (.gz) of
|
||||
# static files available. If not it should be left off as it will cause extra I/O
|
||||
# for the check. It is best if you enable this in a location{} block for
|
||||
# a specific directory, or on an individual server{} level.
|
||||
# gzip_static on;
|
||||
|
||||
# Content type for FIDO U2F facets
|
||||
map $uri $fido_content_type {
|
||||
default "application/fido.trusted-apps+json";
|
||||
}
|
||||
|
||||
# Include files in the sites-enabled folder. server{} configuration files should be
|
||||
# placed in the sites-available folder, and then the configuration should be enabled
|
||||
# by creating a symlink to it in the sites-enabled folder.
|
||||
# See doc/sites-enabled.md for more info.
|
||||
include conf.d/*.conf;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
add_header Referrer-Policy same-origin;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
@@ -32,6 +32,7 @@ mkhomedir_helper $USERNAME
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
cp /etc/bitwarden/web/app-id.json /app/app-id.json
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
#chown -R $USERNAME:$GROUPNAME /bitwarden_server
|
||||
chown -R $USERNAME:$GROUPNAME /bitwarden_server
|
||||
|
||||
#exec nginx -g 'daemon off;'
|
||||
exec gosu $USERNAME:$GROUPNAME dotnet /bitwarden_server/Server.dll \
|
||||
/contentRoot=/app /webRoot=. /serveUnknown=false /webVault=true
|
||||
2
jslib
2
jslib
Submodule jslib updated: e372bf242b...212bd70986
6857
package-lock.json
generated
6857
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/web-vault",
|
||||
"version": "2.25.1",
|
||||
"version": "2.27.0",
|
||||
"license": "GPL-3.0",
|
||||
"repository": "https://github.com/bitwarden/web",
|
||||
"scripts": {
|
||||
@@ -29,8 +29,8 @@
|
||||
"dist:bit:selfhost": "npm run build:bit:selfhost:prod",
|
||||
"deploy": "npm run dist:bit && gh-pages -d build",
|
||||
"deploy:dev": "npm run dist:bit && gh-pages -d build -r git@github.com:kspearrin/bitwarden-web-dev.git",
|
||||
"lint": "tslint 'src/**/*.ts' 'bitwarden_license/src/**/*.ts' && prettier --check .",
|
||||
"lint:fix": "tslint 'src/**/*.ts' 'bitwarden_license/src/**/*.ts' --fix",
|
||||
"lint": "eslint . && prettier --check .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"prettier": "prettier --write .",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
@@ -41,11 +41,17 @@
|
||||
"@types/node": "^16.11.12",
|
||||
"@types/webcrypto": "^0.0.28",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||
"@typescript-eslint/parser": "^5.10.1",
|
||||
"buffer": "^6.0.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^10.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^6.5.1",
|
||||
"eslint": "^8.7.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"gh-pages": "^3.1.0",
|
||||
"html-loader": "^3.0.1",
|
||||
"html-webpack-injector": "1.1.4",
|
||||
@@ -55,13 +61,12 @@
|
||||
"mini-css-extract-plugin": "^2.4.5",
|
||||
"prettier": "2.5.1",
|
||||
"process": "^0.11.10",
|
||||
"rimraf": "^3.0.2",
|
||||
"sass": "^1.32.10",
|
||||
"sass-loader": "^12.4.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"terser-webpack-plugin": "^5.2.5",
|
||||
"ts-loader": "^9.2.5",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-loader": "^3.5.4",
|
||||
"typescript": "4.3.5",
|
||||
"util": "^0.12.4",
|
||||
"webpack": "^5.64.4",
|
||||
@@ -85,10 +90,11 @@
|
||||
"browser-hrtime": "^1.1.8",
|
||||
"core-js": "^3.11.0",
|
||||
"date-input-polyfill": "^2.14.0",
|
||||
"font-awesome": "4.7.0",
|
||||
"jquery": "3.6.0",
|
||||
"jszip": "^3.7.1",
|
||||
"ngx-infinite-scroll": "^10.0.1",
|
||||
"ngx-toastr": "14.1.4",
|
||||
"node-forge": "^0.10.0",
|
||||
"popper.js": "1.16.1",
|
||||
"qrious": "4.0.2",
|
||||
"rxjs": "^7.4.0",
|
||||
@@ -101,7 +107,8 @@
|
||||
"npm": "~8"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "prettier --ignore-unknown --write",
|
||||
"./!(jslib)**": "prettier --ignore-unknown --write",
|
||||
"*.ts": "eslint --fix",
|
||||
"*.png": "node scripts/optimize.js"
|
||||
}
|
||||
}
|
||||
|
||||
9
src/abstractions/state.service.ts
Normal file
9
src/abstractions/state.service.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
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>;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { EmergencyAccessAcceptRequest } from "jslib-common/models/request/emergencyAccessAcceptRequest";
|
||||
|
||||
import { BaseAcceptComponent } from "../common/base.accept.component";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -8,12 +8,11 @@ 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 { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { Policy } from "jslib-common/models/domain/policy";
|
||||
import { OrganizationUserAcceptRequest } from "jslib-common/models/request/organizationUserAcceptRequest";
|
||||
import { OrganizationUserResetPasswordEnrollmentRequest } from "jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { Policy } from "jslib-common/models/domain/policy";
|
||||
import { BaseAcceptComponent } from "../common/base.accept.component";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
|
||||
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 { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-hint",
|
||||
templateUrl: "hint.component.html",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, NgZone } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
@@ -14,8 +15,6 @@ import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.serv
|
||||
|
||||
import { RouterService } from "../services/router.service";
|
||||
|
||||
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-lock",
|
||||
templateUrl: "lock.component.html",
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
[queryParams]="{ email: email }"
|
||||
class="btn btn-outline-secondary btn-block ml-2 mt-0"
|
||||
>
|
||||
<i class="bwi bwi-pencil-square-o" aria-hidden="true"></i>
|
||||
<i class="bwi bwi-pencil-square" aria-hidden="true"></i>
|
||||
{{ "createAccount" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
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 { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
|
||||
|
||||
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";
|
||||
|
||||
@Component({
|
||||
selector: "app-login",
|
||||
@@ -24,13 +27,14 @@ import { Policy } from "jslib-common/models/domain/policy";
|
||||
})
|
||||
export class LoginComponent extends BaseLoginComponent {
|
||||
showResetPasswordAutoEnrollWarning = false;
|
||||
enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions;
|
||||
policies: ListResponse<PolicyResponse>;
|
||||
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
private route: ActivatedRoute,
|
||||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
@@ -38,7 +42,9 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
private apiService: ApiService,
|
||||
private policyService: PolicyService,
|
||||
logService: LogService,
|
||||
ngZone: NgZone
|
||||
ngZone: NgZone,
|
||||
protected stateService: StateService,
|
||||
private messagingService: MessagingService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
@@ -52,6 +58,9 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
logService,
|
||||
ngZone
|
||||
);
|
||||
this.onSuccessfulLogin = async () => {
|
||||
this.messagingService.send("setFullWidth");
|
||||
};
|
||||
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
||||
}
|
||||
|
||||
@@ -78,35 +87,64 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
});
|
||||
}
|
||||
await super.ngOnInit();
|
||||
this.rememberEmail = await this.stateService.getRememberEmail();
|
||||
});
|
||||
|
||||
const invite = await this.stateService.getOrganizationInvitation();
|
||||
if (invite != null) {
|
||||
let policyList: Policy[] = null;
|
||||
try {
|
||||
const policies = await this.apiService.getPoliciesByToken(
|
||||
this.policies = await this.apiService.getPoliciesByToken(
|
||||
invite.organizationId,
|
||||
invite.token,
|
||||
invite.email,
|
||||
invite.organizationUserId
|
||||
);
|
||||
policyList = this.policyService.mapPoliciesFromToken(policies);
|
||||
policyList = this.policyService.mapPoliciesFromToken(this.policies);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
|
||||
if (policyList != null) {
|
||||
const result = this.policyService.getResetPasswordPolicyOptions(
|
||||
const resetPasswordPolicy = this.policyService.getResetPasswordPolicyOptions(
|
||||
policyList,
|
||||
invite.organizationId
|
||||
);
|
||||
// Set to true if policy enabled and auto-enroll enabled
|
||||
this.showResetPasswordAutoEnrollWarning = result[1] && result[0].autoEnrollEnabled;
|
||||
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 loginRedirect = await this.stateService.getLoginRedirect();
|
||||
if (loginRedirect != null) {
|
||||
this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams });
|
||||
@@ -115,4 +153,27 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ 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 { DeleteRecoverRequest } from "jslib-common/models/request/deleteRecoverRequest";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<p>
|
||||
{{ "recoverAccountTwoStepDesc" | i18n }}
|
||||
<a
|
||||
href="https://help.bitwarden.com/article/lost-two-step-device/"
|
||||
href="https://bitwarden.com/help/lost-two-step-device/"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>{{ "learnMore" | i18n }}</a
|
||||
|
||||
@@ -7,7 +7,6 @@ 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 { TwoFactorRecoveryRequest } from "jslib-common/models/request/twoFactorRecoveryRequest";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,44 +1,100 @@
|
||||
<div class="layout" [ngClass]="['layout', layout]">
|
||||
<header class="header" *ngIf="layout === 'enterprise2'">
|
||||
<!-- TEAMS 1 Header -->
|
||||
<header
|
||||
class="header"
|
||||
*ngIf="
|
||||
layout === 'default' ||
|
||||
layout === 'teams' ||
|
||||
layout === 'teams1' ||
|
||||
layout === 'teams2' ||
|
||||
layout === 'enterprise' ||
|
||||
layout === 'enterprise1' ||
|
||||
layout === 'enterprise2' ||
|
||||
layout === 'cnetcmpgnent' ||
|
||||
layout === 'cnetcmpgnteams' ||
|
||||
layout === 'cnetcmpgnind'
|
||||
"
|
||||
>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-7">
|
||||
<img
|
||||
alt="Bitwarden"
|
||||
class="logo mb-2"
|
||||
src="../../images/register-layout/logo-horizontal-white.png"
|
||||
src="../../images/register-layout/logo-horizontal-white.svg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" class="container" ngNativeValidate>
|
||||
<div class="row">
|
||||
<div class="col-7" *ngIf="layout">
|
||||
<div class="mt-5">
|
||||
<div *ngIf="layout === 'enterprise2'">
|
||||
<h2>Companies globally trust Bitwarden for password management.</h2>
|
||||
<p>Start your 7-day free trial!</p>
|
||||
<p class="highlight">Quickly deploy your <b>organization</b></p>
|
||||
<p>Use Bitwarden across all platforms</p>
|
||||
<!-- Default Body -->
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'teams' ||
|
||||
layout === 'enterprise' ||
|
||||
layout === 'enterprise1' ||
|
||||
layout === 'default'
|
||||
"
|
||||
>
|
||||
<h1>The Bitwarden Password Manager</h1>
|
||||
<h2>
|
||||
Trusted by millions of individuals, teams, and organizations worldwide for secure
|
||||
password storage and sharing.
|
||||
</h2>
|
||||
<p>Store logins, secure notes, and more</p>
|
||||
<p>Collaborate and share securely</p>
|
||||
<figure>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img src="../../images/register-layout/wired-logo.png" alt="Wired" />
|
||||
</cite>
|
||||
</figcaption>
|
||||
<blockquote>
|
||||
"Bitwarden has become a popular choice among open-source software advocates. After
|
||||
using it for a few months, I can see why." - February 2020
|
||||
</blockquote>
|
||||
</figure>
|
||||
<p>Access anywhere on any device</p>
|
||||
<p>Create your account to get started</p>
|
||||
</div>
|
||||
<div *ngIf="layout === 'enterprise3'">
|
||||
<p>Enterprise 3 layout</p>
|
||||
|
||||
<!-- Teams & Enterprise Body -->
|
||||
<div *ngIf="layout === 'teams1' || layout === 'teams2' || layout === 'enterprise2'">
|
||||
<h1>
|
||||
Start Your <span *ngIf="layout === 'teams1' || layout === 'teams1'">Teams<br /></span
|
||||
><span *ngIf="layout === 'enterprise2'">Enterprise</span> Free Trial Now
|
||||
</h1>
|
||||
<h2>
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
||||
password storage and sharing.
|
||||
</h2>
|
||||
<p>Collaborate and share securely</p>
|
||||
<p>Deploy and manage quickly and easily</p>
|
||||
<p>Access anywhere on any device</p>
|
||||
<p>Create your account to get started</p>
|
||||
</div>
|
||||
<div *ngIf="layout === 'enterprise4'">
|
||||
<p>Enterprise 4 layout</p>
|
||||
|
||||
<!-- CNET Campaign Teams & Enterprise Body -->
|
||||
<div *ngIf="layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnent'">
|
||||
<h1>
|
||||
Start Your <span *ngIf="layout === 'cnetcmpgnteams'">Teams<br /></span
|
||||
><span *ngIf="layout === 'cnetcmpgnent'">Enterprise</span> Free Trial Now
|
||||
</h1>
|
||||
<h2>
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
||||
password storage and sharing.
|
||||
</h2>
|
||||
<p>Collaborate and share securely</p>
|
||||
<p>Deploy and manage quickly and easily</p>
|
||||
<p>Access anywhere on any device</p>
|
||||
<p>Create your account to get started</p>
|
||||
</div>
|
||||
|
||||
<!-- CNET Campaign Premium Body -->
|
||||
<div *ngIf="layout === 'cnetcmpgnind'">
|
||||
<h1>Start Your Premium Account Now</h1>
|
||||
<h2>
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
||||
password storage and sharing.
|
||||
</h2>
|
||||
<p>Store logins, secure notes, and more</p>
|
||||
<p>Secure your account with advanced two-step login</p>
|
||||
<p>Access anywhere on any device</p>
|
||||
<p>Create your account to get started</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -212,5 +268,88 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-7 d-flex align-items-center">
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
|
||||
"
|
||||
>
|
||||
<figure>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img
|
||||
src="../../images/register-layout/cnet-logo.svg"
|
||||
class="w-25 d-block mx-auto"
|
||||
alt="cnet logo"
|
||||
/>
|
||||
</cite>
|
||||
</figcaption>
|
||||
<blockquote class="mx-auto text-center px-4">
|
||||
"No more excuses; start using Bitwarden today. The identity you save could be your
|
||||
own. The money definitely will be."
|
||||
</blockquote>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'teams' ||
|
||||
layout === 'teams1' ||
|
||||
layout === 'teams2' ||
|
||||
layout === 'enterprise' ||
|
||||
layout === 'enterprise1' ||
|
||||
layout === 'enterprise2' ||
|
||||
layout === 'default'
|
||||
"
|
||||
>
|
||||
<figure>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img
|
||||
src="../../images/register-layout/forbes-logo.svg"
|
||||
class="w-25 d-block mx-auto"
|
||||
alt="Forbes Logo"
|
||||
/>
|
||||
</cite>
|
||||
</figcaption>
|
||||
<blockquote class="mx-auto text-center px-4">
|
||||
“Bitwarden boasts the backing of some of the world's best security experts and an
|
||||
attractive, easy-to-use interface”
|
||||
</blockquote>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
|
||||
"
|
||||
class="col-5 d-flex align-items-center justify-content-center"
|
||||
>
|
||||
<img
|
||||
src="../../images/register-layout/usnews-360-badge.svg"
|
||||
class="w-50 d-block"
|
||||
alt="US News 360 Reviews Best Password Manager"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'teams' ||
|
||||
layout === 'teams1' ||
|
||||
layout === 'teams2' ||
|
||||
layout === 'enterprise' ||
|
||||
layout === 'enterprise1' ||
|
||||
layout === 'enterprise2' ||
|
||||
layout === 'default'
|
||||
"
|
||||
class="col-5 d-flex align-items-center justify-content-center"
|
||||
>
|
||||
<img
|
||||
src="../../images/register-layout/usnews-360-badge.svg"
|
||||
class="w-50 d-block"
|
||||
alt="US News 360 Reviews Best Password Manager"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
@@ -13,13 +13,9 @@ import { PasswordGenerationService } from "jslib-common/abstractions/passwordGen
|
||||
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 { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
|
||||
|
||||
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 { PolicyData } from "jslib-common/models/data/policyData";
|
||||
import { ReferenceEventRequest } from "jslib-common/models/request/referenceEventRequest";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-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";
|
||||
@@ -11,8 +12,6 @@ import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-set-password",
|
||||
templateUrl: "set-password.component.html",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.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";
|
||||
@@ -13,8 +13,6 @@ import { PasswordGenerationService } from "jslib-common/abstractions/passwordGen
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-sso",
|
||||
templateUrl: "sso.component.html",
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
|
||||
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-two-factor-options",
|
||||
@@ -13,11 +12,11 @@ import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jsli
|
||||
})
|
||||
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
twoFactorService: TwoFactorService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService
|
||||
) {
|
||||
super(authService, router, i18nService, platformUtilsService, window);
|
||||
super(twoFactorService, router, i18nService, platformUtilsService, window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,9 @@
|
||||
<p>{{ "noTwoStepProviders2" | i18n }}</p>
|
||||
</ng-container>
|
||||
<hr />
|
||||
<div [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
<div class="d-flex mb-3">
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
@@ -9,13 +10,9 @@ 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 { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
|
||||
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
||||
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
|
||||
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
|
||||
|
||||
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
|
||||
|
||||
@Component({
|
||||
@@ -36,7 +33,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
environmentService: EnvironmentService,
|
||||
private modalService: ModalService,
|
||||
route: ActivatedRoute,
|
||||
logService: LogService
|
||||
logService: LogService,
|
||||
twoFactorService: TwoFactorService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
@@ -48,7 +46,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
environmentService,
|
||||
stateService,
|
||||
route,
|
||||
logService
|
||||
logService,
|
||||
twoFactorService
|
||||
);
|
||||
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
||||
}
|
||||
|
||||
90
src/app/accounts/update-password.component.html
Normal file
90
src/app/accounts/update-password.component.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<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>
|
||||
48
src/app/accounts/update-password.component.ts
Normal file
48
src/app/accounts/update-password.component.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
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,5 +1,6 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-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";
|
||||
@@ -8,10 +9,8 @@ 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 { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-password.component";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-update-temp-password",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
@@ -8,7 +7,6 @@ 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 { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { VerifyEmailRequest } from "jslib-common/models/request/verifyEmailRequest";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
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 { VerifyDeleteRecoverRequest } from "jslib-common/models/request/verifyDeleteRecoverRequest";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -21,14 +21,10 @@ import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
import { SettingsService } from "jslib-common/abstractions/settings.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService } from "jslib-common/abstractions/storage.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
import { TokenService } from "jslib-common/abstractions/token.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { PolicyListService } from "./services/policy-list.service";
|
||||
import { RouterService } from "./services/router.service";
|
||||
|
||||
import { DisableSendPolicy } from "./organizations/policies/disable-send.component";
|
||||
import { MasterPasswordPolicy } from "./organizations/policies/master-password.component";
|
||||
import { PasswordGeneratorPolicy } from "./organizations/policies/password-generator.component";
|
||||
@@ -38,6 +34,8 @@ import { ResetPasswordPolicy } from "./organizations/policies/reset-password.com
|
||||
import { SendOptionsPolicy } from "./organizations/policies/send-options.component";
|
||||
import { SingleOrgPolicy } from "./organizations/policies/single-org.component";
|
||||
import { TwoFactorAuthenticationPolicy } from "./organizations/policies/two-factor-authentication.component";
|
||||
import { PolicyListService } from "./services/policy-list.service";
|
||||
import { RouterService } from "./services/router.service";
|
||||
|
||||
const BroadcasterSubscriptionId = "AppComponent";
|
||||
const IdleTimeout = 60000 * 10; // 10 minutes
|
||||
@@ -117,7 +115,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
break;
|
||||
case "syncCompleted":
|
||||
break;
|
||||
case "upgradeOrganization":
|
||||
case "upgradeOrganization": {
|
||||
const upgradeConfirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("upgradeOrganizationDesc"),
|
||||
this.i18nService.t("upgradeOrganization"),
|
||||
@@ -133,7 +131,8 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
]);
|
||||
}
|
||||
break;
|
||||
case "premiumRequired":
|
||||
}
|
||||
case "premiumRequired": {
|
||||
const premiumConfirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("premiumRequiredDesc"),
|
||||
this.i18nService.t("premiumRequired"),
|
||||
@@ -144,7 +143,8 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
this.router.navigate(["settings/premium"]);
|
||||
}
|
||||
break;
|
||||
case "emailVerificationRequired":
|
||||
}
|
||||
case "emailVerificationRequired": {
|
||||
const emailVerificationConfirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("emailVerificationRequiredDesc"),
|
||||
this.i18nService.t("emailVerificationRequired"),
|
||||
@@ -153,10 +153,11 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
);
|
||||
if (emailVerificationConfirmed) {
|
||||
this.platformUtilsService.launchUri(
|
||||
"https://bitwarden.com/help/article/create-bitwarden-account/"
|
||||
"https://bitwarden.com/help/create-bitwarden-account/"
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "showToast":
|
||||
this.showToast(message);
|
||||
break;
|
||||
@@ -210,7 +211,6 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
await Promise.all([
|
||||
this.eventService.clearEvents(),
|
||||
this.syncService.setLastSync(new Date(0)),
|
||||
this.tokenService.clearToken(),
|
||||
this.cryptoService.clearKeys(),
|
||||
this.settingsService.clear(userId),
|
||||
this.cipherService.clear(userId),
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { InfiniteScrollModule } from "ngx-infinite-scroll";
|
||||
|
||||
import { DragDropModule } from "@angular/cdk/drag-drop";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { InfiniteScrollModule } from "ngx-infinite-scroll";
|
||||
|
||||
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Directive, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
@@ -4,11 +4,9 @@ import { ExportService } from "jslib-common/abstractions/export.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 { EventView } from "jslib-common/models/view/eventView";
|
||||
|
||||
import { EventResponse } from "jslib-common/models/response/eventResponse";
|
||||
import { ListResponse } from "jslib-common/models/response/listResponse";
|
||||
import { EventView } from "jslib-common/models/view/eventView";
|
||||
|
||||
import { EventService } from "src/app/services/event.service";
|
||||
|
||||
@@ -19,7 +17,7 @@ export abstract class BaseEventsComponent {
|
||||
events: EventView[];
|
||||
start: string;
|
||||
end: string;
|
||||
dirtyDates: boolean = true;
|
||||
dirtyDates = true;
|
||||
continuationToken: string;
|
||||
refreshPromise: Promise<any>;
|
||||
exportPromise: Promise<any>;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
|
||||
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ValidationService } from "jslib-angular/services/validation.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";
|
||||
@@ -7,24 +11,15 @@ import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||
|
||||
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
|
||||
import { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "jslib-common/enums/organizationUserType";
|
||||
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
||||
import { ProviderUserType } from "jslib-common/enums/providerUserType";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { ListResponse } from "jslib-common/models/response/listResponse";
|
||||
import { OrganizationUserUserDetailsResponse } from "jslib-common/models/response/organizationUserResponse";
|
||||
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
|
||||
import { UserConfirmComponent } from "../organizations/manage/user-confirm.component";
|
||||
|
||||
type StatusType = OrganizationUserStatusType | ProviderUserStatusType;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
version: string;
|
||||
year: string = "2015";
|
||||
year = "2015";
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService) {}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
|
||||
})
|
||||
export class FrontendLayoutComponent implements OnInit, OnDestroy {
|
||||
version: string;
|
||||
year: string = "2015";
|
||||
year = "2015";
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService) {}
|
||||
|
||||
|
||||
@@ -60,7 +60,12 @@
|
||||
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
|
||||
{{ "myAccount" | i18n }}
|
||||
</a>
|
||||
<a class="dropdown-item" href="https://help.bitwarden.com" target="_blank" rel="noopener">
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="https://bitwarden.com/help/"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-question-circle" aria-hidden="true"></i>
|
||||
{{ "getHelp" | i18n }}
|
||||
</a>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
|
||||
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 { Provider } from "jslib-common/models/domain/provider";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
|
||||
const BroadcasterSubscriptionId = "OrganizationLayoutComponent";
|
||||
|
||||
@@ -5,7 +5,6 @@ import "bootstrap";
|
||||
import "jquery";
|
||||
import "popper.js";
|
||||
|
||||
// tslint:disable-next-line
|
||||
require("../scss/styles.scss");
|
||||
|
||||
import { AppModule } from "./app.module";
|
||||
|
||||
@@ -28,11 +28,7 @@
|
||||
<ng-container *ngIf="!loading && !done">
|
||||
<p>
|
||||
{{ "fingerprintEnsureIntegrityVerify" | i18n }}
|
||||
<a
|
||||
href="https://help.bitwarden.com/article/fingerprint-phrase/"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<a href="https://bitwarden.com/help/fingerprint-phrase/" target="_blank" rel="noopener">
|
||||
{{ "learnMore" | i18n }}</a
|
||||
>
|
||||
</p>
|
||||
|
||||
@@ -3,13 +3,11 @@ import { Component, Input, OnInit } from "@angular/core";
|
||||
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 { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { OrganizationUserBulkConfirmRequest } from "jslib-common/models/request/organizationUserBulkConfirmRequest";
|
||||
import { OrganizationUserBulkRequest } from "jslib-common/models/request/organizationUserBulkRequest";
|
||||
|
||||
import { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { BulkUserDetails } from "./bulk-status.component";
|
||||
|
||||
@Component({
|
||||
@@ -26,8 +24,8 @@ export class BulkConfirmComponent implements OnInit {
|
||||
fingerprints: Map<string, string> = new Map();
|
||||
statuses: Map<string, string> = new Map();
|
||||
|
||||
loading: boolean = true;
|
||||
done: boolean = false;
|
||||
loading = true;
|
||||
done = false;
|
||||
error: string;
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -16,8 +16,8 @@ export class BulkRemoveComponent {
|
||||
|
||||
statuses: Map<string, string> = new Map();
|
||||
|
||||
loading: boolean = false;
|
||||
done: boolean = false;
|
||||
loading = false;
|
||||
done = false;
|
||||
error: string;
|
||||
|
||||
constructor(protected apiService: ApiService, protected i18nService: I18nService) {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
|
||||
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
||||
|
||||
@@ -21,5 +22,5 @@ type BulkStatusEntry = {
|
||||
})
|
||||
export class BulkStatusComponent {
|
||||
users: BulkStatusEntry[];
|
||||
loading: boolean = false;
|
||||
loading = false;
|
||||
}
|
||||
|
||||
@@ -6,15 +6,13 @@ 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 { Utils } from "jslib-common/misc/utils";
|
||||
import { EncString } from "jslib-common/models/domain/encString";
|
||||
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
|
||||
import { CollectionRequest } from "jslib-common/models/request/collectionRequest";
|
||||
import { SelectionReadOnlyRequest } from "jslib-common/models/request/selectionReadOnlyRequest";
|
||||
import { GroupResponse } from "jslib-common/models/response/groupResponse";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
|
||||
@Component({
|
||||
selector: "app-collection-add-edit",
|
||||
templateUrl: "collection-add-edit.component.html",
|
||||
@@ -28,8 +26,8 @@ export class CollectionAddEditComponent implements OnInit {
|
||||
@Output() onDeletedCollection = new EventEmitter();
|
||||
|
||||
loading = true;
|
||||
editMode: boolean = false;
|
||||
accessGroups: boolean = false;
|
||||
editMode = false;
|
||||
accessGroups = false;
|
||||
title: string;
|
||||
name: string;
|
||||
externalId: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
@@ -10,9 +10,6 @@ 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 { SearchService } from "jslib-common/abstractions/search.service";
|
||||
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
|
||||
import { CollectionData } from "jslib-common/models/data/collectionData";
|
||||
import { Collection } from "jslib-common/models/domain/collection";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
@@ -37,7 +34,7 @@ export class CollectionsComponent implements OnInit {
|
||||
|
||||
loading = true;
|
||||
organization: Organization;
|
||||
canCreate: boolean = false;
|
||||
canCreate = false;
|
||||
organizationId: string;
|
||||
collections: CollectionView[];
|
||||
assignedCollections: CollectionView[];
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
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 { EventService } from "../../services/event.service";
|
||||
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { EventResponse } from "jslib-common/models/response/eventResponse";
|
||||
import { ListResponse } from "jslib-common/models/response/listResponse";
|
||||
|
||||
import { EventService } from "../../services/event.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-entity-events",
|
||||
templateUrl: "entity-events.component.html",
|
||||
|
||||
@@ -3,15 +3,13 @@ import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
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 { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "jslib-common/enums/organizationUserType";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { SelectionReadOnlyRequest } from "jslib-common/models/request/selectionReadOnlyRequest";
|
||||
import { OrganizationUserUserDetailsResponse } from "jslib-common/models/response/organizationUserResponse";
|
||||
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
|
||||
@Component({
|
||||
selector: "app-entity-users",
|
||||
templateUrl: "entity-users.component.html",
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { ExportService } from "jslib-common/abstractions/export.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
@@ -10,20 +9,18 @@ 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 { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
import { EventResponse } from "jslib-common/models/response/eventResponse";
|
||||
|
||||
import { EventService } from "../../services/event.service";
|
||||
|
||||
import { BaseEventsComponent } from "../../common/base.events.component";
|
||||
import { EventService } from "../../services/event.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-events",
|
||||
templateUrl: "events.component.html",
|
||||
})
|
||||
export class EventsComponent extends BaseEventsComponent implements OnInit {
|
||||
exportFileName: string = "org-events";
|
||||
exportFileName = "org-events";
|
||||
organizationId: string;
|
||||
organization: Organization;
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
href="https://bitwarden.com/help/article/user-types-access-control/#access-control"
|
||||
href="https://bitwarden.com/help/user-types-access-control/#access-control"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { CollectionService } from "jslib-common/abstractions/collection.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 { CollectionData } from "jslib-common/models/data/collectionData";
|
||||
import { Collection } from "jslib-common/models/domain/collection";
|
||||
import { GroupRequest } from "jslib-common/models/request/groupRequest";
|
||||
@@ -24,7 +23,7 @@ export class GroupAddEditComponent implements OnInit {
|
||||
@Output() onDeletedGroup = new EventEmitter();
|
||||
|
||||
loading = true;
|
||||
editMode: boolean = false;
|
||||
editMode = false;
|
||||
title: string;
|
||||
name: string;
|
||||
externalId: string;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user