mirror of
https://github.com/bitwarden/web
synced 2025-12-06 00:03:28 +00:00
Compare commits
106 Commits
feature/an
...
snyk-fix-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63e24770e1 | ||
|
|
62b20a5c6d | ||
|
|
d56bf1211e | ||
|
|
8831f96fc2 | ||
|
|
f26dc27515 | ||
|
|
cb8a40d9cd | ||
|
|
2652a2deae | ||
|
|
e1c0c9f009 | ||
|
|
612442c1bb | ||
|
|
23b02a770a | ||
|
|
42ececbcf5 | ||
|
|
11034de7d1 | ||
|
|
571aaf31c4 | ||
|
|
0884e2d761 | ||
|
|
00975e6896 | ||
|
|
2c43249e98 | ||
|
|
575847f252 | ||
|
|
d6c181c997 | ||
|
|
9bb004923c | ||
|
|
e08726463e | ||
|
|
fdf93b610c | ||
|
|
144038ed1c | ||
|
|
5cb5e37270 | ||
|
|
e266a740ba | ||
|
|
3b0fc94239 | ||
|
|
32e27b5f08 | ||
|
|
317c40386f | ||
|
|
c9eeca7def | ||
|
|
902c568c09 | ||
|
|
153870693b | ||
|
|
a8cd2a6cf7 | ||
|
|
7404da9b3c | ||
|
|
9b40ce1024 | ||
|
|
80ffa965e1 | ||
|
|
57f1a5e380 | ||
|
|
18f1929f65 | ||
|
|
5cb3941190 | ||
|
|
0e515bc6c1 | ||
|
|
e103ddf02f | ||
|
|
8242989b9d | ||
|
|
5e7d94efb8 | ||
|
|
3bc8955dd5 | ||
|
|
bc05d27082 | ||
|
|
e93c155885 | ||
|
|
1076749635 | ||
|
|
06e1af6d48 | ||
|
|
cf9a90d10e | ||
|
|
6e8c15bccd | ||
|
|
7d018e4b59 | ||
|
|
f832cb4138 | ||
|
|
b8a23cf014 | ||
|
|
d0c0e80b6c | ||
|
|
98fb71fcb6 | ||
|
|
1b52b5a98a | ||
|
|
c3e5c74253 | ||
|
|
df5b175cdf | ||
|
|
1c495e87c9 | ||
|
|
01f128a4a9 | ||
|
|
a4d5b145ac | ||
|
|
d944e0e25c | ||
|
|
d141ccca52 | ||
|
|
9e872bed2c | ||
|
|
c071b692f2 | ||
|
|
041bb1bf0a | ||
|
|
0b5e1eb256 | ||
|
|
8c39fdb21e | ||
|
|
ca3efc8fee | ||
|
|
c323f38f16 | ||
|
|
9df4eb4c0d | ||
|
|
1712ed53be | ||
|
|
45a39f6200 | ||
|
|
a2d241263b | ||
|
|
5987d3deda | ||
|
|
080a3c655e | ||
|
|
dac48242b7 | ||
|
|
e4d9ab52a0 | ||
|
|
aee8a2661e | ||
|
|
ff6bb236c0 | ||
|
|
f79b20294a | ||
|
|
3a0c34b934 | ||
|
|
e09df347f4 | ||
|
|
e68ab0031d | ||
|
|
64416c9406 | ||
|
|
6779adb064 | ||
|
|
1b28a4b954 | ||
|
|
6320498fb3 | ||
|
|
bfd5f3e564 | ||
|
|
c755443735 | ||
|
|
0e5f2530a9 | ||
|
|
5105633fa4 | ||
|
|
e975056c21 | ||
|
|
be21167ef8 | ||
|
|
e09898e4d8 | ||
|
|
868d235faa | ||
|
|
5c764a95f4 | ||
|
|
596c3e86e9 | ||
|
|
8030da2ed5 | ||
|
|
8910430dfb | ||
|
|
6bf6d4b47f | ||
|
|
ca199a398e | ||
|
|
61ab2fbda3 | ||
|
|
d79f074825 | ||
|
|
e3b962a779 | ||
|
|
cc657eb853 | ||
|
|
e14a266ee0 | ||
|
|
e1732cfa10 |
@@ -1,17 +0,0 @@
|
|||||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
|
||||||
# For additional information regarding the format and rule options, please see:
|
|
||||||
# https://github.com/browserslist/browserslist#queries
|
|
||||||
|
|
||||||
# For the full list of supported browsers by the Angular framework, please see:
|
|
||||||
# https://angular.io/guide/browser-support
|
|
||||||
|
|
||||||
# You can see what browsers were selected by your queries by running:
|
|
||||||
# npx browserslist
|
|
||||||
|
|
||||||
last 1 Chrome version
|
|
||||||
last 1 Firefox version
|
|
||||||
last 2 Edge major versions
|
|
||||||
last 2 Safari major versions
|
|
||||||
last 2 iOS major versions
|
|
||||||
Firefox ESR
|
|
||||||
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
|
||||||
8
.eslintignore
Normal file
8
.eslintignore
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
**/dist
|
||||||
|
**/build
|
||||||
|
jslib
|
||||||
|
webpack.config.js
|
||||||
|
scripts/optimize.js
|
||||||
|
config.js
|
||||||
|
|
||||||
|
**/node_modules
|
||||||
31
.eslintrc.json
Normal file
31
.eslintrc.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"extends": ["./jslib/shared/eslintrc.json"],
|
||||||
|
"rules": {
|
||||||
|
"import/order": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"alphabetize": {
|
||||||
|
"order": "asc"
|
||||||
|
},
|
||||||
|
"newlines-between": "always",
|
||||||
|
"pathGroups": [
|
||||||
|
{
|
||||||
|
"pattern": "jslib-*/**",
|
||||||
|
"group": "external",
|
||||||
|
"position": "after"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "src/**/*",
|
||||||
|
"group": "parent",
|
||||||
|
"position": "before"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pathGroupsExcludedImportTypes": ["builtin"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
197
.github/workflows/build.yml
vendored
197
.github/workflows/build.yml
vendored
@@ -11,6 +11,9 @@ on:
|
|||||||
branches-ignore:
|
branches-ignore:
|
||||||
- "l10n_master"
|
- "l10n_master"
|
||||||
- "gh-pages"
|
- "gh-pages"
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/workflows/**'
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cloc:
|
cloc:
|
||||||
@@ -28,6 +31,28 @@ 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
|
||||||
@@ -41,24 +66,25 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
run: echo "::set-output name=value::${GITHUB_SHA:0:7}"
|
run: echo "::set-output name=value::${GITHUB_SHA:0:7}"
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
- name: Set up Node
|
- name: Checkout repo
|
||||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
with:
|
|
||||||
node-version: "16"
|
|
||||||
|
|
||||||
- name: Cache npm
|
- name: Set up Node
|
||||||
id: npm-cache
|
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
|
||||||
with:
|
with:
|
||||||
path: "~/.npm"
|
cache: 'npm'
|
||||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
node-version: "16"
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -70,9 +96,6 @@ jobs:
|
|||||||
echo "GitHub ref: $GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
@@ -88,24 +111,25 @@ jobs:
|
|||||||
path: ./web-${{ env._VERSION }}-selfhosted-open-source.zip
|
path: ./web-${{ env._VERSION }}-selfhosted-open-source.zip
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
- name: Set up Node
|
- name: Checkout repo
|
||||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
with:
|
|
||||||
node-version: "16"
|
|
||||||
|
|
||||||
- name: Cache npm
|
- name: Set up Node
|
||||||
id: npm-cache
|
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
|
||||||
with:
|
with:
|
||||||
path: "~/.npm"
|
cache: 'npm'
|
||||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
node-version: "16"
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -117,9 +141,6 @@ jobs:
|
|||||||
echo "GitHub ref: $GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
@@ -135,24 +156,25 @@ jobs:
|
|||||||
path: ./web-${{ env._VERSION }}-cloud-COMMERCIAL.zip
|
path: ./web-${{ env._VERSION }}-cloud-COMMERCIAL.zip
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
- name: Set up Node
|
- name: Checkout repo
|
||||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
with:
|
|
||||||
node-version: "16"
|
|
||||||
|
|
||||||
- name: Cache npm
|
- name: Set up Node
|
||||||
id: npm-cache
|
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
|
||||||
with:
|
with:
|
||||||
path: "~/.npm"
|
cache: 'npm'
|
||||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
node-version: "16"
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -165,19 +187,13 @@ 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:
|
||||||
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: Restore
|
|
||||||
run: dotnet tool restore
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
@@ -212,11 +228,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,31 +250,58 @@ 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
|
||||||
|
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Login to Azure - QA Subscription
|
||||||
|
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Login to Azure ACR
|
||||||
|
run: az acr login -n bitwardenqa
|
||||||
|
|
||||||
|
- name: Tag and Push RC to Azure ACR QA registry
|
||||||
|
env:
|
||||||
|
REGISTRY: bitwardenqa.azurecr.io
|
||||||
|
run: |
|
||||||
|
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
||||||
|
if [[ "$IMAGE_TAG" == "master" ]]; then
|
||||||
|
IMAGE_TAG=dev
|
||||||
|
fi
|
||||||
|
docker tag bitwarden/web \
|
||||||
|
$REGISTRY/web-sh:$IMAGE_TAG
|
||||||
|
docker push $REGISTRY/web-sh:$IMAGE_TAG
|
||||||
|
|
||||||
|
- name: Log out of Docker
|
||||||
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: Checkout repo
|
||||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
with:
|
|
||||||
node-version: "16"
|
|
||||||
|
|
||||||
- name: Cache npm
|
- name: Set up Node
|
||||||
id: npm-cache
|
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
|
||||||
with:
|
with:
|
||||||
path: "~/.npm"
|
cache: 'npm'
|
||||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
node-version: "16"
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -278,12 +321,6 @@ jobs:
|
|||||||
- name: Log into container registry
|
- name: Log into container registry
|
||||||
run: az acr login -n bitwardenqa
|
run: az acr login -n bitwardenqa
|
||||||
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Restore
|
|
||||||
run: dotnet tool restore
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
@@ -339,35 +376,29 @@ jobs:
|
|||||||
- name: Log out of Docker
|
- name: Log out of Docker
|
||||||
run: docker logout
|
run: docker logout
|
||||||
|
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
name: Test code on Windows
|
name: Test code on Windows
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
steps:
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
|
|
||||||
- name: Set up NuGet
|
- name: Set up NuGet
|
||||||
uses: nuget/setup-nuget@04b0c2b8d1b97922f67eca497d7cf0bf17b8ffe1
|
uses: nuget/setup-nuget@04b0c2b8d1b97922f67eca497d7cf0bf17b8ffe1
|
||||||
with:
|
with:
|
||||||
nuget-version: "latest"
|
nuget-version: "latest"
|
||||||
|
|
||||||
- name: Set up MSBuild
|
|
||||||
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
|
|
||||||
|
|
||||||
- name: Cache npm
|
|
||||||
id: npm-cache
|
|
||||||
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
|
||||||
with:
|
|
||||||
path: "~/.npm"
|
|
||||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
||||||
with:
|
with:
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
node-version: "16"
|
node-version: "16"
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
nuget help | grep Version
|
nuget help | grep Version
|
||||||
msbuild -version
|
|
||||||
dotnet --info
|
|
||||||
node --version
|
node --version
|
||||||
npm --version
|
npm --version
|
||||||
echo "GitHub ref: $GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
@@ -376,18 +407,13 @@ jobs:
|
|||||||
GITHUB_REF: ${{ github.ref }}
|
GITHUB_REF: ${{ github.ref }}
|
||||||
GITHUB_EVENT: ${{ github.event_name }}
|
GITHUB_EVENT: ${{ github.event_name }}
|
||||||
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- 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
|
||||||
|
|
||||||
|
|
||||||
crowdin-push:
|
crowdin-push:
|
||||||
name: Crowdin Push
|
name: Crowdin Push
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
@@ -426,6 +452,7 @@ jobs:
|
|||||||
upload_sources: true
|
upload_sources: true
|
||||||
upload_translations: false
|
upload_translations: false
|
||||||
|
|
||||||
|
|
||||||
check-failures:
|
check-failures:
|
||||||
name: Check for failures
|
name: Check for failures
|
||||||
if: always()
|
if: always()
|
||||||
@@ -433,6 +460,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 +472,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 +483,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
16
.github/workflows/enforce-labels.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Enforce PR labels
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [labeled, unlabeled, opened, edited, synchronize]
|
||||||
|
jobs:
|
||||||
|
enforce-label:
|
||||||
|
name: EnforceLabel
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Enforce Label
|
||||||
|
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
|
||||||
|
with:
|
||||||
|
BANNED_LABELS: "hold"
|
||||||
|
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"
|
||||||
116
.github/workflows/release.yml
vendored
116
.github/workflows/release.yml
vendored
@@ -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: |
|
||||||
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION
|
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
||||||
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest
|
docker tag bitwarden/web:latest bitwarden/web:dryrun
|
||||||
|
else
|
||||||
- name: List Docker images
|
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION
|
||||||
run: docker images
|
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest
|
||||||
|
fi
|
||||||
|
|
||||||
- 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,49 @@ 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
|
||||||
|
|
||||||
|
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web-sh:$_RELEASE_VERSION
|
||||||
|
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web-sh:latest
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Push version and latest image
|
||||||
|
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||||
|
env:
|
||||||
|
REGISTRY: bitwardenqa.azurecr.io
|
||||||
|
run: |
|
||||||
|
docker push $REGISTRY/web:$_RELEASE_VERSION
|
||||||
|
docker push $REGISTRY/web:latest
|
||||||
|
|
||||||
|
docker push $REGISTRY/web-sh:$_RELEASE_VERSION
|
||||||
|
docker push $REGISTRY/web-sh:latest
|
||||||
|
|
||||||
- name: Log out of Docker
|
- 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 +168,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 +178,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 +192,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 +200,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 +208,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 +221,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 +235,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 +245,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 +256,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
11
.github/workflows/workflow-linter.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: Workflow Linter
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .github/workflows/**
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-workflow:
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,4 +13,3 @@ dist/
|
|||||||
build/
|
build/
|
||||||
!dev-server.shared.pem
|
!dev-server.shared.pem
|
||||||
config/local.json
|
config/local.json
|
||||||
.angular/
|
|
||||||
|
|||||||
0
.husky/pre-commit
Normal file → Executable file
0
.husky/pre-commit
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
FROM bitwarden/server:dev
|
FROM bitwarden/server
|
||||||
|
|
||||||
LABEL com.bitwarden.product="bitwarden"
|
LABEL com.bitwarden.product="bitwarden"
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
42
SECURITY.md
42
SECURITY.md
@@ -1,39 +1,11 @@
|
|||||||
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
|
Bitwarden believes that working with security researchers across the globe is crucial to keeping our users safe. If you believe you've found a security issue in our product or service, we encourage you to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We welcome working with you to resolve the issue promptly. Thanks in advance!
|
||||||
users safe. If you believe you've found a security issue in our product or service, we encourage you to
|
|
||||||
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
|
|
||||||
|
|
||||||
# Disclosure Policy
|
# Disclosure Policy
|
||||||
|
|
||||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
|
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue.
|
||||||
effort to quickly resolve the issue.
|
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||||
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
|
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder.
|
||||||
third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
||||||
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
|
|
||||||
degradation of our service. Only interact with accounts you own or with explicit permission of the
|
|
||||||
account holder.
|
|
||||||
- If you would like to encrypt your report, please use the PGP key with long ID
|
|
||||||
`0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
|
||||||
|
|
||||||
# In-scope
|
|
||||||
|
|
||||||
- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
|
|
||||||
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
|
|
||||||
code is available at https://github.com/bitwarden.
|
|
||||||
|
|
||||||
# Exclusions
|
|
||||||
|
|
||||||
The following bug classes are out-of scope:
|
|
||||||
|
|
||||||
- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
|
|
||||||
or that we already know of. Note that some of our issue tracking is private.
|
|
||||||
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
|
|
||||||
upstream maintainer.
|
|
||||||
- Attacks requiring physical access to a user's device.
|
|
||||||
- Self-XSS
|
|
||||||
- Issues related to software or protocols not under Bitwarden's control
|
|
||||||
- Vulnerabilities in outdated versions of Bitwarden
|
|
||||||
- Missing security best practices that do not directly lead to a vulnerability
|
|
||||||
- Issues that do not have any impact on the general public
|
|
||||||
|
|
||||||
While researching, we'd like to ask you to refrain from:
|
While researching, we'd like to ask you to refrain from:
|
||||||
|
|
||||||
@@ -42,4 +14,8 @@ While researching, we'd like to ask you to refrain from:
|
|||||||
- Social engineering (including phishing) of Bitwarden staff or contractors
|
- Social engineering (including phishing) of Bitwarden staff or contractors
|
||||||
- Any physical attempts against Bitwarden property or data centers
|
- Any physical attempts against Bitwarden property or data centers
|
||||||
|
|
||||||
|
# We want to help you!
|
||||||
|
|
||||||
|
If you have something that you feel is close to exploitation, or if you'd like some information regarding the internal API, or generally have any questions regarding the app that would help in your efforts, please email us at https://bitwarden.com/contact and ask for that information. As stated above, Bitwarden wants to help you find issues, and is more than willing to help.
|
||||||
|
|
||||||
Thank you for helping keep Bitwarden and our users safe!
|
Thank you for helping keep Bitwarden and our users safe!
|
||||||
|
|||||||
310
angular.json
310
angular.json
@@ -1,310 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
||||||
"version": 1,
|
|
||||||
"newProjectRoot": "projects",
|
|
||||||
"projects": {
|
|
||||||
"web-vault": {
|
|
||||||
"projectType": "application",
|
|
||||||
"schematics": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"style": "scss"
|
|
||||||
},
|
|
||||||
"@schematics/angular:application": {
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "projects/web-vault",
|
|
||||||
"sourceRoot": "projects/web-vault/src",
|
|
||||||
"prefix": "app",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-builders/custom-webpack:browser",
|
|
||||||
"options": {
|
|
||||||
"customWebpackConfig": {
|
|
||||||
"path": "extra-webpack.config.ts"
|
|
||||||
},
|
|
||||||
"outputPath": "dist/web-vault",
|
|
||||||
"index": "projects/web-vault/src/index.html",
|
|
||||||
"main": "projects/web-vault/src/main.ts",
|
|
||||||
"polyfills": "projects/web-vault/src/polyfills.ts",
|
|
||||||
"tsConfig": "projects/web-vault/tsconfig.app.json",
|
|
||||||
"inlineStyleLanguage": "scss",
|
|
||||||
"assets": [
|
|
||||||
"projects/web-vault/src/assets",
|
|
||||||
{ "glob": "favicon.ico", "input": "projects/web-vault-internal/src", "output": "" },
|
|
||||||
{ "glob": ".nojekyll", "input": "projects/web-vault-internal/src", "output": "" },
|
|
||||||
{ "glob": "manifest.json", "input": "projects/web-vault-internal/src", "output": "" },
|
|
||||||
{ "glob": "app-id.json", "input": "projects/web-vault-internal/src", "output": "" },
|
|
||||||
{ "glob": "404.html", "input": "projects/web-vault-internal/src", "output": "" },
|
|
||||||
{ "glob": "**/*", "input": "projects/web-vault-internal/src/404", "output": "404" },
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "projects/web-vault-internal/src/locales",
|
|
||||||
"output": "locales"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "projects/web-vault-internal/src/images",
|
|
||||||
"output": "images"
|
|
||||||
},
|
|
||||||
{ "glob": "qrious.min.js", "input": "node_modules/qrious/dist", "output": "scripts" },
|
|
||||||
{
|
|
||||||
"glob": "dropin.js",
|
|
||||||
"input": "node_modules/braintree-web-drop-in/dist/browser",
|
|
||||||
"output": "scripts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"projects/web-vault/src/scss/styles.scss",
|
|
||||||
{
|
|
||||||
"input": "projects/web-vault-internal/src/connectors/captcha.scss",
|
|
||||||
"inject": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"input": "projects/web-vault-internal/src/connectors/captcha-mobile.scss",
|
|
||||||
"inject": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"input": "projects/web-vault-internal/src/connectors/duo.scss",
|
|
||||||
"inject": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"input": "projects/web-vault-internal/src/connectors/sso.scss",
|
|
||||||
"inject": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"input": "projects/web-vault-internal/src/connectors/webauthn.scss",
|
|
||||||
"inject": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"resourcesOutputPath": "resources",
|
|
||||||
"scripts": ["projects/web-vault-internal/src/theme.js"]
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"budgets": [
|
|
||||||
{
|
|
||||||
"type": "initial",
|
|
||||||
"maximumWarning": "7mb",
|
|
||||||
"maximumError": "8mb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "anyComponentStyle",
|
|
||||||
"maximumWarning": "2kb",
|
|
||||||
"maximumError": "4kb"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "projects/web-vault/src/environments/environment.ts",
|
|
||||||
"with": "projects/web-vault/src/environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputHashing": "all"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"buildOptimizer": false,
|
|
||||||
"optimization": false,
|
|
||||||
"vendorChunk": true,
|
|
||||||
"extractLicenses": false,
|
|
||||||
"sourceMap": true,
|
|
||||||
"namedChunks": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "production"
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"builder": "@angular-builders/custom-webpack:dev-server",
|
|
||||||
"options": {
|
|
||||||
"ssl": true,
|
|
||||||
"proxyConfig": "proxy.config.js"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"browserTarget": "web-vault:build:production"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"browserTarget": "web-vault:build:development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "development"
|
|
||||||
},
|
|
||||||
"extract-i18n": {
|
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "web-vault:build"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
|
||||||
"options": {
|
|
||||||
"main": "projects/web-vault/src/test.ts",
|
|
||||||
"polyfills": "projects/web-vault/src/polyfills.ts",
|
|
||||||
"tsConfig": "projects/web-vault/tsconfig.spec.json",
|
|
||||||
"karmaConfig": "projects/web-vault/karma.conf.js",
|
|
||||||
"inlineStyleLanguage": "scss",
|
|
||||||
"assets": ["projects/web-vault/src/favicon.ico", "projects/web-vault/src/assets"],
|
|
||||||
"styles": ["projects/web-vault/src/styles.scss"],
|
|
||||||
"scripts": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oss-vault": {
|
|
||||||
"projectType": "application",
|
|
||||||
"schematics": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"style": "scss"
|
|
||||||
},
|
|
||||||
"@schematics/angular:application": {
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "projects/oss-vault",
|
|
||||||
"sourceRoot": "projects/oss-vault/src",
|
|
||||||
"prefix": "app",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-devkit/build-angular:browser",
|
|
||||||
"options": {
|
|
||||||
"outputPath": "dist/oss-vault",
|
|
||||||
"index": "projects/oss-vault/src/index.html",
|
|
||||||
"main": "projects/oss-vault/src/main.ts",
|
|
||||||
"polyfills": "projects/oss-vault/src/polyfills.ts",
|
|
||||||
"tsConfig": "projects/oss-vault/tsconfig.app.json",
|
|
||||||
"inlineStyleLanguage": "scss",
|
|
||||||
"assets": ["projects/oss-vault/src/favicon.ico", "projects/oss-vault/src/assets"],
|
|
||||||
"styles": ["projects/oss-vault/src/styles.scss"],
|
|
||||||
"scripts": []
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"budgets": [
|
|
||||||
{
|
|
||||||
"type": "initial",
|
|
||||||
"maximumWarning": "500kb",
|
|
||||||
"maximumError": "1mb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "anyComponentStyle",
|
|
||||||
"maximumWarning": "2kb",
|
|
||||||
"maximumError": "4kb"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "projects/oss-vault/src/environments/environment.ts",
|
|
||||||
"with": "projects/oss-vault/src/environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputHashing": "all"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"buildOptimizer": false,
|
|
||||||
"optimization": false,
|
|
||||||
"vendorChunk": true,
|
|
||||||
"extractLicenses": false,
|
|
||||||
"sourceMap": true,
|
|
||||||
"namedChunks": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "production"
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"browserTarget": "oss-vault:build:production"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"browserTarget": "oss-vault:build:development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "development"
|
|
||||||
},
|
|
||||||
"extract-i18n": {
|
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "oss-vault:build"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
|
||||||
"options": {
|
|
||||||
"main": "projects/oss-vault/src/test.ts",
|
|
||||||
"polyfills": "projects/oss-vault/src/polyfills.ts",
|
|
||||||
"tsConfig": "projects/oss-vault/tsconfig.spec.json",
|
|
||||||
"karmaConfig": "projects/oss-vault/karma.conf.js",
|
|
||||||
"inlineStyleLanguage": "scss",
|
|
||||||
"assets": ["projects/oss-vault/src/favicon.ico", "projects/oss-vault/src/assets"],
|
|
||||||
"styles": ["projects/oss-vault/src/styles.scss"],
|
|
||||||
"scripts": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"web": {
|
|
||||||
"projectType": "library",
|
|
||||||
"root": "projects/web",
|
|
||||||
"sourceRoot": "projects/web/src",
|
|
||||||
"prefix": "lib",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
|
||||||
"options": {
|
|
||||||
"project": "projects/web/ng-package.json"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"tsConfig": "projects/web/tsconfig.lib.prod.json"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"tsConfig": "projects/web/tsconfig.lib.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "production"
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
|
||||||
"options": {
|
|
||||||
"main": "projects/web/src/test.ts",
|
|
||||||
"tsConfig": "projects/web/tsconfig.spec.json",
|
|
||||||
"karmaConfig": "projects/web/karma.conf.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"webs": {
|
|
||||||
"projectType": "library",
|
|
||||||
"root": "projects/webs",
|
|
||||||
"sourceRoot": "projects/webs/src",
|
|
||||||
"prefix": "bitwarden",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
|
||||||
"options": {
|
|
||||||
"project": "projects/webs/ng-package.json"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"tsConfig": "projects/webs/tsconfig.lib.prod.json"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"tsConfig": "projects/webs/tsconfig.lib.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "production"
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
|
||||||
"options": {
|
|
||||||
"main": "projects/webs/src/test.ts",
|
|
||||||
"tsConfig": "projects/webs/tsconfig.spec.json",
|
|
||||||
"karmaConfig": "projects/webs/karma.conf.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultProject": "web-vault"
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { AppComponent as BaseAppComponent } from "@bitwarden/web-vault-internal/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";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-root",
|
selector: "app-root",
|
||||||
templateUrl: "../../../web-vault-internal/src/app/app.component.html",
|
templateUrl: "../../../src/app/app.component.html",
|
||||||
})
|
})
|
||||||
export class AppComponent extends BaseAppComponent {
|
export class AppComponent extends BaseAppComponent {
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -5,7 +5,11 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
|||||||
import { RouterModule } from "@angular/router";
|
import { RouterModule } from "@angular/router";
|
||||||
import { InfiniteScrollModule } from "ngx-infinite-scroll";
|
import { InfiniteScrollModule } from "ngx-infinite-scroll";
|
||||||
|
|
||||||
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
|
import { JslibModule } from "jslib-angular/jslib.module";
|
||||||
|
|
||||||
|
import { OssRoutingModule } from "src/app/oss-routing.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";
|
||||||
@@ -13,35 +17,25 @@ 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 "@bitwarden/web-vault-internal/app/oss-routing.module";
|
|
||||||
import { OssModule } from "@bitwarden/web-vault-internal/app/oss.module";
|
|
||||||
import { ServicesModule } from "@bitwarden/web-vault-internal/app/services/services.module";
|
|
||||||
import { WildcardRoutingModule } from "@bitwarden/web-vault-internal/app/wildcard-routing.module";
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
OssModule,
|
JslibModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
ServicesModule,
|
ServicesModule,
|
||||||
BitwardenToastModule.forRoot({
|
|
||||||
maxOpened: 5,
|
|
||||||
autoDismiss: true,
|
|
||||||
closeButton: true,
|
|
||||||
}),
|
|
||||||
InfiniteScrollModule,
|
InfiniteScrollModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
OssRoutingModule,
|
OssRoutingModule,
|
||||||
OrganizationsModule,
|
OrganizationsModule, // Must be after OssRoutingModule for competing routes to resolve properly
|
||||||
RouterModule,
|
RouterModule,
|
||||||
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
|
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
MaximumVaultTimeoutPolicyComponent,
|
|
||||||
DisablePersonalVaultExportPolicyComponent,
|
DisablePersonalVaultExportPolicyComponent,
|
||||||
|
MaximumVaultTimeoutPolicyComponent,
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
@@ -5,15 +5,13 @@ import "bootstrap";
|
|||||||
import "jquery";
|
import "jquery";
|
||||||
import "popper.js";
|
import "popper.js";
|
||||||
|
|
||||||
import { AppModule } from "./app/app.module";
|
require("src/scss/styles.scss");
|
||||||
|
require("src/scss/tailwind.css");
|
||||||
|
|
||||||
// TODO: Investigate if we can use environment.
|
import { AppModule } from "./app.module";
|
||||||
// import { environment } from './environments/environment';
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "production") {
|
if (process.env.NODE_ENV === "production") {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic()
|
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true });
|
||||||
.bootstrapModule(AppModule)
|
|
||||||
.catch((err) => console.error(err));
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { Directive, Input, OnInit, Self } from "@angular/core";
|
||||||
|
import { ControlValueAccessor, FormControl, NgControl, Validators } from "@angular/forms";
|
||||||
|
|
||||||
|
import { dirtyRequired } from "jslib-angular/validators/dirty.validator";
|
||||||
|
|
||||||
|
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||||
|
@Directive()
|
||||||
|
export abstract class BaseCvaComponent implements ControlValueAccessor, OnInit {
|
||||||
|
get describedById() {
|
||||||
|
return this.showDescribedBy ? this.controlId + "Desc" : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get showDescribedBy() {
|
||||||
|
return this.helperText != null || this.controlDir.control.hasError("required");
|
||||||
|
}
|
||||||
|
|
||||||
|
get isRequired() {
|
||||||
|
return (
|
||||||
|
this.controlDir.control.hasValidator(Validators.required) ||
|
||||||
|
this.controlDir.control.hasValidator(dirtyRequired)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() label: string;
|
||||||
|
@Input() controlId: string;
|
||||||
|
@Input() helperText: string;
|
||||||
|
|
||||||
|
internalControl = new FormControl("");
|
||||||
|
|
||||||
|
protected onChange: any;
|
||||||
|
protected onTouched: any;
|
||||||
|
|
||||||
|
constructor(@Self() public controlDir: NgControl) {
|
||||||
|
this.controlDir.valueAccessor = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.internalControl.valueChanges.subscribe(this.onValueChangesInternal);
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlurInternal() {
|
||||||
|
this.onTouched();
|
||||||
|
}
|
||||||
|
|
||||||
|
// CVA interfaces
|
||||||
|
writeValue(value: string) {
|
||||||
|
this.internalControl.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any) {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any) {
|
||||||
|
this.onTouched = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean) {
|
||||||
|
if (isDisabled) {
|
||||||
|
this.internalControl.disable();
|
||||||
|
} else {
|
||||||
|
this.internalControl.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onValueChangesInternal: any = (value: string) => this.onChange(value);
|
||||||
|
// End CVA interfaces
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
[attr.id]="controlId"
|
||||||
|
[attr.aria-describedby]="describedById"
|
||||||
|
[formControl]="internalControl"
|
||||||
|
(blur)="onBlurInternal()"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" [attr.for]="controlId">{{ label }}</label>
|
||||||
|
</div>
|
||||||
|
<small *ngIf="showDescribedBy" [attr.id]="describedById" class="form-text text-muted">{{
|
||||||
|
helperText
|
||||||
|
}}</small>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
|
import { BaseCvaComponent } from "./base-cva.component";
|
||||||
|
|
||||||
|
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||||
|
@Component({
|
||||||
|
selector: "app-input-checkbox",
|
||||||
|
templateUrl: "input-checkbox.component.html",
|
||||||
|
})
|
||||||
|
export class InputCheckboxComponent extends BaseCvaComponent {}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<div class="form-group">
|
||||||
|
<label>{{ label }}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="form-control" readonly [value]="controlValue" />
|
||||||
|
<div class="input-group-append" *ngIf="showLaunch">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
appA11yTitle="{{ 'launch' | i18n }}"
|
||||||
|
(click)="launchUri(controlValue)"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-lg bwi-external-link" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="input-group-append" *ngIf="showCopy">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||||
|
(click)="copy(controlValue)"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
|
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||||
|
@Component({
|
||||||
|
selector: "app-input-text-readonly",
|
||||||
|
templateUrl: "input-text-readonly.component.html",
|
||||||
|
})
|
||||||
|
export class InputTextReadOnlyComponent {
|
||||||
|
@Input() controlValue: string;
|
||||||
|
@Input() label: string;
|
||||||
|
@Input() showCopy = true;
|
||||||
|
@Input() showLaunch = false;
|
||||||
|
|
||||||
|
constructor(private platformUtilsService: PlatformUtilsService) {}
|
||||||
|
|
||||||
|
copy(value: string) {
|
||||||
|
this.platformUtilsService.copyToClipboard(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
launchUri(url: string) {
|
||||||
|
this.platformUtilsService.launchUri(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="form-group">
|
||||||
|
<label [attr.for]="controlId">
|
||||||
|
{{ label }}
|
||||||
|
<small *ngIf="isRequired" class="text-muted form-text d-inline"
|
||||||
|
>({{ "required" | i18n }})</small
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
[formControl]="internalControl"
|
||||||
|
class="form-control"
|
||||||
|
[attr.id]="controlId"
|
||||||
|
[attr.aria-describedby]="describedById"
|
||||||
|
[attr.aria-invalid]="controlDir.control.invalid"
|
||||||
|
(blur)="onBlurInternal()"
|
||||||
|
/>
|
||||||
|
<div *ngIf="showDescribedBy" [attr.id]="describedById">
|
||||||
|
<small
|
||||||
|
*ngIf="helperText != null && !controlDir.control.hasError(helperTextSameAsError)"
|
||||||
|
class="form-text text-muted"
|
||||||
|
>
|
||||||
|
{{ helperText }}
|
||||||
|
</small>
|
||||||
|
<small class="error-inline" *ngIf="controlDir.control.hasError('required')" role="alert">
|
||||||
|
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||||
|
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||||
|
{{
|
||||||
|
controlDir.control.hasError(helperTextSameAsError)
|
||||||
|
? helperText
|
||||||
|
: ("fieldRequiredError" | i18n: label)
|
||||||
|
}}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Component, Input, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
import { BaseCvaComponent } from "./base-cva.component";
|
||||||
|
|
||||||
|
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||||
|
@Component({
|
||||||
|
selector: "app-input-text[label][controlId]",
|
||||||
|
templateUrl: "input-text.component.html",
|
||||||
|
})
|
||||||
|
export class InputTextComponent extends BaseCvaComponent implements OnInit {
|
||||||
|
@Input() helperTextSameAsError: string;
|
||||||
|
@Input() requiredErrorMessage: string;
|
||||||
|
@Input() stripSpaces = false;
|
||||||
|
|
||||||
|
transformValue: (value: string) => string = null;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
super.ngOnInit();
|
||||||
|
if (this.stripSpaces) {
|
||||||
|
this.transformValue = this.doStripSpaces;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: string) {
|
||||||
|
this.internalControl.setValue(value == null ? "" : value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onValueChangesInternal: any = (value: string) => {
|
||||||
|
let newValue = value;
|
||||||
|
if (this.transformValue != null) {
|
||||||
|
newValue = this.transformValue(value);
|
||||||
|
this.internalControl.setValue(newValue, { emitEvent: false });
|
||||||
|
}
|
||||||
|
this.onChange(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected onValueChangeInternal(value: string) {
|
||||||
|
let newValue = value;
|
||||||
|
if (this.transformValue != null) {
|
||||||
|
newValue = this.transformValue(value);
|
||||||
|
this.internalControl.setValue(newValue, { emitEvent: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private doStripSpaces(value: string) {
|
||||||
|
return value.replace(/ /g, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<div class="form-group">
|
||||||
|
<label [attr.for]="controlId">
|
||||||
|
{{ label }}
|
||||||
|
<small *ngIf="isRequired" class="text-muted form-text d-inline"
|
||||||
|
>({{ "required" | i18n }})</small
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
[attr.id]="controlId"
|
||||||
|
[attr.aria-invalid]="controlDir.control.invalid"
|
||||||
|
[formControl]="internalControl"
|
||||||
|
(blur)="onBlurInternal()"
|
||||||
|
>
|
||||||
|
<option *ngFor="let o of selectOptions" [ngValue]="o.value" disabled="{{ o.disabled }}">
|
||||||
|
{{ o.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
|
import { SelectOptions } from "jslib-angular/interfaces/selectOptions";
|
||||||
|
|
||||||
|
import { BaseCvaComponent } from "./base-cva.component";
|
||||||
|
|
||||||
|
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||||
|
@Component({
|
||||||
|
selector: "app-select",
|
||||||
|
templateUrl: "select.component.html",
|
||||||
|
})
|
||||||
|
export class SelectComponent extends BaseCvaComponent {
|
||||||
|
@Input() selectOptions: SelectOptions[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,448 @@
|
|||||||
|
<div class="page-header d-flex">
|
||||||
|
<h1>{{ "singleSignOn" | i18n }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="loading">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<form
|
||||||
|
#form
|
||||||
|
(ngSubmit)="submit()"
|
||||||
|
[formGroup]="ssoConfigForm"
|
||||||
|
[appApiAction]="formPromise"
|
||||||
|
*ngIf="!loading"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
{{ "ssoPolicyHelpStart" | i18n }}
|
||||||
|
<a routerLink="../policies">{{ "ssoPolicyHelpLink" | i18n }}</a>
|
||||||
|
{{ "ssoPolicyHelpEnd" | i18n }}
|
||||||
|
<br />
|
||||||
|
{{ "ssoPolicyHelpKeyConnector" | i18n }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Root form -->
|
||||||
|
<ng-container>
|
||||||
|
<app-input-checkbox
|
||||||
|
controlId="enabled"
|
||||||
|
[formControl]="enabled"
|
||||||
|
[label]="'allowSso' | i18n"
|
||||||
|
[helperText]="'allowSsoDesc' | i18n"
|
||||||
|
></app-input-checkbox>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ "memberDecryptionOption" | i18n }}</label>
|
||||||
|
<div class="form-check form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
id="memberDecryptionPass"
|
||||||
|
[value]="false"
|
||||||
|
formControlName="keyConnectorEnabled"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="memberDecryptionPass">
|
||||||
|
{{ "masterPass" | i18n }}
|
||||||
|
<small>{{ "memberDecryptionPassDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mt-2 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
id="memberDecryptionKey"
|
||||||
|
[value]="true"
|
||||||
|
formControlName="keyConnectorEnabled"
|
||||||
|
[attr.disabled]="!organization.useKeyConnector || null"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="memberDecryptionKey">
|
||||||
|
{{ "keyConnector" | i18n }}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
|
href="https://bitwarden.com/help/about-key-connector/"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
<small>{{ "memberDecryptionKeyConnectorDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Key Connector -->
|
||||||
|
<ng-container *ngIf="ssoConfigForm.get('keyConnectorEnabled').value">
|
||||||
|
<app-callout type="warning" [useAlertRole]="true">
|
||||||
|
{{ "keyConnectorWarning" | i18n }}
|
||||||
|
</app-callout>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="keyConnectorUrl">
|
||||||
|
{{ "keyConnectorUrl" | i18n }}
|
||||||
|
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
|
||||||
|
</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
class="form-control"
|
||||||
|
formControlName="keyConnectorUrl"
|
||||||
|
id="keyConnectorUrl"
|
||||||
|
aria-describedby="keyConnectorUrlDesc"
|
||||||
|
(change)="haveTestedKeyConnector = false"
|
||||||
|
appInputStripSpaces
|
||||||
|
appA11yInvalid
|
||||||
|
/>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
(click)="validateKeyConnectorUrl()"
|
||||||
|
[disabled]="!enableTestKeyConnector"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
*ngIf="keyConnectorUrl.pending"
|
||||||
|
></i>
|
||||||
|
<span *ngIf="!keyConnectorUrl.pending">
|
||||||
|
{{ "keyConnectorTest" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="haveTestedKeyConnector" id="keyConnectorUrlDesc" aria-live="polite">
|
||||||
|
<small
|
||||||
|
class="error-inline"
|
||||||
|
*ngIf="keyConnectorUrl.hasError('invalidUrl'); else keyConnectorSuccess"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||||
|
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||||
|
{{ "keyConnectorTestFail" | i18n }}
|
||||||
|
</small>
|
||||||
|
<ng-template #keyConnectorSuccess>
|
||||||
|
<small class="text-success">
|
||||||
|
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||||
|
{{ "keyConnectorTestSuccess" | i18n }}
|
||||||
|
</small>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<app-select
|
||||||
|
controlId="type"
|
||||||
|
[label]="'type' | i18n"
|
||||||
|
[selectOptions]="ssoTypeOptions"
|
||||||
|
formControlName="configType"
|
||||||
|
>
|
||||||
|
</app-select>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- OIDC -->
|
||||||
|
<div
|
||||||
|
*ngIf="ssoConfigForm.get('configType').value === ssoType.OpenIdConnect"
|
||||||
|
[formGroup]="openIdForm"
|
||||||
|
>
|
||||||
|
<div class="config-section">
|
||||||
|
<h2 class="secondary-header">{{ "openIdConnectConfig" | i18n }}</h2>
|
||||||
|
|
||||||
|
<app-input-text-readonly
|
||||||
|
[label]="'callbackPath' | i18n"
|
||||||
|
[controlValue]="callbackPath"
|
||||||
|
></app-input-text-readonly>
|
||||||
|
|
||||||
|
<app-input-text-readonly
|
||||||
|
[label]="'signedOutCallbackPath' | i18n"
|
||||||
|
[controlValue]="signedOutCallbackPath"
|
||||||
|
></app-input-text-readonly>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'authority' | i18n"
|
||||||
|
controlId="authority"
|
||||||
|
[stripSpaces]="true"
|
||||||
|
formControlName="authority"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'clientId' | i18n"
|
||||||
|
controlId="clientId"
|
||||||
|
[stripSpaces]="true"
|
||||||
|
formControlName="clientId"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'clientSecret' | i18n"
|
||||||
|
controlId="clientSecret"
|
||||||
|
[stripSpaces]="true"
|
||||||
|
formControlName="clientSecret"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'metadataAddress' | i18n"
|
||||||
|
controlId="metadataAddress"
|
||||||
|
[stripSpaces]="true"
|
||||||
|
[helperText]="'openIdAuthorityRequired' | i18n"
|
||||||
|
formControlName="metadataAddress"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-select
|
||||||
|
controlId="redirectBehavior"
|
||||||
|
[label]="'oidcRedirectBehavior' | i18n"
|
||||||
|
[selectOptions]="connectRedirectOptions"
|
||||||
|
formControlName="redirectBehavior"
|
||||||
|
>
|
||||||
|
</app-select>
|
||||||
|
|
||||||
|
<app-input-checkbox
|
||||||
|
controlId="getClaimsFromUserInfoEndpoint"
|
||||||
|
formControlName="getClaimsFromUserInfoEndpoint"
|
||||||
|
[label]="'getClaimsFromUserInfoEndpoint' | i18n"
|
||||||
|
></app-input-checkbox>
|
||||||
|
|
||||||
|
<!-- Optional customizations -->
|
||||||
|
<div
|
||||||
|
class="section-header d-flex flex-row align-items-center mt-3 mb-3"
|
||||||
|
(click)="toggleOpenIdCustomizations()"
|
||||||
|
>
|
||||||
|
<h3 class="mb-0 mr-2" id="customizations-header">
|
||||||
|
{{ "openIdOptionalCustomizations" | i18n }}
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
class="mb-1 btn btn-link"
|
||||||
|
type="button"
|
||||||
|
appStopClick
|
||||||
|
role="button"
|
||||||
|
aria-controls="customizations"
|
||||||
|
[attr.aria-expanded]="showOpenIdCustomizations"
|
||||||
|
aria-labelledby="customizations-header"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="bwi"
|
||||||
|
aria-hidden="true"
|
||||||
|
[ngClass]="{
|
||||||
|
'bwi-angle-down': !showOpenIdCustomizations,
|
||||||
|
'bwi-chevron-up': showOpenIdCustomizations
|
||||||
|
}"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="customizations" [hidden]="!showOpenIdCustomizations">
|
||||||
|
<app-input-text
|
||||||
|
[label]="'additionalScopes' | i18n"
|
||||||
|
controlId="additionalScopes"
|
||||||
|
[helperText]="'separateMultipleWithComma' | i18n"
|
||||||
|
formControlName="additionalScopes"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'additionalUserIdClaimTypes' | i18n"
|
||||||
|
controlId="additionalUserIdClaimTypes"
|
||||||
|
[helperText]="'separateMultipleWithComma' | i18n"
|
||||||
|
formControlName="additionalUserIdClaimTypes"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'additionalEmailClaimTypes' | i18n"
|
||||||
|
controlId="additionalEmailClaimTypes"
|
||||||
|
[helperText]="'separateMultipleWithComma' | i18n"
|
||||||
|
formControlName="additionalEmailClaimTypes"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'additionalNameClaimTypes' | i18n"
|
||||||
|
controlId="additionalNameClaimTypes"
|
||||||
|
[helperText]="'separateMultipleWithComma' | i18n"
|
||||||
|
formControlName="additionalNameClaimTypes"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'acrValues' | i18n"
|
||||||
|
controlId="acrValues"
|
||||||
|
helperText="acr_values"
|
||||||
|
formControlName="acrValues"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'expectedReturnAcrValue' | i18n"
|
||||||
|
controlId="expectedReturnAcrValue"
|
||||||
|
helperText="acr_validation"
|
||||||
|
formControlName="expectedReturnAcrValue"
|
||||||
|
></app-input-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SAML2 SP -->
|
||||||
|
<div *ngIf="ssoConfigForm.get('configType').value === ssoType.Saml2" [formGroup]="samlForm">
|
||||||
|
<!-- SAML2 SP -->
|
||||||
|
<div class="config-section">
|
||||||
|
<h2 class="secondary-header">{{ "samlSpConfig" | i18n }}</h2>
|
||||||
|
|
||||||
|
<app-input-text-readonly
|
||||||
|
[label]="'spEntityId' | i18n"
|
||||||
|
[controlValue]="spEntityId"
|
||||||
|
></app-input-text-readonly>
|
||||||
|
|
||||||
|
<app-input-text-readonly
|
||||||
|
[label]="'spMetadataUrl' | i18n"
|
||||||
|
[controlValue]="spMetadataUrl"
|
||||||
|
[showLaunch]="true"
|
||||||
|
></app-input-text-readonly>
|
||||||
|
|
||||||
|
<app-input-text-readonly
|
||||||
|
[label]="'spAcsUrl' | i18n"
|
||||||
|
[controlValue]="spAcsUrl"
|
||||||
|
></app-input-text-readonly>
|
||||||
|
|
||||||
|
<app-select
|
||||||
|
controlId="spNameIdFormat"
|
||||||
|
[label]="'spNameIdFormat' | i18n"
|
||||||
|
[selectOptions]="saml2NameIdFormatOptions"
|
||||||
|
formControlName="spNameIdFormat"
|
||||||
|
>
|
||||||
|
</app-select>
|
||||||
|
|
||||||
|
<app-select
|
||||||
|
controlId="spOutboundSigningAlgorithm"
|
||||||
|
[label]="'spOutboundSigningAlgorithm' | i18n"
|
||||||
|
[selectOptions]="samlSigningAlgorithmOptions"
|
||||||
|
formControlName="spOutboundSigningAlgorithm"
|
||||||
|
>
|
||||||
|
</app-select>
|
||||||
|
|
||||||
|
<app-select
|
||||||
|
controlId="spSigningBehavior"
|
||||||
|
[label]="'spSigningBehavior' | i18n"
|
||||||
|
[selectOptions]="saml2SigningBehaviourOptions"
|
||||||
|
formControlName="spSigningBehavior"
|
||||||
|
>
|
||||||
|
</app-select>
|
||||||
|
|
||||||
|
<app-select
|
||||||
|
controlId="spMinIncomingSigningAlgorithm"
|
||||||
|
[label]="'spMinIncomingSigningAlgorithm' | i18n"
|
||||||
|
[selectOptions]="samlSigningAlgorithmOptions"
|
||||||
|
formControlName="spMinIncomingSigningAlgorithm"
|
||||||
|
>
|
||||||
|
</app-select>
|
||||||
|
|
||||||
|
<app-input-checkbox
|
||||||
|
controlId="spWantAssertionsSigned"
|
||||||
|
formControlName="spWantAssertionsSigned"
|
||||||
|
[label]="'spWantAssertionsSigned' | i18n"
|
||||||
|
></app-input-checkbox>
|
||||||
|
|
||||||
|
<app-input-checkbox
|
||||||
|
controlId="spValidateCertificates"
|
||||||
|
formControlName="spValidateCertificates"
|
||||||
|
[label]="'spValidateCertificates' | i18n"
|
||||||
|
></app-input-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SAML2 IDP -->
|
||||||
|
<div class="config-section">
|
||||||
|
<h2 class="secondary-header">{{ "samlIdpConfig" | i18n }}</h2>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'idpEntityId' | i18n"
|
||||||
|
controlId="idpEntityId"
|
||||||
|
formControlName="idpEntityId"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-select
|
||||||
|
controlId="idpBindingType"
|
||||||
|
[label]="'idpBindingType' | i18n"
|
||||||
|
[selectOptions]="saml2BindingTypeOptions"
|
||||||
|
formControlName="idpBindingType"
|
||||||
|
>
|
||||||
|
</app-select>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'idpSingleSignOnServiceUrl' | i18n"
|
||||||
|
controlId="idpSingleSignOnServiceUrl"
|
||||||
|
[helperText]="'idpSingleSignOnServiceUrlRequired' | i18n"
|
||||||
|
[stripSpaces]="true"
|
||||||
|
formControlName="idpSingleSignOnServiceUrl"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<app-input-text
|
||||||
|
[label]="'idpSingleLogoutServiceUrl' | i18n"
|
||||||
|
controlId="idpSingleLogoutServiceUrl"
|
||||||
|
[stripSpaces]="true"
|
||||||
|
formControlName="idpSingleLogoutServiceUrl"
|
||||||
|
></app-input-text>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="idpX509PublicCert">
|
||||||
|
{{ "idpX509PublicCert" | i18n }}
|
||||||
|
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
formControlName="idpX509PublicCert"
|
||||||
|
class="form-control form-control-sm text-monospace"
|
||||||
|
rows="6"
|
||||||
|
id="idpX509PublicCert"
|
||||||
|
appA11yInvalid
|
||||||
|
aria-describedby="idpX509PublicCertDesc"
|
||||||
|
></textarea>
|
||||||
|
<small
|
||||||
|
id="idpX509PublicCertDesc"
|
||||||
|
class="error-inline"
|
||||||
|
role="alert"
|
||||||
|
*ngIf="samlForm.get('idpX509PublicCert').hasError('required')"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||||
|
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||||
|
{{ "fieldRequiredError" | i18n: ("idpX509PublicCert" | i18n) }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<app-select
|
||||||
|
controlId="idpOutboundSigningAlgorithm"
|
||||||
|
[label]="'idpOutboundSigningAlgorithm' | i18n"
|
||||||
|
[selectOptions]="samlSigningAlgorithmOptions"
|
||||||
|
formControlName="idpOutboundSigningAlgorithm"
|
||||||
|
>
|
||||||
|
</app-select>
|
||||||
|
|
||||||
|
<!--TODO: Uncomment once Unsolicited IdP Response is supported-->
|
||||||
|
<!-- <app-input-checkbox
|
||||||
|
controlId="idpAllowUnsolicitedAuthnResponse"
|
||||||
|
formControlName="idpAllowUnsolicitedAuthnResponse"
|
||||||
|
[label]="'idpAllowUnsolicitedAuthnResponse' | i18n"
|
||||||
|
></app-input-checkbox> -->
|
||||||
|
|
||||||
|
<app-input-checkbox
|
||||||
|
controlId="idpAllowOutboundLogoutRequests"
|
||||||
|
formControlName="idpAllowOutboundLogoutRequests"
|
||||||
|
[label]="'idpAllowOutboundLogoutRequests' | i18n"
|
||||||
|
></app-input-checkbox>
|
||||||
|
|
||||||
|
<app-input-checkbox
|
||||||
|
controlId="idpWantAuthnRequestsSigned"
|
||||||
|
formControlName="idpWantAuthnRequestsSigned"
|
||||||
|
[label]="'idpSignAuthenticationRequests' | i18n"
|
||||||
|
></app-input-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||||
|
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||||
|
<span>{{ "save" | i18n }}</span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
id="errorSummary"
|
||||||
|
class="error-summary text-danger"
|
||||||
|
*ngIf="this.getErrorCount(ssoConfigForm) as errorCount"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||||
|
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||||
|
{{
|
||||||
|
(errorCount === 1 ? "formErrorSummarySingle" : "formErrorSummaryPlural") | i18n: errorCount
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
318
bitwarden_license/src/app/organizations/manage/sso.component.ts
Normal file
318
bitwarden_license/src/app/organizations/manage/sso.component.ts
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { AbstractControl, FormBuilder, FormGroup } from "@angular/forms";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
|
import { SelectOptions } from "jslib-angular/interfaces/selectOptions";
|
||||||
|
import { dirtyRequired } from "jslib-angular/validators/dirty.validator";
|
||||||
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||||
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
import {
|
||||||
|
OpenIdConnectRedirectBehavior,
|
||||||
|
Saml2BindingType,
|
||||||
|
Saml2NameIdFormat,
|
||||||
|
Saml2SigningBehavior,
|
||||||
|
SsoType,
|
||||||
|
} from "jslib-common/enums/ssoEnums";
|
||||||
|
import { Utils } from "jslib-common/misc/utils";
|
||||||
|
import { SsoConfigApi } from "jslib-common/models/api/ssoConfigApi";
|
||||||
|
import { Organization } from "jslib-common/models/domain/organization";
|
||||||
|
import { OrganizationSsoRequest } from "jslib-common/models/request/organization/organizationSsoRequest";
|
||||||
|
import { OrganizationSsoResponse } from "jslib-common/models/response/organization/organizationSsoResponse";
|
||||||
|
import { SsoConfigView } from "jslib-common/models/view/ssoConfigView";
|
||||||
|
|
||||||
|
const defaultSigningAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-org-manage-sso",
|
||||||
|
templateUrl: "sso.component.html",
|
||||||
|
})
|
||||||
|
export class SsoComponent implements OnInit {
|
||||||
|
readonly ssoType = SsoType;
|
||||||
|
|
||||||
|
readonly ssoTypeOptions: SelectOptions[] = [
|
||||||
|
{ name: this.i18nService.t("selectType"), value: SsoType.None, disabled: true },
|
||||||
|
{ name: "OpenID Connect", value: SsoType.OpenIdConnect },
|
||||||
|
{ name: "SAML 2.0", value: SsoType.Saml2 },
|
||||||
|
];
|
||||||
|
|
||||||
|
readonly samlSigningAlgorithms = [
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
||||||
|
"http://www.w3.org/2000/09/xmldsig#rsa-sha384",
|
||||||
|
"http://www.w3.org/2000/09/xmldsig#rsa-sha512",
|
||||||
|
"http://www.w3.org/2000/09/xmldsig#rsa-sha1",
|
||||||
|
];
|
||||||
|
|
||||||
|
readonly saml2SigningBehaviourOptions: SelectOptions[] = [
|
||||||
|
{
|
||||||
|
name: "If IdP Wants Authn Requests Signed",
|
||||||
|
value: Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned,
|
||||||
|
},
|
||||||
|
{ name: "Always", value: Saml2SigningBehavior.Always },
|
||||||
|
{ name: "Never", value: Saml2SigningBehavior.Never },
|
||||||
|
];
|
||||||
|
readonly saml2BindingTypeOptions: SelectOptions[] = [
|
||||||
|
{ name: "Redirect", value: Saml2BindingType.HttpRedirect },
|
||||||
|
{ name: "HTTP POST", value: Saml2BindingType.HttpPost },
|
||||||
|
];
|
||||||
|
readonly saml2NameIdFormatOptions: SelectOptions[] = [
|
||||||
|
{ name: "Not Configured", value: Saml2NameIdFormat.NotConfigured },
|
||||||
|
{ name: "Unspecified", value: Saml2NameIdFormat.Unspecified },
|
||||||
|
{ name: "Email Address", value: Saml2NameIdFormat.EmailAddress },
|
||||||
|
{ name: "X.509 Subject Name", value: Saml2NameIdFormat.X509SubjectName },
|
||||||
|
{ name: "Windows Domain Qualified Name", value: Saml2NameIdFormat.WindowsDomainQualifiedName },
|
||||||
|
{ name: "Kerberos Principal Name", value: Saml2NameIdFormat.KerberosPrincipalName },
|
||||||
|
{ name: "Entity Identifier", value: Saml2NameIdFormat.EntityIdentifier },
|
||||||
|
{ name: "Persistent", value: Saml2NameIdFormat.Persistent },
|
||||||
|
{ name: "Transient", value: Saml2NameIdFormat.Transient },
|
||||||
|
];
|
||||||
|
|
||||||
|
readonly connectRedirectOptions: SelectOptions[] = [
|
||||||
|
{ name: "Redirect GET", value: OpenIdConnectRedirectBehavior.RedirectGet },
|
||||||
|
{ name: "Form POST", value: OpenIdConnectRedirectBehavior.FormPost },
|
||||||
|
];
|
||||||
|
|
||||||
|
showOpenIdCustomizations = false;
|
||||||
|
|
||||||
|
loading = true;
|
||||||
|
haveTestedKeyConnector = false;
|
||||||
|
organizationId: string;
|
||||||
|
organization: Organization;
|
||||||
|
formPromise: Promise<any>;
|
||||||
|
|
||||||
|
callbackPath: string;
|
||||||
|
signedOutCallbackPath: string;
|
||||||
|
spEntityId: string;
|
||||||
|
spMetadataUrl: string;
|
||||||
|
spAcsUrl: string;
|
||||||
|
|
||||||
|
enabled = this.formBuilder.control(false);
|
||||||
|
|
||||||
|
openIdForm = this.formBuilder.group(
|
||||||
|
{
|
||||||
|
authority: ["", dirtyRequired],
|
||||||
|
clientId: ["", dirtyRequired],
|
||||||
|
clientSecret: ["", dirtyRequired],
|
||||||
|
metadataAddress: [],
|
||||||
|
redirectBehavior: [OpenIdConnectRedirectBehavior.RedirectGet, dirtyRequired],
|
||||||
|
getClaimsFromUserInfoEndpoint: [],
|
||||||
|
additionalScopes: [],
|
||||||
|
additionalUserIdClaimTypes: [],
|
||||||
|
additionalEmailClaimTypes: [],
|
||||||
|
additionalNameClaimTypes: [],
|
||||||
|
acrValues: [],
|
||||||
|
expectedReturnAcrValue: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
updateOn: "blur",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
samlForm = this.formBuilder.group(
|
||||||
|
{
|
||||||
|
spNameIdFormat: [Saml2NameIdFormat.NotConfigured],
|
||||||
|
spOutboundSigningAlgorithm: [defaultSigningAlgorithm],
|
||||||
|
spSigningBehavior: [Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned],
|
||||||
|
spMinIncomingSigningAlgorithm: [defaultSigningAlgorithm],
|
||||||
|
spWantAssertionsSigned: [],
|
||||||
|
spValidateCertificates: [],
|
||||||
|
|
||||||
|
idpEntityId: ["", dirtyRequired],
|
||||||
|
idpBindingType: [Saml2BindingType.HttpRedirect],
|
||||||
|
idpSingleSignOnServiceUrl: [],
|
||||||
|
idpSingleLogoutServiceUrl: [],
|
||||||
|
idpX509PublicCert: ["", dirtyRequired],
|
||||||
|
idpOutboundSigningAlgorithm: [defaultSigningAlgorithm],
|
||||||
|
idpAllowUnsolicitedAuthnResponse: [],
|
||||||
|
idpAllowOutboundLogoutRequests: [true],
|
||||||
|
idpWantAuthnRequestsSigned: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
updateOn: "blur",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ssoConfigForm = this.formBuilder.group({
|
||||||
|
configType: [SsoType.None],
|
||||||
|
keyConnectorEnabled: [false],
|
||||||
|
keyConnectorUrl: [""],
|
||||||
|
openId: this.openIdForm,
|
||||||
|
saml: this.samlForm,
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private organizationService: OrganizationService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.ssoConfigForm.get("configType").valueChanges.subscribe((newType: SsoType) => {
|
||||||
|
if (newType === SsoType.OpenIdConnect) {
|
||||||
|
this.openIdForm.enable();
|
||||||
|
this.samlForm.disable();
|
||||||
|
} else if (newType === SsoType.Saml2) {
|
||||||
|
this.openIdForm.disable();
|
||||||
|
this.samlForm.enable();
|
||||||
|
} else {
|
||||||
|
this.openIdForm.disable();
|
||||||
|
this.samlForm.disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.samlForm
|
||||||
|
.get("spSigningBehavior")
|
||||||
|
.valueChanges.subscribe(() =>
|
||||||
|
this.samlForm.get("idpX509PublicCert").updateValueAndValidity()
|
||||||
|
);
|
||||||
|
|
||||||
|
this.route.parent.parent.params.subscribe(async (params) => {
|
||||||
|
this.organizationId = params.organizationId;
|
||||||
|
await this.load();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
this.organization = await this.organizationService.get(this.organizationId);
|
||||||
|
const ssoSettings = await this.apiService.getOrganizationSso(this.organizationId);
|
||||||
|
this.populateForm(ssoSettings);
|
||||||
|
|
||||||
|
this.callbackPath = ssoSettings.urls.callbackPath;
|
||||||
|
this.signedOutCallbackPath = ssoSettings.urls.signedOutCallbackPath;
|
||||||
|
this.spEntityId = ssoSettings.urls.spEntityId;
|
||||||
|
this.spMetadataUrl = ssoSettings.urls.spMetadataUrl;
|
||||||
|
this.spAcsUrl = ssoSettings.urls.spAcsUrl;
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
this.validateForm(this.ssoConfigForm);
|
||||||
|
|
||||||
|
if (this.ssoConfigForm.get("keyConnectorEnabled").value) {
|
||||||
|
await this.validateKeyConnectorUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.ssoConfigForm.valid) {
|
||||||
|
this.readOutErrors();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = new OrganizationSsoRequest();
|
||||||
|
request.enabled = this.enabled.value;
|
||||||
|
request.data = SsoConfigApi.fromView(this.ssoConfigForm.value as SsoConfigView);
|
||||||
|
|
||||||
|
this.formPromise = this.apiService.postOrganizationSso(this.organizationId, request);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.formPromise;
|
||||||
|
this.populateForm(response);
|
||||||
|
this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved"));
|
||||||
|
} catch {
|
||||||
|
// Logged by appApiAction, do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
this.formPromise = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateKeyConnectorUrl() {
|
||||||
|
if (this.haveTestedKeyConnector) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.keyConnectorUrl.markAsPending();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.apiService.getKeyConnectorAlive(this.keyConnectorUrl.value);
|
||||||
|
this.keyConnectorUrl.updateValueAndValidity();
|
||||||
|
} catch {
|
||||||
|
this.keyConnectorUrl.setErrors({
|
||||||
|
invalidUrl: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.haveTestedKeyConnector = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleOpenIdCustomizations() {
|
||||||
|
this.showOpenIdCustomizations = !this.showOpenIdCustomizations;
|
||||||
|
}
|
||||||
|
|
||||||
|
getErrorCount(form: FormGroup): number {
|
||||||
|
return Object.values(form.controls).reduce((acc: number, control: AbstractControl) => {
|
||||||
|
if (control instanceof FormGroup) {
|
||||||
|
return acc + this.getErrorCount(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (control.errors == null) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
return acc + Object.keys(control.errors).length;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get enableTestKeyConnector() {
|
||||||
|
return (
|
||||||
|
this.ssoConfigForm.get("keyConnectorEnabled").value &&
|
||||||
|
!Utils.isNullOrWhitespace(this.keyConnectorUrl?.value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get keyConnectorUrl() {
|
||||||
|
return this.ssoConfigForm.get("keyConnectorUrl");
|
||||||
|
}
|
||||||
|
|
||||||
|
get samlSigningAlgorithmOptions(): SelectOptions[] {
|
||||||
|
return this.samlSigningAlgorithms.map((algorithm) => ({ name: algorithm, value: algorithm }));
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateForm(form: FormGroup) {
|
||||||
|
Object.values(form.controls).forEach((control: AbstractControl) => {
|
||||||
|
if (control.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (control instanceof FormGroup) {
|
||||||
|
this.validateForm(control);
|
||||||
|
} else {
|
||||||
|
control.markAsDirty();
|
||||||
|
control.markAsTouched();
|
||||||
|
control.updateValueAndValidity();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private populateForm(ssoSettings: OrganizationSsoResponse) {
|
||||||
|
this.enabled.setValue(ssoSettings.enabled);
|
||||||
|
if (ssoSettings.data != null) {
|
||||||
|
const ssoConfigView = new SsoConfigView(ssoSettings.data);
|
||||||
|
this.ssoConfigForm.patchValue(ssoConfigView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readOutErrors() {
|
||||||
|
const errorText = this.i18nService.t("error");
|
||||||
|
const errorCount = this.getErrorCount(this.ssoConfigForm);
|
||||||
|
const errorCountText = this.i18nService.t(
|
||||||
|
errorCount === 1 ? "formErrorSummarySingle" : "formErrorSummaryPlural",
|
||||||
|
errorCount.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.className = "sr-only";
|
||||||
|
div.id = "srErrorCount";
|
||||||
|
div.setAttribute("aria-live", "polite");
|
||||||
|
div.innerText = errorText + ": " + errorCountText;
|
||||||
|
|
||||||
|
const existing = document.getElementById("srErrorCount");
|
||||||
|
if (existing != null) {
|
||||||
|
existing.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.append(div);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,12 @@ 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 "@bitwarden/web-vault-internal/app/layouts/organization-layout.component";
|
import { OrganizationLayoutComponent } from "src/app/layouts/organization-layout.component";
|
||||||
import { ManageComponent } from "@bitwarden/web-vault-internal/app/organizations/manage/manage.component";
|
import { ManageComponent } from "src/app/organizations/manage/manage.component";
|
||||||
import { OrganizationGuardService } from "@bitwarden/web-vault-internal/app/services/organization-guard.service";
|
import { OrganizationGuardService } from "src/app/services/organization-guard.service";
|
||||||
import { OrganizationTypeGuardService } from "@bitwarden/web-vault-internal/app/services/organization-type-guard.service";
|
import { OrganizationTypeGuardService } from "src/app/services/organization-type-guard.service";
|
||||||
|
|
||||||
import { SsoComponent } from "./manage/sso.component";
|
import { SsoComponent } from "./manage/sso.component";
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
|
import { JslibModule } from "jslib-angular/jslib.module";
|
||||||
|
|
||||||
|
import { InputCheckboxComponent } from "./components/input-checkbox.component";
|
||||||
|
import { InputTextReadOnlyComponent } from "./components/input-text-readonly.component";
|
||||||
|
import { InputTextComponent } from "./components/input-text.component";
|
||||||
|
import { SelectComponent } from "./components/select.component";
|
||||||
|
import { SsoComponent } from "./manage/sso.component";
|
||||||
|
import { OrganizationsRoutingModule } from "./organizations-routing.module";
|
||||||
|
|
||||||
|
// Form components are for use in the SSO Configuration Form only and should not be exported for use elsewhere.
|
||||||
|
// They will be deprecated by the Component Library.
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
JslibModule,
|
||||||
|
OrganizationsRoutingModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
InputCheckboxComponent,
|
||||||
|
InputTextComponent,
|
||||||
|
InputTextReadOnlyComponent,
|
||||||
|
SelectComponent,
|
||||||
|
SsoComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class OrganizationsModule {}
|
||||||
@@ -5,7 +5,7 @@ import { PolicyType } from "jslib-common/enums/policyType";
|
|||||||
import {
|
import {
|
||||||
BasePolicy,
|
BasePolicy,
|
||||||
BasePolicyComponent,
|
BasePolicyComponent,
|
||||||
} from "@bitwarden/web-vault-internal/app/organizations/policies/base-policy.component";
|
} from "src/app/organizations/policies/base-policy.component";
|
||||||
|
|
||||||
export class DisablePersonalVaultExportPolicy extends BasePolicy {
|
export class DisablePersonalVaultExportPolicy extends BasePolicy {
|
||||||
name = "disablePersonalVaultExport";
|
name = "disablePersonalVaultExport";
|
||||||
@@ -2,15 +2,13 @@ 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 {
|
||||||
BasePolicy,
|
BasePolicy,
|
||||||
BasePolicyComponent,
|
BasePolicyComponent,
|
||||||
} from "@bitwarden/web-vault-internal/app/organizations/policies/base-policy.component";
|
} from "src/app/organizations/policies/base-policy.component";
|
||||||
|
|
||||||
export class MaximumVaultTimeoutPolicy extends BasePolicy {
|
export class MaximumVaultTimeoutPolicy extends BasePolicy {
|
||||||
name = "maximumVaultTimeout";
|
name = "maximumVaultTimeout";
|
||||||
@@ -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",
|
||||||
@@ -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";
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
import { OrganizationPlansComponent } from "@bitwarden/web-vault-internal/app/settings/organization-plans.component";
|
import { OrganizationPlansComponent } from "src/app/settings/organization-plans.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-create-organization",
|
selector: "app-create-organization",
|
||||||
@@ -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 "@bitwarden/web-vault-internal/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",
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
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 { BulkUserDetails } from "src/app/organizations/manage/bulk/bulk-status.component";
|
||||||
import { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "@bitwarden/web-vault-internal/app/organizations/manage/bulk/bulk-confirm.component";
|
|
||||||
import { BulkUserDetails } from "@bitwarden/web-vault-internal/app/organizations/manage/bulk/bulk-status.component";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl:
|
templateUrl: "../../../../../../src/app/organizations/manage/bulk/bulk-confirm.component.html",
|
||||||
"../../../../../../web-vault-internal/src/app/organizations/manage/bulk/bulk-confirm.component.html",
|
|
||||||
})
|
})
|
||||||
export class BulkConfirmComponent extends OrganizationBulkConfirmComponent {
|
export class BulkConfirmComponent extends OrganizationBulkConfirmComponent {
|
||||||
@Input() providerId: string;
|
@Input() providerId: string;
|
||||||
@@ -2,11 +2,10 @@ import { Component, Input } from "@angular/core";
|
|||||||
|
|
||||||
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
|
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
|
||||||
|
|
||||||
import { BulkRemoveComponent as OrganizationBulkRemoveComponent } from "@bitwarden/web-vault-internal/app/organizations/manage/bulk/bulk-remove.component";
|
import { BulkRemoveComponent as OrganizationBulkRemoveComponent } from "src/app/organizations/manage/bulk/bulk-remove.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl:
|
templateUrl: "../../../../../../src/app/organizations/manage/bulk/bulk-remove.component.html",
|
||||||
"../../../../../../web-vault-internal/src/app/organizations/manage/bulk/bulk-remove.component.html",
|
|
||||||
})
|
})
|
||||||
export class BulkRemoveComponent extends OrganizationBulkRemoveComponent {
|
export class BulkRemoveComponent extends OrganizationBulkRemoveComponent {
|
||||||
@Input() providerId: string;
|
@Input() providerId: string;
|
||||||
@@ -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 "@bitwarden/web-vault-internal/app/services/event.service";
|
import { BaseEventsComponent } from "src/app/common/base.events.component";
|
||||||
|
import { EventService } from "src/app/services/event.service";
|
||||||
import { BaseEventsComponent } from "@bitwarden/web-vault-internal/app/common/base.events.component";
|
|
||||||
|
|
||||||
@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>();
|
||||||
@@ -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({
|
||||||
@@ -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>
|
||||||
@@ -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 { BulkStatusComponent } from "src/app/organizations/manage/bulk/bulk-status.component";
|
||||||
|
import { EntityEventsComponent } from "src/app/organizations/manage/entity-events.component";
|
||||||
|
|
||||||
import { BasePeopleComponent } from "@bitwarden/web-vault-internal/app/common/base.people.component";
|
|
||||||
import { BulkStatusComponent } from "@bitwarden/web-vault-internal/app/organizations/manage/bulk/bulk-status.component";
|
|
||||||
import { EntityEventsComponent } from "@bitwarden/web-vault-internal/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,
|
comp.name = this.userNamePipe.transform(user);
|
||||||
this.eventsModalRef,
|
comp.providerId = this.providerId;
|
||||||
(comp) => {
|
comp.entityId = user.id;
|
||||||
comp.name = this.userNamePipe.transform(user);
|
comp.showUser = false;
|
||||||
comp.providerId = this.providerId;
|
comp.entity = "user";
|
||||||
comp.entityId = user.id;
|
});
|
||||||
comp.showUser = false;
|
|
||||||
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,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -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>
|
||||||
@@ -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;
|
||||||
@@ -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({
|
||||||
@@ -4,6 +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 { 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";
|
||||||
@@ -11,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 "@bitwarden/web-vault-internal/app/layouts/frontend-layout.component";
|
|
||||||
|
|
||||||
import { ProvidersComponent } from "@bitwarden/web-vault-internal/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 = [
|
||||||
{
|
{
|
||||||
@@ -1,21 +1,15 @@
|
|||||||
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 { JslibModule } from "jslib-angular/jslib.module";
|
||||||
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,17 +17,18 @@ 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 "@bitwarden/web-vault-internal/app/oss.module";
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, FormsModule, OssModule, ProvidersRoutingModule],
|
imports: [CommonModule, FormsModule, OssModule, JslibModule, ProvidersRoutingModule],
|
||||||
declarations: [
|
declarations: [
|
||||||
AcceptProviderComponent,
|
AcceptProviderComponent,
|
||||||
AccountComponent,
|
AccountComponent,
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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({
|
||||||
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { BaseAcceptComponent } from "@bitwarden/web-vault-internal/app/common/base.accept.component";
|
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-setup-provider",
|
selector: "app-setup-provider",
|
||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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";
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
"buttonAction": "https://www.sandbox.paypal.com/cgi-bin/webscr"
|
"buttonAction": "https://www.sandbox.paypal.com/cgi-bin/webscr"
|
||||||
},
|
},
|
||||||
"dev": {
|
"dev": {
|
||||||
|
"port": 8080,
|
||||||
"allowedHosts": "auto"
|
"allowedHosts": "auto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,9 @@
|
|||||||
{}
|
{
|
||||||
|
"dev": {
|
||||||
|
"proxyApi": "http://localhost:4001",
|
||||||
|
"proxyIdentity": "http://localhost:33657",
|
||||||
|
"proxyEvents": "http://localhost:46274",
|
||||||
|
"proxyNotifications": "http://localhost:61841",
|
||||||
|
"port": 8081
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import { CustomWebpackBrowserSchema, TargetOptions } from "@angular-builders/custom-webpack";
|
|
||||||
import * as webpack from "webpack";
|
|
||||||
import * as CopyPlugin from "copy-webpack-plugin";
|
|
||||||
import * as HtmlWebpackPlugin from "html-webpack-plugin";
|
|
||||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
|
||||||
|
|
||||||
// We need to extend the default angular webpack to support web vault specific logic.
|
|
||||||
//
|
|
||||||
// Namely:
|
|
||||||
// * Connectors
|
|
||||||
// * Environment variables
|
|
||||||
|
|
||||||
export default (
|
|
||||||
config: webpack.Configuration,
|
|
||||||
options: CustomWebpackBrowserSchema,
|
|
||||||
targetOptions: TargetOptions
|
|
||||||
) => {
|
|
||||||
const plugins = [
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: "./projects/web-vault-internal/src/connectors/duo.html",
|
|
||||||
filename: "duo-connector.html",
|
|
||||||
chunks: ["connectors/duo"],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: "./projects/web-vault-internal/src/connectors/webauthn.html",
|
|
||||||
filename: "webauthn-connector.html",
|
|
||||||
chunks: ["connectors/webauthn"],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: "./projects/web-vault-internal/src/connectors/webauthn-mobile.html",
|
|
||||||
filename: "webauthn-mobile-connector.html",
|
|
||||||
chunks: ["connectors/webauthn"],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: "./projects/web-vault-internal/src/connectors/webauthn-fallback.html",
|
|
||||||
filename: "webauthn-fallback-connector.html",
|
|
||||||
chunks: ["connectors/webauthn-fallback"],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: "./projects/web-vault-internal/src/connectors/sso.html",
|
|
||||||
filename: "sso-connector.html",
|
|
||||||
chunks: ["connectors/sso"],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: "./projects/web-vault-internal/src/connectors/captcha.html",
|
|
||||||
filename: "captcha-connector.html",
|
|
||||||
chunks: ["connectors/captcha"],
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: "./projects/web-vault-internal/src/connectors/captcha-mobile.html",
|
|
||||||
filename: "captcha-mobile-connector.html",
|
|
||||||
chunks: ["connectors/captcha"],
|
|
||||||
}),
|
|
||||||
// TODO: Replace with angular cli copy
|
|
||||||
new CopyPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: "./projects/web-vault-internal/src/version.json",
|
|
||||||
transform(content, path) {
|
|
||||||
return content.toString().replace("process.env.APPLICATION_VERSION", "12");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
new webpack.EnvironmentPlugin({
|
|
||||||
ENV: "development",
|
|
||||||
NODE_ENV: "development",
|
|
||||||
APPLICATION_VERSION: "123",
|
|
||||||
CACHE_TAG: Math.random().toString(36).substring(7),
|
|
||||||
URLS: {},
|
|
||||||
STRIPE_KEY: "",
|
|
||||||
BRAINTREE_KEY: "",
|
|
||||||
PAYPAL_CONFIG: {},
|
|
||||||
}),
|
|
||||||
new webpack.ProvidePlugin({
|
|
||||||
process: "process/browser",
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
config.plugins?.push(...plugins);
|
|
||||||
|
|
||||||
(config.resolve as any).fallback = {
|
|
||||||
buffer: false,
|
|
||||||
util: require.resolve("util/"),
|
|
||||||
assert: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Figure out if we have to cast it to any
|
|
||||||
(config.entry as any)["connectors/webauthn"] = [
|
|
||||||
"./projects/web-vault-internal/src/connectors/webauthn.ts",
|
|
||||||
];
|
|
||||||
(config.entry as any)["connectors/webauthn-fallback"] = [
|
|
||||||
"./projects/web-vault-internal/src/connectors/webauthn-fallback.ts",
|
|
||||||
];
|
|
||||||
(config.entry as any)["connectors/duo"] = ["./projects/web-vault-internal/src/connectors/duo.ts"];
|
|
||||||
(config.entry as any)["connectors/sso"] = ["./projects/web-vault-internal/src/connectors/sso.ts"];
|
|
||||||
(config.entry as any)["connectors/captcha"] = [
|
|
||||||
"./projects/web-vault-internal/src/connectors/captcha.ts",
|
|
||||||
];
|
|
||||||
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
2
jslib
2
jslib
Submodule jslib updated: e372bf242b...3b9ef68f4b
@@ -1,41 +0,0 @@
|
|||||||
// Karma configuration file, see link for more information
|
|
||||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
basePath: "",
|
|
||||||
frameworks: ["jasmine", "@angular-devkit/build-angular"],
|
|
||||||
plugins: [
|
|
||||||
require("karma-jasmine"),
|
|
||||||
require("karma-chrome-launcher"),
|
|
||||||
require("karma-jasmine-html-reporter"),
|
|
||||||
require("karma-coverage"),
|
|
||||||
require("@angular-devkit/build-angular/plugins/karma"),
|
|
||||||
],
|
|
||||||
client: {
|
|
||||||
jasmine: {
|
|
||||||
// you can add configuration options for Jasmine here
|
|
||||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
|
||||||
// for example, you can disable the random execution with `random: false`
|
|
||||||
// or set a specific seed with `seed: 4321`
|
|
||||||
},
|
|
||||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
|
||||||
},
|
|
||||||
jasmineHtmlReporter: {
|
|
||||||
suppressAll: true, // removes the duplicated traces
|
|
||||||
},
|
|
||||||
coverageReporter: {
|
|
||||||
dir: require("path").join(__dirname, "./coverage/lawl"),
|
|
||||||
subdir: ".",
|
|
||||||
reporters: [{ type: "html" }, { type: "text-summary" }],
|
|
||||||
},
|
|
||||||
reporters: ["progress", "kjhtml"],
|
|
||||||
port: 9876,
|
|
||||||
colors: true,
|
|
||||||
logLevel: config.LOG_INFO,
|
|
||||||
autoWatch: true,
|
|
||||||
browsers: ["Chrome"],
|
|
||||||
singleRun: false,
|
|
||||||
restartOnFileChange: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
26674
package-lock.json
generated
26674
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
177
package.json
177
package.json
@@ -1,83 +1,118 @@
|
|||||||
{
|
{
|
||||||
"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": {
|
||||||
"ng": "ng",
|
"sub:init": "git submodule update --init --recursive",
|
||||||
"start": "ng serve",
|
"sub:update": "git submodule update --remote",
|
||||||
"build": "ng build",
|
"sub:pull": "git submodule foreach git pull origin master",
|
||||||
"watch": "ng build --watch --configuration development",
|
"preinstall": "npm run sub:init",
|
||||||
"test": "ng test"
|
"symlink:win": "rm -rf ./jslib && cmd /c mklink /J .\\jslib ..\\jslib",
|
||||||
},
|
"symlink:mac": "npm run symlink:lin",
|
||||||
"private": true,
|
"symlink:lin": "rm -rf ./jslib && ln -s ../jslib ./jslib",
|
||||||
"lint-staged": {
|
"build:oss": "webpack",
|
||||||
"*": "prettier --ignore-unknown --write",
|
"build:bit": "webpack -c bitwarden_license/webpack.config.js",
|
||||||
"*.png": "node scripts/optimize.js"
|
"build:oss:watch": "webpack serve",
|
||||||
},
|
"build:bit:watch": "webpack serve -c bitwarden_license/webpack.config.js",
|
||||||
"dependencies": {
|
"build:bit:dev": "cross-env ENV=development npm run build:bit",
|
||||||
"@angular/animations": "~12.2.0",
|
"build:bit:dev:watch": "cross-env ENV=development npm run build:bit:watch",
|
||||||
"@angular/cdk": "~12.2.0",
|
"build:bit:qa": "cross-env NODE_ENV=production ENV=qa npm run build:bit",
|
||||||
"@angular/common": "~12.2.0",
|
"build:bit:cloud": "cross-env NODE_ENV=production ENV=cloud npm run build:bit",
|
||||||
"@angular/compiler": "~12.2.0",
|
"build:oss:selfhost:watch": "cross-env ENV=selfhosted npm run build:oss:watch",
|
||||||
"@angular/core": "~12.2.0",
|
"build:bit:selfhost:watch": "cross-env ENV=selfhosted npm run build:bit:watch",
|
||||||
"@angular/forms": "~12.2.0",
|
"build:oss:selfhost:prod": "cross-env ENV=selfhosted NODE_ENV=production npm run build:oss",
|
||||||
"@angular/platform-browser": "~12.2.0",
|
"build:bit:selfhost:prod": "cross-env ENV=selfhosted NODE_ENV=production npm run build:bit",
|
||||||
"@angular/platform-browser-dynamic": "~12.2.0",
|
"clean:l10n": "git push origin --delete l10n_master",
|
||||||
"@angular/router": "~12.2.0",
|
"dist:bit:cloud": "npm run build:bit:cloud",
|
||||||
"@bitwarden/jslib-angular": "file:jslib/angular",
|
"dist:oss:selfhost": "npm run build:oss:selfhost:prod",
|
||||||
"@bitwarden/jslib-common": "file:jslib/common",
|
"dist:bit:selfhost": "npm run build:bit:selfhost:prod",
|
||||||
"@microsoft/signalr": "5.0.10",
|
"deploy": "npm run dist:bit && gh-pages -d build",
|
||||||
"@microsoft/signalr-protocol-msgpack": "5.0.10",
|
"deploy:dev": "npm run dist:bit && gh-pages -d build -r git@github.com:kspearrin/bitwarden-web-dev.git",
|
||||||
"assert": "^2.0.0",
|
"lint": "eslint . && prettier --check .",
|
||||||
"big-integer": "1.6.48",
|
"lint:fix": "eslint . --fix",
|
||||||
"bootstrap": "4.6.0",
|
"prettier": "prettier --write .",
|
||||||
"braintree-web-drop-in": "1.30.1",
|
"prepare": "husky install"
|
||||||
"browser-hrtime": "^1.1.8",
|
|
||||||
"buffer": "^6.0.3",
|
|
||||||
"date-input-polyfill": "^2.14.0",
|
|
||||||
"duo_web_sdk": "github:duosecurity/duo_web_sdk",
|
|
||||||
"font-awesome": "4.7.0",
|
|
||||||
"jquery": "3.6.0",
|
|
||||||
"lunr": "^2.3.9",
|
|
||||||
"ngx-infinite-scroll": "^10.0.1",
|
|
||||||
"ngx-toastr": "14.1.4",
|
|
||||||
"node-forge": "^0.10.0",
|
|
||||||
"papaparse": "^5.3.1",
|
|
||||||
"process": "^0.11.10",
|
|
||||||
"qrious": "4.0.2",
|
|
||||||
"rxjs": "^7.4.0",
|
|
||||||
"sweetalert2": "^10.16.9",
|
|
||||||
"tslib": "^2.3.0",
|
|
||||||
"util": "^0.12.4",
|
|
||||||
"zone.js": "~0.11.4",
|
|
||||||
"zxcvbn": "^4.4.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "^12.1.3",
|
"@angular/compiler-cli": "^12.2.13",
|
||||||
"@angular-devkit/build-angular": "~12.2.15",
|
"@ngtools/webpack": "^12.2.13",
|
||||||
"@angular/cli": "~12.2.15",
|
"@types/jquery": "^3.5.5",
|
||||||
"@angular/compiler-cli": "~12.2.0",
|
"@types/node": "^16.11.12",
|
||||||
"@types/duo_web_sdk": "^2.7.1",
|
"@types/webcrypto": "^0.0.28",
|
||||||
"@types/jasmine": "~3.8.0",
|
"@types/webpack": "^5.28.0",
|
||||||
"@types/jquery": "^3.5.13",
|
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||||
"@types/lunr": "^2.3.4",
|
"@typescript-eslint/parser": "^5.10.1",
|
||||||
"@types/node": "^16.11.21",
|
"autoprefixer": "^10.4.2",
|
||||||
"@types/node-forge": "^0.9.7",
|
"buffer": "^6.0.3",
|
||||||
"@types/papaparse": "^5.3.1",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"@types/zxcvbn": "^4.4.1",
|
"copy-webpack-plugin": "^10.0.0",
|
||||||
"copy-webpack-plugin": "^10.2.1",
|
"cross-env": "^7.0.3",
|
||||||
|
"css-loader": "^6.5.1",
|
||||||
|
"eslint": "^8.7.0",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-import-resolver-typescript": "^2.5.0",
|
||||||
|
"eslint-plugin-import": "^2.25.4",
|
||||||
|
"gh-pages": "^3.1.0",
|
||||||
|
"html-loader": "^3.0.1",
|
||||||
|
"html-webpack-injector": "1.1.4",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"jasmine-core": "~3.8.0",
|
|
||||||
"karma": "~6.3.0",
|
|
||||||
"karma-chrome-launcher": "~3.1.0",
|
|
||||||
"karma-coverage": "~2.0.3",
|
|
||||||
"karma-jasmine": "~4.0.0",
|
|
||||||
"karma-jasmine-html-reporter": "~1.7.0",
|
|
||||||
"lint-staged": "^12.1.2",
|
"lint-staged": "^12.1.2",
|
||||||
"ng-packagr": "^12.1.1",
|
"mini-css-extract-plugin": "^2.4.5",
|
||||||
"prettier": "^2.5.1",
|
"postcss": "^8.4.6",
|
||||||
"typescript": "~4.3.5"
|
"postcss-loader": "^6.2.1",
|
||||||
|
"prettier": "2.5.1",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"sass": "^1.32.10",
|
||||||
|
"sass-loader": "^12.4.0",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"tailwindcss": "^3.0.18",
|
||||||
|
"terser-webpack-plugin": "^5.2.5",
|
||||||
|
"ts-loader": "^9.2.5",
|
||||||
|
"typescript": "4.3.5",
|
||||||
|
"util": "^0.12.4",
|
||||||
|
"webpack": "^5.64.4",
|
||||||
|
"webpack-cli": "^4.9.1",
|
||||||
|
"webpack-dev-server": "^4.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "^12.2.13",
|
||||||
|
"@angular/cdk": "^12.2.13",
|
||||||
|
"@angular/common": "^12.2.13",
|
||||||
|
"@angular/compiler": "^12.2.13",
|
||||||
|
"@angular/core": "^12.2.13",
|
||||||
|
"@angular/forms": "^12.2.13",
|
||||||
|
"@angular/platform-browser": "^12.2.13",
|
||||||
|
"@angular/platform-browser-dynamic": "^12.2.13",
|
||||||
|
"@angular/router": "^12.2.13",
|
||||||
|
"@bitwarden/jslib-angular": "file:jslib/angular",
|
||||||
|
"@bitwarden/jslib-common": "file:jslib/common",
|
||||||
|
"bootstrap": "4.6.0",
|
||||||
|
"braintree-web-drop-in": "1.33.1",
|
||||||
|
"browser-hrtime": "^1.1.8",
|
||||||
|
"core-js": "^3.11.0",
|
||||||
|
"date-input-polyfill": "^2.14.0",
|
||||||
|
"jquery": "3.6.0",
|
||||||
|
"jszip": "^3.7.1",
|
||||||
|
"ngx-infinite-scroll": "^10.0.1",
|
||||||
|
"ngx-toastr": "14.1.4",
|
||||||
|
"popper.js": "1.16.1",
|
||||||
|
"qrious": "4.0.2",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"sweetalert2": "^10.16.6",
|
||||||
|
"webcrypto-shim": "0.1.7",
|
||||||
|
"whatwg-fetch": "3.6.2",
|
||||||
|
"zone.js": "0.11.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "~16",
|
||||||
|
"npm": "~8"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"./!(jslib)**": "prettier --ignore-unknown --write",
|
||||||
|
"*.ts": "eslint --fix",
|
||||||
|
"*.png": "node scripts/optimize.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
postcss.config.js
Normal file
4
postcss.config.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/* eslint-disable no-undef */
|
||||||
|
module.exports = {
|
||||||
|
plugins: [require("tailwindcss"), require("autoprefixer"), require("postcss-nested")],
|
||||||
|
};
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
|
||||||
# For additional information regarding the format and rule options, please see:
|
|
||||||
# https://github.com/browserslist/browserslist#queries
|
|
||||||
|
|
||||||
# For the full list of supported browsers by the Angular framework, please see:
|
|
||||||
# https://angular.io/guide/browser-support
|
|
||||||
|
|
||||||
# You can see what browsers were selected by your queries by running:
|
|
||||||
# npx browserslist
|
|
||||||
|
|
||||||
last 1 Chrome version
|
|
||||||
last 1 Firefox version
|
|
||||||
last 2 Edge major versions
|
|
||||||
last 2 Safari major versions
|
|
||||||
last 2 iOS major versions
|
|
||||||
Firefox ESR
|
|
||||||
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// Karma configuration file, see link for more information
|
|
||||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
basePath: "",
|
|
||||||
frameworks: ["jasmine", "@angular-devkit/build-angular"],
|
|
||||||
plugins: [
|
|
||||||
require("karma-jasmine"),
|
|
||||||
require("karma-chrome-launcher"),
|
|
||||||
require("karma-jasmine-html-reporter"),
|
|
||||||
require("karma-coverage"),
|
|
||||||
require("@angular-devkit/build-angular/plugins/karma"),
|
|
||||||
],
|
|
||||||
client: {
|
|
||||||
jasmine: {
|
|
||||||
// you can add configuration options for Jasmine here
|
|
||||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
|
||||||
// for example, you can disable the random execution with `random: false`
|
|
||||||
// or set a specific seed with `seed: 4321`
|
|
||||||
},
|
|
||||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
|
||||||
},
|
|
||||||
jasmineHtmlReporter: {
|
|
||||||
suppressAll: true, // removes the duplicated traces
|
|
||||||
},
|
|
||||||
coverageReporter: {
|
|
||||||
dir: require("path").join(__dirname, "../../coverage/oss-vault"),
|
|
||||||
subdir: ".",
|
|
||||||
reporters: [{ type: "html" }, { type: "text-summary" }],
|
|
||||||
},
|
|
||||||
reporters: ["progress", "kjhtml"],
|
|
||||||
port: 9876,
|
|
||||||
colors: true,
|
|
||||||
logLevel: config.LOG_INFO,
|
|
||||||
autoWatch: true,
|
|
||||||
browsers: ["Chrome"],
|
|
||||||
singleRun: false,
|
|
||||||
restartOnFileChange: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { NgModule } from "@angular/core";
|
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
|
||||||
|
|
||||||
const routes: Routes = [];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [RouterModule.forRoot(routes)],
|
|
||||||
exports: [RouterModule],
|
|
||||||
})
|
|
||||||
export class AppRoutingModule {}
|
|
||||||
@@ -1,839 +0,0 @@
|
|||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * Delete the template below * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * to get started with your project! * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
|
|
||||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
box-sizing: border-box;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacer {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 60px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #1976d2;
|
|
||||||
color: white;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar img {
|
|
||||||
margin: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar #twitter-logo {
|
|
||||||
height: 40px;
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar #youtube-logo {
|
|
||||||
height: 40px;
|
|
||||||
margin: 0 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar #twitter-logo:hover,
|
|
||||||
.toolbar #youtube-logo:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
margin: 82px auto 32px;
|
|
||||||
padding: 0 16px;
|
|
||||||
max-width: 960px;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg.material-icons {
|
|
||||||
height: 24px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg.material-icons:not(:last-child) {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card svg.material-icons path {
|
|
||||||
fill: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
all: unset;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
background-color: #fafafa;
|
|
||||||
height: 40px;
|
|
||||||
width: 200px;
|
|
||||||
margin: 0 8px 16px;
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(:last-child) {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.card-small {
|
|
||||||
height: 16px;
|
|
||||||
width: 168px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card):hover {
|
|
||||||
transform: translateY(-3px);
|
|
||||||
box-shadow: 0 4px 17px rgba(0, 0, 0, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card):hover .material-icons path {
|
|
||||||
fill: rgb(105, 103, 103);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.highlight-card {
|
|
||||||
background-color: #1976d2;
|
|
||||||
color: white;
|
|
||||||
font-weight: 600;
|
|
||||||
border: none;
|
|
||||||
width: auto;
|
|
||||||
min-width: 30%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.card.highlight-card span {
|
|
||||||
margin-left: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg#rocket {
|
|
||||||
width: 80px;
|
|
||||||
position: absolute;
|
|
||||||
left: -10px;
|
|
||||||
top: -24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg#rocket-smoke {
|
|
||||||
height: calc(100vh - 95px);
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 180px;
|
|
||||||
z-index: -10;
|
|
||||||
}
|
|
||||||
|
|
||||||
a,
|
|
||||||
a:visited,
|
|
||||||
a:hover {
|
|
||||||
color: #1976d2;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #125699;
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminal {
|
|
||||||
position: relative;
|
|
||||||
width: 80%;
|
|
||||||
max-width: 600px;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding-top: 45px;
|
|
||||||
margin-top: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: rgb(15, 15, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminal::before {
|
|
||||||
content: "\2022 \2022 \2022";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 4px;
|
|
||||||
background: rgb(58, 58, 58);
|
|
||||||
color: #c2c3c4;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 2rem;
|
|
||||||
line-height: 0;
|
|
||||||
padding: 14px 0;
|
|
||||||
text-indent: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminal pre {
|
|
||||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
|
||||||
color: white;
|
|
||||||
padding: 0 1rem 1rem;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-link {
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
border-radius: 40px;
|
|
||||||
margin: 8px;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #eeeeee;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
|
||||||
transition: 1s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.circle-link:hover {
|
|
||||||
transform: translateY(-0.25rem);
|
|
||||||
box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
margin-top: 8px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-badge {
|
|
||||||
color: #24292e;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 3px 10px;
|
|
||||||
border: 1px solid rgba(27, 31, 35, 0.2);
|
|
||||||
border-radius: 3px;
|
|
||||||
background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
|
|
||||||
margin-left: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-badge:hover {
|
|
||||||
background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
|
|
||||||
border-color: rgba(27, 31, 35, 0.35);
|
|
||||||
background-position: -0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-badge .material-icons {
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg#clouds {
|
|
||||||
position: fixed;
|
|
||||||
bottom: -160px;
|
|
||||||
left: -230px;
|
|
||||||
z-index: -10;
|
|
||||||
width: 1920px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive Styles */
|
|
||||||
@media screen and (max-width: 767px) {
|
|
||||||
.card-container > *:not(.circle-link),
|
|
||||||
.terminal {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:not(.highlight-card) {
|
|
||||||
height: 16px;
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.highlight-card span {
|
|
||||||
margin-left: 72px;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg#rocket-smoke {
|
|
||||||
right: 120px;
|
|
||||||
transform: rotate(-5deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 575px) {
|
|
||||||
svg#rocket-smoke {
|
|
||||||
display: none;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Toolbar -->
|
|
||||||
<div class="toolbar" role="banner">
|
|
||||||
<img
|
|
||||||
width="40"
|
|
||||||
alt="Angular Logo"
|
|
||||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="
|
|
||||||
/>
|
|
||||||
<span>Welcome</span>
|
|
||||||
<div class="spacer"></div>
|
|
||||||
<a
|
|
||||||
aria-label="Angular on twitter"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
href="https://twitter.com/angular"
|
|
||||||
title="Twitter"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
id="twitter-logo"
|
|
||||||
height="24"
|
|
||||||
data-name="Logo"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 400 400"
|
|
||||||
>
|
|
||||||
<rect width="400" height="400" fill="none" />
|
|
||||||
<path
|
|
||||||
d="M153.62,301.59c94.34,0,145.94-78.16,145.94-145.94,0-2.22,0-4.43-.15-6.63A104.36,104.36,0,0,0,325,122.47a102.38,102.38,0,0,1-29.46,8.07,51.47,51.47,0,0,0,22.55-28.37,102.79,102.79,0,0,1-32.57,12.45,51.34,51.34,0,0,0-87.41,46.78A145.62,145.62,0,0,1,92.4,107.81a51.33,51.33,0,0,0,15.88,68.47A50.91,50.91,0,0,1,85,169.86c0,.21,0,.43,0,.65a51.31,51.31,0,0,0,41.15,50.28,51.21,51.21,0,0,1-23.16.88,51.35,51.35,0,0,0,47.92,35.62,102.92,102.92,0,0,1-63.7,22A104.41,104.41,0,0,1,75,278.55a145.21,145.21,0,0,0,78.62,23"
|
|
||||||
fill="#fff"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
aria-label="Angular on YouTube"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
href="https://youtube.com/angular"
|
|
||||||
title="YouTube"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
id="youtube-logo"
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
data-name="Logo"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="#fff"
|
|
||||||
>
|
|
||||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
||||||
<path
|
|
||||||
d="M21.58 7.19c-.23-.86-.91-1.54-1.77-1.77C18.25 5 12 5 12 5s-6.25 0-7.81.42c-.86.23-1.54.91-1.77 1.77C2 8.75 2 12 2 12s0 3.25.42 4.81c.23.86.91 1.54 1.77 1.77C5.75 19 12 19 12 19s6.25 0 7.81-.42c.86-.23 1.54-.91 1.77-1.77C22 15.25 22 12 22 12s0-3.25-.42-4.81zM10 15V9l5.2 3-5.2 3z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content" role="main">
|
|
||||||
<!-- Highlight Card -->
|
|
||||||
<div class="card highlight-card card-small">
|
|
||||||
<svg
|
|
||||||
id="rocket"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="101.678"
|
|
||||||
height="101.678"
|
|
||||||
viewBox="0 0 101.678 101.678"
|
|
||||||
>
|
|
||||||
<title>Rocket Ship</title>
|
|
||||||
<g id="Group_83" data-name="Group 83" transform="translate(-141 -696)">
|
|
||||||
<circle
|
|
||||||
id="Ellipse_8"
|
|
||||||
data-name="Ellipse 8"
|
|
||||||
cx="50.839"
|
|
||||||
cy="50.839"
|
|
||||||
r="50.839"
|
|
||||||
transform="translate(141 696)"
|
|
||||||
fill="#dd0031"
|
|
||||||
/>
|
|
||||||
<g id="Group_47" data-name="Group 47" transform="translate(165.185 720.185)">
|
|
||||||
<path
|
|
||||||
id="Path_33"
|
|
||||||
data-name="Path 33"
|
|
||||||
d="M3.4,42.615a3.084,3.084,0,0,0,3.553,3.553,21.419,21.419,0,0,0,12.215-6.107L9.511,30.4A21.419,21.419,0,0,0,3.4,42.615Z"
|
|
||||||
transform="translate(0.371 3.363)"
|
|
||||||
fill="#fff"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
id="Path_34"
|
|
||||||
data-name="Path 34"
|
|
||||||
d="M53.3,3.221A3.09,3.09,0,0,0,50.081,0,48.227,48.227,0,0,0,18.322,13.437c-6-1.666-14.991-1.221-18.322,7.218A33.892,33.892,0,0,1,9.439,25.1l-.333.666a3.013,3.013,0,0,0,.555,3.553L23.985,43.641a2.9,2.9,0,0,0,3.553.555l.666-.333A33.892,33.892,0,0,1,32.647,53.3c8.55-3.664,8.884-12.326,7.218-18.322A48.227,48.227,0,0,0,53.3,3.221ZM34.424,9.772a6.439,6.439,0,1,1,9.106,9.106,6.368,6.368,0,0,1-9.106,0A6.467,6.467,0,0,1,34.424,9.772Z"
|
|
||||||
transform="translate(0 0.005)"
|
|
||||||
fill="#fff"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<span>{{ title }} app is running!</span>
|
|
||||||
|
|
||||||
<svg
|
|
||||||
id="rocket-smoke"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="516.119"
|
|
||||||
height="1083.632"
|
|
||||||
viewBox="0 0 516.119 1083.632"
|
|
||||||
>
|
|
||||||
<title>Rocket Ship Smoke</title>
|
|
||||||
<path
|
|
||||||
id="Path_40"
|
|
||||||
data-name="Path 40"
|
|
||||||
d="M644.6,141S143.02,215.537,147.049,870.207s342.774,201.755,342.774,201.755S404.659,847.213,388.815,762.2c-27.116-145.51-11.551-384.124,271.9-609.1C671.15,139.365,644.6,141,644.6,141Z"
|
|
||||||
transform="translate(-147.025 -140.939)"
|
|
||||||
fill="#f5f5f5"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Resources -->
|
|
||||||
<h2>Resources</h2>
|
|
||||||
<p>Here are some links to help you get started:</p>
|
|
||||||
|
|
||||||
<div class="card-container">
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://angular.io/tutorial">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z" />
|
|
||||||
</svg>
|
|
||||||
<span>Learn Angular</span>
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://angular.io/cli">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>CLI Documentation</span>
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://blog.angular.io/">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>Angular Blog</span>
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="card" target="_blank" rel="noopener" href="https://angular.io/devtools/">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
enable-background="new 0 0 24 24"
|
|
||||||
height="24px"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="24px"
|
|
||||||
fill="#000000"
|
|
||||||
>
|
|
||||||
<g><rect fill="none" height="24" width="24" /></g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path
|
|
||||||
d="M14.73,13.31C15.52,12.24,16,10.93,16,9.5C16,5.91,13.09,3,9.5,3S3,5.91,3,9.5C3,13.09,5.91,16,9.5,16 c1.43,0,2.74-0.48,3.81-1.27L19.59,21L21,19.59L14.73,13.31z M9.5,14C7.01,14,5,11.99,5,9.5S7.01,5,9.5,5S14,7.01,14,9.5 S11.99,14,9.5,14z"
|
|
||||||
/>
|
|
||||||
<polygon
|
|
||||||
points="10.29,8.44 9.5,6 8.71,8.44 6.25,8.44 8.26,10.03 7.49,12.5 9.5,10.97 11.51,12.5 10.74,10.03 12.75,8.44"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<span>Angular DevTools</span>
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Next Steps -->
|
|
||||||
<h2>Next Steps</h2>
|
|
||||||
<p>What do you want to do next with your app?</p>
|
|
||||||
|
|
||||||
<input type="hidden" #selection />
|
|
||||||
|
|
||||||
<div class="card-container">
|
|
||||||
<button class="card card-small" (click)="selection.value = 'component'" tabindex="0">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
|
||||||
</svg>
|
|
||||||
<span>New Component</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'material'" tabindex="0">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
|
||||||
</svg>
|
|
||||||
<span>Angular Material</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'pwa'" tabindex="0">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
|
||||||
</svg>
|
|
||||||
<span>Add PWA Support</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'dependency'" tabindex="0">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
|
||||||
</svg>
|
|
||||||
<span>Add Dependency</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'test'" tabindex="0">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
|
||||||
</svg>
|
|
||||||
<span>Run and Watch Tests</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="card card-small" (click)="selection.value = 'build'" tabindex="0">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
|
||||||
</svg>
|
|
||||||
<span>Build for Production</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Terminal -->
|
|
||||||
<div class="terminal" [ngSwitch]="selection.value">
|
|
||||||
<pre *ngSwitchDefault>ng generate component xyz</pre>
|
|
||||||
<pre *ngSwitchCase="'material'">ng add @angular/material</pre>
|
|
||||||
<pre *ngSwitchCase="'pwa'">ng add @angular/pwa</pre>
|
|
||||||
<pre *ngSwitchCase="'dependency'">ng add _____</pre>
|
|
||||||
<pre *ngSwitchCase="'test'">ng test</pre>
|
|
||||||
<pre *ngSwitchCase="'build'">ng build</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Links -->
|
|
||||||
<div class="card-container">
|
|
||||||
<a
|
|
||||||
class="circle-link"
|
|
||||||
title="Animations"
|
|
||||||
href="https://angular.io/guide/animations"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
id="Group_20"
|
|
||||||
data-name="Group 20"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="21.813"
|
|
||||||
height="23.453"
|
|
||||||
viewBox="0 0 21.813 23.453"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
id="Path_15"
|
|
||||||
data-name="Path 15"
|
|
||||||
d="M4099.584,972.736h0l-10.882,3.9,1.637,14.4,9.245,5.153,9.245-5.153,1.686-14.4Z"
|
|
||||||
transform="translate(-4088.702 -972.736)"
|
|
||||||
fill="#ffa726"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
id="Path_16"
|
|
||||||
data-name="Path 16"
|
|
||||||
d="M4181.516,972.736v23.453l9.245-5.153,1.686-14.4Z"
|
|
||||||
transform="translate(-4170.633 -972.736)"
|
|
||||||
fill="#fb8c00"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
id="Path_17"
|
|
||||||
data-name="Path 17"
|
|
||||||
d="M4137.529,1076.127l-7.7-3.723,4.417-2.721,7.753,3.723Z"
|
|
||||||
transform="translate(-4125.003 -1058.315)"
|
|
||||||
fill="#ffe0b2"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
id="Path_18"
|
|
||||||
data-name="Path 18"
|
|
||||||
d="M4137.529,1051.705l-7.7-3.723,4.417-2.721,7.753,3.723Z"
|
|
||||||
transform="translate(-4125.003 -1036.757)"
|
|
||||||
fill="#fff3e0"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
id="Path_19"
|
|
||||||
data-name="Path 19"
|
|
||||||
d="M4137.529,1027.283l-7.7-3.723,4.417-2.721,7.753,3.723Z"
|
|
||||||
transform="translate(-4125.003 -1015.199)"
|
|
||||||
fill="#fff"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
class="circle-link"
|
|
||||||
title="CLI"
|
|
||||||
href="https://cli.angular.io/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="21.762"
|
|
||||||
height="23.447"
|
|
||||||
viewBox="0 0 21.762 23.447"
|
|
||||||
>
|
|
||||||
<title>Angular CLI Logo</title>
|
|
||||||
<g id="Group_21" data-name="Group 21" transform="translate(0)">
|
|
||||||
<path
|
|
||||||
id="Path_20"
|
|
||||||
data-name="Path 20"
|
|
||||||
d="M2660.313,313.618h0l-10.833,3.9,1.637,14.4,9.2,5.152,9.244-5.152,1.685-14.4Z"
|
|
||||||
transform="translate(-2649.48 -313.618)"
|
|
||||||
fill="#37474f"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
id="Path_21"
|
|
||||||
data-name="Path 21"
|
|
||||||
d="M2741.883,313.618v23.447l9.244-5.152,1.685-14.4Z"
|
|
||||||
transform="translate(-2731.05 -313.618)"
|
|
||||||
fill="#263238"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
id="Path_22"
|
|
||||||
data-name="Path 22"
|
|
||||||
d="M2692.293,379.169h11.724V368.618h-11.724Zm11.159-.6h-10.608v-9.345h10.621v9.345Z"
|
|
||||||
transform="translate(-2687.274 -362.17)"
|
|
||||||
fill="#fff"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
id="Path_23"
|
|
||||||
data-name="Path 23"
|
|
||||||
d="M2709.331,393.688l.4.416,2.265-2.28-2.294-2.294-.4.4,1.893,1.893Z"
|
|
||||||
transform="translate(-2702.289 -380.631)"
|
|
||||||
fill="#fff"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
id="Rectangle_12"
|
|
||||||
data-name="Rectangle 12"
|
|
||||||
width="3.517"
|
|
||||||
height="0.469"
|
|
||||||
transform="translate(9.709 13.744)"
|
|
||||||
fill="#fff"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
class="circle-link"
|
|
||||||
title="Find a Local Meetup"
|
|
||||||
href="https://www.meetup.com/find/?keywords=angular"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24.607"
|
|
||||||
height="23.447"
|
|
||||||
viewBox="0 0 24.607 23.447"
|
|
||||||
>
|
|
||||||
<title>Meetup Logo</title>
|
|
||||||
<path
|
|
||||||
id="logo--mSwarm"
|
|
||||||
d="M21.221,14.95A4.393,4.393,0,0,1,17.6,19.281a4.452,4.452,0,0,1-.8.069c-.09,0-.125.035-.154.117a2.939,2.939,0,0,1-2.506,2.091,2.868,2.868,0,0,1-2.248-.624.168.168,0,0,0-.245-.005,3.926,3.926,0,0,1-2.589.741,4.015,4.015,0,0,1-3.7-3.347,2.7,2.7,0,0,1-.043-.38c0-.106-.042-.146-.143-.166a3.524,3.524,0,0,1-1.516-.69A3.623,3.623,0,0,1,2.23,14.557a3.66,3.66,0,0,1,1.077-3.085.138.138,0,0,0,.026-.2,3.348,3.348,0,0,1-.451-1.821,3.46,3.46,0,0,1,2.749-3.28.44.44,0,0,0,.355-.281,5.072,5.072,0,0,1,3.863-3,5.028,5.028,0,0,1,3.555.666.31.31,0,0,0,.271.03A4.5,4.5,0,0,1,18.3,4.7a4.4,4.4,0,0,1,1.334,2.751,3.658,3.658,0,0,1,.022.706.131.131,0,0,0,.1.157,2.432,2.432,0,0,1,1.574,1.645,2.464,2.464,0,0,1-.7,2.616c-.065.064-.051.1-.014.166A4.321,4.321,0,0,1,21.221,14.95ZM13.4,14.607a2.09,2.09,0,0,0,1.409,1.982,4.7,4.7,0,0,0,1.275.221,1.807,1.807,0,0,0,.9-.151.542.542,0,0,0,.321-.545.558.558,0,0,0-.359-.534,1.2,1.2,0,0,0-.254-.078c-.262-.047-.526-.086-.787-.138a.674.674,0,0,1-.617-.75,3.394,3.394,0,0,1,.218-1.109c.217-.658.509-1.286.79-1.918a15.609,15.609,0,0,0,.745-1.86,1.95,1.95,0,0,0,.06-1.073,1.286,1.286,0,0,0-1.051-1.033,1.977,1.977,0,0,0-1.521.2.339.339,0,0,1-.446-.042c-.1-.092-.2-.189-.307-.284a1.214,1.214,0,0,0-1.643-.061,7.563,7.563,0,0,1-.614.512A.588.588,0,0,1,10.883,8c-.215-.115-.437-.215-.659-.316a2.153,2.153,0,0,0-.695-.248A2.091,2.091,0,0,0,7.541,8.562a9.915,9.915,0,0,0-.405.986c-.559,1.545-1.015,3.123-1.487,4.7a1.528,1.528,0,0,0,.634,1.777,1.755,1.755,0,0,0,1.5.211,1.35,1.35,0,0,0,.824-.858c.543-1.281,1.032-2.584,1.55-3.875.142-.355.28-.712.432-1.064a.548.548,0,0,1,.851-.24.622.622,0,0,1,.185.539,2.161,2.161,0,0,1-.181.621c-.337.852-.68,1.7-1.018,2.552a2.564,2.564,0,0,0-.173.528.624.624,0,0,0,.333.71,1.073,1.073,0,0,0,.814.034,1.22,1.22,0,0,0,.657-.655q.758-1.488,1.511-2.978.35-.687.709-1.37a1.073,1.073,0,0,1,.357-.434.43.43,0,0,1,.463-.016.373.373,0,0,1,.153.387.7.7,0,0,1-.057.236c-.065.157-.127.316-.2.469-.42.883-.846,1.763-1.262,2.648A2.463,2.463,0,0,0,13.4,14.607Zm5.888,6.508a1.09,1.09,0,0,0-2.179.006,1.09,1.09,0,0,0,2.179-.006ZM1.028,12.139a1.038,1.038,0,1,0,.01-2.075,1.038,1.038,0,0,0-.01,2.075ZM13.782.528a1.027,1.027,0,1,0-.011,2.055A1.027,1.027,0,0,0,13.782.528ZM22.21,6.95a.882.882,0,0,0-1.763.011A.882.882,0,0,0,22.21,6.95ZM4.153,4.439a.785.785,0,1,0,.787-.78A.766.766,0,0,0,4.153,4.439Zm8.221,18.22a.676.676,0,1,0-.677.666A.671.671,0,0,0,12.374,22.658ZM22.872,12.2a.674.674,0,0,0-.665.665.656.656,0,0,0,.655.643.634.634,0,0,0,.655-.644A.654.654,0,0,0,22.872,12.2ZM7.171-.123A.546.546,0,0,0,6.613.43a.553.553,0,1,0,1.106,0A.539.539,0,0,0,7.171-.123ZM24.119,9.234a.507.507,0,0,0-.493.488.494.494,0,0,0,.494.494.48.48,0,0,0,.487-.483A.491.491,0,0,0,24.119,9.234Zm-19.454,9.7a.5.5,0,0,0-.488-.488.491.491,0,0,0-.487.5.483.483,0,0,0,.491.479A.49.49,0,0,0,4.665,18.936Z"
|
|
||||||
transform="translate(0 0.123)"
|
|
||||||
fill="#f64060"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
class="circle-link"
|
|
||||||
title="Join the Conversation on Discord"
|
|
||||||
href="https://discord.gg/angular"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 245 240">
|
|
||||||
<title>Discord Logo</title>
|
|
||||||
<path
|
|
||||||
d="M104.4 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1.1-6.1-4.5-11.1-10.2-11.1zM140.9 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1s-4.5-11.1-10.2-11.1z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M189.5 20h-134C44.2 20 35 29.2 35 40.6v135.2c0 11.4 9.2 20.6 20.5 20.6h113.4l-5.3-18.5 12.8 11.9 12.1 11.2 21.5 19V40.6c0-11.4-9.2-20.6-20.5-20.6zm-38.6 130.6s-3.6-4.3-6.6-8.1c13.1-3.7 18.1-11.9 18.1-11.9-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.5-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.7-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.3-1.8-1-2.8-1.7-2.8-1.7s4.8 8 17.5 11.8c-3 3.8-6.7 8.3-6.7 8.3-22.1-.7-30.5-15.2-30.5-15.2 0-32.2 14.4-58.3 14.4-58.3 14.4-10.8 28.1-10.5 28.1-10.5l1 1.2c-18 5.2-26.3 13.1-26.3 13.1s2.2-1.2 5.9-2.9c10.7-4.7 19.2-6 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.6 0 0-7.9-7.5-24.9-12.7l1.4-1.6s13.7-.3 28.1 10.5c0 0 14.4 26.1 14.4 58.3 0 0-8.5 14.5-30.6 15.2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer>
|
|
||||||
Love Angular?
|
|
||||||
<a href="https://github.com/angular/angular" target="_blank" rel="noopener">
|
|
||||||
Give our repo a star.
|
|
||||||
<div class="github-star-badge">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path
|
|
||||||
d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Star
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/angular/angular" target="_blank" rel="noopener">
|
|
||||||
<svg
|
|
||||||
class="material-icons"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" fill="#1976d2" />
|
|
||||||
<path d="M0 0h24v24H0z" fill="none" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<svg
|
|
||||||
id="clouds"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="2611.084"
|
|
||||||
height="485.677"
|
|
||||||
viewBox="0 0 2611.084 485.677"
|
|
||||||
>
|
|
||||||
<title>Gray Clouds Background</title>
|
|
||||||
<path
|
|
||||||
id="Path_39"
|
|
||||||
data-name="Path 39"
|
|
||||||
d="M2379.709,863.793c10-93-77-171-168-149-52-114-225-105-264,15-75,3-140,59-152,133-30,2.83-66.725,9.829-93.5,26.25-26.771-16.421-63.5-23.42-93.5-26.25-12-74-77-130-152-133-39-120-212-129-264-15-54.084-13.075-106.753,9.173-138.488,48.9-31.734-39.726-84.4-61.974-138.487-48.9-52-114-225-105-264,15a162.027,162.027,0,0,0-103.147,43.044c-30.633-45.365-87.1-72.091-145.206-58.044-52-114-225-105-264,15-75,3-140,59-152,133-53,5-127,23-130,83-2,42,35,72,70,86,49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33,61.112,8.015,113.854-5.72,150.492-29.764a165.62,165.62,0,0,0,110.861-3.236c47,94,178,113,251,33,31.385,4.116,60.563,2.495,86.487-3.311,25.924,5.806,55.1,7.427,86.488,3.311,73,80,204,61,251-33a165.625,165.625,0,0,0,120,0c51,13,108,15,157-5a147.188,147.188,0,0,0,33.5-18.694,147.217,147.217,0,0,0,33.5,18.694c49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33C2446.709,1093.793,2554.709,922.793,2379.709,863.793Z"
|
|
||||||
transform="translate(142.69 -634.312)"
|
|
||||||
fill="#eee"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { TestBed } from "@angular/core/testing";
|
|
||||||
import { RouterTestingModule } from "@angular/router/testing";
|
|
||||||
import { AppComponent } from "./app.component";
|
|
||||||
|
|
||||||
describe("AppComponent", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [RouterTestingModule],
|
|
||||||
declarations: [AppComponent],
|
|
||||||
}).compileComponents();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should create the app", () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
const app = fixture.componentInstance;
|
|
||||||
expect(app).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should have as title 'oss-vault'`, () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
const app = fixture.componentInstance;
|
|
||||||
expect(app.title).toEqual("oss-vault");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render title", () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
const compiled = fixture.nativeElement as HTMLElement;
|
|
||||||
expect(compiled.querySelector(".content span")?.textContent).toContain(
|
|
||||||
"oss-vault app is running!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { Component } from "@angular/core";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-root",
|
|
||||||
templateUrl: "./app.component.html",
|
|
||||||
styleUrls: ["./app.component.scss"],
|
|
||||||
})
|
|
||||||
export class AppComponent {
|
|
||||||
title = "oss-vault";
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { NgModule } from "@angular/core";
|
|
||||||
import { BrowserModule } from "@angular/platform-browser";
|
|
||||||
|
|
||||||
import { AppRoutingModule } from "./app-routing.module";
|
|
||||||
import { AppComponent } from "./app.component";
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [AppComponent],
|
|
||||||
imports: [BrowserModule, AppRoutingModule],
|
|
||||||
providers: [],
|
|
||||||
bootstrap: [AppComponent],
|
|
||||||
})
|
|
||||||
export class AppModule {}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const environment = {
|
|
||||||
production: true,
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// This file can be replaced during build by using the `fileReplacements` array.
|
|
||||||
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
|
|
||||||
// The list of file replacements can be found in `angular.json`.
|
|
||||||
|
|
||||||
export const environment = {
|
|
||||||
production: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For easier debugging in development mode, you can import the following file
|
|
||||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
|
||||||
*
|
|
||||||
* This import should be commented out in production mode because it will have a negative impact
|
|
||||||
* on performance if an error is thrown.
|
|
||||||
*/
|
|
||||||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 948 B |
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>OssVault</title>
|
|
||||||
<base href="/" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<app-root></app-root>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { enableProdMode } from "@angular/core";
|
|
||||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
|
||||||
|
|
||||||
import { AppModule } from "./app/app.module";
|
|
||||||
import { environment } from "./environments/environment";
|
|
||||||
|
|
||||||
if (environment.production) {
|
|
||||||
enableProdMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
platformBrowserDynamic()
|
|
||||||
.bootstrapModule(AppModule)
|
|
||||||
.catch((err) => console.error(err));
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
|
||||||
* You can add your own extra polyfills to this file.
|
|
||||||
*
|
|
||||||
* This file is divided into 2 sections:
|
|
||||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
|
||||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
|
||||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
|
||||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
|
||||||
*
|
|
||||||
* Learn more in https://angular.io/guide/browser-support
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* BROWSER POLYFILLS
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IE11 requires the following for NgClass support on SVG elements
|
|
||||||
*/
|
|
||||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Web Animations `@angular/platform-browser/animations`
|
|
||||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
|
||||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
|
||||||
*/
|
|
||||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
|
||||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
|
||||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
|
||||||
* will put import in the top of bundle, so user need to create a separate file
|
|
||||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
|
||||||
* into that file, and then add the following code before importing zone.js.
|
|
||||||
* import './zone-flags';
|
|
||||||
*
|
|
||||||
* The flags allowed in zone-flags.ts are listed here.
|
|
||||||
*
|
|
||||||
* The following flags will work for all browsers.
|
|
||||||
*
|
|
||||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
|
||||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
|
||||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
|
||||||
*
|
|
||||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
|
||||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
|
||||||
*
|
|
||||||
* (window as any).__Zone_enable_cross_context_check = true;
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* Zone JS is required by default for Angular itself.
|
|
||||||
*/
|
|
||||||
import "zone.js"; // Included with Angular CLI.
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* APPLICATION IMPORTS
|
|
||||||
*/
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
|
||||||
|
|
||||||
import "zone.js/testing";
|
|
||||||
import { getTestBed } from "@angular/core/testing";
|
|
||||||
import {
|
|
||||||
BrowserDynamicTestingModule,
|
|
||||||
platformBrowserDynamicTesting,
|
|
||||||
} from "@angular/platform-browser-dynamic/testing";
|
|
||||||
|
|
||||||
declare const require: {
|
|
||||||
context(
|
|
||||||
path: string,
|
|
||||||
deep?: boolean,
|
|
||||||
filter?: RegExp
|
|
||||||
): {
|
|
||||||
keys(): string[];
|
|
||||||
<T>(id: string): T;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// First, initialize the Angular testing environment.
|
|
||||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
|
|
||||||
teardown: { destroyAfterEach: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
// Then we find all the tests.
|
|
||||||
const context = require.context("./", true, /\.spec\.ts$/);
|
|
||||||
// And load the modules.
|
|
||||||
context.keys().map(context);
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../out-tsc/app",
|
|
||||||
"types": []
|
|
||||||
},
|
|
||||||
"files": ["src/main.ts", "src/polyfills.ts"],
|
|
||||||
"include": ["src/**/*.d.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../out-tsc/spec",
|
|
||||||
"types": ["jasmine"]
|
|
||||||
},
|
|
||||||
"files": ["src/test.ts", "src/polyfills.ts"],
|
|
||||||
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Web
|
|
||||||
|
|
||||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
|
|
||||||
|
|
||||||
## Code scaffolding
|
|
||||||
|
|
||||||
Run `ng generate component component-name --project web` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project web`.
|
|
||||||
|
|
||||||
> Note: Don't forget to add `--project web` or else it will be added to the default project in your `angular.json` file.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Run `ng build web` to build the project. The build artifacts will be stored in the `dist/` directory.
|
|
||||||
|
|
||||||
## Publishing
|
|
||||||
|
|
||||||
After building your library with `ng build web`, go to the dist folder `cd dist/web` and run `npm publish`.
|
|
||||||
|
|
||||||
## Running unit tests
|
|
||||||
|
|
||||||
Run `ng test web` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
||||||
|
|
||||||
## Further help
|
|
||||||
|
|
||||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// Karma configuration file, see link for more information
|
|
||||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
basePath: "",
|
|
||||||
frameworks: ["jasmine", "@angular-devkit/build-angular"],
|
|
||||||
plugins: [
|
|
||||||
require("karma-jasmine"),
|
|
||||||
require("karma-chrome-launcher"),
|
|
||||||
require("karma-jasmine-html-reporter"),
|
|
||||||
require("karma-coverage"),
|
|
||||||
require("@angular-devkit/build-angular/plugins/karma"),
|
|
||||||
],
|
|
||||||
client: {
|
|
||||||
jasmine: {
|
|
||||||
// you can add configuration options for Jasmine here
|
|
||||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
|
||||||
// for example, you can disable the random execution with `random: false`
|
|
||||||
// or set a specific seed with `seed: 4321`
|
|
||||||
},
|
|
||||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
|
||||||
},
|
|
||||||
jasmineHtmlReporter: {
|
|
||||||
suppressAll: true, // removes the duplicated traces
|
|
||||||
},
|
|
||||||
coverageReporter: {
|
|
||||||
dir: require("path").join(__dirname, "../../coverage/web"),
|
|
||||||
subdir: ".",
|
|
||||||
reporters: [{ type: "html" }, { type: "text-summary" }],
|
|
||||||
},
|
|
||||||
reporters: ["progress", "kjhtml"],
|
|
||||||
port: 9876,
|
|
||||||
colors: true,
|
|
||||||
logLevel: config.LOG_INFO,
|
|
||||||
autoWatch: true,
|
|
||||||
browsers: ["Chrome"],
|
|
||||||
singleRun: false,
|
|
||||||
restartOnFileChange: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
|
||||||
"dest": "../../dist/web",
|
|
||||||
"lib": {
|
|
||||||
"entryFile": "src/public-api.ts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@bitwarden/web-vault-internal",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@angular/common": "^12.2.0",
|
|
||||||
"@angular/core": "^12.2.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9480
projects/web-vault-internal/src/404/bootstrap.min.css
vendored
9480
projects/web-vault-internal/src/404/bootstrap.min.css
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user