1
0
mirror of https://github.com/bitwarden/web synced 2025-12-10 05:13:40 +00:00

Compare commits

..

76 Commits

Author SHA1 Message Date
github-actions[bot]
05a17629a5 Bump version to 2.27.0 (#1545)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit e266a740ba)
2022-03-15 15:44:02 -06:00
Matt Gibson
a1bd0b7eec Update jslib 2022-03-15 08:47:49 -04:00
Joseph Flinn
f263156484 Update hotfix release branch name to hotfix-rc (#1532)
Fix confilct with newer commit in master

(cherry picked from commit 153870693b)
2022-03-09 12:59:13 -08:00
Addison Beck
c174751b9c [bug] Partially revert #1516 (#1531) 2022-03-09 11:17:52 -05:00
Daniel James Smith
970d3162ac Pull jslib for https://github.com/bitwarden/jslib/pull/714 (#1526)
(cherry picked from commit 80ffa965e1)
2022-03-08 13:54:38 +01:00
Daniel James Smith
18f1929f65 Pull in Dashlane and Myki importer (#1523)
* Pull jslib

* Update copy for Dashlane json and csv importers
2022-03-08 09:01:19 +10:00
Micaiah Martin
5cb3941190 BEEEP - organize linting in CI (#1520) 2022-03-07 11:02:23 -05:00
Daniel James Smith
0e515bc6c1 Remove 2fa.directory/v2 from CSP (#1519) 2022-03-04 19:04:49 +01:00
Addison Beck
e103ddf02f [feature] Refine content of the organization delete request confirmation warning (#1508)
This commit updates the organization delete request confirmation warning based on new copy from the product team.

Changes are as follows:
* Add a load toggle to the organization delete modal, as we now have data to collect.
* Adjust how the families for enterprise error state for invalid sponserships connects with the organization delete component. Previously it just sent in a localization key to use for the description, but this commit adds a union type for identifying different delete flows and moves the FOE description localization key into the template with a condition.
* Move the callout on the organization delete component to above the description text.
* Adjust content of the typical organization delete request description based on copy from the product team.
  * This includes a list of item types in use by the organization that will be deleted and the amount of each type that exist in the organization.
2022-03-04 15:03:48 +01:00
github-actions[bot]
8242989b9d Autosync the updated translations (#1518)
Co-authored-by: github-actions <>
2022-03-04 01:25:14 +01:00
Vincent Salucci
5e7d94efb8 [Captcha] Implement captcha for 2fa (#1513)
* [Captcha] Implement captcha for 2fa

* Removed center justified captcha for now
2022-03-03 18:20:43 -06:00
Addison Beck
3bc8955dd5 [dep] Update jslib (#1517) 2022-03-03 20:14:19 +01:00
Vince Grassia
bc05d27082 Add logic for pushing latest image to QA container registry (#1515) 2022-03-03 14:00:41 -05:00
Addison Beck
e93c155885 [bug] Set full width on login (#1516) 2022-03-03 12:17:19 -05:00
Daniel James Smith
1076749635 Add importing of 1passwords 1pux files (#1507)
* Pull in jslib

* Install jszip

* Display help on selecting 1pux importer

* Unzip 1pux and pass content of export.data to 1pux importer

* Update jslib
2022-03-03 15:41:48 +01:00
Thomas Rittson
06e1af6d48 Improve SSO Config validation (#1332)
* Break form controls up into reusable components

* Add proper form styling, validation, inline error messages, etc

* Move control options into class instead of template

* Add accessibility
2022-03-03 11:08:41 +01:00
github-actions[bot]
cf9a90d10e Bumped version to 2.26.2 (#1511)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-03-02 15:08:58 -06:00
Matt Gibson
6e8c15bccd Fix mobile + url encoding issue (#1510) 2022-03-02 14:49:35 -06:00
Matt Gibson
7d018e4b59 Fix dev server connector headers (#1509) 2022-03-02 14:27:34 -06:00
Chad Scharf
f832cb4138 Remove artifact binding from SSO config UI (#1502) 2022-02-28 13:43:31 -05:00
Addison Beck
b8a23cf014 [feature] Implement scope warning for exports (#1504) 2022-02-28 11:37:02 -05:00
Daniel James Smith
d0c0e80b6c BEEEP: Add missing languages (#1501)
* Pull in jslib

* Import and register missing locales

* Update supportedTranslationLocales
2022-02-25 20:59:07 +01:00
Thomas Rittson
98fb71fcb6 Update jslib (#1494)
* Update jslib

* Add i18n error message

* Update imports
2022-02-25 10:19:37 -05:00
Micaiah Martin
1b52b5a98a Added dry run logic (#1497) 2022-02-25 09:07:54 -05:00
github-actions[bot]
c3e5c74253 Autosync the updated translations (#1500)
Co-authored-by: github-actions <>
2022-02-25 12:27:49 +01:00
Micaiah Martin
df5b175cdf Remove workflows from build trigger (#1496) 2022-02-24 08:42:14 -06:00
Oscar Hinton
1c495e87c9 Add eslint (#1437) 2022-02-24 12:10:07 +01:00
Oscar Hinton
01f128a4a9 Remove dotnet restore (#1493) 2022-02-22 16:22:45 +01:00
Oscar Hinton
a4d5b145ac Exclude connectors from CSP rules (#1489) 2022-02-22 15:46:59 +01:00
Chad Scharf
d944e0e25c We're Hiring (#1492)
Added link to README.md for Bitwarden Careers page.
2022-02-22 14:04:06 +01:00
Vincent Salucci
d141ccca52 [Icons Bug] fa-remove -> bwi-close (#1487) 2022-02-20 17:03:58 -06:00
Micaiah Martin
9e872bed2c Create initial workflow (#1482) 2022-02-18 13:28:53 -06:00
github-actions[bot]
c071b692f2 Autosync the updated translations (#1486)
Co-authored-by: github-actions <>
2022-02-18 10:24:33 +01:00
Matt Gibson
041bb1bf0a Enforce Hold label (#1478)
* Enforce Hold label

* Linting

Co-authored-by: Micaiah Martin <77340197+mimartin12@users.noreply.github.com>
2022-02-16 08:43:56 -06:00
Thomas Rittson
0b5e1eb256 Exclude jslib from prettier hook (#1458)
* Exclude jslib from prettier hook
2022-02-16 07:41:41 +10:00
Addison Beck
8c39fdb21e [lib] Update jslib (#1479) 2022-02-15 15:05:49 -05:00
Addison Beck
ca3efc8fee [bug] Disable state cache (#1477) 2022-02-14 12:26:22 -05:00
github-actions[bot]
c323f38f16 Autosync the updated translations (#1461)
Co-authored-by: github-actions <>
2022-02-11 22:48:28 +01:00
Addison Beck
9df4eb4c0d [bug] Store last sync in memory (#1471) 2022-02-11 03:39:48 -05:00
Addison Beck
1712ed53be [bug] Store vault data in memory (#1470) 2022-02-11 09:21:51 +01:00
github-actions[bot]
45a39f6200 Bumped version to 2.26.1 (#1468)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-10 21:51:45 -08:00
Thomas Rittson
a2d241263b Update jslib (#1466) 2022-02-11 00:30:37 -05:00
Thomas Rittson
5987d3deda Update jslib (#1465) 2022-02-11 14:58:33 +10:00
Thomas Rittson
080a3c655e Update jslib (#1464) 2022-02-11 14:30:07 +10:00
Justin Baur
dac48242b7 Update jslib (#1462) 2022-02-10 22:16:44 -05:00
Robyn MacCallum
e4d9ab52a0 update jslib (#1460)
* update jslib

* Remove call to tokenService.clear() from logout
2022-02-10 19:51:36 -05:00
Thomas Rittson
aee8a2661e Fix Copy Verification Code not showing after first log in (#1459)
* Move init logic to load()
2022-02-11 09:48:54 +10:00
Robyn MacCallum
ff6bb236c0 Update jslib (#1457) 2022-02-10 14:22:44 -05:00
Vincent Salucci
f79b20294a [Help] Update links to new pattern (#1454)
* [Help] Update links to new pattern

* Close help

* Update jslib
2022-02-08 17:44:47 -06:00
github-actions[bot]
3a0c34b934 Bump version to 2.26.0 (#1452)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-08 15:54:35 -07:00
github-actions[bot]
e09df347f4 Bump version to 1.26.0 (#1450)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-08 14:46:12 -07:00
Oscar Hinton
e68ab0031d Client & Version headers (#1434) 2022-02-08 13:22:31 +01:00
Justin Baur
64416c9406 Send in error message (#1449) 2022-02-07 16:15:49 -05:00
Matt Gibson
6779adb064 Handle password protected import export (#1448)
This updates requirements without implementing any way for the UI to
interact with the new feature
2022-02-07 14:15:22 -06:00
Thomas Rittson
1b28a4b954 Update client for authService refactor (#1387)
Co-authored-by: Hinton <oscar@oscarhinton.com>
2022-02-07 20:24:49 +01:00
Addison Beck
6320498fb3 [chore] Update jslib (#1447) 2022-02-07 12:04:51 -05:00
Oscar Hinton
bfd5f3e564 Fix register button using wrong icon (#1446) 2022-02-07 12:28:28 +01:00
Thomas Rittson
c755443735 Make husky pre-commit hook executable (#1432) 2022-02-07 12:18:33 +10:00
Justin Baur
0e5f2530a9 Switch option values to be a number (#1444) 2022-02-04 16:59:20 -05:00
Vincent Salucci
5105633fa4 [Icons] Fix button icon/text margins (#1443) 2022-02-04 13:49:44 -06:00
github-actions[bot]
e975056c21 Autosync the updated translations (#1441)
Co-authored-by: github-actions <>
2022-02-04 12:48:56 +01:00
Daniel James Smith
be21167ef8 Pull jslib for https://github.com/bitwarden/jslib/pull/654 (#1439) 2022-02-03 23:44:28 +01:00
Addison Beck
e09898e4d8 Update jslib (#1436) 2022-02-03 13:54:15 -05:00
Vincent Salucci
868d235faa [Icons] FF - requested icon changes (#1435)
* [Icons] Removed FA

* Icon changes // Webpack correction // Padding updates
2022-02-03 10:20:31 -06:00
Oscar Hinton
5c764a95f4 Add CSP for development (#1431) 2022-02-03 10:17:33 +01:00
Jake Fink
596c3e86e9 Master password policy is not checked when accepting invite from an existing account (#1371)
* validate password against org policy and create update-password component

* linting and prettier

* [bug] Default rememberEmail to true (#1429)

* switching the dashes to underscores for the branch name (#1433)

(cherry picked from commit 8910430dfb)

* fix merge conflicts

* Update src/app/accounts/update-password.component.html

Co-authored-by: Justin Baur <admin@justinbaur.com>

* Update src/locales/en/messages.json

Co-authored-by: Justin Baur <admin@justinbaur.com>

* update jslib

* prettier

Co-authored-by: Addison Beck <abeck@bitwarden.com>
Co-authored-by: Joseph Flinn <58369717+joseph-flinn@users.noreply.github.com>
Co-authored-by: Justin Baur <admin@justinbaur.com>
2022-02-03 00:00:57 -05:00
Addison Beck
8030da2ed5 [bug] Default rememberEmail to true (#1429) 2022-02-02 11:20:15 -05:00
Joseph Flinn
8910430dfb switching the dashes to underscores for the branch name (#1433) 2022-02-02 07:30:19 -08:00
Joseph Flinn
6bf6d4b47f Reverting tis change for next release (#1430) 2022-02-02 06:22:25 -08:00
Oscar Hinton
ca199a398e Fix captcha and webauthn-fallback not using theme_light (#1427) 2022-02-01 15:46:16 +01:00
Chad Scharf
61ab2fbda3 Hide unsolicited SSO option in config (#1425) 2022-01-31 17:43:56 -05:00
Addison Beck
d79f074825 [bug] Extend GlobalState to supply correct default theme (#1422)
* [bug] Extend GlobalState to supply correct default theme

The default theme for most clients is System, but web uses Light.
We need to extend GlobalState in web to reflect this.

* [chore] Update jslib
2022-01-31 14:53:55 -05:00
Oscar Hinton
e3b962a779 Apply prettier on help pr (#1423) 2022-01-31 20:31:50 +01:00
DanHillesheim
cc657eb853 Update help site URLs (#1409) 2022-01-31 20:11:27 +01:00
DanHillesheim
e14a266ee0 Registration page updates (#1390)
Co-authored-by: Hinton <oscar@oscarhinton.com>
2022-01-31 20:10:35 +01:00
Thomas Rittson
e1732cfa10 Fix various bugs in Options page (#1418)
* Remove duplicate message in toast

* Set starting variables properly
2022-01-31 09:11:25 -05:00
309 changed files with 16029 additions and 5612 deletions

View File

@@ -1,4 +1,3 @@
* *
!docker/*
!build/* !build/*
!entrypoint.sh !entrypoint.sh

8
.eslintignore Normal file
View File

@@ -0,0 +1,8 @@
**/dist
**/build
jslib
webpack.config.js
scripts/optimize.js
config.js
**/node_modules

31
.eslintrc.json Normal file
View 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"]
}
]
}
}

View File

@@ -11,6 +11,8 @@ on:
branches-ignore: branches-ignore:
- "l10n_master" - "l10n_master"
- "gh-pages" - "gh-pages"
paths-ignore:
- '.github/workflows/**'
jobs: jobs:
cloc: cloc:
@@ -28,6 +30,26 @@ jobs:
- name: Print lines of code - name: Print lines of code
run: cloc --include-lang TypeScript,JavaScript,HTML,Sass,CSS --vcs git 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: setup:
name: Setup name: Setup
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@@ -44,7 +66,9 @@ jobs:
build-oss-selfhost: build-oss-selfhost:
name: Build OSS zip name: Build OSS zip
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: setup needs:
- setup
- lint
env: env:
_VERSION: ${{ needs.setup.outputs.version }} _VERSION: ${{ needs.setup.outputs.version }}
steps: steps:
@@ -91,7 +115,9 @@ jobs:
build-cloud: build-cloud:
name: Build Cloud zip name: Build Cloud zip
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: setup needs:
- setup
- lint
env: env:
_VERSION: ${{ needs.setup.outputs.version }} _VERSION: ${{ needs.setup.outputs.version }}
steps: steps:
@@ -138,7 +164,9 @@ jobs:
build-commercial-selfhost: build-commercial-selfhost:
name: Build SelfHost Docker image name: Build SelfHost Docker image
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: setup needs:
- setup
- lint
env: env:
_VERSION: ${{ needs.setup.outputs.version }} _VERSION: ${{ needs.setup.outputs.version }}
steps: steps:
@@ -165,7 +193,7 @@ jobs:
echo "GitHub event: $GITHUB_EVENT" echo "GitHub event: $GITHUB_EVENT"
- name: Setup DCT - 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 id: setup-dct
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
with: with:
@@ -175,9 +203,6 @@ jobs:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Restore
run: dotnet tool restore
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
@@ -212,11 +237,11 @@ jobs:
run: docker tag bitwarden/web bitwarden/web:dev run: docker tag bitwarden/web bitwarden/web:dev
- name: Tag hotfix branch - name: Tag hotfix branch
if: github.ref == 'refs/heads/hotfix' if: github.ref == 'refs/heads/hotfix-rc'
run: docker tag bitwarden/web bitwarden/web:hotfix run: docker tag bitwarden/web bitwarden/web:hotfix-rc
- name: List Docker images - 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 run: docker images
- name: Push rc image - name: Push rc image
@@ -234,19 +259,22 @@ jobs:
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }} DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
- name: Push hotfix image - name: Push hotfix image
if: github.ref == 'refs/heads/hotfix' if: github.ref == 'refs/heads/hotfix-rc'
run: docker push bitwarden/web:hotfix run: docker push bitwarden/web:hotfix-rc
env: env:
DOCKER_CONTENT_TRUST: 1 DOCKER_CONTENT_TRUST: 1
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }} DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
- name: Log out of Docker - 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 run: docker logout
build-qa: build-qa:
name: Build Docker images for QA environment name: Build Docker images for QA environment
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs:
- setup
- lint
steps: steps:
- name: Set up Node - name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
@@ -281,9 +309,6 @@ jobs:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Restore
run: dotnet tool restore
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
@@ -367,7 +392,6 @@ jobs:
run: | run: |
nuget help | grep Version nuget help | grep Version
msbuild -version msbuild -version
dotnet --info
node --version node --version
npm --version npm --version
echo "GitHub ref: $GITHUB_REF" echo "GitHub ref: $GITHUB_REF"
@@ -382,9 +406,6 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
- name: Run linter
run: npm run lint
- name: NPM build - name: NPM build
run: npm run build:bit:cloud run: npm run build:bit:cloud
@@ -433,6 +454,7 @@ jobs:
needs: needs:
- cloc - cloc
- setup - setup
- lint
- build-oss-selfhost - build-oss-selfhost
- build-cloud - build-cloud
- build-commercial-selfhost - build-commercial-selfhost
@@ -444,6 +466,7 @@ jobs:
if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }} if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
env: env:
CLOC_STATUS: ${{ needs.cloc.result }} CLOC_STATUS: ${{ needs.cloc.result }}
LINT_STATUS: ${{ needs.lint.result }}
SETUP_STATUS: ${{ needs.setup.result }} SETUP_STATUS: ${{ needs.setup.result }}
BUILD_OSS_SELFHOST_STATUS: ${{ needs.build-oss-selfhost.result }} BUILD_OSS_SELFHOST_STATUS: ${{ needs.build-oss-selfhost.result }}
BUILD_CLOUD_STATUS: ${{ needs.build-cloud.result }} BUILD_CLOUD_STATUS: ${{ needs.build-cloud.result }}
@@ -454,6 +477,8 @@ jobs:
run: | run: |
if [ "$CLOC_STATUS" = "failure" ]; then if [ "$CLOC_STATUS" = "failure" ]; then
exit 1 exit 1
elif [ "$LINT_STATUS" = "failure" ]; then
exit 1
elif [ "$SETUP_STATUS" = "failure" ]; then elif [ "$SETUP_STATUS" = "failure" ]; then
exit 1 exit 1
elif [ "$BUILD_OSS_SELFHOST_STATUS" = "failure" ]; then elif [ "$BUILD_OSS_SELFHOST_STATUS" = "failure" ]; then

16
.github/workflows/enforce-labels.yml vendored Normal file
View 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"

View File

@@ -12,6 +12,7 @@ on:
options: options:
- Initial Release - Initial Release
- Redeploy - Redeploy
- Dry Run
jobs: jobs:
setup: setup:
@@ -20,19 +21,20 @@ jobs:
outputs: outputs:
release_version: ${{ steps.version.outputs.package }} release_version: ${{ steps.version.outputs.package }}
tag_version: ${{ steps.version.outputs.tag }} tag_version: ${{ steps.version.outputs.tag }}
branch-name: ${{ steps.branch.outputs.branch-name }} branch_name: ${{ steps.branch.outputs.branch_name }}
steps: steps:
- name: Branch check - name: Branch check
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
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 "==================================="
echo "[!] Can only release from the 'rc' or 'hotfix' branches" echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
echo "===================================" echo "==================================="
exit 1 exit 1
fi fi
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # 2.3.4 uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # 2.4.0
- name: Check Release Version - name: Check Release Version
id: version id: version
@@ -55,15 +57,17 @@ jobs:
id: branch id: branch
run: | run: |
BRANCH_NAME=$(basename ${{ github.ref }}) BRANCH_NAME=$(basename ${{ github.ref }})
echo "::set-output name=branch-name::$BRANCH_NAME" echo "::set-output name=branch_name::$BRANCH_NAME"
self-host: self-host:
name: Release self-host docker name: Release self-host docker
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: setup needs: setup
env: env:
_BRANCH_NAME: ${{ needs.setup.outputs.branch-name }} _BRANCH_NAME: ${{ needs.setup.outputs.branch_name }}
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} _RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
_RELEASE_OPTION: ${{ github.event.inputs.release_type }}
steps: steps:
- name: Print environment - name: Print environment
run: | run: |
@@ -71,7 +75,12 @@ jobs:
docker --version docker --version
echo "GitHub ref: $GITHUB_REF" echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT" echo "GitHub event: $GITHUB_EVENT"
echo "Github Release Option: $_RELEASE_OPTION"
- name: Checkout repo
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
########## DockerHub ##########
- name: Setup DCT - name: Setup DCT
id: setup-dct id: setup-dct
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
@@ -79,21 +88,25 @@ jobs:
azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
azure-keyvault-name: "bitwarden-prod-kv" azure-keyvault-name: "bitwarden-prod-kv"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Pull latest selfhost image - 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 - name: Tag version and latest
run: | run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker tag bitwarden/web:latest bitwarden/web:dryrun
else
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest
fi
- name: List Docker images
run: docker images
- name: Push version and latest image - name: Push version and latest image
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env: env:
DOCKER_CONTENT_TRUST: 1 DOCKER_CONTENT_TRUST: 1
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }} 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:$_RELEASE_VERSION
docker push bitwarden/web:latest 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 - name: Log out of Docker
run: docker logout run: docker logout
ghpages-deploy: ghpages-deploy:
name: Deploy Web Vault name: Deploy Web Vault
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@@ -115,7 +162,7 @@ jobs:
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }} _TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
with: with:
ref: gh-pages ref: gh-pages
@@ -125,7 +172,7 @@ jobs:
git push -u origin deploy-$_TAG_VERSION git push -u origin deploy-$_TAG_VERSION
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
- name: Setup git config - name: Setup git config
run: | run: |
@@ -139,7 +186,7 @@ jobs:
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success
branch: ${{ needs.setup.outputs.branch-name }} branch: ${{ needs.setup.outputs.branch_name }}
artifacts: web-*-cloud-COMMERCIAL.zip artifacts: web-*-cloud-COMMERCIAL.zip
# This should result in a build directory in the current working directory # This should result in a build directory in the current working directory
@@ -147,7 +194,7 @@ jobs:
run: unzip web-*-cloud-COMMERCIAL.zip run: unzip web-*-cloud-COMMERCIAL.zip
- name: Deploy GitHub Pages - 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: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
@@ -155,8 +202,10 @@ jobs:
build_dir: build build_dir: build
keep_history: true keep_history: true
commit_message: "Staging deploy ${{ needs.setup.outputs.release_version }}" commit_message: "Staging deploy ${{ needs.setup.outputs.release_version }}"
dry_run: ${{ github.event.inputs.release_type == 'Dry Run' }}
- name: Create Deploy PR - name: Create Deploy PR
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env: env:
PR_BRANCH: deploy-${{ env._TAG_VERSION }} PR_BRANCH: deploy-${{ env._TAG_VERSION }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -166,6 +215,7 @@ jobs:
--base gh-pages \ --base gh-pages \
--head "$PR_BRANCH" --head "$PR_BRANCH"
release: release:
name: Create GitHub Release name: Create GitHub Release
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@@ -179,7 +229,7 @@ jobs:
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success
branch: ${{ needs.setup.outputs.branch-name }} branch: ${{ needs.setup.outputs.branch_name }}
artifacts: "web-*-selfhosted-COMMERCIAL.zip, artifacts: "web-*-selfhosted-COMMERCIAL.zip,
web-*-selfhosted-open-source.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 mv web-*-selfhosted-open-source.zip web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip
- name: Create release - name: Create release
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01
with: with:
name: "Version ${{ needs.setup.outputs.release_version }}" name: "Version ${{ needs.setup.outputs.release_version }}"
commit: ${{ github.sha }} commit: ${{ github.sha }}
@@ -199,3 +250,20 @@ jobs:
web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip" web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip"
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
draft: true 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
View 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
View File

View File

@@ -1,4 +1,4 @@
FROM nginx:stable FROM bitwarden/server
LABEL com.bitwarden.product="bitwarden" LABEL com.bitwarden.product="bitwarden"
@@ -8,29 +8,13 @@ RUN apt-get update \
curl \ curl \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY docker/nginx.conf /etc/nginx ENV ASPNETCORE_URLS http://+:5000
COPY docker/nginx-web.conf /etc/nginx
COPY docker/mime.types /etc/nginx
COPY docker/security-headers.conf /etc/nginx
WORKDIR /app WORKDIR /app
EXPOSE 5000
COPY ./build . COPY ./build .
COPY docker/entrypoint.sh / COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh RUN chmod +x /entrypoint.sh
RUN bash /entrypoint.sh HEALTHCHECK CMD curl -f http://localhost:5000 || exit 1
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
USER bitwarden ENTRYPOINT ["/entrypoint.sh"]
EXPOSE 8080
HEALTHCHECK CMD curl -f http://localhost:8080 || exit 1
#ENTRYPOINT ["/entrypoint.sh"]
#CMD ["tail", "-f", "/dev/null"]
CMD nginx -g 'daemon off;'

View File

@@ -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). 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 ## 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. 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.

View File

@@ -1,6 +1,7 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { AppComponent as BaseAppComponent } from "src/app/app.component"; import { AppComponent as BaseAppComponent } from "src/app/app.component";
import { DisablePersonalVaultExportPolicy } from "./policies/disable-personal-vault-export.component"; import { DisablePersonalVaultExportPolicy } from "./policies/disable-personal-vault-export.component";
import { MaximumVaultTimeoutPolicy } from "./policies/maximum-vault-timeout.component"; import { MaximumVaultTimeoutPolicy } from "./policies/maximum-vault-timeout.component";

View File

@@ -7,17 +7,17 @@ import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { BitwardenToastModule } from "jslib-angular/components/toastr.component"; 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 { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component"; import { AppComponent } from "./app.component";
import { OrganizationsModule } from "./organizations/organizations.module"; import { OrganizationsModule } from "./organizations/organizations.module";
import { DisablePersonalVaultExportPolicyComponent } from "./policies/disable-personal-vault-export.component"; import { DisablePersonalVaultExportPolicyComponent } from "./policies/disable-personal-vault-export.component";
import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-timeout.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({ @NgModule({
imports: [ imports: [
OssModule, OssModule,

View File

@@ -5,7 +5,6 @@ import "bootstrap";
import "jquery"; import "jquery";
import "popper.js"; import "popper.js";
// tslint:disable-next-line
require("src/scss/styles.scss"); require("src/scss/styles.scss");
import { AppModule } from "./app.module"; import { AppModule } from "./app.module";

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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 {}

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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, "");
}
}

View File

@@ -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>

View File

@@ -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[];
}

View File

@@ -14,10 +14,9 @@
<form <form
#form #form
(ngSubmit)="submit()" (ngSubmit)="submit()"
[formGroup]="data" [formGroup]="ssoConfigForm"
[appApiAction]="formPromise" [appApiAction]="formPromise"
*ngIf="!loading" *ngIf="!loading"
ngNativeValidate
> >
<p> <p>
{{ "ssoPolicyHelpStart" | i18n }} {{ "ssoPolicyHelpStart" | i18n }}
@@ -27,19 +26,14 @@
{{ "ssoPolicyHelpKeyConnector" | i18n }} {{ "ssoPolicyHelpKeyConnector" | i18n }}
</p> </p>
<div class="form-group"> <!-- Root form -->
<div class="form-check"> <ng-container>
<input <app-input-checkbox
class="form-check-input" controlId="enabled"
type="checkbox"
id="enabled"
[formControl]="enabled" [formControl]="enabled"
name="Enabled" [label]="'allowSso' | i18n"
/> [helperText]="'allowSsoDesc' | i18n"
<label class="form-check-label" for="enabled">{{ "allowSso" | i18n }}</label> ></app-input-checkbox>
</div>
<small class="form-text text-muted">{{ "allowSsoDesc" | i18n }}</small>
</div>
<div class="form-group"> <div class="form-group">
<label>{{ "memberDecryptionOption" | i18n }}</label> <label>{{ "memberDecryptionOption" | i18n }}</label>
@@ -71,7 +65,7 @@
target="_blank" target="_blank"
rel="noopener" rel="noopener"
appA11yTitle="{{ 'learnMore' | i18n }}" appA11yTitle="{{ 'learnMore' | i18n }}"
href="https://bitwarden.com/help/article/about-key-connector/" href="https://bitwarden.com/help/about-key-connector/"
> >
<i class="bwi bwi-question-circle" aria-hidden="true"></i> <i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a> </a>
@@ -80,19 +74,26 @@
</div> </div>
</div> </div>
<ng-container *ngIf="data.value.keyConnectorEnabled"> <!-- Key Connector -->
<ng-container *ngIf="ssoConfigForm.get('keyConnectorEnabled').value">
<app-callout type="warning" [useAlertRole]="true"> <app-callout type="warning" [useAlertRole]="true">
{{ "keyConnectorWarning" | i18n }} {{ "keyConnectorWarning" | i18n }}
</app-callout> </app-callout>
<div class="form-group"> <div class="form-group">
<label for="keyConnectorUrl">{{ "keyConnectorUrl" | i18n }}</label> <label for="keyConnectorUrl">
{{ "keyConnectorUrl" | i18n }}
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
</label>
<div class="input-group"> <div class="input-group">
<input <input
class="form-control" class="form-control"
formControlName="keyConnectorUrl" formControlName="keyConnectorUrl"
id="keyConnectorUrl" id="keyConnectorUrl"
required aria-describedby="keyConnectorUrlDesc"
(change)="haveTestedKeyConnector = false"
appInputStripSpaces
appA11yInvalid
/> />
<div class="input-group-append"> <div class="input-group-append">
<button <button
@@ -113,375 +114,319 @@
</button> </button>
</div> </div>
</div> </div>
<ng-container *ngIf="keyConnectorUrl.pristine && !keyConnectorUrl.pending"> <div *ngIf="haveTestedKeyConnector" id="keyConnectorUrlDesc" aria-live="polite">
<div class="text-danger" *ngIf="keyConnectorUrl.hasError('invalidUrl')" role="alert"> <small
class="error-inline"
*ngIf="keyConnectorUrl.hasError('invalidUrl'); else keyConnectorSuccess"
>
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i> <i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
<span class="sr-only">{{ "error" | i18n }}:</span>
{{ "keyConnectorTestFail" | i18n }} {{ "keyConnectorTestFail" | i18n }}
</div> </small>
<div class="text-success" *ngIf="!keyConnectorUrl.hasError('invalidUrl')" role="alert"> <ng-template #keyConnectorSuccess>
<small class="text-success">
<i class="bwi bwi-check-circle" aria-hidden="true"></i> <i class="bwi bwi-check-circle" aria-hidden="true"></i>
{{ "keyConnectorTestSuccess" | i18n }} {{ "keyConnectorTestSuccess" | i18n }}
</small>
</ng-template>
</div> </div>
</ng-container>
</div> </div>
</ng-container> </ng-container>
<div class="form-group"> <app-select
<label for="type">{{ "type" | i18n }}</label> controlId="type"
<select class="form-control" id="type" formControlName="configType"> [label]="'type' | i18n"
<option [ngValue]="0" disabled>{{ "selectType" | i18n }}</option> [selectOptions]="ssoTypeOptions"
<option [ngValue]="1">OpenID Connect</option> formControlName="configType"
<option [ngValue]="2">SAML 2.0</option> >
</select> </app-select>
</div> </ng-container>
<!-- OIDC --> <!-- OIDC -->
<div *ngIf="data.value.configType == 1"> <div
*ngIf="ssoConfigForm.get('configType').value === ssoType.OpenIdConnect"
[formGroup]="openIdForm"
>
<div class="config-section"> <div class="config-section">
<h2>{{ "openIdConnectConfig" | i18n }}</h2> <h2 class="secondary-header">{{ "openIdConnectConfig" | i18n }}</h2>
<div class="form-group">
<label>{{ "callbackPath" | i18n }}</label> <app-input-text-readonly
<div class="input-group"> [label]="'callbackPath' | i18n"
<input class="form-control" readonly [value]="callbackPath" /> [controlValue]="callbackPath"
<div class="input-group-append"> ></app-input-text-readonly>
<button
type="button" <app-input-text-readonly
class="btn btn-outline-secondary" [label]="'signedOutCallbackPath' | i18n"
appA11yTitle="{{ 'copyValue' | i18n }}" [controlValue]="signedOutCallbackPath"
(click)="copy(callbackPath)" ></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"
> >
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i> </app-select>
</button>
</div> <app-input-checkbox
</div> controlId="getClaimsFromUserInfoEndpoint"
</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" formControlName="getClaimsFromUserInfoEndpoint"
/> [label]="'getClaimsFromUserInfoEndpoint' | i18n"
<label class="form-check-label" for="getClaimsFromUserInfoEndpoint"> ></app-input-checkbox>
{{ "getClaimsFromUserInfoEndpoint" | i18n }}
</label> <!-- 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>
</div> <div id="customizations" [hidden]="!showOpenIdCustomizations">
<div class="form-group"> <app-input-text
<label for="additionalScopes">{{ "additionalScopes" | i18n }}</label> [label]="'additionalScopes' | i18n"
<input class="form-control" formControlName="additionalScopes" id="additionalScopes" /> controlId="additionalScopes"
</div> [helperText]="'separateMultipleWithComma' | i18n"
<div class="form-group"> formControlName="additionalScopes"
<label for="additionalUserIdClaimTypes">{{ "additionalUserIdClaimTypes" | i18n }}</label> ></app-input-text>
<input
class="form-control" <app-input-text
[label]="'additionalUserIdClaimTypes' | i18n"
controlId="additionalUserIdClaimTypes"
[helperText]="'separateMultipleWithComma' | i18n"
formControlName="additionalUserIdClaimTypes" formControlName="additionalUserIdClaimTypes"
id="additionalUserIdClaimTypes" ></app-input-text>
/>
</div> <app-input-text
<div class="form-group"> [label]="'additionalEmailClaimTypes' | i18n"
<label for="additionalEmailClaimTypes">{{ "additionalEmailClaimTypes" | i18n }}</label> controlId="additionalEmailClaimTypes"
<input [helperText]="'separateMultipleWithComma' | i18n"
class="form-control"
formControlName="additionalEmailClaimTypes" formControlName="additionalEmailClaimTypes"
id="additionalEmailClaimTypes" ></app-input-text>
/>
</div> <app-input-text
<div class="form-group"> [label]="'additionalNameClaimTypes' | i18n"
<label for="additionalNameClaimTypes">{{ "additionalNameClaimTypes" | i18n }}</label> controlId="additionalNameClaimTypes"
<input [helperText]="'separateMultipleWithComma' | i18n"
class="form-control"
formControlName="additionalNameClaimTypes" formControlName="additionalNameClaimTypes"
id="additionalNameClaimTypes" ></app-input-text>
/>
</div> <app-input-text
<div class="form-group"> [label]="'acrValues' | i18n"
<label for="acrValues">{{ "acrValues" | i18n }}</label> controlId="acrValues"
<input class="form-control" formControlName="acrValues" id="acrValues" /> helperText="acr_values"
</div> formControlName="acrValues"
<div class="form-group"> ></app-input-text>
<label for="expectedReturnAcrValue">{{ "expectedReturnAcrValue" | i18n }}</label>
<input <app-input-text
class="form-control" [label]="'expectedReturnAcrValue' | i18n"
controlId="expectedReturnAcrValue"
helperText="acr_validation"
formControlName="expectedReturnAcrValue" formControlName="expectedReturnAcrValue"
id="expectedReturnAcrValue" ></app-input-text>
/>
</div> </div>
</div> </div>
</div> </div>
<div *ngIf="data.value.configType == 2"> <!-- SAML2 SP -->
<div *ngIf="ssoConfigForm.get('configType').value === ssoType.Saml2" [formGroup]="samlForm">
<!-- SAML2 SP --> <!-- SAML2 SP -->
<div class="config-section"> <div class="config-section">
<h2>{{ "samlSpConfig" | i18n }}</h2> <h2 class="secondary-header">{{ "samlSpConfig" | i18n }}</h2>
<div class="form-group">
<label>{{ "spEntityId" | i18n }}</label> <app-input-text-readonly
<div class="input-group"> [label]="'spEntityId' | i18n"
<input class="form-control" readonly [value]="spEntityId" /> [controlValue]="spEntityId"
<div class="input-group-append"> ></app-input-text-readonly>
<button
type="button" <app-input-text-readonly
class="btn btn-outline-secondary" [label]="'spMetadataUrl' | i18n"
appA11yTitle="{{ 'copyValue' | i18n }}" [controlValue]="spMetadataUrl"
(click)="copy(spEntityId)" [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"
> >
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i> </app-select>
</button>
</div> <app-select
</div> controlId="spOutboundSigningAlgorithm"
</div> [label]="'spOutboundSigningAlgorithm' | i18n"
<div class="form-group"> [selectOptions]="samlSigningAlgorithmOptions"
<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" formControlName="spOutboundSigningAlgorithm"
id="spOutboundSigningAlgorithm"
> >
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option> </app-select>
</select>
</div> <app-select
<div class="form-group"> controlId="spSigningBehavior"
<label for="spSigningBehavior">{{ "spSigningBehavior" | i18n }}</label> [label]="'spSigningBehavior' | i18n"
<select class="form-control" formControlName="spSigningBehavior" id="spSigningBehavior"> [selectOptions]="saml2SigningBehaviourOptions"
<option value="0">If IdP Wants Authn Requests Signed</option> formControlName="spSigningBehavior"
<option value="1">Always</option> >
<option value="3">Never</option> </app-select>
</select>
</div> <app-select
<div class="form-group"> controlId="spMinIncomingSigningAlgorithm"
<label for="spMinIncomingSigningAlgorithm">{{ [label]="'spMinIncomingSigningAlgorithm' | i18n"
"spMinIncomingSigningAlgorithm" | i18n [selectOptions]="samlSigningAlgorithmOptions"
}}</label>
<select
class="form-control"
formControlName="spMinIncomingSigningAlgorithm" formControlName="spMinIncomingSigningAlgorithm"
id="spMinIncomingSigningAlgorithm"
> >
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option> </app-select>
</select>
</div> <app-input-checkbox
<div class="form-group"> controlId="spWantAssertionsSigned"
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="spWantAssertionsSigned"
formControlName="spWantAssertionsSigned" formControlName="spWantAssertionsSigned"
/> [label]="'spWantAssertionsSigned' | i18n"
<label class="form-check-label" for="spWantAssertionsSigned"> ></app-input-checkbox>
{{ "spWantAssertionsSigned" | i18n }}
</label> <app-input-checkbox
</div> controlId="spValidateCertificates"
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="spValidateCertificates"
formControlName="spValidateCertificates" formControlName="spValidateCertificates"
/> [label]="'spValidateCertificates' | i18n"
<label class="form-check-label" for="spValidateCertificates"> ></app-input-checkbox>
{{ "spValidateCertificates" | i18n }}
</label>
</div>
</div>
</div> </div>
<!-- SAML2 IDP --> <!-- SAML2 IDP -->
<div class="config-section"> <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"> <div class="form-group">
<label for="idpEntityId">{{ "idpEntityId" | i18n }}</label> <label for="idpX509PublicCert">
<input class="form-control" formControlName="idpEntityId" id="idpEntityId" /> {{ "idpX509PublicCert" | i18n }}
</div> <small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
<div class="form-group"> </label>
<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>
<textarea <textarea
formControlName="idpX509PublicCert" formControlName="idpX509PublicCert"
class="form-control form-control-sm text-monospace" class="form-control form-control-sm text-monospace"
rows="6" rows="6"
id="idpX509PublicCert" id="idpX509PublicCert"
appA11yInvalid
aria-describedby="idpX509PublicCertDesc"
></textarea> ></textarea>
</div> <small
<div class="form-group"> id="idpX509PublicCertDesc"
<label for="idpOutboundSigningAlgorithm">{{ "idpOutboundSigningAlgorithm" | i18n }}</label> class="error-inline"
<select role="alert"
class="form-control" *ngIf="samlForm.get('idpX509PublicCert').hasError('required')"
formControlName="idpOutboundSigningAlgorithm"
id="idpOutboundSigningAlgorithm"
> >
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option> <i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
</select> <span class="sr-only">{{ "error" | i18n }}:</span>
{{ "fieldRequiredError" | i18n: ("idpX509PublicCert" | i18n) }}
</small>
</div> </div>
<div class="form-group">
<div class="form-check"> <app-select
<input controlId="idpOutboundSigningAlgorithm"
class="form-check-input" [label]="'idpOutboundSigningAlgorithm' | i18n"
type="checkbox" [selectOptions]="samlSigningAlgorithmOptions"
id="idpAllowUnsolicitedAuthnResponse" formControlName="idpOutboundSigningAlgorithm"
>
</app-select>
<!--TODO: Uncomment once Unsolicited IdP Response is supported-->
<!-- <app-input-checkbox
controlId="idpAllowUnsolicitedAuthnResponse"
formControlName="idpAllowUnsolicitedAuthnResponse" formControlName="idpAllowUnsolicitedAuthnResponse"
/> [label]="'idpAllowUnsolicitedAuthnResponse' | i18n"
<label class="form-check-label" for="idpAllowUnsolicitedAuthnResponse"> ></app-input-checkbox> -->
{{ "idpAllowUnsolicitedAuthnResponse" | i18n }}
</label> <app-input-checkbox
</div> controlId="idpAllowOutboundLogoutRequests"
</div> formControlName="idpAllowOutboundLogoutRequests"
<div class="form-group"> [label]="'idpAllowOutboundLogoutRequests' | i18n"
<div class="form-check"> ></app-input-checkbox>
<input
class="form-check-input" <app-input-checkbox
type="checkbox" controlId="idpWantAuthnRequestsSigned"
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" formControlName="idpWantAuthnRequestsSigned"
/> [label]="'idpSignAuthenticationRequests' | i18n"
<label class="form-check-label" for="idpWantAuthnRequestsSigned"> ></app-input-checkbox>
{{ "idpWantAuthnRequestsSigned" | i18n }}
</label>
</div>
</div>
</div> </div>
</div> </div>
@@ -489,4 +434,15 @@
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i> <i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "save" | i18n }}</span> <span>{{ "save" | i18n }}</span>
</button> </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> </form>

View File

@@ -1,29 +1,82 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { AbstractControl, FormBuilder, FormGroup } from "@angular/forms";
import { ActivatedRoute } from "@angular/router"; 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 { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.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 { Organization } from "jslib-common/models/domain/organization";
import { OrganizationSsoRequest } from "jslib-common/models/request/organization/organizationSsoRequest"; 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({ @Component({
selector: "app-org-manage-sso", selector: "app-org-manage-sso",
templateUrl: "sso.component.html", templateUrl: "sso.component.html",
}) })
export class SsoComponent implements OnInit { 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/2001/04/xmldsig-more#rsa-sha256",
"http://www.w3.org/2000/09/xmldsig#rsa-sha384", "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-sha512",
"http://www.w3.org/2000/09/xmldsig#rsa-sha1", "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; loading = true;
haveTestedKeyConnector = false;
organizationId: string; organizationId: string;
organization: Organization; organization: Organization;
formPromise: Promise<any>; formPromise: Promise<any>;
@@ -35,18 +88,14 @@ export class SsoComponent implements OnInit {
spAcsUrl: string; spAcsUrl: string;
enabled = this.formBuilder.control(false); enabled = this.formBuilder.control(false);
data = this.formBuilder.group({
configType: [],
keyConnectorEnabled: [], openIdForm = this.formBuilder.group(
keyConnectorUrl: [], {
authority: ["", dirtyRequired],
// OpenId clientId: ["", dirtyRequired],
authority: [], clientSecret: ["", dirtyRequired],
clientId: [],
clientSecret: [],
metadataAddress: [], metadataAddress: [],
redirectBehavior: [], redirectBehavior: [OpenIdConnectRedirectBehavior.RedirectGet, dirtyRequired],
getClaimsFromUserInfoEndpoint: [], getClaimsFromUserInfoEndpoint: [],
additionalScopes: [], additionalScopes: [],
additionalUserIdClaimTypes: [], additionalUserIdClaimTypes: [],
@@ -54,25 +103,42 @@ export class SsoComponent implements OnInit {
additionalNameClaimTypes: [], additionalNameClaimTypes: [],
acrValues: [], acrValues: [],
expectedReturnAcrValue: [], expectedReturnAcrValue: [],
},
{
updateOn: "blur",
}
);
// SAML samlForm = this.formBuilder.group(
spNameIdFormat: [], {
spOutboundSigningAlgorithm: [], spNameIdFormat: [Saml2NameIdFormat.NotConfigured],
spSigningBehavior: [], spOutboundSigningAlgorithm: [defaultSigningAlgorithm],
spMinIncomingSigningAlgorithm: [], spSigningBehavior: [Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned],
spMinIncomingSigningAlgorithm: [defaultSigningAlgorithm],
spWantAssertionsSigned: [], spWantAssertionsSigned: [],
spValidateCertificates: [], spValidateCertificates: [],
idpEntityId: [], idpEntityId: ["", dirtyRequired],
idpBindingType: [], idpBindingType: [Saml2BindingType.HttpRedirect],
idpSingleSignOnServiceUrl: [], idpSingleSignOnServiceUrl: [],
idpSingleLogoutServiceUrl: [], idpSingleLogoutServiceUrl: [],
idpArtifactResolutionServiceUrl: [], idpX509PublicCert: ["", dirtyRequired],
idpX509PublicCert: [], idpOutboundSigningAlgorithm: [defaultSigningAlgorithm],
idpOutboundSigningAlgorithm: [],
idpAllowUnsolicitedAuthnResponse: [], idpAllowUnsolicitedAuthnResponse: [],
idpDisableOutboundLogoutRequests: [], idpAllowOutboundLogoutRequests: [true],
idpWantAuthnRequestsSigned: [], idpWantAuthnRequestsSigned: [],
},
{
updateOn: "blur",
}
);
ssoConfigForm = this.formBuilder.group({
configType: [SsoType.None],
keyConnectorEnabled: [false],
keyConnectorUrl: [""],
openId: this.openIdForm,
saml: this.samlForm,
}); });
constructor( constructor(
@@ -85,6 +151,25 @@ export class SsoComponent implements OnInit {
) {} ) {}
async ngOnInit() { 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.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId; this.organizationId = params.organizationId;
await this.load(); await this.load();
@@ -94,9 +179,7 @@ export class SsoComponent implements OnInit {
async load() { async load() {
this.organization = await this.organizationService.get(this.organizationId); this.organization = await this.organizationService.get(this.organizationId);
const ssoSettings = await this.apiService.getOrganizationSso(this.organizationId); const ssoSettings = await this.apiService.getOrganizationSso(this.organizationId);
this.populateForm(ssoSettings);
this.data.patchValue(ssoSettings.data);
this.enabled.setValue(ssoSettings.enabled);
this.callbackPath = ssoSettings.urls.callbackPath; this.callbackPath = ssoSettings.urls.callbackPath;
this.signedOutCallbackPath = ssoSettings.urls.signedOutCallbackPath; this.signedOutCallbackPath = ssoSettings.urls.signedOutCallbackPath;
@@ -104,28 +187,30 @@ export class SsoComponent implements OnInit {
this.spMetadataUrl = ssoSettings.urls.spMetadataUrl; this.spMetadataUrl = ssoSettings.urls.spMetadataUrl;
this.spAcsUrl = ssoSettings.urls.spAcsUrl; this.spAcsUrl = ssoSettings.urls.spAcsUrl;
this.keyConnectorUrl.markAsDirty();
this.loading = false; this.loading = false;
} }
copy(value: string) {
this.platformUtilsService.copyToClipboard(value);
}
launchUri(url: string) {
this.platformUtilsService.launchUri(url);
}
async submit() { 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 { try {
const response = await this.formPromise; const response = await this.formPromise;
this.populateForm(response);
this.data.patchValue(response.data);
this.enabled.setValue(response.enabled);
this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved")); this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved"));
} catch { } catch {
// Logged by appApiAction, do nothing // Logged by appApiAction, do nothing
@@ -134,24 +219,8 @@ export class SsoComponent implements OnInit {
this.formPromise = null; 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() { async validateKeyConnectorUrl() {
if (this.keyConnectorUrl.pristine) { if (this.haveTestedKeyConnector) {
return; 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() { get enableTestKeyConnector() {
return ( return (
this.data.get("keyConnectorEnabled").value && this.ssoConfigForm.get("keyConnectorEnabled").value &&
this.keyConnectorUrl != null && !Utils.isNullOrWhitespace(this.keyConnectorUrl?.value)
this.keyConnectorUrl.value !== ""
); );
} }
get keyConnectorUrl() { 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);
} }
} }

View File

@@ -2,7 +2,6 @@ import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { AuthGuardService } from "jslib-angular/services/auth-guard.service"; import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
import { Permissions } from "jslib-common/enums/permissions"; import { Permissions } from "jslib-common/enums/permissions";
import { OrganizationLayoutComponent } from "src/app/layouts/organization-layout.component"; import { OrganizationLayoutComponent } from "src/app/layouts/organization-layout.component";

View File

@@ -4,11 +4,23 @@ import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { OssModule } from "src/app/oss.module"; 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 { SsoComponent } from "./manage/sso.component";
import { OrganizationsRoutingModule } from "./organizations-routing.module"; 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({ @NgModule({
imports: [CommonModule, FormsModule, ReactiveFormsModule, OssModule, OrganizationsRoutingModule], imports: [CommonModule, FormsModule, ReactiveFormsModule, OssModule, OrganizationsRoutingModule],
declarations: [SsoComponent], declarations: [
InputCheckboxComponent,
InputTextComponent,
InputTextReadOnlyComponent,
SelectComponent,
SsoComponent,
],
}) })
export class OrganizationsModule {} export class OrganizationsModule {}

View File

@@ -1,12 +1,7 @@
import { Component } from "@angular/core"; 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 { PolicyType } from "jslib-common/enums/policyType";
import { PolicyRequest } from "jslib-common/models/request/policyRequest";
import { import {
BasePolicy, BasePolicy,
BasePolicyComponent, BasePolicyComponent,

View File

@@ -2,9 +2,7 @@ import { Component } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder } from "@angular/forms";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PolicyType } from "jslib-common/enums/policyType"; import { PolicyType } from "jslib-common/enums/policyType";
import { PolicyRequest } from "jslib-common/models/request/policyRequest"; import { PolicyRequest } from "jslib-common/models/request/policyRequest";
import { import {

View File

@@ -1,16 +1,14 @@
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; 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 { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.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 { Organization } from "jslib-common/models/domain/organization";
import { Provider } from "jslib-common/models/domain/provider"; import { Provider } from "jslib-common/models/domain/provider";
import { WebProviderService } from "../services/webProvider.service";
@Component({ @Component({
selector: "provider-add-organization", selector: "provider-add-organization",
templateUrl: "add-organization.component.html", templateUrl: "add-organization.component.html",

View File

@@ -1,8 +1,9 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { first } from "rxjs/operators"; 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 { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.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 { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
import { SearchService } from "jslib-common/abstractions/search.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 { PlanType } from "jslib-common/enums/planType";
import { ProviderUserType } from "jslib-common/enums/providerUserType"; import { ProviderUserType } from "jslib-common/enums/providerUserType";
import { Organization } from "jslib-common/models/domain/organization"; import { Organization } from "jslib-common/models/domain/organization";
import { ProviderOrganizationOrganizationDetailsResponse } from "jslib-common/models/response/provider/providerOrganizationResponse"; import { ProviderOrganizationOrganizationDetailsResponse } from "jslib-common/models/response/provider/providerOrganizationResponse";

View File

@@ -1,15 +1,14 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service"; import { StateService } from "jslib-common/abstractions/state.service";
import { ProviderUserAcceptRequest } from "jslib-common/models/request/provider/providerUserAcceptRequest"; import { ProviderUserAcceptRequest } from "jslib-common/models/request/provider/providerUserAcceptRequest";
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
@Component({ @Component({
selector: "app-accept-provider", selector: "app-accept-provider",
templateUrl: "accept-provider.component.html", templateUrl: "accept-provider.component.html",

View File

@@ -1,10 +1,9 @@
import { Component, Input } from "@angular/core"; import { Component, Input } from "@angular/core";
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
import { ProviderUserBulkConfirmRequest } from "jslib-common/models/request/provider/providerUserBulkConfirmRequest"; import { ProviderUserBulkConfirmRequest } from "jslib-common/models/request/provider/providerUserBulkConfirmRequest";
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest"; 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 { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "src/app/organizations/manage/bulk/bulk-confirm.component";
import { BulkUserDetails } from "src/app/organizations/manage/bulk/bulk-status.component"; import { BulkUserDetails } from "src/app/organizations/manage/bulk/bulk-status.component";

View File

@@ -1,27 +1,24 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { ExportService } from "jslib-common/abstractions/export.service"; import { ExportService } from "jslib-common/abstractions/export.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.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 { 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 { BaseEventsComponent } from "src/app/common/base.events.component";
import { EventService } from "src/app/services/event.service";
@Component({ @Component({
selector: "provider-events", selector: "provider-events",
templateUrl: "events.component.html", templateUrl: "events.component.html",
}) })
export class EventsComponent extends BaseEventsComponent implements OnInit { export class EventsComponent extends BaseEventsComponent implements OnInit {
exportFileName: string = "provider-events"; exportFileName = "provider-events";
providerId: string; providerId: string;
private providerUsersUserIdMap = new Map<string, any>(); private providerUsersUserIdMap = new Map<string, any>();

View File

@@ -2,7 +2,6 @@ import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Provider } from "jslib-common/models/domain/provider"; import { Provider } from "jslib-common/models/domain/provider";
@Component({ @Component({

View File

@@ -214,7 +214,7 @@
{{ "eventLogs" | i18n }} {{ "eventLogs" | i18n }}
</a> </a>
<a class="dropdown-item text-danger" href="#" appStopClick (click)="remove(u)"> <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 }} {{ "remove" | i18n }}
</a> </a>
</div> </div>

View File

@@ -1,8 +1,11 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; 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 { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.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 { ProviderService } from "jslib-common/abstractions/provider.service";
import { SearchService } from "jslib-common/abstractions/search.service"; import { SearchService } from "jslib-common/abstractions/search.service";
import { StateService } from "jslib-common/abstractions/state.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 { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
import { ProviderUserType } from "jslib-common/enums/providerUserType"; 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 { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
import { ProviderUserConfirmRequest } from "jslib-common/models/request/provider/providerUserConfirmRequest"; 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 { 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 { BasePeopleComponent } from "src/app/common/base.people.component";
import { BulkStatusComponent } from "src/app/organizations/manage/bulk/bulk-status.component"; import { BulkStatusComponent } from "src/app/organizations/manage/bulk/bulk-status.component";
import { EntityEventsComponent } from "src/app/organizations/manage/entity-events.component"; import { EntityEventsComponent } from "src/app/organizations/manage/entity-events.component";
import { BulkConfirmComponent } from "./bulk/bulk-confirm.component"; import { BulkConfirmComponent } from "./bulk/bulk-confirm.component";
import { BulkRemoveComponent } from "./bulk/bulk-remove.component"; import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
import { UserAddEditComponent } from "./user-add-edit.component"; import { UserAddEditComponent } from "./user-add-edit.component";
@@ -158,17 +153,13 @@ export class PeopleComponent
} }
async events(user: ProviderUserUserDetailsResponse) { async events(user: ProviderUserUserDetailsResponse) {
const [modal] = await this.modalService.openViewRef( await this.modalService.openViewRef(EntityEventsComponent, this.eventsModalRef, (comp) => {
EntityEventsComponent,
this.eventsModalRef,
(comp) => {
comp.name = this.userNamePipe.transform(user); comp.name = this.userNamePipe.transform(user);
comp.providerId = this.providerId; comp.providerId = this.providerId;
comp.entityId = user.id; comp.entityId = user.id;
comp.showUser = false; comp.showUser = false;
comp.entity = "user"; comp.entity = "user";
} });
);
} }
async bulkRemove() { async bulkRemove() {
@@ -272,13 +263,14 @@ export class PeopleComponent
childComponent.users = users.map((user) => { childComponent.users = users.map((user) => {
let message = keyedErrors[user.id] ?? successfullMessage; let message = keyedErrors[user.id] ?? successfullMessage;
// eslint-disable-next-line
if (!keyedFilteredUsers.hasOwnProperty(user.id)) { if (!keyedFilteredUsers.hasOwnProperty(user.id)) {
message = this.i18nService.t("bulkFilteredMessage"); message = this.i18nService.t("bulkFilteredMessage");
} }
return { return {
user: user, user: user,
error: keyedErrors.hasOwnProperty(user.id), error: keyedErrors.hasOwnProperty(user.id), // eslint-disable-line
message: message, message: message,
}; };
}); });

View File

@@ -52,7 +52,7 @@
target="_blank" target="_blank"
rel="noopener" rel="noopener"
appA11yTitle="{{ 'learnMore' | i18n }}" 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> <i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a> </a>

View File

@@ -4,12 +4,9 @@ import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.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 { 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"; import { ProviderUserUpdateRequest } from "jslib-common/models/request/provider/providerUserUpdateRequest";
@Component({ @Component({
@@ -24,7 +21,7 @@ export class UserAddEditComponent implements OnInit {
@Output() onDeletedUser = new EventEmitter(); @Output() onDeletedUser = new EventEmitter();
loading = true; loading = true;
editMode: boolean = false; editMode = false;
title: string; title: string;
emails: string; emails: string;
type: ProviderUserType = ProviderUserType.ServiceUser; type: ProviderUserType = ProviderUserType.ServiceUser;

View File

@@ -2,7 +2,6 @@ import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Provider } from "jslib-common/models/domain/provider"; import { Provider } from "jslib-common/models/domain/provider";
@Component({ @Component({

View File

@@ -4,7 +4,9 @@ import { RouterModule, Routes } from "@angular/router";
import { AuthGuardService } from "jslib-angular/services/auth-guard.service"; import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
import { Permissions } from "jslib-common/enums/permissions"; 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 { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.component"; import { CreateOrganizationComponent } from "./clients/create-organization.component";
import { AcceptProviderComponent } from "./manage/accept-provider.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 { ManageComponent } from "./manage/manage.component";
import { PeopleComponent } from "./manage/people.component"; import { PeopleComponent } from "./manage/people.component";
import { ProvidersLayoutComponent } from "./providers-layout.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 { ProviderGuardService } from "./services/provider-guard.service";
import { ProviderTypeGuardService } from "./services/provider-type-guard.service"; import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
import { AccountComponent } from "./settings/account.component"; 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 = [ const routes: Routes = [
{ {

View File

@@ -1,21 +1,14 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { ComponentFactoryResolver } from "@angular/core"; import { ComponentFactoryResolver, NgModule } from "@angular/core";
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms"; import { FormsModule } from "@angular/forms";
import { ModalService } from "jslib-angular/services/modal.service"; import { ModalService } from "jslib-angular/services/modal.service";
import { ProviderGuardService } from "./services/provider-guard.service"; import { OssModule } from "src/app/oss.module";
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 { AddOrganizationComponent } from "./clients/add-organization.component"; import { AddOrganizationComponent } from "./clients/add-organization.component";
import { ClientsComponent } from "./clients/clients.component"; import { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.component"; import { CreateOrganizationComponent } from "./clients/create-organization.component";
import { AcceptProviderComponent } from "./manage/accept-provider.component"; import { AcceptProviderComponent } from "./manage/accept-provider.component";
import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component"; import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component";
import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.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 { ManageComponent } from "./manage/manage.component";
import { PeopleComponent } from "./manage/people.component"; import { PeopleComponent } from "./manage/people.component";
import { UserAddEditComponent } from "./manage/user-add-edit.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 { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component"; import { SettingsComponent } from "./settings/settings.component";
import { SetupProviderComponent } from "./setup/setup-provider.component"; import { SetupProviderComponent } from "./setup/setup-provider.component";
import { SetupComponent } from "./setup/setup.component"; import { SetupComponent } from "./setup/setup.component";
import { OssModule } from "src/app/oss.module";
@NgModule({ @NgModule({
imports: [CommonModule, FormsModule, OssModule, ProvidersRoutingModule], imports: [CommonModule, FormsModule, OssModule, ProvidersRoutingModule],
declarations: [ declarations: [

View File

@@ -2,7 +2,6 @@ import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router"; import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Permissions } from "jslib-common/enums/permissions"; import { Permissions } from "jslib-common/enums/permissions";
@Injectable() @Injectable()

View File

@@ -3,7 +3,6 @@ import { Injectable } from "@angular/core";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { SyncService } from "jslib-common/abstractions/sync.service"; import { SyncService } from "jslib-common/abstractions/sync.service";
import { ProviderAddOrganizationRequest } from "jslib-common/models/request/provider/providerAddOrganizationRequest"; import { ProviderAddOrganizationRequest } from "jslib-common/models/request/provider/providerAddOrganizationRequest";
@Injectable() @Injectable()

View File

@@ -6,9 +6,7 @@ import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { SyncService } from "jslib-common/abstractions/sync.service"; import { SyncService } from "jslib-common/abstractions/sync.service";
import { ProviderUpdateRequest } from "jslib-common/models/request/provider/providerUpdateRequest"; import { ProviderUpdateRequest } from "jslib-common/models/request/provider/providerUpdateRequest";
import { ProviderResponse } from "jslib-common/models/response/provider/providerResponse"; import { ProviderResponse } from "jslib-common/models/response/provider/providerResponse";
@Component({ @Component({

View File

@@ -1,7 +1,6 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
@Component({ @Component({
@@ -9,15 +8,11 @@ import { ProviderService } from "jslib-common/abstractions/provider.service";
templateUrl: "settings.component.html", templateUrl: "settings.component.html",
}) })
export class SettingsComponent { export class SettingsComponent {
constructor( constructor(private route: ActivatedRoute, private providerService: ProviderService) {}
private route: ActivatedRoute,
private providerService: ProviderService,
private platformUtilsService: PlatformUtilsService
) {}
ngOnInit() { ngOnInit() {
this.route.parent.params.subscribe(async (params) => { this.route.parent.params.subscribe(async (params) => {
const provider = await this.providerService.get(params.providerId); await this.providerService.get(params.providerId);
}); });
} }
} }

View File

@@ -16,6 +16,7 @@ export class SetupProviderComponent extends BaseAcceptComponent {
this.router.navigate(["/providers/setup"], { queryParams: qParams }); this.router.navigate(["/providers/setup"], { queryParams: qParams });
} }
// tslint:disable-next-line async unauthedHandler(qParams: any) {
async unauthedHandler(qParams: any) {} // Empty
}
} }

View File

@@ -1,12 +1,10 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { ValidationService } from "jslib-angular/services/validation.service";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.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 { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { SyncService } from "jslib-common/abstractions/sync.service"; import { SyncService } from "jslib-common/abstractions/sync.service";

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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";

View File

@@ -32,6 +32,7 @@ mkhomedir_helper $USERNAME
chown -R $USERNAME:$GROUPNAME /etc/bitwarden chown -R $USERNAME:$GROUPNAME /etc/bitwarden
cp /etc/bitwarden/web/app-id.json /app/app-id.json cp /etc/bitwarden/web/app-id.json /app/app-id.json
chown -R $USERNAME:$GROUPNAME /app 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

Submodule jslib updated: e372bf242b...212bd70986

6823
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "@bitwarden/web-vault", "name": "@bitwarden/web-vault",
"version": "2.25.1", "version": "2.27.0",
"license": "GPL-3.0", "license": "GPL-3.0",
"repository": "https://github.com/bitwarden/web", "repository": "https://github.com/bitwarden/web",
"scripts": { "scripts": {
@@ -29,8 +29,8 @@
"dist:bit:selfhost": "npm run build:bit:selfhost:prod", "dist:bit:selfhost": "npm run build:bit:selfhost:prod",
"deploy": "npm run dist:bit && gh-pages -d build", "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", "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": "eslint . && prettier --check .",
"lint:fix": "tslint 'src/**/*.ts' 'bitwarden_license/src/**/*.ts' --fix", "lint:fix": "eslint . --fix",
"prettier": "prettier --write .", "prettier": "prettier --write .",
"prepare": "husky install" "prepare": "husky install"
}, },
@@ -41,11 +41,17 @@
"@types/node": "^16.11.12", "@types/node": "^16.11.12",
"@types/webcrypto": "^0.0.28", "@types/webcrypto": "^0.0.28",
"@types/webpack": "^5.28.0", "@types/webpack": "^5.28.0",
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^5.10.1",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"clean-webpack-plugin": "^4.0.0", "clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^10.0.0", "copy-webpack-plugin": "^10.0.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.5.1", "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", "gh-pages": "^3.1.0",
"html-loader": "^3.0.1", "html-loader": "^3.0.1",
"html-webpack-injector": "1.1.4", "html-webpack-injector": "1.1.4",
@@ -55,13 +61,12 @@
"mini-css-extract-plugin": "^2.4.5", "mini-css-extract-plugin": "^2.4.5",
"prettier": "2.5.1", "prettier": "2.5.1",
"process": "^0.11.10", "process": "^0.11.10",
"rimraf": "^3.0.2",
"sass": "^1.32.10", "sass": "^1.32.10",
"sass-loader": "^12.4.0", "sass-loader": "^12.4.0",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.2.5", "terser-webpack-plugin": "^5.2.5",
"ts-loader": "^9.2.5", "ts-loader": "^9.2.5",
"tslint": "^6.1.3",
"tslint-loader": "^3.5.4",
"typescript": "4.3.5", "typescript": "4.3.5",
"util": "^0.12.4", "util": "^0.12.4",
"webpack": "^5.64.4", "webpack": "^5.64.4",
@@ -85,10 +90,11 @@
"browser-hrtime": "^1.1.8", "browser-hrtime": "^1.1.8",
"core-js": "^3.11.0", "core-js": "^3.11.0",
"date-input-polyfill": "^2.14.0", "date-input-polyfill": "^2.14.0",
"font-awesome": "4.7.0",
"jquery": "3.6.0", "jquery": "3.6.0",
"jszip": "^3.7.1",
"ngx-infinite-scroll": "^10.0.1", "ngx-infinite-scroll": "^10.0.1",
"ngx-toastr": "14.1.4", "ngx-toastr": "14.1.4",
"node-forge": "^0.10.0",
"popper.js": "1.16.1", "popper.js": "1.16.1",
"qrious": "4.0.2", "qrious": "4.0.2",
"rxjs": "^7.4.0", "rxjs": "^7.4.0",
@@ -101,7 +107,8 @@
"npm": "~8" "npm": "~8"
}, },
"lint-staged": { "lint-staged": {
"*": "prettier --ignore-unknown --write", "./!(jslib)**": "prettier --ignore-unknown --write",
"*.ts": "eslint --fix",
"*.png": "node scripts/optimize.js" "*.png": "node scripts/optimize.js"
} }
} }

View 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>;
}

View File

@@ -6,6 +6,7 @@ import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service"; import { StateService } from "jslib-common/abstractions/state.service";
import { EmergencyAccessAcceptRequest } from "jslib-common/models/request/emergencyAccessAcceptRequest"; import { EmergencyAccessAcceptRequest } from "jslib-common/models/request/emergencyAccessAcceptRequest";
import { BaseAcceptComponent } from "../common/base.accept.component"; import { BaseAcceptComponent } from "../common/base.accept.component";
@Component({ @Component({

View File

@@ -8,12 +8,11 @@ import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service"; import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.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 { OrganizationUserAcceptRequest } from "jslib-common/models/request/organizationUserAcceptRequest";
import { OrganizationUserResetPasswordEnrollmentRequest } from "jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest"; 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"; import { BaseAcceptComponent } from "../common/base.accept.component";
@Component({ @Component({

View File

@@ -1,13 +1,12 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
@Component({ @Component({
selector: "app-hint", selector: "app-hint",
templateUrl: "hint.component.html", templateUrl: "hint.component.html",

View File

@@ -1,6 +1,7 @@
import { Component, NgZone } from "@angular/core"; import { Component, NgZone } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.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 { RouterService } from "../services/router.service";
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
@Component({ @Component({
selector: "app-lock", selector: "app-lock",
templateUrl: "lock.component.html", templateUrl: "lock.component.html",

View File

@@ -86,7 +86,7 @@
[queryParams]="{ email: email }" [queryParams]="{ email: email }"
class="btn btn-outline-secondary btn-block ml-2 mt-0" 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 }} {{ "createAccount" | i18n }}
</a> </a>
</div> </div>

View File

@@ -1,22 +1,25 @@
import { Component, NgZone } from "@angular/core"; import { Component, NgZone } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { AuthService } from "jslib-common/abstractions/auth.service"; import { AuthService } from "jslib-common/abstractions/auth.service";
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.service"; import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.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 { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service"; import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service"; import { PolicyData } from "jslib-common/models/data/policyData";
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
import { Policy } from "jslib-common/models/domain/policy"; 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({ @Component({
selector: "app-login", selector: "app-login",
@@ -24,13 +27,14 @@ import { Policy } from "jslib-common/models/domain/policy";
}) })
export class LoginComponent extends BaseLoginComponent { export class LoginComponent extends BaseLoginComponent {
showResetPasswordAutoEnrollWarning = false; showResetPasswordAutoEnrollWarning = false;
enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions;
policies: ListResponse<PolicyResponse>;
constructor( constructor(
authService: AuthService, authService: AuthService,
router: Router, router: Router,
i18nService: I18nService, i18nService: I18nService,
private route: ActivatedRoute, private route: ActivatedRoute,
stateService: StateService,
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService, environmentService: EnvironmentService,
passwordGenerationService: PasswordGenerationService, passwordGenerationService: PasswordGenerationService,
@@ -38,7 +42,9 @@ export class LoginComponent extends BaseLoginComponent {
private apiService: ApiService, private apiService: ApiService,
private policyService: PolicyService, private policyService: PolicyService,
logService: LogService, logService: LogService,
ngZone: NgZone ngZone: NgZone,
protected stateService: StateService,
private messagingService: MessagingService
) { ) {
super( super(
authService, authService,
@@ -52,6 +58,9 @@ export class LoginComponent extends BaseLoginComponent {
logService, logService,
ngZone ngZone
); );
this.onSuccessfulLogin = async () => {
this.messagingService.send("setFullWidth");
};
this.onSuccessfulLoginNavigate = this.goAfterLogIn; this.onSuccessfulLoginNavigate = this.goAfterLogIn;
} }
@@ -78,35 +87,64 @@ export class LoginComponent extends BaseLoginComponent {
}); });
} }
await super.ngOnInit(); await super.ngOnInit();
this.rememberEmail = await this.stateService.getRememberEmail();
}); });
const invite = await this.stateService.getOrganizationInvitation(); const invite = await this.stateService.getOrganizationInvitation();
if (invite != null) { if (invite != null) {
let policyList: Policy[] = null; let policyList: Policy[] = null;
try { try {
const policies = await this.apiService.getPoliciesByToken( this.policies = await this.apiService.getPoliciesByToken(
invite.organizationId, invite.organizationId,
invite.token, invite.token,
invite.email, invite.email,
invite.organizationUserId invite.organizationUserId
); );
policyList = this.policyService.mapPoliciesFromToken(policies); policyList = this.policyService.mapPoliciesFromToken(this.policies);
} catch (e) { } catch (e) {
this.logService.error(e); this.logService.error(e);
} }
if (policyList != null) { if (policyList != null) {
const result = this.policyService.getResetPasswordPolicyOptions( const resetPasswordPolicy = this.policyService.getResetPasswordPolicyOptions(
policyList, policyList,
invite.organizationId invite.organizationId
); );
// Set to true if policy enabled and auto-enroll enabled // 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() { 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(); const loginRedirect = await this.stateService.getLoginRedirect();
if (loginRedirect != null) { if (loginRedirect != null) {
this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams }); this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams });
@@ -115,4 +153,27 @@ export class LoginComponent extends BaseLoginComponent {
this.router.navigate([this.successRoute]); 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;
}
} }

View File

@@ -5,7 +5,6 @@ import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { DeleteRecoverRequest } from "jslib-common/models/request/deleteRecoverRequest"; import { DeleteRecoverRequest } from "jslib-common/models/request/deleteRecoverRequest";
@Component({ @Component({

View File

@@ -7,7 +7,7 @@
<p> <p>
{{ "recoverAccountTwoStepDesc" | i18n }} {{ "recoverAccountTwoStepDesc" | i18n }}
<a <a
href="https://help.bitwarden.com/article/lost-two-step-device/" href="https://bitwarden.com/help/lost-two-step-device/"
target="_blank" target="_blank"
rel="noopener" rel="noopener"
>{{ "learnMore" | i18n }}</a >{{ "learnMore" | i18n }}</a

View File

@@ -7,7 +7,6 @@ import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { TwoFactorRecoveryRequest } from "jslib-common/models/request/twoFactorRecoveryRequest"; import { TwoFactorRecoveryRequest } from "jslib-common/models/request/twoFactorRecoveryRequest";
@Component({ @Component({

View File

@@ -1,44 +1,100 @@
<div class="layout" [ngClass]="['layout', layout]"> <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="container">
<div class="row"> <div class="row">
<div class="col-7"> <div class="col-7">
<img <img
alt="Bitwarden" alt="Bitwarden"
class="logo mb-2" class="logo mb-2"
src="../../images/register-layout/logo-horizontal-white.png" src="../../images/register-layout/logo-horizontal-white.svg"
/> />
</div> </div>
</div> </div>
</div> </div>
</header> </header>
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" class="container" ngNativeValidate> <form #form (ngSubmit)="submit()" [appApiAction]="formPromise" class="container" ngNativeValidate>
<div class="row"> <div class="row">
<div class="col-7" *ngIf="layout"> <div class="col-7" *ngIf="layout">
<div class="mt-5"> <div class="mt-5">
<div *ngIf="layout === 'enterprise2'"> <!-- Default Body -->
<h2>Companies globally trust Bitwarden for password management.</h2> <div
<p>Start your 7-day free trial!</p> *ngIf="
<p class="highlight">Quickly deploy your <b>organization</b></p> layout === 'teams' ||
<p>Use Bitwarden across all platforms</p> 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> <p>Collaborate and share securely</p>
<figure> <p>Access anywhere on any device</p>
<figcaption> <p>Create your account to get started</p>
<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>
</div> </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>
<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> </div>
</div> </div>
@@ -212,5 +268,88 @@
</div> </div>
</div> </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> </form>
</div> </div>

View File

@@ -1,8 +1,8 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { AuthService } from "jslib-common/abstractions/auth.service"; import { AuthService } from "jslib-common/abstractions/auth.service";
import { CryptoService } from "jslib-common/abstractions/crypto.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 { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service"; import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service"; import { StateService } from "jslib-common/abstractions/state.service";
import { PolicyData } from "jslib-common/models/data/policyData";
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions"; import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
import { Policy } from "jslib-common/models/domain/policy"; import { Policy } from "jslib-common/models/domain/policy";
import { PolicyData } from "jslib-common/models/data/policyData";
import { ReferenceEventRequest } from "jslib-common/models/request/referenceEventRequest"; import { ReferenceEventRequest } from "jslib-common/models/request/referenceEventRequest";
@Component({ @Component({

View File

@@ -1,6 +1,7 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; 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 { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.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 { StateService } from "jslib-common/abstractions/state.service";
import { SyncService } from "jslib-common/abstractions/sync.service"; import { SyncService } from "jslib-common/abstractions/sync.service";
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
@Component({ @Component({
selector: "app-set-password", selector: "app-set-password",
templateUrl: "set-password.component.html", templateUrl: "set-password.component.html",

View File

@@ -1,8 +1,8 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { AuthService } from "jslib-common/abstractions/auth.service"; import { AuthService } from "jslib-common/abstractions/auth.service";
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.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 { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service"; import { StateService } from "jslib-common/abstractions/state.service";
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
@Component({ @Component({
selector: "app-sso", selector: "app-sso",
templateUrl: "sso.component.html", templateUrl: "sso.component.html",

View File

@@ -1,11 +1,10 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { Router } from "@angular/router"; 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 { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
@Component({ @Component({
selector: "app-two-factor-options", selector: "app-two-factor-options",
@@ -13,11 +12,11 @@ import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jsli
}) })
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent { export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
constructor( constructor(
authService: AuthService, twoFactorService: TwoFactorService,
router: Router, router: Router,
i18nService: I18nService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService platformUtilsService: PlatformUtilsService
) { ) {
super(authService, router, i18nService, platformUtilsService, window); super(twoFactorService, router, i18nService, platformUtilsService, window);
} }
} }

View File

@@ -114,6 +114,9 @@
<p>{{ "noTwoStepProviders2" | i18n }}</p> <p>{{ "noTwoStepProviders2" | i18n }}</p>
</ng-container> </ng-container>
<hr /> <hr />
<div [hidden]="!showCaptcha()">
<iframe id="hcaptcha_iframe" height="80"></iframe>
</div>
<div class="d-flex mb-3"> <div class="d-flex mb-3">
<button <button
type="submit" type="submit"

View File

@@ -1,7 +1,8 @@
import { Component, ViewChild, ViewContainerRef } from "@angular/core"; import { Component, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; 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 { ApiService } from "jslib-common/abstractions/api.service";
import { AuthService } from "jslib-common/abstractions/auth.service"; import { AuthService } from "jslib-common/abstractions/auth.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.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 { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service"; import { StateService } from "jslib-common/abstractions/state.service";
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
import { ModalService } from "jslib-angular/services/modal.service";
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType"; 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"; import { TwoFactorOptionsComponent } from "./two-factor-options.component";
@Component({ @Component({
@@ -36,7 +33,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
environmentService: EnvironmentService, environmentService: EnvironmentService,
private modalService: ModalService, private modalService: ModalService,
route: ActivatedRoute, route: ActivatedRoute,
logService: LogService logService: LogService,
twoFactorService: TwoFactorService
) { ) {
super( super(
authService, authService,
@@ -48,7 +46,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
environmentService, environmentService,
stateService, stateService,
route, route,
logService logService,
twoFactorService
); );
this.onSuccessfulLoginNavigate = this.goAfterLogIn; this.onSuccessfulLoginNavigate = this.goAfterLogIn;
} }

View 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>

View 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
);
}
}

View File

@@ -1,5 +1,6 @@
import { Component } from "@angular/core"; 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 { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.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 { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.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 { StateService } from "jslib-common/abstractions/state.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
@Component({ @Component({
selector: "app-update-temp-password", selector: "app-update-temp-password",

View File

@@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { ApiService } from "jslib-common/abstractions/api.service"; 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 { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service"; import { StateService } from "jslib-common/abstractions/state.service";
import { VerifyEmailRequest } from "jslib-common/models/request/verifyEmailRequest"; import { VerifyEmailRequest } from "jslib-common/models/request/verifyEmailRequest";
@Component({ @Component({

View File

@@ -1,13 +1,11 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { VerifyDeleteRecoverRequest } from "jslib-common/models/request/verifyDeleteRecoverRequest"; import { VerifyDeleteRecoverRequest } from "jslib-common/models/request/verifyDeleteRecoverRequest";
@Component({ @Component({

View File

@@ -21,14 +21,10 @@ import { PolicyService } from "jslib-common/abstractions/policy.service";
import { SearchService } from "jslib-common/abstractions/search.service"; import { SearchService } from "jslib-common/abstractions/search.service";
import { SettingsService } from "jslib-common/abstractions/settings.service"; import { SettingsService } from "jslib-common/abstractions/settings.service";
import { StateService } from "jslib-common/abstractions/state.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 { SyncService } from "jslib-common/abstractions/sync.service";
import { TokenService } from "jslib-common/abstractions/token.service"; import { TokenService } from "jslib-common/abstractions/token.service";
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.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 { DisableSendPolicy } from "./organizations/policies/disable-send.component";
import { MasterPasswordPolicy } from "./organizations/policies/master-password.component"; import { MasterPasswordPolicy } from "./organizations/policies/master-password.component";
import { PasswordGeneratorPolicy } from "./organizations/policies/password-generator.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 { SendOptionsPolicy } from "./organizations/policies/send-options.component";
import { SingleOrgPolicy } from "./organizations/policies/single-org.component"; import { SingleOrgPolicy } from "./organizations/policies/single-org.component";
import { TwoFactorAuthenticationPolicy } from "./organizations/policies/two-factor-authentication.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 BroadcasterSubscriptionId = "AppComponent";
const IdleTimeout = 60000 * 10; // 10 minutes const IdleTimeout = 60000 * 10; // 10 minutes
@@ -117,7 +115,7 @@ export class AppComponent implements OnDestroy, OnInit {
break; break;
case "syncCompleted": case "syncCompleted":
break; break;
case "upgradeOrganization": case "upgradeOrganization": {
const upgradeConfirmed = await this.platformUtilsService.showDialog( const upgradeConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("upgradeOrganizationDesc"), this.i18nService.t("upgradeOrganizationDesc"),
this.i18nService.t("upgradeOrganization"), this.i18nService.t("upgradeOrganization"),
@@ -133,7 +131,8 @@ export class AppComponent implements OnDestroy, OnInit {
]); ]);
} }
break; break;
case "premiumRequired": }
case "premiumRequired": {
const premiumConfirmed = await this.platformUtilsService.showDialog( const premiumConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("premiumRequiredDesc"), this.i18nService.t("premiumRequiredDesc"),
this.i18nService.t("premiumRequired"), this.i18nService.t("premiumRequired"),
@@ -144,7 +143,8 @@ export class AppComponent implements OnDestroy, OnInit {
this.router.navigate(["settings/premium"]); this.router.navigate(["settings/premium"]);
} }
break; break;
case "emailVerificationRequired": }
case "emailVerificationRequired": {
const emailVerificationConfirmed = await this.platformUtilsService.showDialog( const emailVerificationConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("emailVerificationRequiredDesc"), this.i18nService.t("emailVerificationRequiredDesc"),
this.i18nService.t("emailVerificationRequired"), this.i18nService.t("emailVerificationRequired"),
@@ -153,10 +153,11 @@ export class AppComponent implements OnDestroy, OnInit {
); );
if (emailVerificationConfirmed) { if (emailVerificationConfirmed) {
this.platformUtilsService.launchUri( this.platformUtilsService.launchUri(
"https://bitwarden.com/help/article/create-bitwarden-account/" "https://bitwarden.com/help/create-bitwarden-account/"
); );
} }
break; break;
}
case "showToast": case "showToast":
this.showToast(message); this.showToast(message);
break; break;
@@ -210,7 +211,6 @@ export class AppComponent implements OnDestroy, OnInit {
await Promise.all([ await Promise.all([
this.eventService.clearEvents(), this.eventService.clearEvents(),
this.syncService.setLastSync(new Date(0)), this.syncService.setLastSync(new Date(0)),
this.tokenService.clearToken(),
this.cryptoService.clearKeys(), this.cryptoService.clearKeys(),
this.settingsService.clear(userId), this.settingsService.clear(userId),
this.cipherService.clear(userId), this.cipherService.clear(userId),

View File

@@ -1,9 +1,8 @@
import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { DragDropModule } from "@angular/cdk/drag-drop"; import { DragDropModule } from "@angular/cdk/drag-drop";
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms"; import { FormsModule } from "@angular/forms";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { BitwardenToastModule } from "jslib-angular/components/toastr.component"; import { BitwardenToastModule } from "jslib-angular/components/toastr.component";

View File

@@ -1,6 +1,5 @@
import { Directive, OnInit } from "@angular/core"; import { Directive, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";

View File

@@ -4,11 +4,9 @@ import { ExportService } from "jslib-common/abstractions/export.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.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 { EventResponse } from "jslib-common/models/response/eventResponse";
import { ListResponse } from "jslib-common/models/response/listResponse"; import { ListResponse } from "jslib-common/models/response/listResponse";
import { EventView } from "jslib-common/models/view/eventView";
import { EventService } from "src/app/services/event.service"; import { EventService } from "src/app/services/event.service";
@@ -19,7 +17,7 @@ export abstract class BaseEventsComponent {
events: EventView[]; events: EventView[];
start: string; start: string;
end: string; end: string;
dirtyDates: boolean = true; dirtyDates = true;
continuationToken: string; continuationToken: string;
refreshPromise: Promise<any>; refreshPromise: Promise<any>;
exportPromise: Promise<any>; exportPromise: Promise<any>;

View File

@@ -1,5 +1,9 @@
import { Directive, ViewChild, ViewContainerRef } from "@angular/core"; 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 { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.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 { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { SearchService } from "jslib-common/abstractions/search.service"; import { SearchService } from "jslib-common/abstractions/search.service";
import { StateService } from "jslib-common/abstractions/state.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 { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
import { OrganizationUserType } from "jslib-common/enums/organizationUserType"; import { OrganizationUserType } from "jslib-common/enums/organizationUserType";
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType"; import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
import { ProviderUserType } from "jslib-common/enums/providerUserType"; import { ProviderUserType } from "jslib-common/enums/providerUserType";
import { Utils } from "jslib-common/misc/utils";
import { ListResponse } from "jslib-common/models/response/listResponse"; import { ListResponse } from "jslib-common/models/response/listResponse";
import { OrganizationUserUserDetailsResponse } from "jslib-common/models/response/organizationUserResponse"; import { OrganizationUserUserDetailsResponse } from "jslib-common/models/response/organizationUserResponse";
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse"; import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
import { Utils } from "jslib-common/misc/utils";
import { UserConfirmComponent } from "../organizations/manage/user-confirm.component"; import { UserConfirmComponent } from "../organizations/manage/user-confirm.component";
type StatusType = OrganizationUserStatusType | ProviderUserStatusType; type StatusType = OrganizationUserStatusType | ProviderUserStatusType;

View File

@@ -1,4 +1,5 @@
import { Component, EventEmitter, Input, Output } from "@angular/core"; import { Component, EventEmitter, Input, Output } from "@angular/core";
import { Utils } from "jslib-common/misc/utils"; import { Utils } from "jslib-common/misc/utils";
@Component({ @Component({

View File

@@ -8,7 +8,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
}) })
export class FooterComponent implements OnInit { export class FooterComponent implements OnInit {
version: string; version: string;
year: string = "2015"; year = "2015";
constructor(private platformUtilsService: PlatformUtilsService) {} constructor(private platformUtilsService: PlatformUtilsService) {}

View File

@@ -8,7 +8,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
}) })
export class FrontendLayoutComponent implements OnInit, OnDestroy { export class FrontendLayoutComponent implements OnInit, OnDestroy {
version: string; version: string;
year: string = "2015"; year = "2015";
constructor(private platformUtilsService: PlatformUtilsService) {} constructor(private platformUtilsService: PlatformUtilsService) {}

View File

@@ -60,7 +60,12 @@
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i> <i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
{{ "myAccount" | i18n }} {{ "myAccount" | i18n }}
</a> </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> <i class="bwi bwi-fw bwi-question-circle" aria-hidden="true"></i>
{{ "getHelp" | i18n }} {{ "getHelp" | i18n }}
</a> </a>

View File

@@ -5,7 +5,6 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
import { SyncService } from "jslib-common/abstractions/sync.service"; import { SyncService } from "jslib-common/abstractions/sync.service";
import { TokenService } from "jslib-common/abstractions/token.service"; import { TokenService } from "jslib-common/abstractions/token.service";
import { Provider } from "jslib-common/models/domain/provider"; import { Provider } from "jslib-common/models/domain/provider";
@Component({ @Component({

View File

@@ -1,10 +1,8 @@
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core"; import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service"; import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { Organization } from "jslib-common/models/domain/organization"; import { Organization } from "jslib-common/models/domain/organization";
const BroadcasterSubscriptionId = "OrganizationLayoutComponent"; const BroadcasterSubscriptionId = "OrganizationLayoutComponent";

View File

@@ -5,7 +5,6 @@ import "bootstrap";
import "jquery"; import "jquery";
import "popper.js"; import "popper.js";
// tslint:disable-next-line
require("../scss/styles.scss"); require("../scss/styles.scss");
import { AppModule } from "./app.module"; import { AppModule } from "./app.module";

View File

@@ -28,11 +28,7 @@
<ng-container *ngIf="!loading && !done"> <ng-container *ngIf="!loading && !done">
<p> <p>
{{ "fingerprintEnsureIntegrityVerify" | i18n }} {{ "fingerprintEnsureIntegrityVerify" | i18n }}
<a <a href="https://bitwarden.com/help/fingerprint-phrase/" target="_blank" rel="noopener">
href="https://help.bitwarden.com/article/fingerprint-phrase/"
target="_blank"
rel="noopener"
>
{{ "learnMore" | i18n }}</a {{ "learnMore" | i18n }}</a
> >
</p> </p>

View File

@@ -3,13 +3,11 @@ import { Component, Input, OnInit } from "@angular/core";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.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 { OrganizationUserBulkConfirmRequest } from "jslib-common/models/request/organizationUserBulkConfirmRequest";
import { OrganizationUserBulkRequest } from "jslib-common/models/request/organizationUserBulkRequest"; 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"; import { BulkUserDetails } from "./bulk-status.component";
@Component({ @Component({
@@ -26,8 +24,8 @@ export class BulkConfirmComponent implements OnInit {
fingerprints: Map<string, string> = new Map(); fingerprints: Map<string, string> = new Map();
statuses: Map<string, string> = new Map(); statuses: Map<string, string> = new Map();
loading: boolean = true; loading = true;
done: boolean = false; done = false;
error: string; error: string;
constructor( constructor(

View File

@@ -16,8 +16,8 @@ export class BulkRemoveComponent {
statuses: Map<string, string> = new Map(); statuses: Map<string, string> = new Map();
loading: boolean = false; loading = false;
done: boolean = false; done = false;
error: string; error: string;
constructor(protected apiService: ApiService, protected i18nService: I18nService) {} constructor(protected apiService: ApiService, protected i18nService: I18nService) {}

View File

@@ -1,4 +1,5 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType"; import { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType"; import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
@@ -21,5 +22,5 @@ type BulkStatusEntry = {
}) })
export class BulkStatusComponent { export class BulkStatusComponent {
users: BulkStatusEntry[]; users: BulkStatusEntry[];
loading: boolean = false; loading = false;
} }

View File

@@ -6,15 +6,13 @@ import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.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 { EncString } from "jslib-common/models/domain/encString";
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey"; import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
import { CollectionRequest } from "jslib-common/models/request/collectionRequest"; import { CollectionRequest } from "jslib-common/models/request/collectionRequest";
import { SelectionReadOnlyRequest } from "jslib-common/models/request/selectionReadOnlyRequest"; import { SelectionReadOnlyRequest } from "jslib-common/models/request/selectionReadOnlyRequest";
import { GroupResponse } from "jslib-common/models/response/groupResponse"; import { GroupResponse } from "jslib-common/models/response/groupResponse";
import { Utils } from "jslib-common/misc/utils";
@Component({ @Component({
selector: "app-collection-add-edit", selector: "app-collection-add-edit",
templateUrl: "collection-add-edit.component.html", templateUrl: "collection-add-edit.component.html",
@@ -28,8 +26,8 @@ export class CollectionAddEditComponent implements OnInit {
@Output() onDeletedCollection = new EventEmitter(); @Output() onDeletedCollection = new EventEmitter();
loading = true; loading = true;
editMode: boolean = false; editMode = false;
accessGroups: boolean = false; accessGroups = false;
title: string; title: string;
name: string; name: string;
externalId: string; externalId: string;

View File

@@ -1,8 +1,8 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { ModalService } from "jslib-angular/services/modal.service";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { CollectionService } from "jslib-common/abstractions/collection.service"; import { CollectionService } from "jslib-common/abstractions/collection.service";
import { I18nService } from "jslib-common/abstractions/i18n.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 { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { SearchService } from "jslib-common/abstractions/search.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 { CollectionData } from "jslib-common/models/data/collectionData";
import { Collection } from "jslib-common/models/domain/collection"; import { Collection } from "jslib-common/models/domain/collection";
import { Organization } from "jslib-common/models/domain/organization"; import { Organization } from "jslib-common/models/domain/organization";
@@ -37,7 +34,7 @@ export class CollectionsComponent implements OnInit {
loading = true; loading = true;
organization: Organization; organization: Organization;
canCreate: boolean = false; canCreate = false;
organizationId: string; organizationId: string;
collections: CollectionView[]; collections: CollectionView[];
assignedCollections: CollectionView[]; assignedCollections: CollectionView[];

View File

@@ -1,17 +1,15 @@
import { Component, Input, OnInit } from "@angular/core"; 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 { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.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 { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { EventResponse } from "jslib-common/models/response/eventResponse"; import { EventResponse } from "jslib-common/models/response/eventResponse";
import { ListResponse } from "jslib-common/models/response/listResponse"; import { ListResponse } from "jslib-common/models/response/listResponse";
import { EventService } from "../../services/event.service";
@Component({ @Component({
selector: "app-entity-events", selector: "app-entity-events",
templateUrl: "entity-events.component.html", templateUrl: "entity-events.component.html",

View File

@@ -3,15 +3,13 @@ import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.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 { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
import { OrganizationUserType } from "jslib-common/enums/organizationUserType"; import { OrganizationUserType } from "jslib-common/enums/organizationUserType";
import { Utils } from "jslib-common/misc/utils";
import { SelectionReadOnlyRequest } from "jslib-common/models/request/selectionReadOnlyRequest"; import { SelectionReadOnlyRequest } from "jslib-common/models/request/selectionReadOnlyRequest";
import { OrganizationUserUserDetailsResponse } from "jslib-common/models/response/organizationUserResponse"; import { OrganizationUserUserDetailsResponse } from "jslib-common/models/response/organizationUserResponse";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { Utils } from "jslib-common/misc/utils";
@Component({ @Component({
selector: "app-entity-users", selector: "app-entity-users",
templateUrl: "entity-users.component.html", templateUrl: "entity-users.component.html",

View File

@@ -2,7 +2,6 @@ import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe"; import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { ExportService } from "jslib-common/abstractions/export.service"; import { ExportService } from "jslib-common/abstractions/export.service";
import { I18nService } from "jslib-common/abstractions/i18n.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 { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Organization } from "jslib-common/models/domain/organization"; import { Organization } from "jslib-common/models/domain/organization";
import { EventResponse } from "jslib-common/models/response/eventResponse"; import { EventResponse } from "jslib-common/models/response/eventResponse";
import { EventService } from "../../services/event.service";
import { BaseEventsComponent } from "../../common/base.events.component"; import { BaseEventsComponent } from "../../common/base.events.component";
import { EventService } from "../../services/event.service";
@Component({ @Component({
selector: "app-org-events", selector: "app-org-events",
templateUrl: "events.component.html", templateUrl: "events.component.html",
}) })
export class EventsComponent extends BaseEventsComponent implements OnInit { export class EventsComponent extends BaseEventsComponent implements OnInit {
exportFileName: string = "org-events"; exportFileName = "org-events";
organizationId: string; organizationId: string;
organization: Organization; organization: Organization;

View File

@@ -56,7 +56,7 @@
target="_blank" target="_blank"
rel="noopener" rel="noopener"
appA11yTitle="{{ 'learnMore' | i18n }}" 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> <i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a> </a>

View File

@@ -5,7 +5,6 @@ import { CollectionService } from "jslib-common/abstractions/collection.service"
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { CollectionData } from "jslib-common/models/data/collectionData"; import { CollectionData } from "jslib-common/models/data/collectionData";
import { Collection } from "jslib-common/models/domain/collection"; import { Collection } from "jslib-common/models/domain/collection";
import { GroupRequest } from "jslib-common/models/request/groupRequest"; import { GroupRequest } from "jslib-common/models/request/groupRequest";
@@ -24,7 +23,7 @@ export class GroupAddEditComponent implements OnInit {
@Output() onDeletedGroup = new EventEmitter(); @Output() onDeletedGroup = new EventEmitter();
loading = true; loading = true;
editMode: boolean = false; editMode = false;
title: string; title: string;
name: string; name: string;
externalId: string; externalId: string;

Some files were not shown because too many files have changed in this diff Show More