mirror of
https://github.com/bitwarden/web
synced 2025-12-06 00:03:28 +00:00
Compare commits
2 Commits
bug/PS-250
...
feature/ex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5d3caba65 | ||
|
|
5a2294fb4f |
@@ -1,8 +0,0 @@
|
|||||||
**/dist
|
|
||||||
**/build
|
|
||||||
jslib
|
|
||||||
webpack.config.js
|
|
||||||
scripts/optimize.js
|
|
||||||
config.js
|
|
||||||
|
|
||||||
**/node_modules
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"browser": true
|
|
||||||
},
|
|
||||||
"extends": ["./jslib/shared/eslintrc.json"],
|
|
||||||
"rules": {
|
|
||||||
"import/order": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"alphabetize": {
|
|
||||||
"order": "asc"
|
|
||||||
},
|
|
||||||
"newlines-between": "always",
|
|
||||||
"pathGroups": [
|
|
||||||
{
|
|
||||||
"pattern": "jslib-*/**",
|
|
||||||
"group": "external",
|
|
||||||
"position": "after"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pattern": "src/**/*",
|
|
||||||
"group": "parent",
|
|
||||||
"position": "before"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"pathGroupsExcludedImportTypes": ["builtin"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -21,6 +21,10 @@
|
|||||||
|
|
||||||
<!--Required for any UI changes. Delete if not applicable-->
|
<!--Required for any UI changes. Delete if not applicable-->
|
||||||
|
|
||||||
|
## Testing requirements
|
||||||
|
|
||||||
|
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
||||||
|
|
||||||
## Before you submit
|
## Before you submit
|
||||||
|
|
||||||
- [ ] I have checked for **linting** errors (`npm run lint`) (required)
|
- [ ] I have checked for **linting** errors (`npm run lint`) (required)
|
||||||
|
|||||||
192
.github/workflows/build.yml
vendored
192
.github/workflows/build.yml
vendored
@@ -11,10 +11,6 @@ on:
|
|||||||
branches-ignore:
|
branches-ignore:
|
||||||
- "l10n_master"
|
- "l10n_master"
|
||||||
- "gh-pages"
|
- "gh-pages"
|
||||||
- "deploy"
|
|
||||||
paths-ignore:
|
|
||||||
- '.github/workflows/**'
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cloc:
|
cloc:
|
||||||
@@ -32,28 +28,6 @@ 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
|
||||||
@@ -67,26 +41,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:
|
needs: setup
|
||||||
- setup
|
|
||||||
- lint
|
|
||||||
env:
|
env:
|
||||||
_VERSION: ${{ needs.setup.outputs.version }}
|
_VERSION: ${{ needs.setup.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
|
||||||
cache-dependency-path: '**/package-lock.json'
|
|
||||||
node-version: "16"
|
node-version: "16"
|
||||||
|
|
||||||
|
- name: Cache npm
|
||||||
|
id: npm-cache
|
||||||
|
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||||
|
with:
|
||||||
|
path: "~/.npm"
|
||||||
|
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
whoami
|
whoami
|
||||||
@@ -97,6 +70,9 @@ 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
|
||||||
|
|
||||||
@@ -112,26 +88,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:
|
needs: setup
|
||||||
- setup
|
|
||||||
- lint
|
|
||||||
env:
|
env:
|
||||||
_VERSION: ${{ needs.setup.outputs.version }}
|
_VERSION: ${{ needs.setup.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
|
||||||
cache-dependency-path: '**/package-lock.json'
|
|
||||||
node-version: "16"
|
node-version: "16"
|
||||||
|
|
||||||
|
- name: Cache npm
|
||||||
|
id: npm-cache
|
||||||
|
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||||
|
with:
|
||||||
|
path: "~/.npm"
|
||||||
|
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
whoami
|
whoami
|
||||||
@@ -142,6 +117,9 @@ 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
|
||||||
|
|
||||||
@@ -157,26 +135,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:
|
needs: setup
|
||||||
- setup
|
|
||||||
- lint
|
|
||||||
env:
|
env:
|
||||||
_VERSION: ${{ needs.setup.outputs.version }}
|
_VERSION: ${{ needs.setup.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
|
||||||
cache-dependency-path: '**/package-lock.json'
|
|
||||||
node-version: "16"
|
node-version: "16"
|
||||||
|
|
||||||
|
- name: Cache npm
|
||||||
|
id: npm-cache
|
||||||
|
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||||
|
with:
|
||||||
|
path: "~/.npm"
|
||||||
|
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
whoami
|
whoami
|
||||||
@@ -188,13 +165,19 @@ 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-rc'
|
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||||
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
|
||||||
|
|
||||||
@@ -229,11 +212,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-rc'
|
if: github.ref == 'refs/heads/hotfix'
|
||||||
run: docker tag bitwarden/web bitwarden/web:hotfix-rc
|
run: docker tag bitwarden/web bitwarden/web:hotfix
|
||||||
|
|
||||||
- name: List Docker images
|
- name: List Docker images
|
||||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||||
run: docker images
|
run: docker images
|
||||||
|
|
||||||
- name: Push rc image
|
- name: Push rc image
|
||||||
@@ -251,59 +234,32 @@ 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-rc'
|
if: github.ref == 'refs/heads/hotfix'
|
||||||
run: docker push bitwarden/web:hotfix-rc
|
run: docker push bitwarden/web:hotfix
|
||||||
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-rc'
|
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||||
run: |
|
|
||||||
docker logout
|
|
||||||
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- 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: Checkout repo
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||||
with:
|
with:
|
||||||
cache: 'npm'
|
|
||||||
cache-dependency-path: '**/package-lock.json'
|
|
||||||
node-version: "16"
|
node-version: "16"
|
||||||
|
|
||||||
|
- name: Cache npm
|
||||||
|
id: npm-cache
|
||||||
|
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||||
|
with:
|
||||||
|
path: "~/.npm"
|
||||||
|
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
whoami
|
whoami
|
||||||
@@ -322,6 +278,12 @@ 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
|
||||||
|
|
||||||
@@ -377,29 +339,35 @@ 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 Node
|
- name: Set up MSBuild
|
||||||
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
|
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
|
||||||
|
|
||||||
|
- name: Cache npm
|
||||||
|
id: npm-cache
|
||||||
|
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
|
||||||
|
with:
|
||||||
|
path: "~/.npm"
|
||||||
|
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
|
- name: Set up Node
|
||||||
|
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||||
with:
|
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"
|
||||||
@@ -408,13 +376,18 @@ 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'
|
||||||
@@ -453,7 +426,6 @@ 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()
|
||||||
@@ -461,7 +433,6 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- cloc
|
- cloc
|
||||||
- setup
|
- setup
|
||||||
- lint
|
|
||||||
- build-oss-selfhost
|
- build-oss-selfhost
|
||||||
- build-cloud
|
- build-cloud
|
||||||
- build-commercial-selfhost
|
- build-commercial-selfhost
|
||||||
@@ -473,7 +444,6 @@ 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 }}
|
||||||
@@ -484,8 +454,6 @@ 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
16
.github/workflows/enforce-labels.yml
vendored
@@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
name: Enforce PR labels
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [labeled, unlabeled, opened, edited, synchronize]
|
|
||||||
jobs:
|
|
||||||
enforce-label:
|
|
||||||
name: EnforceLabel
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Enforce Label
|
|
||||||
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
|
|
||||||
with:
|
|
||||||
BANNED_LABELS: "hold"
|
|
||||||
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"
|
|
||||||
202
.github/workflows/release.yml
vendored
202
.github/workflows/release.yml
vendored
@@ -12,7 +12,6 @@ on:
|
|||||||
options:
|
options:
|
||||||
- Initial Release
|
- Initial Release
|
||||||
- Redeploy
|
- Redeploy
|
||||||
- Dry Run
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
setup:
|
setup:
|
||||||
@@ -21,20 +20,19 @@ 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-rc" ]]; then
|
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix" ]]; then
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
|
echo "[!] Can only release from the 'rc' or 'hotfix' branches"
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # 2.4.0
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # 2.3.4
|
||||||
|
|
||||||
- name: Check Release Version
|
- name: Check Release Version
|
||||||
id: version
|
id: version
|
||||||
@@ -57,17 +55,15 @@ 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: |
|
||||||
@@ -75,12 +71,7 @@ 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
|
||||||
@@ -88,25 +79,21 @@ 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: |
|
run: docker pull bitwarden/web:$_BRANCH_NAME
|
||||||
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
|
||||||
docker pull bitwarden/web:latest
|
|
||||||
else
|
|
||||||
docker pull bitwarden/web:$_BRANCH_NAME
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Tag version and latest
|
- name: Tag version and latest
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
|
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION
|
||||||
docker tag bitwarden/web:latest bitwarden/web:dryrun
|
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest
|
||||||
else
|
|
||||||
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION
|
- name: List Docker images
|
||||||
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest
|
run: docker images
|
||||||
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 }}
|
||||||
@@ -114,51 +101,11 @@ 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 to GitHub Pages
|
name: Deploy Web Vault
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
needs:
|
needs:
|
||||||
- setup
|
- setup
|
||||||
@@ -168,17 +115,17 @@ 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@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
with:
|
with:
|
||||||
ref: gh-pages
|
ref: gh-pages
|
||||||
|
|
||||||
- name: Create gh-pages-deploy branch
|
- name: Create deploy branch
|
||||||
run: |
|
run: |
|
||||||
git switch -c gh-pages-deploy-$_TAG_VERSION
|
git switch -c deploy-$_TAG_VERSION
|
||||||
git push -u origin gh-pages-deploy-$_TAG_VERSION
|
git push -u origin deploy-$_TAG_VERSION
|
||||||
|
|
||||||
- name: Checkout Repo
|
- name: Checkout Repo
|
||||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
- name: Setup git config
|
- name: Setup git config
|
||||||
run: |
|
run: |
|
||||||
@@ -188,11 +135,11 @@ jobs:
|
|||||||
git config --global url."https://".insteadOf ssh://
|
git config --global url."https://".insteadOf ssh://
|
||||||
|
|
||||||
- name: Download latest cloud asset
|
- name: Download latest cloud asset
|
||||||
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
|
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
||||||
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
|
||||||
@@ -200,92 +147,25 @@ 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@a117e4aa1fb4854d021546d2abdfac95be568a3a # v2.6.0
|
uses: crazy-max/ghaction-github-pages@db4476a01402e1a7ce05f41832040eef16d14925 # v2.5.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
target_branch: gh-pages-deploy-${{ needs.setup.outputs.tag_version }}
|
target_branch: deploy-${{ needs.setup.outputs.tag_version }}
|
||||||
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 GitHub Pages Deploy PR
|
- name: Create Deploy PR
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
|
||||||
env:
|
env:
|
||||||
PR_BRANCH: gh-pages-deploy-${{ env._TAG_VERSION }}
|
PR_BRANCH: deploy-${{ env._TAG_VERSION }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
gh pr create --title "Deploy $_RELEASE_VERSION to GitHub Pages" \
|
gh pr create --title "Deploy $_RELEASE_VERSION" \
|
||||||
--body "Deploying $_RELEASE_VERSION" \
|
--body "Deploying $_RELEASE_VERSION" \
|
||||||
--base gh-pages \
|
--base gh-pages \
|
||||||
--head "$PR_BRANCH"
|
--head "$PR_BRANCH"
|
||||||
|
|
||||||
|
|
||||||
cfpages-deploy:
|
|
||||||
name: Deploy Web Vault to CloudFlare Pages branch
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
needs:
|
|
||||||
- setup
|
|
||||||
- self-host
|
|
||||||
env:
|
|
||||||
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
|
|
||||||
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repo
|
|
||||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
|
||||||
|
|
||||||
- name: Download latest cloud asset
|
|
||||||
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
|
|
||||||
with:
|
|
||||||
workflow: build.yml
|
|
||||||
workflow_conclusion: success
|
|
||||||
branch: ${{ needs.setup.outputs.branch_name }}
|
|
||||||
artifacts: web-*-cloud-COMMERCIAL.zip
|
|
||||||
|
|
||||||
# This should result in a build directory in the current working directory
|
|
||||||
- name: Unzip build asset
|
|
||||||
run: unzip web-*-cloud-COMMERCIAL.zip
|
|
||||||
|
|
||||||
- name: Checkout Repo
|
|
||||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
|
||||||
with:
|
|
||||||
ref: deploy
|
|
||||||
path: deployment
|
|
||||||
|
|
||||||
- name: Setup git config
|
|
||||||
run: |
|
|
||||||
git config --global user.name = "GitHub Action Bot"
|
|
||||||
git config --global user.email = "<>"
|
|
||||||
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
|
|
||||||
git config --global url."https://".insteadOf ssh://
|
|
||||||
|
|
||||||
- name: Deploy CloudFlare Pages
|
|
||||||
run: |
|
|
||||||
rm -rf ./*
|
|
||||||
cp -R ../build/* .
|
|
||||||
working-directory: deployment
|
|
||||||
|
|
||||||
- name: Create cf-pages-deploy branch
|
|
||||||
run: |
|
|
||||||
git switch -c cf-pages-deploy-$_TAG_VERSION
|
|
||||||
git add .
|
|
||||||
git commit -m "Staging deploy ${{ needs.setup.outputs.release_version }}"
|
|
||||||
git push -u origin cf-pages-deploy-$_TAG_VERSION
|
|
||||||
working-directory: deployment
|
|
||||||
|
|
||||||
- name: Create CloudFlare Pages Deploy PR
|
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
|
||||||
env:
|
|
||||||
PR_BRANCH: cf-pages-deploy-${{ env._TAG_VERSION }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
gh pr create --title "Deploy $_RELEASE_VERSION to CloudFlare Pages" \
|
|
||||||
--body "Deploying $_RELEASE_VERSION" \
|
|
||||||
--base deploy \
|
|
||||||
--head "$PR_BRANCH"
|
|
||||||
|
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Create GitHub Release
|
name: Create GitHub Release
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
@@ -293,14 +173,13 @@ jobs:
|
|||||||
- setup
|
- setup
|
||||||
- self-host
|
- self-host
|
||||||
- ghpages-deploy
|
- ghpages-deploy
|
||||||
- cfpages-deploy
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download latest build artifacts
|
- name: Download latest build artifacts
|
||||||
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
|
||||||
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"
|
||||||
|
|
||||||
@@ -310,8 +189,7 @@ 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
|
||||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09
|
||||||
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 }}
|
||||||
@@ -321,23 +199,3 @@ 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 gh-pages-deploy branch
|
|
||||||
run: git push origin --delete gh-pages-deploy-$_TAG_VERSION
|
|
||||||
|
|
||||||
- name: Remove cf-pages-deploy branch
|
|
||||||
run: git push origin --delete cf-pages-deploy-$_TAG_VERSION
|
|
||||||
|
|||||||
11
.github/workflows/workflow-linter.yml
vendored
11
.github/workflows/workflow-linter.yml
vendored
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
name: Workflow Linter
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- .github/workflows/**
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
call-workflow:
|
|
||||||
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master
|
|
||||||
0
.husky/pre-commit
Executable file → Normal file
0
.husky/pre-commit
Executable file → Normal file
@@ -10,7 +10,7 @@ Here is how you can get involved:
|
|||||||
- **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
- **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
||||||
- **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
- **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
||||||
- **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
- **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||||
- **Help other users:** Go to the [Ask the Bitwarden Community category](https://community.bitwarden.com/c/support/) on the Community Forums
|
- **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||||
- **Translate:** See the localization (l10n) section below
|
- **Translate:** See the localization (l10n) section below
|
||||||
|
|
||||||
## Contributor Agreement
|
## Contributor Agreement
|
||||||
@@ -31,6 +31,6 @@ We use a translation tool called [Crowdin](https://crowdin.com) to help manage o
|
|||||||
|
|
||||||
If you are interested in helping translate the Bitwarden web vault into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-web
|
If you are interested in helping translate the Bitwarden web vault into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-web
|
||||||
|
|
||||||
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/dwbit).
|
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/kspearrin).
|
||||||
|
|
||||||
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/
|
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM bitwarden/server
|
FROM bitwarden/server:dev
|
||||||
|
|
||||||
LABEL com.bitwarden.product="bitwarden"
|
LABEL com.bitwarden.product="bitwarden"
|
||||||
|
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -1,9 +1,3 @@
|
|||||||
> **Repository Reorganization in Progress**
|
|
||||||
>
|
|
||||||
> We are currently migrating some projects over to a mono repository. For existing PR's we will be providing documentation on how to move/migrate them. To minimize the overhead we are actively reviewing open PRs. If possible please ensure any pending comments are resolved as soon as possible.
|
|
||||||
>
|
|
||||||
> New pull requests created during this transition period may not get addressed —if needed, please create a new PR after the reorganization is complete.
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/web-vault-macbook.png" alt="" width="600" height="358" />
|
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/web-vault-macbook.png" alt="" width="600" height="358" />
|
||||||
</p>
|
</p>
|
||||||
@@ -67,10 +61,6 @@ You can also manually adjusting your API endpoint settings by adding `config/loc
|
|||||||
|
|
||||||
Where the `urls` object is defined by the [Urls type in jslib](https://github.com/bitwarden/jslib/blob/master/common/src/abstractions/environment.service.ts).
|
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,11 +1,39 @@
|
|||||||
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!
|
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
|
||||||
|
users safe. If you believe you've found a security issue in our product or service, we encourage you to
|
||||||
|
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
|
||||||
|
|
||||||
# Disclosure Policy
|
# Disclosure Policy
|
||||||
|
|
||||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue.
|
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
|
||||||
- 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.
|
effort to quickly resolve the issue.
|
||||||
- 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.
|
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
|
||||||
- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||||
|
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
|
||||||
|
degradation of our service. Only interact with accounts you own or with explicit permission of the
|
||||||
|
account holder.
|
||||||
|
- If you would like to encrypt your report, please use the PGP key with long ID
|
||||||
|
`0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
||||||
|
|
||||||
|
# In-scope
|
||||||
|
|
||||||
|
- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
|
||||||
|
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
|
||||||
|
code is available at https://github.com/bitwarden.
|
||||||
|
|
||||||
|
# Exclusions
|
||||||
|
|
||||||
|
The following bug classes are out-of scope:
|
||||||
|
|
||||||
|
- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
|
||||||
|
or that we already know of. Note that some of our issue tracking is private.
|
||||||
|
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
|
||||||
|
upstream maintainer.
|
||||||
|
- Attacks requiring physical access to a user's device.
|
||||||
|
- Self-XSS
|
||||||
|
- Issues related to software or protocols not under Bitwarden's control
|
||||||
|
- Vulnerabilities in outdated versions of Bitwarden
|
||||||
|
- Missing security best practices that do not directly lead to a vulnerability
|
||||||
|
- Issues that do not have any impact on the general public
|
||||||
|
|
||||||
While researching, we'd like to ask you to refrain from:
|
While researching, we'd like to ask you to refrain from:
|
||||||
|
|
||||||
@@ -14,8 +42,4 @@ 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!
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { AppComponent as BaseAppComponent } from "src/app/app.component";
|
import { AppComponent as BaseAppComponent } from "src/app/app.component";
|
||||||
|
|
||||||
import { DisablePersonalVaultExportPolicy } from "./policies/disable-personal-vault-export.component";
|
import { DisablePersonalVaultExportPolicy } from "./policies/disable-personal-vault-export.component";
|
||||||
import { MaximumVaultTimeoutPolicy } from "./policies/maximum-vault-timeout.component";
|
import { MaximumVaultTimeoutPolicy } from "./policies/maximum-vault-timeout.component";
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import { DragDropModule } from "@angular/cdk/drag-drop";
|
import { DragDropModule } from "@angular/cdk/drag-drop";
|
||||||
import { OverlayModule } from "@angular/cdk/overlay";
|
|
||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
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 { JslibModule } from "jslib-angular/jslib.module";
|
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
|
||||||
|
|
||||||
import { OssRoutingModule } from "src/app/oss-routing.module";
|
|
||||||
import { OssModule } from "src/app/oss.module";
|
|
||||||
import { ServicesModule } from "src/app/services/services.module";
|
|
||||||
import { WildcardRoutingModule } from "src/app/wildcard-routing.module";
|
|
||||||
|
|
||||||
import { AppRoutingModule } from "./app-routing.module";
|
import { AppRoutingModule } from "./app-routing.module";
|
||||||
import { AppComponent } from "./app.component";
|
import { AppComponent } from "./app.component";
|
||||||
@@ -19,27 +13,35 @@ import { OrganizationsModule } from "./organizations/organizations.module";
|
|||||||
import { DisablePersonalVaultExportPolicyComponent } from "./policies/disable-personal-vault-export.component";
|
import { DisablePersonalVaultExportPolicyComponent } from "./policies/disable-personal-vault-export.component";
|
||||||
import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-timeout.component";
|
import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-timeout.component";
|
||||||
|
|
||||||
|
import { OssRoutingModule } from "src/app/oss-routing.module";
|
||||||
|
import { OssModule } from "src/app/oss.module";
|
||||||
|
import { ServicesModule } from "src/app/services/services.module";
|
||||||
|
import { WildcardRoutingModule } from "src/app/wildcard-routing.module";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
OverlayModule,
|
|
||||||
OssModule,
|
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, // Must be after OssRoutingModule for competing routes to resolve properly
|
OrganizationsModule,
|
||||||
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,
|
||||||
DisablePersonalVaultExportPolicyComponent,
|
|
||||||
MaximumVaultTimeoutPolicyComponent,
|
MaximumVaultTimeoutPolicyComponent,
|
||||||
|
DisablePersonalVaultExportPolicyComponent,
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import "bootstrap";
|
|||||||
import "jquery";
|
import "jquery";
|
||||||
import "popper.js";
|
import "popper.js";
|
||||||
|
|
||||||
|
// tslint:disable-next-line
|
||||||
require("src/scss/styles.scss");
|
require("src/scss/styles.scss");
|
||||||
require("src/scss/tailwind.css");
|
|
||||||
|
|
||||||
import { AppModule } from "./app.module";
|
import { AppModule } from "./app.module";
|
||||||
|
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
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 {}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
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, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Component, Input } from "@angular/core";
|
|
||||||
|
|
||||||
import { SelectOptions } from "jslib-angular/interfaces/selectOptions";
|
|
||||||
|
|
||||||
import { BaseCvaComponent } from "./base-cva.component";
|
|
||||||
|
|
||||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
|
||||||
@Component({
|
|
||||||
selector: "app-select",
|
|
||||||
templateUrl: "select.component.html",
|
|
||||||
})
|
|
||||||
export class SelectComponent extends BaseCvaComponent {
|
|
||||||
@Input() selectOptions: SelectOptions[];
|
|
||||||
}
|
|
||||||
@@ -14,9 +14,10 @@
|
|||||||
<form
|
<form
|
||||||
#form
|
#form
|
||||||
(ngSubmit)="submit()"
|
(ngSubmit)="submit()"
|
||||||
[formGroup]="ssoConfigForm"
|
[formGroup]="data"
|
||||||
[appApiAction]="formPromise"
|
[appApiAction]="formPromise"
|
||||||
*ngIf="!loading"
|
*ngIf="!loading"
|
||||||
|
ngNativeValidate
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
{{ "ssoPolicyHelpStart" | i18n }}
|
{{ "ssoPolicyHelpStart" | i18n }}
|
||||||
@@ -26,407 +27,462 @@
|
|||||||
{{ "ssoPolicyHelpKeyConnector" | i18n }}
|
{{ "ssoPolicyHelpKeyConnector" | i18n }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Root form -->
|
<div class="form-group">
|
||||||
<ng-container>
|
<div class="form-check">
|
||||||
<app-input-checkbox
|
<input
|
||||||
controlId="enabled"
|
class="form-check-input"
|
||||||
[formControl]="enabled"
|
type="checkbox"
|
||||||
[label]="'allowSso' | i18n"
|
id="enabled"
|
||||||
[helperText]="'allowSsoDesc' | i18n"
|
[formControl]="enabled"
|
||||||
></app-input-checkbox>
|
name="Enabled"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="enabled">{{ "allowSso" | i18n }}</label>
|
||||||
|
</div>
|
||||||
|
<small class="form-text text-muted">{{ "allowSsoDesc" | i18n }}</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ "memberDecryptionOption" | i18n }}</label>
|
||||||
|
<div class="form-check form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
id="memberDecryptionPass"
|
||||||
|
[value]="false"
|
||||||
|
formControlName="keyConnectorEnabled"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="memberDecryptionPass">
|
||||||
|
{{ "masterPass" | i18n }}
|
||||||
|
<small>{{ "memberDecryptionPassDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mt-2 form-check-block">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
id="memberDecryptionKey"
|
||||||
|
[value]="true"
|
||||||
|
formControlName="keyConnectorEnabled"
|
||||||
|
[attr.disabled]="!organization.useKeyConnector || null"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="memberDecryptionKey">
|
||||||
|
{{ "keyConnector" | i18n }}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
|
href="https://bitwarden.com/help/about-key-connector/"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
<small>{{ "memberDecryptionKeyConnectorDesc" | i18n }}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="data.value.keyConnectorEnabled">
|
||||||
|
<app-callout type="warning" [useAlertRole]="true">
|
||||||
|
{{ "keyConnectorWarning" | i18n }}
|
||||||
|
</app-callout>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ "memberDecryptionOption" | i18n }}</label>
|
<label for="keyConnectorUrl">{{ "keyConnectorUrl" | i18n }}</label>
|
||||||
<div class="form-check form-check-block">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
class="form-control"
|
||||||
type="radio"
|
formControlName="keyConnectorUrl"
|
||||||
id="memberDecryptionPass"
|
id="keyConnectorUrl"
|
||||||
[value]="false"
|
required
|
||||||
formControlName="keyConnectorEnabled"
|
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" for="memberDecryptionPass">
|
<div class="input-group-append">
|
||||||
{{ "masterPass" | i18n }}
|
<button
|
||||||
<small>{{ "memberDecryptionPassDesc" | i18n }}</small>
|
type="button"
|
||||||
</label>
|
class="btn btn-outline-secondary"
|
||||||
</div>
|
(click)="validateKeyConnectorUrl()"
|
||||||
<div class="form-check mt-2 form-check-block">
|
[disabled]="!enableTestKeyConnector"
|
||||||
<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>
|
<i
|
||||||
</a>
|
class="bwi bwi-spinner bwi-spin"
|
||||||
<small>{{ "memberDecryptionKeyConnectorDesc" | i18n }}</small>
|
title="{{ 'loading' | i18n }}"
|
||||||
</label>
|
aria-hidden="true"
|
||||||
|
*ngIf="keyConnectorUrl.pending"
|
||||||
|
></i>
|
||||||
|
<span *ngIf="!keyConnectorUrl.pending">
|
||||||
|
{{ "keyConnectorTest" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-container *ngIf="keyConnectorUrl.pristine && !keyConnectorUrl.pending">
|
||||||
|
<div class="text-danger" *ngIf="keyConnectorUrl.hasError('invalidUrl')" role="alert">
|
||||||
|
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||||
|
{{ "keyConnectorTestFail" | i18n }}
|
||||||
|
</div>
|
||||||
|
<div class="text-success" *ngIf="!keyConnectorUrl.hasError('invalidUrl')" role="alert">
|
||||||
|
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||||
|
{{ "keyConnectorTestSuccess" | i18n }}
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<!-- Key Connector -->
|
<div class="form-group">
|
||||||
<ng-container *ngIf="ssoConfigForm.get('keyConnectorEnabled').value">
|
<label for="type">{{ "type" | i18n }}</label>
|
||||||
<app-callout type="warning" [useAlertRole]="true">
|
<select class="form-control" id="type" formControlName="configType">
|
||||||
{{ "keyConnectorWarning" | i18n }}
|
<option [ngValue]="0" disabled>{{ "selectType" | i18n }}</option>
|
||||||
</app-callout>
|
<option [ngValue]="1">OpenID Connect</option>
|
||||||
|
<option [ngValue]="2">SAML 2.0</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- OIDC -->
|
||||||
|
<div *ngIf="data.value.configType == 1">
|
||||||
|
<div class="config-section">
|
||||||
|
<h2>{{ "openIdConnectConfig" | i18n }}</h2>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="keyConnectorUrl">
|
<label>{{ "callbackPath" | i18n }}</label>
|
||||||
{{ "keyConnectorUrl" | i18n }}
|
|
||||||
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
|
|
||||||
</label>
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input class="form-control" readonly [value]="callbackPath" />
|
||||||
class="form-control"
|
|
||||||
formControlName="keyConnectorUrl"
|
|
||||||
id="keyConnectorUrl"
|
|
||||||
aria-describedby="keyConnectorUrlDesc"
|
|
||||||
(change)="haveTestedKeyConnector = false"
|
|
||||||
appInputStripSpaces
|
|
||||||
appA11yInvalid
|
|
||||||
/>
|
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline-secondary"
|
class="btn btn-outline-secondary"
|
||||||
(click)="validateKeyConnectorUrl()"
|
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||||
[disabled]="!enableTestKeyConnector"
|
(click)="copy(callbackPath)"
|
||||||
>
|
>
|
||||||
<i
|
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="haveTestedKeyConnector" id="keyConnectorUrlDesc" aria-live="polite">
|
</div>
|
||||||
<small
|
<div class="form-group">
|
||||||
class="error-inline"
|
<label>{{ "signedOutCallbackPath" | i18n }}</label>
|
||||||
*ngIf="keyConnectorUrl.hasError('invalidUrl'); else keyConnectorSuccess"
|
<div class="input-group">
|
||||||
>
|
<input class="form-control" readonly [value]="signedOutCallbackPath" />
|
||||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
<div class="input-group-append">
|
||||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
<button
|
||||||
{{ "keyConnectorTestFail" | i18n }}
|
type="button"
|
||||||
</small>
|
class="btn btn-outline-secondary"
|
||||||
<ng-template #keyConnectorSuccess>
|
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||||
<small class="text-success">
|
(click)="copy(signedOutCallbackPath)"
|
||||||
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
>
|
||||||
{{ "keyConnectorTestSuccess" | i18n }}
|
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||||
</small>
|
</button>
|
||||||
</ng-template>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
<div class="form-group">
|
||||||
|
<label for="authority">{{ "authority" | i18n }}</label>
|
||||||
<app-select
|
<input class="form-control" formControlName="authority" id="authority" />
|
||||||
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>
|
||||||
<div id="customizations" [hidden]="!showOpenIdCustomizations">
|
<div class="form-group">
|
||||||
<app-input-text
|
<label for="clientId">{{ "clientId" | i18n }}</label>
|
||||||
[label]="'additionalScopes' | i18n"
|
<input class="form-control" formControlName="clientId" id="clientId" />
|
||||||
controlId="additionalScopes"
|
</div>
|
||||||
[helperText]="'separateMultipleWithComma' | i18n"
|
<div class="form-group">
|
||||||
formControlName="additionalScopes"
|
<label for="clientSecret">{{ "clientSecret" | i18n }}</label>
|
||||||
></app-input-text>
|
<input class="form-control" formControlName="clientSecret" id="clientSecret" />
|
||||||
|
</div>
|
||||||
<app-input-text
|
<div class="form-group">
|
||||||
[label]="'additionalUserIdClaimTypes' | i18n"
|
<label for="metadataAddress">{{ "metadataAddress" | i18n }}</label>
|
||||||
controlId="additionalUserIdClaimTypes"
|
<input class="form-control" formControlName="metadataAddress" id="metadataAddress" />
|
||||||
[helperText]="'separateMultipleWithComma' | i18n"
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="redirectBehavior">{{ "oidcRedirectBehavior" | i18n }}</label>
|
||||||
|
<select class="form-control" formControlName="redirectBehavior" id="redirectBehavior">
|
||||||
|
<option [ngValue]="0">Redirect GET</option>
|
||||||
|
<option [ngValue]="1">Form POST</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="getClaimsFromUserInfoEndpoint"
|
||||||
|
formControlName="getClaimsFromUserInfoEndpoint"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="getClaimsFromUserInfoEndpoint">
|
||||||
|
{{ "getClaimsFromUserInfoEndpoint" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="additionalScopes">{{ "additionalScopes" | i18n }}</label>
|
||||||
|
<input class="form-control" formControlName="additionalScopes" id="additionalScopes" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="additionalUserIdClaimTypes">{{ "additionalUserIdClaimTypes" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
class="form-control"
|
||||||
formControlName="additionalUserIdClaimTypes"
|
formControlName="additionalUserIdClaimTypes"
|
||||||
></app-input-text>
|
id="additionalUserIdClaimTypes"
|
||||||
|
/>
|
||||||
<app-input-text
|
</div>
|
||||||
[label]="'additionalEmailClaimTypes' | i18n"
|
<div class="form-group">
|
||||||
controlId="additionalEmailClaimTypes"
|
<label for="additionalEmailClaimTypes">{{ "additionalEmailClaimTypes" | i18n }}</label>
|
||||||
[helperText]="'separateMultipleWithComma' | i18n"
|
<input
|
||||||
|
class="form-control"
|
||||||
formControlName="additionalEmailClaimTypes"
|
formControlName="additionalEmailClaimTypes"
|
||||||
></app-input-text>
|
id="additionalEmailClaimTypes"
|
||||||
|
/>
|
||||||
<app-input-text
|
</div>
|
||||||
[label]="'additionalNameClaimTypes' | i18n"
|
<div class="form-group">
|
||||||
controlId="additionalNameClaimTypes"
|
<label for="additionalNameClaimTypes">{{ "additionalNameClaimTypes" | i18n }}</label>
|
||||||
[helperText]="'separateMultipleWithComma' | i18n"
|
<input
|
||||||
|
class="form-control"
|
||||||
formControlName="additionalNameClaimTypes"
|
formControlName="additionalNameClaimTypes"
|
||||||
></app-input-text>
|
id="additionalNameClaimTypes"
|
||||||
|
/>
|
||||||
<app-input-text
|
</div>
|
||||||
[label]="'acrValues' | i18n"
|
<div class="form-group">
|
||||||
controlId="acrValues"
|
<label for="acrValues">{{ "acrValues" | i18n }}</label>
|
||||||
helperText="acr_values"
|
<input class="form-control" formControlName="acrValues" id="acrValues" />
|
||||||
formControlName="acrValues"
|
</div>
|
||||||
></app-input-text>
|
<div class="form-group">
|
||||||
|
<label for="expectedReturnAcrValue">{{ "expectedReturnAcrValue" | i18n }}</label>
|
||||||
<app-input-text
|
<input
|
||||||
[label]="'expectedReturnAcrValue' | i18n"
|
class="form-control"
|
||||||
controlId="expectedReturnAcrValue"
|
|
||||||
helperText="acr_validation"
|
|
||||||
formControlName="expectedReturnAcrValue"
|
formControlName="expectedReturnAcrValue"
|
||||||
></app-input-text>
|
id="expectedReturnAcrValue"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- SAML2 SP -->
|
<div *ngIf="data.value.configType == 2">
|
||||||
<div *ngIf="ssoConfigForm.get('configType').value === ssoType.Saml2" [formGroup]="samlForm">
|
|
||||||
<!-- SAML2 SP -->
|
<!-- SAML2 SP -->
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<h2 class="secondary-header">{{ "samlSpConfig" | i18n }}</h2>
|
<h2>{{ "samlSpConfig" | i18n }}</h2>
|
||||||
|
<div class="form-group">
|
||||||
<app-input-text-readonly
|
<label>{{ "spEntityId" | i18n }}</label>
|
||||||
[label]="'spEntityId' | i18n"
|
<div class="input-group">
|
||||||
[controlValue]="spEntityId"
|
<input class="form-control" readonly [value]="spEntityId" />
|
||||||
></app-input-text-readonly>
|
<div class="input-group-append">
|
||||||
|
<button
|
||||||
<app-input-text-readonly
|
type="button"
|
||||||
[label]="'spMetadataUrl' | i18n"
|
class="btn btn-outline-secondary"
|
||||||
[controlValue]="spMetadataUrl"
|
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||||
[showLaunch]="true"
|
(click)="copy(spEntityId)"
|
||||||
></app-input-text-readonly>
|
>
|
||||||
|
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||||
<app-input-text-readonly
|
</button>
|
||||||
[label]="'spAcsUrl' | i18n"
|
</div>
|
||||||
[controlValue]="spAcsUrl"
|
</div>
|
||||||
></app-input-text-readonly>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<app-select
|
<label>{{ "spMetadataUrl" | i18n }}</label>
|
||||||
controlId="spNameIdFormat"
|
<div class="input-group">
|
||||||
[label]="'spNameIdFormat' | i18n"
|
<input class="form-control" readonly [value]="spMetadataUrl" />
|
||||||
[selectOptions]="saml2NameIdFormatOptions"
|
<div class="input-group-append">
|
||||||
formControlName="spNameIdFormat"
|
<button
|
||||||
>
|
type="button"
|
||||||
</app-select>
|
class="btn btn-outline-secondary"
|
||||||
|
appA11yTitle="{{ 'launch' | i18n }}"
|
||||||
<app-select
|
(click)="launchUri(spMetadataUrl)"
|
||||||
controlId="spOutboundSigningAlgorithm"
|
>
|
||||||
[label]="'spOutboundSigningAlgorithm' | i18n"
|
<i class="bwi bwi-lg bwi-external-link" aria-hidden="true"></i>
|
||||||
[selectOptions]="samlSigningAlgorithmOptions"
|
</button>
|
||||||
formControlName="spOutboundSigningAlgorithm"
|
<button
|
||||||
>
|
type="button"
|
||||||
</app-select>
|
class="btn btn-outline-secondary"
|
||||||
|
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||||
<app-select
|
(click)="copy(spMetadataUrl)"
|
||||||
controlId="spSigningBehavior"
|
>
|
||||||
[label]="'spSigningBehavior' | i18n"
|
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||||
[selectOptions]="saml2SigningBehaviourOptions"
|
</button>
|
||||||
formControlName="spSigningBehavior"
|
</div>
|
||||||
>
|
</div>
|
||||||
</app-select>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<app-select
|
<label>{{ "spAcsUrl" | i18n }}</label>
|
||||||
controlId="spMinIncomingSigningAlgorithm"
|
<div class="input-group">
|
||||||
[label]="'spMinIncomingSigningAlgorithm' | i18n"
|
<input class="form-control" readonly [value]="spAcsUrl" />
|
||||||
[selectOptions]="samlSigningAlgorithmOptions"
|
<div class="input-group-append">
|
||||||
formControlName="spMinIncomingSigningAlgorithm"
|
<button
|
||||||
>
|
type="button"
|
||||||
</app-select>
|
class="btn btn-outline-secondary"
|
||||||
|
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||||
<app-input-checkbox
|
(click)="copy(spAcsUrl)"
|
||||||
controlId="spWantAssertionsSigned"
|
>
|
||||||
formControlName="spWantAssertionsSigned"
|
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||||
[label]="'spWantAssertionsSigned' | i18n"
|
</button>
|
||||||
></app-input-checkbox>
|
</div>
|
||||||
|
</div>
|
||||||
<app-input-checkbox
|
</div>
|
||||||
controlId="spValidateCertificates"
|
<div class="form-group">
|
||||||
formControlName="spValidateCertificates"
|
<label for="spNameIdFormat">{{ "spNameIdFormat" | i18n }}</label>
|
||||||
[label]="'spValidateCertificates' | i18n"
|
<select class="form-control" formControlName="spNameIdFormat" id="spNameIdFormat">
|
||||||
></app-input-checkbox>
|
<option value="0">Not Configured</option>
|
||||||
|
<option value="1">Unspecified</option>
|
||||||
|
<option value="2">Email Address</option>
|
||||||
|
<option value="3">X.509 Subject Name</option>
|
||||||
|
<option value="4">Windows Domain Qualified Name</option>
|
||||||
|
<option value="5">Kerberos Principal Name</option>
|
||||||
|
<option value="6">Entity Identifier</option>
|
||||||
|
<option value="7">Persistent</option>
|
||||||
|
<option value="8">Transient</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="spOutboundSigningAlgorithm">{{ "spOutboundSigningAlgorithm" | i18n }}</label>
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
formControlName="spOutboundSigningAlgorithm"
|
||||||
|
id="spOutboundSigningAlgorithm"
|
||||||
|
>
|
||||||
|
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="spSigningBehavior">{{ "spSigningBehavior" | i18n }}</label>
|
||||||
|
<select class="form-control" formControlName="spSigningBehavior" id="spSigningBehavior">
|
||||||
|
<option value="0">If IdP Wants Authn Requests Signed</option>
|
||||||
|
<option value="1">Always</option>
|
||||||
|
<option value="3">Never</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="spMinIncomingSigningAlgorithm">{{
|
||||||
|
"spMinIncomingSigningAlgorithm" | i18n
|
||||||
|
}}</label>
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
formControlName="spMinIncomingSigningAlgorithm"
|
||||||
|
id="spMinIncomingSigningAlgorithm"
|
||||||
|
>
|
||||||
|
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="spWantAssertionsSigned"
|
||||||
|
formControlName="spWantAssertionsSigned"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="spWantAssertionsSigned">
|
||||||
|
{{ "spWantAssertionsSigned" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="spValidateCertificates"
|
||||||
|
formControlName="spValidateCertificates"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="spValidateCertificates">
|
||||||
|
{{ "spValidateCertificates" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- SAML2 IDP -->
|
<!-- SAML2 IDP -->
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<h2 class="secondary-header">{{ "samlIdpConfig" | i18n }}</h2>
|
<h2>{{ "samlIdpConfig" | i18n }}</h2>
|
||||||
|
|
||||||
<app-input-text
|
|
||||||
[label]="'idpEntityId' | i18n"
|
|
||||||
controlId="idpEntityId"
|
|
||||||
formControlName="idpEntityId"
|
|
||||||
></app-input-text>
|
|
||||||
|
|
||||||
<app-select
|
|
||||||
controlId="idpBindingType"
|
|
||||||
[label]="'idpBindingType' | i18n"
|
|
||||||
[selectOptions]="saml2BindingTypeOptions"
|
|
||||||
formControlName="idpBindingType"
|
|
||||||
>
|
|
||||||
</app-select>
|
|
||||||
|
|
||||||
<app-input-text
|
|
||||||
[label]="'idpSingleSignOnServiceUrl' | i18n"
|
|
||||||
controlId="idpSingleSignOnServiceUrl"
|
|
||||||
[helperText]="'idpSingleSignOnServiceUrlRequired' | i18n"
|
|
||||||
[stripSpaces]="true"
|
|
||||||
formControlName="idpSingleSignOnServiceUrl"
|
|
||||||
></app-input-text>
|
|
||||||
|
|
||||||
<app-input-text
|
|
||||||
[label]="'idpSingleLogoutServiceUrl' | i18n"
|
|
||||||
controlId="idpSingleLogoutServiceUrl"
|
|
||||||
[stripSpaces]="true"
|
|
||||||
formControlName="idpSingleLogoutServiceUrl"
|
|
||||||
></app-input-text>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="idpX509PublicCert">
|
<label for="idpEntityId">{{ "idpEntityId" | i18n }}</label>
|
||||||
{{ "idpX509PublicCert" | i18n }}
|
<input class="form-control" formControlName="idpEntityId" id="idpEntityId" />
|
||||||
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
|
</div>
|
||||||
</label>
|
<div class="form-group">
|
||||||
|
<label for="idpBindingType">{{ "idpBindingType" | i18n }}</label>
|
||||||
|
<select class="form-control" formControlName="idpBindingType" id="idpBindingType">
|
||||||
|
<option value="1">Redirect</option>
|
||||||
|
<option value="2">HTTP POST</option>
|
||||||
|
<option value="4">Artifact</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="idpSingleSignOnServiceUrl">{{ "idpSingleSignOnServiceUrl" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
class="form-control"
|
||||||
|
formControlName="idpSingleSignOnServiceUrl"
|
||||||
|
id="idpSingleSignOnServiceUrl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="idpSingleLogoutServiceUrl">{{ "idpSingleLogoutServiceUrl" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
class="form-control"
|
||||||
|
formControlName="idpSingleLogoutServiceUrl"
|
||||||
|
id="idpSingleLogoutServiceUrl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="idpArtifactResolutionServiceUrl">{{
|
||||||
|
"idpArtifactResolutionServiceUrl" | i18n
|
||||||
|
}}</label>
|
||||||
|
<input
|
||||||
|
class="form-control"
|
||||||
|
formControlName="idpArtifactResolutionServiceUrl"
|
||||||
|
id="idpArtifactResolutionServiceUrl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="idpX509PublicCert">{{ "idpX509PublicCert" | i18n }}</label>
|
||||||
<textarea
|
<textarea
|
||||||
formControlName="idpX509PublicCert"
|
formControlName="idpX509PublicCert"
|
||||||
class="form-control form-control-sm text-monospace"
|
class="form-control form-control-sm text-monospace"
|
||||||
rows="6"
|
rows="6"
|
||||||
id="idpX509PublicCert"
|
id="idpX509PublicCert"
|
||||||
appA11yInvalid
|
|
||||||
aria-describedby="idpX509PublicCertDesc"
|
|
||||||
></textarea>
|
></textarea>
|
||||||
<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>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<app-select
|
<label for="idpOutboundSigningAlgorithm">{{ "idpOutboundSigningAlgorithm" | i18n }}</label>
|
||||||
controlId="idpOutboundSigningAlgorithm"
|
<select
|
||||||
[label]="'idpOutboundSigningAlgorithm' | i18n"
|
class="form-control"
|
||||||
[selectOptions]="samlSigningAlgorithmOptions"
|
formControlName="idpOutboundSigningAlgorithm"
|
||||||
formControlName="idpOutboundSigningAlgorithm"
|
id="idpOutboundSigningAlgorithm"
|
||||||
>
|
>
|
||||||
</app-select>
|
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
|
||||||
|
</select>
|
||||||
<!--TODO: Uncomment once Unsolicited IdP Response is supported-->
|
</div>
|
||||||
<!-- <app-input-checkbox
|
<div class="form-group" [hidden]="true">
|
||||||
controlId="idpAllowUnsolicitedAuthnResponse"
|
<!--TODO: Unhide once Unsolicited IdP Response is supported-->
|
||||||
formControlName="idpAllowUnsolicitedAuthnResponse"
|
<div class="form-check">
|
||||||
[label]="'idpAllowUnsolicitedAuthnResponse' | i18n"
|
<input
|
||||||
></app-input-checkbox> -->
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
<app-input-checkbox
|
id="idpAllowUnsolicitedAuthnResponse"
|
||||||
controlId="idpAllowOutboundLogoutRequests"
|
formControlName="idpAllowUnsolicitedAuthnResponse"
|
||||||
formControlName="idpAllowOutboundLogoutRequests"
|
/>
|
||||||
[label]="'idpAllowOutboundLogoutRequests' | i18n"
|
<label class="form-check-label" for="idpAllowUnsolicitedAuthnResponse">
|
||||||
></app-input-checkbox>
|
{{ "idpAllowUnsolicitedAuthnResponse" | i18n }}
|
||||||
|
</label>
|
||||||
<app-input-checkbox
|
</div>
|
||||||
controlId="idpWantAuthnRequestsSigned"
|
</div>
|
||||||
formControlName="idpWantAuthnRequestsSigned"
|
<div class="form-group">
|
||||||
[label]="'idpSignAuthenticationRequests' | i18n"
|
<div class="form-check">
|
||||||
></app-input-checkbox>
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="idpDisableOutboundLogoutRequests"
|
||||||
|
formControlName="idpDisableOutboundLogoutRequests"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="idpDisableOutboundLogoutRequests">
|
||||||
|
{{ "idpDisableOutboundLogoutRequests" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="idpWantAuthnRequestsSigned"
|
||||||
|
formControlName="idpWantAuthnRequestsSigned"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="idpWantAuthnRequestsSigned">
|
||||||
|
{{ "idpWantAuthnRequestsSigned" | i18n }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -434,15 +490,4 @@
|
|||||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||||
<span>{{ "save" | i18n }}</span>
|
<span>{{ "save" | i18n }}</span>
|
||||||
</button>
|
</button>
|
||||||
<div
|
|
||||||
id="errorSummary"
|
|
||||||
class="error-summary text-danger"
|
|
||||||
*ngIf="this.getErrorCount(ssoConfigForm) as errorCount"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
|
||||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
|
||||||
{{
|
|
||||||
(errorCount === 1 ? "formErrorSummarySingle" : "formErrorSummaryPlural") | i18n: errorCount
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,82 +1,29 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { AbstractControl, FormBuilder, FormGroup } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
import { SelectOptions } from "jslib-angular/interfaces/selectOptions";
|
|
||||||
import { dirtyRequired } from "jslib-angular/validators/dirty.validator";
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import {
|
|
||||||
OpenIdConnectRedirectBehavior,
|
|
||||||
Saml2BindingType,
|
|
||||||
Saml2NameIdFormat,
|
|
||||||
Saml2SigningBehavior,
|
|
||||||
SsoType,
|
|
||||||
} from "jslib-common/enums/ssoEnums";
|
|
||||||
import { Utils } from "jslib-common/misc/utils";
|
|
||||||
import { SsoConfigApi } from "jslib-common/models/api/ssoConfigApi";
|
|
||||||
import { Organization } from "jslib-common/models/domain/organization";
|
|
||||||
import { 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";
|
import { Organization } from "jslib-common/models/domain/organization";
|
||||||
|
|
||||||
|
import { OrganizationSsoRequest } from "jslib-common/models/request/organization/organizationSsoRequest";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-org-manage-sso",
|
selector: "app-org-manage-sso",
|
||||||
templateUrl: "sso.component.html",
|
templateUrl: "sso.component.html",
|
||||||
})
|
})
|
||||||
export class SsoComponent implements OnInit {
|
export class SsoComponent implements OnInit {
|
||||||
readonly ssoType = SsoType;
|
samlSigningAlgorithms = [
|
||||||
|
|
||||||
readonly ssoTypeOptions: SelectOptions[] = [
|
|
||||||
{ name: this.i18nService.t("selectType"), value: SsoType.None, disabled: true },
|
|
||||||
{ name: "OpenID Connect", value: SsoType.OpenIdConnect },
|
|
||||||
{ name: "SAML 2.0", value: SsoType.Saml2 },
|
|
||||||
];
|
|
||||||
|
|
||||||
readonly samlSigningAlgorithms = [
|
|
||||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
||||||
"http://www.w3.org/2000/09/xmldsig#rsa-sha384",
|
"http://www.w3.org/2000/09/xmldsig#rsa-sha384",
|
||||||
"http://www.w3.org/2000/09/xmldsig#rsa-sha512",
|
"http://www.w3.org/2000/09/xmldsig#rsa-sha512",
|
||||||
"http://www.w3.org/2000/09/xmldsig#rsa-sha1",
|
"http://www.w3.org/2000/09/xmldsig#rsa-sha1",
|
||||||
];
|
];
|
||||||
|
|
||||||
readonly saml2SigningBehaviourOptions: SelectOptions[] = [
|
|
||||||
{
|
|
||||||
name: "If IdP Wants Authn Requests Signed",
|
|
||||||
value: Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned,
|
|
||||||
},
|
|
||||||
{ name: "Always", value: Saml2SigningBehavior.Always },
|
|
||||||
{ name: "Never", value: Saml2SigningBehavior.Never },
|
|
||||||
];
|
|
||||||
readonly saml2BindingTypeOptions: SelectOptions[] = [
|
|
||||||
{ name: "Redirect", value: Saml2BindingType.HttpRedirect },
|
|
||||||
{ name: "HTTP POST", value: Saml2BindingType.HttpPost },
|
|
||||||
];
|
|
||||||
readonly saml2NameIdFormatOptions: SelectOptions[] = [
|
|
||||||
{ name: "Not Configured", value: Saml2NameIdFormat.NotConfigured },
|
|
||||||
{ name: "Unspecified", value: Saml2NameIdFormat.Unspecified },
|
|
||||||
{ name: "Email Address", value: Saml2NameIdFormat.EmailAddress },
|
|
||||||
{ name: "X.509 Subject Name", value: Saml2NameIdFormat.X509SubjectName },
|
|
||||||
{ name: "Windows Domain Qualified Name", value: Saml2NameIdFormat.WindowsDomainQualifiedName },
|
|
||||||
{ name: "Kerberos Principal Name", value: Saml2NameIdFormat.KerberosPrincipalName },
|
|
||||||
{ name: "Entity Identifier", value: Saml2NameIdFormat.EntityIdentifier },
|
|
||||||
{ name: "Persistent", value: Saml2NameIdFormat.Persistent },
|
|
||||||
{ name: "Transient", value: Saml2NameIdFormat.Transient },
|
|
||||||
];
|
|
||||||
|
|
||||||
readonly connectRedirectOptions: SelectOptions[] = [
|
|
||||||
{ name: "Redirect GET", value: OpenIdConnectRedirectBehavior.RedirectGet },
|
|
||||||
{ name: "Form POST", value: OpenIdConnectRedirectBehavior.FormPost },
|
|
||||||
];
|
|
||||||
|
|
||||||
showOpenIdCustomizations = false;
|
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
haveTestedKeyConnector = false;
|
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
organization: Organization;
|
organization: Organization;
|
||||||
formPromise: Promise<any>;
|
formPromise: Promise<any>;
|
||||||
@@ -88,57 +35,44 @@ export class SsoComponent implements OnInit {
|
|||||||
spAcsUrl: string;
|
spAcsUrl: string;
|
||||||
|
|
||||||
enabled = this.formBuilder.control(false);
|
enabled = this.formBuilder.control(false);
|
||||||
|
data = this.formBuilder.group({
|
||||||
|
configType: [],
|
||||||
|
|
||||||
openIdForm = this.formBuilder.group(
|
keyConnectorEnabled: [],
|
||||||
{
|
keyConnectorUrl: [],
|
||||||
authority: ["", dirtyRequired],
|
|
||||||
clientId: ["", dirtyRequired],
|
|
||||||
clientSecret: ["", dirtyRequired],
|
|
||||||
metadataAddress: [],
|
|
||||||
redirectBehavior: [OpenIdConnectRedirectBehavior.RedirectGet, dirtyRequired],
|
|
||||||
getClaimsFromUserInfoEndpoint: [],
|
|
||||||
additionalScopes: [],
|
|
||||||
additionalUserIdClaimTypes: [],
|
|
||||||
additionalEmailClaimTypes: [],
|
|
||||||
additionalNameClaimTypes: [],
|
|
||||||
acrValues: [],
|
|
||||||
expectedReturnAcrValue: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
updateOn: "blur",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
samlForm = this.formBuilder.group(
|
// OpenId
|
||||||
{
|
authority: [],
|
||||||
spNameIdFormat: [Saml2NameIdFormat.NotConfigured],
|
clientId: [],
|
||||||
spOutboundSigningAlgorithm: [defaultSigningAlgorithm],
|
clientSecret: [],
|
||||||
spSigningBehavior: [Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned],
|
metadataAddress: [],
|
||||||
spMinIncomingSigningAlgorithm: [defaultSigningAlgorithm],
|
redirectBehavior: [],
|
||||||
spWantAssertionsSigned: [],
|
getClaimsFromUserInfoEndpoint: [],
|
||||||
spValidateCertificates: [],
|
additionalScopes: [],
|
||||||
|
additionalUserIdClaimTypes: [],
|
||||||
|
additionalEmailClaimTypes: [],
|
||||||
|
additionalNameClaimTypes: [],
|
||||||
|
acrValues: [],
|
||||||
|
expectedReturnAcrValue: [],
|
||||||
|
|
||||||
idpEntityId: ["", dirtyRequired],
|
// SAML
|
||||||
idpBindingType: [Saml2BindingType.HttpRedirect],
|
spNameIdFormat: [],
|
||||||
idpSingleSignOnServiceUrl: [],
|
spOutboundSigningAlgorithm: [],
|
||||||
idpSingleLogoutServiceUrl: [],
|
spSigningBehavior: [],
|
||||||
idpX509PublicCert: ["", dirtyRequired],
|
spMinIncomingSigningAlgorithm: [],
|
||||||
idpOutboundSigningAlgorithm: [defaultSigningAlgorithm],
|
spWantAssertionsSigned: [],
|
||||||
idpAllowUnsolicitedAuthnResponse: [],
|
spValidateCertificates: [],
|
||||||
idpAllowOutboundLogoutRequests: [true],
|
|
||||||
idpWantAuthnRequestsSigned: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
updateOn: "blur",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
ssoConfigForm = this.formBuilder.group({
|
idpEntityId: [],
|
||||||
configType: [SsoType.None],
|
idpBindingType: [],
|
||||||
keyConnectorEnabled: [false],
|
idpSingleSignOnServiceUrl: [],
|
||||||
keyConnectorUrl: [""],
|
idpSingleLogoutServiceUrl: [],
|
||||||
openId: this.openIdForm,
|
idpArtifactResolutionServiceUrl: [],
|
||||||
saml: this.samlForm,
|
idpX509PublicCert: [],
|
||||||
|
idpOutboundSigningAlgorithm: [],
|
||||||
|
idpAllowUnsolicitedAuthnResponse: [],
|
||||||
|
idpDisableOutboundLogoutRequests: [],
|
||||||
|
idpWantAuthnRequestsSigned: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -151,25 +85,6 @@ export class SsoComponent implements OnInit {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.ssoConfigForm.get("configType").valueChanges.subscribe((newType: SsoType) => {
|
|
||||||
if (newType === SsoType.OpenIdConnect) {
|
|
||||||
this.openIdForm.enable();
|
|
||||||
this.samlForm.disable();
|
|
||||||
} else if (newType === SsoType.Saml2) {
|
|
||||||
this.openIdForm.disable();
|
|
||||||
this.samlForm.enable();
|
|
||||||
} else {
|
|
||||||
this.openIdForm.disable();
|
|
||||||
this.samlForm.disable();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.samlForm
|
|
||||||
.get("spSigningBehavior")
|
|
||||||
.valueChanges.subscribe(() =>
|
|
||||||
this.samlForm.get("idpX509PublicCert").updateValueAndValidity()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.route.parent.parent.params.subscribe(async (params) => {
|
this.route.parent.parent.params.subscribe(async (params) => {
|
||||||
this.organizationId = params.organizationId;
|
this.organizationId = params.organizationId;
|
||||||
await this.load();
|
await this.load();
|
||||||
@@ -179,7 +94,9 @@ export class SsoComponent implements OnInit {
|
|||||||
async load() {
|
async load() {
|
||||||
this.organization = await this.organizationService.get(this.organizationId);
|
this.organization = await this.organizationService.get(this.organizationId);
|
||||||
const ssoSettings = await this.apiService.getOrganizationSso(this.organizationId);
|
const ssoSettings = await this.apiService.getOrganizationSso(this.organizationId);
|
||||||
this.populateForm(ssoSettings);
|
|
||||||
|
this.data.patchValue(ssoSettings.data);
|
||||||
|
this.enabled.setValue(ssoSettings.enabled);
|
||||||
|
|
||||||
this.callbackPath = ssoSettings.urls.callbackPath;
|
this.callbackPath = ssoSettings.urls.callbackPath;
|
||||||
this.signedOutCallbackPath = ssoSettings.urls.signedOutCallbackPath;
|
this.signedOutCallbackPath = ssoSettings.urls.signedOutCallbackPath;
|
||||||
@@ -187,30 +104,28 @@ export class SsoComponent implements OnInit {
|
|||||||
this.spMetadataUrl = ssoSettings.urls.spMetadataUrl;
|
this.spMetadataUrl = ssoSettings.urls.spMetadataUrl;
|
||||||
this.spAcsUrl = ssoSettings.urls.spAcsUrl;
|
this.spAcsUrl = ssoSettings.urls.spAcsUrl;
|
||||||
|
|
||||||
|
this.keyConnectorUrl.markAsDirty();
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copy(value: string) {
|
||||||
|
this.platformUtilsService.copyToClipboard(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
launchUri(url: string) {
|
||||||
|
this.platformUtilsService.launchUri(url);
|
||||||
|
}
|
||||||
|
|
||||||
async submit() {
|
async submit() {
|
||||||
this.validateForm(this.ssoConfigForm);
|
this.formPromise = this.postData();
|
||||||
|
|
||||||
if (this.ssoConfigForm.get("keyConnectorEnabled").value) {
|
|
||||||
await this.validateKeyConnectorUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.ssoConfigForm.valid) {
|
|
||||||
this.readOutErrors();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = new OrganizationSsoRequest();
|
|
||||||
request.enabled = this.enabled.value;
|
|
||||||
request.data = SsoConfigApi.fromView(this.ssoConfigForm.value as SsoConfigView);
|
|
||||||
|
|
||||||
this.formPromise = this.apiService.postOrganizationSso(this.organizationId, request);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this.formPromise;
|
const response = await this.formPromise;
|
||||||
this.populateForm(response);
|
|
||||||
|
this.data.patchValue(response.data);
|
||||||
|
this.enabled.setValue(response.enabled);
|
||||||
|
|
||||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved"));
|
this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved"));
|
||||||
} catch {
|
} catch {
|
||||||
// Logged by appApiAction, do nothing
|
// Logged by appApiAction, do nothing
|
||||||
@@ -219,8 +134,24 @@ export class SsoComponent implements OnInit {
|
|||||||
this.formPromise = null;
|
this.formPromise = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async postData() {
|
||||||
|
if (this.data.get("keyConnectorEnabled").value) {
|
||||||
|
await this.validateKeyConnectorUrl();
|
||||||
|
|
||||||
|
if (this.keyConnectorUrl.hasError("invalidUrl")) {
|
||||||
|
throw new Error(this.i18nService.t("keyConnectorTestFail"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = new OrganizationSsoRequest();
|
||||||
|
request.enabled = this.enabled.value;
|
||||||
|
request.data = this.data.value;
|
||||||
|
|
||||||
|
return this.apiService.postOrganizationSso(this.organizationId, request);
|
||||||
|
}
|
||||||
|
|
||||||
async validateKeyConnectorUrl() {
|
async validateKeyConnectorUrl() {
|
||||||
if (this.haveTestedKeyConnector) {
|
if (this.keyConnectorUrl.pristine) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,84 +166,18 @@ export class SsoComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.haveTestedKeyConnector = true;
|
this.keyConnectorUrl.markAsPristine();
|
||||||
}
|
|
||||||
|
|
||||||
toggleOpenIdCustomizations() {
|
|
||||||
this.showOpenIdCustomizations = !this.showOpenIdCustomizations;
|
|
||||||
}
|
|
||||||
|
|
||||||
getErrorCount(form: FormGroup): number {
|
|
||||||
return Object.values(form.controls).reduce((acc: number, control: AbstractControl) => {
|
|
||||||
if (control instanceof FormGroup) {
|
|
||||||
return acc + this.getErrorCount(control);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (control.errors == null) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
return acc + Object.keys(control.errors).length;
|
|
||||||
}, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get enableTestKeyConnector() {
|
get enableTestKeyConnector() {
|
||||||
return (
|
return (
|
||||||
this.ssoConfigForm.get("keyConnectorEnabled").value &&
|
this.data.get("keyConnectorEnabled").value &&
|
||||||
!Utils.isNullOrWhitespace(this.keyConnectorUrl?.value)
|
this.keyConnectorUrl != null &&
|
||||||
|
this.keyConnectorUrl.value !== ""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get keyConnectorUrl() {
|
get keyConnectorUrl() {
|
||||||
return this.ssoConfigForm.get("keyConnectorUrl");
|
return this.data.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
import { AuthGuard } from "jslib-angular/guards/auth.guard";
|
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
|
||||||
|
|
||||||
import { Permissions } from "jslib-common/enums/permissions";
|
import { Permissions } from "jslib-common/enums/permissions";
|
||||||
|
|
||||||
import { PermissionsGuard } from "src/app/organizations/guards/permissions.guard";
|
import { OrganizationLayoutComponent } from "src/app/layouts/organization-layout.component";
|
||||||
import { OrganizationLayoutComponent } from "src/app/organizations/layouts/organization-layout.component";
|
|
||||||
import { ManageComponent } from "src/app/organizations/manage/manage.component";
|
import { ManageComponent } from "src/app/organizations/manage/manage.component";
|
||||||
import { NavigationPermissionsService } from "src/app/organizations/services/navigation-permissions.service";
|
import { OrganizationGuardService } from "src/app/services/organization-guard.service";
|
||||||
|
import { OrganizationTypeGuardService } from "src/app/services/organization-type-guard.service";
|
||||||
|
|
||||||
import { SsoComponent } from "./manage/sso.component";
|
import { SsoComponent } from "./manage/sso.component";
|
||||||
|
|
||||||
@@ -15,16 +16,25 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: "organizations/:organizationId",
|
path: "organizations/:organizationId",
|
||||||
component: OrganizationLayoutComponent,
|
component: OrganizationLayoutComponent,
|
||||||
canActivate: [AuthGuard, PermissionsGuard],
|
canActivate: [AuthGuardService, OrganizationGuardService],
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "manage",
|
path: "manage",
|
||||||
component: ManageComponent,
|
component: ManageComponent,
|
||||||
canActivate: [PermissionsGuard],
|
canActivate: [OrganizationTypeGuardService],
|
||||||
data: {
|
data: {
|
||||||
permissions: NavigationPermissionsService.getPermissions("manage").concat(
|
permissions: [
|
||||||
Permissions.ManageSso
|
Permissions.CreateNewCollections,
|
||||||
),
|
Permissions.EditAnyCollection,
|
||||||
|
Permissions.DeleteAnyCollection,
|
||||||
|
Permissions.EditAssignedCollections,
|
||||||
|
Permissions.DeleteAssignedCollections,
|
||||||
|
Permissions.AccessEventLogs,
|
||||||
|
Permissions.ManageGroups,
|
||||||
|
Permissions.ManageUsers,
|
||||||
|
Permissions.ManagePolicies,
|
||||||
|
Permissions.ManageSso,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,31 +2,13 @@ import { CommonModule } from "@angular/common";
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
import { JslibModule } from "jslib-angular/jslib.module";
|
import { OssModule } from "src/app/oss.module";
|
||||||
|
|
||||||
import { InputCheckboxComponent } from "./components/input-checkbox.component";
|
|
||||||
import { InputTextReadOnlyComponent } from "./components/input-text-readonly.component";
|
|
||||||
import { InputTextComponent } from "./components/input-text.component";
|
|
||||||
import { SelectComponent } from "./components/select.component";
|
|
||||||
import { SsoComponent } from "./manage/sso.component";
|
import { SsoComponent } from "./manage/sso.component";
|
||||||
import { OrganizationsRoutingModule } from "./organizations-routing.module";
|
import { OrganizationsRoutingModule } from "./organizations-routing.module";
|
||||||
|
|
||||||
// Form components are for use in the SSO Configuration Form only and should not be exported for use elsewhere.
|
|
||||||
// They will be deprecated by the Component Library.
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [CommonModule, FormsModule, ReactiveFormsModule, OssModule, OrganizationsRoutingModule],
|
||||||
CommonModule,
|
declarations: [SsoComponent],
|
||||||
FormsModule,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
JslibModule,
|
|
||||||
OrganizationsRoutingModule,
|
|
||||||
],
|
|
||||||
declarations: [
|
|
||||||
InputCheckboxComponent,
|
|
||||||
InputTextComponent,
|
|
||||||
InputTextReadOnlyComponent,
|
|
||||||
SelectComponent,
|
|
||||||
SsoComponent,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class OrganizationsModule {}
|
export class OrganizationsModule {}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { FormBuilder } from "@angular/forms";
|
||||||
|
|
||||||
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
|
||||||
import { PolicyType } from "jslib-common/enums/policyType";
|
import { PolicyType } from "jslib-common/enums/policyType";
|
||||||
|
|
||||||
|
import { PolicyRequest } from "jslib-common/models/request/policyRequest";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BasePolicy,
|
BasePolicy,
|
||||||
BasePolicyComponent,
|
BasePolicyComponent,
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ 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 {
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
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 { Organization } from "jslib-common/models/domain/organization";
|
|
||||||
import { Provider } from "jslib-common/models/domain/provider";
|
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||||
|
|
||||||
import { WebProviderService } from "../services/webProvider.service";
|
import { WebProviderService } from "../services/webProvider.service";
|
||||||
|
|
||||||
|
import { Organization } from "jslib-common/models/domain/organization";
|
||||||
|
import { Provider } from "jslib-common/models/domain/provider";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "provider-add-organization",
|
selector: "provider-add-organization",
|
||||||
templateUrl: "add-organization.component.html",
|
templateUrl: "add-organization.component.html",
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { ModalService } from "jslib-angular/services/modal.service";
|
|
||||||
import { 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";
|
||||||
@@ -11,8 +10,13 @@ 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";
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,7 @@
|
|||||||
<p>{{ "joinProviderDesc" | i18n }}</p>
|
<p>{{ "joinProviderDesc" | i18n }}</p>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a
|
<a routerLink="/" [queryParams]="{ email: email }" class="btn btn-primary btn-block">
|
||||||
routerLink="/login"
|
|
||||||
[queryParams]="{ email: email }"
|
|
||||||
class="btn btn-primary btn-block"
|
|
||||||
>
|
|
||||||
{{ "logIn" | i18n }}
|
{{ "logIn" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
|
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
|
||||||
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
|
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { ProviderUserAcceptRequest } from "jslib-common/models/request/provider/providerUserAcceptRequest";
|
import { ProviderUserAcceptRequest } from "jslib-common/models/request/provider/providerUserAcceptRequest";
|
||||||
|
|
||||||
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-accept-provider",
|
selector: "app-accept-provider",
|
||||||
templateUrl: "accept-provider.component.html",
|
templateUrl: "accept-provider.component.html",
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Component, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
|
||||||
import { ProviderUserBulkConfirmRequest } from "jslib-common/models/request/provider/providerUserBulkConfirmRequest";
|
import { ProviderUserBulkConfirmRequest } from "jslib-common/models/request/provider/providerUserBulkConfirmRequest";
|
||||||
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
|
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
|
||||||
|
|
||||||
|
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
||||||
|
|
||||||
import { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "src/app/organizations/manage/bulk/bulk-confirm.component";
|
import { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "src/app/organizations/manage/bulk/bulk-confirm.component";
|
||||||
import { BulkUserDetails } from "src/app/organizations/manage/bulk/bulk-status.component";
|
import { BulkUserDetails } from "src/app/organizations/manage/bulk/bulk-status.component";
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
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 { BaseEventsComponent } from "src/app/common/base.events.component";
|
|
||||||
import { EventService } from "src/app/services/event.service";
|
import { EventService } from "src/app/services/event.service";
|
||||||
|
|
||||||
|
import { BaseEventsComponent } from "src/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 = "provider-events";
|
exportFileName: string = "provider-events";
|
||||||
providerId: string;
|
providerId: string;
|
||||||
|
|
||||||
private providerUsersUserIdMap = new Map<string, any>();
|
private providerUsersUserIdMap = new Map<string, any>();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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-close" aria-hidden="true"></i>
|
<i class="bwi bwi-fw bwi-remove" aria-hidden="true"></i>
|
||||||
{{ "remove" | i18n }}
|
{{ "remove" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
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";
|
||||||
@@ -14,18 +11,26 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
|
|||||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
|
||||||
|
import { ModalService } from "jslib-angular/services/modal.service";
|
||||||
|
import { ValidationService } from "jslib-angular/services/validation.service";
|
||||||
|
|
||||||
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
|
||||||
import { ProviderUserType } from "jslib-common/enums/providerUserType";
|
import { ProviderUserType } from "jslib-common/enums/providerUserType";
|
||||||
|
|
||||||
|
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
|
||||||
|
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
|
||||||
|
|
||||||
|
import { ListResponse } from "jslib-common/models/response/listResponse";
|
||||||
|
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
|
||||||
|
|
||||||
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
|
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
|
||||||
import { ProviderUserConfirmRequest } from "jslib-common/models/request/provider/providerUserConfirmRequest";
|
import { ProviderUserConfirmRequest } from "jslib-common/models/request/provider/providerUserConfirmRequest";
|
||||||
import { ListResponse } from "jslib-common/models/response/listResponse";
|
|
||||||
import { ProviderUserBulkResponse } from "jslib-common/models/response/provider/providerUserBulkResponse";
|
import { ProviderUserBulkResponse } from "jslib-common/models/response/provider/providerUserBulkResponse";
|
||||||
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
|
|
||||||
|
|
||||||
import { BasePeopleComponent } from "src/app/common/base.people.component";
|
import { BasePeopleComponent } from "src/app/common/base.people.component";
|
||||||
import { BulkStatusComponent } from "src/app/organizations/manage/bulk/bulk-status.component";
|
import { BulkStatusComponent } from "src/app/organizations/manage/bulk/bulk-status.component";
|
||||||
import { EntityEventsComponent } from "src/app/organizations/manage/entity-events.component";
|
import { EntityEventsComponent } from "src/app/organizations/manage/entity-events.component";
|
||||||
|
|
||||||
import { BulkConfirmComponent } from "./bulk/bulk-confirm.component";
|
import { BulkConfirmComponent } from "./bulk/bulk-confirm.component";
|
||||||
import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
|
import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
|
||||||
import { UserAddEditComponent } from "./user-add-edit.component";
|
import { UserAddEditComponent } from "./user-add-edit.component";
|
||||||
@@ -153,13 +158,17 @@ export class PeopleComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
async events(user: ProviderUserUserDetailsResponse) {
|
async events(user: ProviderUserUserDetailsResponse) {
|
||||||
await this.modalService.openViewRef(EntityEventsComponent, this.eventsModalRef, (comp) => {
|
const [modal] = await this.modalService.openViewRef(
|
||||||
comp.name = this.userNamePipe.transform(user);
|
EntityEventsComponent,
|
||||||
comp.providerId = this.providerId;
|
this.eventsModalRef,
|
||||||
comp.entityId = user.id;
|
(comp) => {
|
||||||
comp.showUser = false;
|
comp.name = this.userNamePipe.transform(user);
|
||||||
comp.entity = "user";
|
comp.providerId = this.providerId;
|
||||||
});
|
comp.entityId = user.id;
|
||||||
|
comp.showUser = false;
|
||||||
|
comp.entity = "user";
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async bulkRemove() {
|
async bulkRemove() {
|
||||||
@@ -263,14 +272,13 @@ 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), // eslint-disable-line
|
error: keyedErrors.hasOwnProperty(user.id),
|
||||||
message: message,
|
message: message,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ 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 { ProviderUserType } from "jslib-common/enums/providerUserType";
|
|
||||||
import { PermissionsApi } from "jslib-common/models/api/permissionsApi";
|
|
||||||
import { ProviderUserInviteRequest } from "jslib-common/models/request/provider/providerUserInviteRequest";
|
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 { ProviderUserUpdateRequest } from "jslib-common/models/request/provider/providerUserUpdateRequest";
|
import { ProviderUserUpdateRequest } from "jslib-common/models/request/provider/providerUserUpdateRequest";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -21,7 +24,7 @@ export class UserAddEditComponent implements OnInit {
|
|||||||
@Output() onDeletedUser = new EventEmitter();
|
@Output() onDeletedUser = new EventEmitter();
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
editMode = false;
|
editMode: boolean = false;
|
||||||
title: string;
|
title: string;
|
||||||
emails: string;
|
emails: string;
|
||||||
type: ProviderUserType = ProviderUserType.ServiceUser;
|
type: ProviderUserType = ProviderUserType.ServiceUser;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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({
|
||||||
|
|||||||
@@ -1,30 +1,32 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
import { AuthGuard } from "jslib-angular/guards/auth.guard";
|
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 { AddOrganizationComponent } from "./clients/add-organization.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 { PermissionsGuard } from "./guards/provider-type.guard";
|
|
||||||
import { ProviderGuard } from "./guards/provider.guard";
|
|
||||||
import { AcceptProviderComponent } from "./manage/accept-provider.component";
|
import { AcceptProviderComponent } from "./manage/accept-provider.component";
|
||||||
import { EventsComponent } from "./manage/events.component";
|
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 { 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 { FrontendLayoutComponent } from "src/app/layouts/frontend-layout.component";
|
||||||
|
|
||||||
|
import { ProvidersComponent } from "src/app/providers/providers.component";
|
||||||
|
import { ProviderGuardService } from "./services/provider-guard.service";
|
||||||
|
import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
|
||||||
|
import { AccountComponent } from "./settings/account.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuardService],
|
||||||
component: ProvidersComponent,
|
component: ProvidersComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -45,7 +47,7 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuardService],
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "setup",
|
path: "setup",
|
||||||
@@ -54,7 +56,7 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: ":providerId",
|
path: ":providerId",
|
||||||
component: ProvidersLayoutComponent,
|
component: ProvidersLayoutComponent,
|
||||||
canActivate: [ProviderGuard],
|
canActivate: [ProviderGuardService],
|
||||||
children: [
|
children: [
|
||||||
{ path: "", pathMatch: "full", redirectTo: "clients" },
|
{ path: "", pathMatch: "full", redirectTo: "clients" },
|
||||||
{ path: "clients/create", component: CreateOrganizationComponent },
|
{ path: "clients/create", component: CreateOrganizationComponent },
|
||||||
@@ -71,7 +73,7 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: "people",
|
path: "people",
|
||||||
component: PeopleComponent,
|
component: PeopleComponent,
|
||||||
canActivate: [PermissionsGuard],
|
canActivate: [ProviderTypeGuardService],
|
||||||
data: {
|
data: {
|
||||||
titleId: "people",
|
titleId: "people",
|
||||||
permissions: [Permissions.ManageUsers],
|
permissions: [Permissions.ManageUsers],
|
||||||
@@ -80,7 +82,7 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: "events",
|
path: "events",
|
||||||
component: EventsComponent,
|
component: EventsComponent,
|
||||||
canActivate: [PermissionsGuard],
|
canActivate: [ProviderTypeGuardService],
|
||||||
data: {
|
data: {
|
||||||
titleId: "eventLogs",
|
titleId: "eventLogs",
|
||||||
permissions: [Permissions.AccessEventLogs],
|
permissions: [Permissions.AccessEventLogs],
|
||||||
@@ -100,7 +102,7 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: "account",
|
path: "account",
|
||||||
component: AccountComponent,
|
component: AccountComponent,
|
||||||
canActivate: [PermissionsGuard],
|
canActivate: [ProviderTypeGuardService],
|
||||||
data: {
|
data: {
|
||||||
titleId: "myProvider",
|
titleId: "myProvider",
|
||||||
permissions: [Permissions.ManageProvider],
|
permissions: [Permissions.ManageProvider],
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { ComponentFactoryResolver, NgModule } from "@angular/core";
|
import { ComponentFactoryResolver } 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 { OssModule } from "src/app/oss.module";
|
import { ProviderGuardService } from "./services/provider-guard.service";
|
||||||
|
import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
|
||||||
|
import { WebProviderService } from "./services/webProvider.service";
|
||||||
|
|
||||||
|
import { ProvidersLayoutComponent } from "./providers-layout.component";
|
||||||
|
import { ProvidersRoutingModule } from "./providers-routing.module";
|
||||||
|
|
||||||
import { 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 { PermissionsGuard } from "./guards/provider-type.guard";
|
|
||||||
import { ProviderGuard } from "./guards/provider.guard";
|
|
||||||
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";
|
||||||
@@ -19,16 +23,17 @@ 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 { WebProviderService } from "./services/webProvider.service";
|
|
||||||
import { AccountComponent } from "./settings/account.component";
|
import { AccountComponent } from "./settings/account.component";
|
||||||
import { SettingsComponent } from "./settings/settings.component";
|
import { SettingsComponent } from "./settings/settings.component";
|
||||||
|
|
||||||
import { SetupProviderComponent } from "./setup/setup-provider.component";
|
import { SetupProviderComponent } from "./setup/setup-provider.component";
|
||||||
import { SetupComponent } from "./setup/setup.component";
|
import { SetupComponent } from "./setup/setup.component";
|
||||||
|
|
||||||
|
import { OssModule } from "src/app/oss.module";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, FormsModule, OssModule, JslibModule, ProvidersRoutingModule],
|
imports: [CommonModule, FormsModule, OssModule, ProvidersRoutingModule],
|
||||||
declarations: [
|
declarations: [
|
||||||
AcceptProviderComponent,
|
AcceptProviderComponent,
|
||||||
AccountComponent,
|
AccountComponent,
|
||||||
@@ -46,7 +51,7 @@ import { SetupComponent } from "./setup/setup.component";
|
|||||||
SetupProviderComponent,
|
SetupProviderComponent,
|
||||||
UserAddEditComponent,
|
UserAddEditComponent,
|
||||||
],
|
],
|
||||||
providers: [WebProviderService, ProviderGuard, PermissionsGuard],
|
providers: [WebProviderService, ProviderGuardService, ProviderTypeGuardService],
|
||||||
})
|
})
|
||||||
export class ProvidersModule {
|
export class ProvidersModule {
|
||||||
constructor(modalService: ModalService, componentFactoryResolver: ComponentFactoryResolver) {
|
constructor(modalService: ModalService, componentFactoryResolver: ComponentFactoryResolver) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
|
|||||||
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
import { ProviderService } from "jslib-common/abstractions/provider.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProviderGuard implements CanActivate {
|
export class ProviderGuardService implements CanActivate {
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
@@ -2,10 +2,11 @@ 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()
|
||||||
export class PermissionsGuard implements CanActivate {
|
export class ProviderTypeGuardService implements CanActivate {
|
||||||
constructor(private providerService: ProviderService, private router: Router) {}
|
constructor(private providerService: ProviderService, private router: Router) {}
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot) {
|
async canActivate(route: ActivatedRouteSnapshot) {
|
||||||
@@ -3,6 +3,7 @@ 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,7 +6,9 @@ import { I18nService } from "jslib-common/abstractions/i18n.service";
|
|||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { 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,6 +1,7 @@
|
|||||||
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({
|
||||||
@@ -8,11 +9,15 @@ import { ProviderService } from "jslib-common/abstractions/provider.service";
|
|||||||
templateUrl: "settings.component.html",
|
templateUrl: "settings.component.html",
|
||||||
})
|
})
|
||||||
export class SettingsComponent {
|
export class SettingsComponent {
|
||||||
constructor(private route: ActivatedRoute, private providerService: ProviderService) {}
|
constructor(
|
||||||
|
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) => {
|
||||||
await this.providerService.get(params.providerId);
|
const provider = await this.providerService.get(params.providerId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,7 @@
|
|||||||
<p>{{ "setupProviderLoginDesc" | i18n }}</p>
|
<p>{{ "setupProviderLoginDesc" | i18n }}</p>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a
|
<a routerLink="/" [queryParams]="{ email: email }" class="btn btn-primary btn-block">
|
||||||
routerLink="/login"
|
|
||||||
[queryParams]="{ email: email }"
|
|
||||||
class="btn btn-primary btn-block"
|
|
||||||
>
|
|
||||||
{{ "logIn" | i18n }}
|
{{ "logIn" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export class SetupProviderComponent extends BaseAcceptComponent {
|
|||||||
this.router.navigate(["/providers/setup"], { queryParams: qParams });
|
this.router.navigate(["/providers/setup"], { queryParams: qParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
async unauthedHandler(qParams: any) {
|
// tslint:disable-next-line
|
||||||
// Empty
|
async unauthedHandler(qParams: any) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
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,7 +7,6 @@
|
|||||||
"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,9 +1 @@
|
|||||||
{
|
{}
|
||||||
"dev": {
|
|
||||||
"proxyApi": "http://localhost:4001",
|
|
||||||
"proxyIdentity": "http://localhost:33657",
|
|
||||||
"proxyEvents": "http://localhost:46274",
|
|
||||||
"proxyNotifications": "http://localhost:61841",
|
|
||||||
"port": 8081
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html class="theme_light">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta
|
<meta
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<body class="layout_frontend">
|
<body class="layout_frontend">
|
||||||
<div class="row justify-content-md-center mt-5">
|
<div class="row justify-content-md-center mt-5">
|
||||||
<div>
|
<div>
|
||||||
<img src="..//images/logo-dark@2x.png" class="logo mb-2" alt="Bitwarden" />
|
<img src="../../src/images/logo-dark@2x.png" class="logo mb-2" alt="Bitwarden" />
|
||||||
<p id="captchaRequired" class="lead text-center mx-4 mb-4">Captcha Required</p>
|
<p id="captchaRequired" class="lead text-center mx-4 mb-4">Captcha Required</p>
|
||||||
<div id="captcha"></div>
|
<div id="captcha"></div>
|
||||||
</div>
|
</div>
|
||||||
46
connectors/src/captcha/captcha-mobile.scss
Normal file
46
connectors/src/captcha/captcha-mobile.scss
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
@import "../common/styles.scss";
|
||||||
|
|
||||||
|
.justify-content-md-center {
|
||||||
|
justify-content: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-right: -10px;
|
||||||
|
margin-left: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-5,
|
||||||
|
.my-5 {
|
||||||
|
margin-top: 3rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-2,
|
||||||
|
.my-2 {
|
||||||
|
margin-bottom: 0.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-4,
|
||||||
|
.mx-4 {
|
||||||
|
margin-left: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-4,
|
||||||
|
.my-4 {
|
||||||
|
margin-bottom: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr-4,
|
||||||
|
.mx-4 {
|
||||||
|
margin-right: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { b64Decode, getQsParam } from "./common";
|
import { b64Decode, getQsParam } from "../common";
|
||||||
|
|
||||||
declare let hcaptcha: any;
|
declare var hcaptcha: any;
|
||||||
|
|
||||||
if (window.location.pathname.includes("mobile")) {
|
if (window.location.pathname.includes("mobile")) {
|
||||||
|
// tslint:disable-next-line
|
||||||
require("./captcha-mobile.scss");
|
require("./captcha-mobile.scss");
|
||||||
} else {
|
} else {
|
||||||
|
// tslint:disable-next-line
|
||||||
require("./captcha.scss");
|
require("./captcha.scss");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ async function start() {
|
|||||||
|
|
||||||
let decodedData: any;
|
let decodedData: any;
|
||||||
try {
|
try {
|
||||||
decodedData = JSON.parse(b64Decode(data, true));
|
decodedData = JSON.parse(b64Decode(data));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error("Cannot parse data.");
|
error("Cannot parse data.");
|
||||||
return;
|
return;
|
||||||
@@ -69,7 +71,7 @@ async function start() {
|
|||||||
script.src = src;
|
script.src = src;
|
||||||
script.async = true;
|
script.async = true;
|
||||||
script.defer = true;
|
script.defer = true;
|
||||||
script.addEventListener("load", () => {
|
script.addEventListener("load", (e) => {
|
||||||
hcaptcha.render("captcha", {
|
hcaptcha.render("captcha", {
|
||||||
sitekey: encodeURIComponent(decodedData.siteKey),
|
sitekey: encodeURIComponent(decodedData.siteKey),
|
||||||
callback: "captchaSuccess",
|
callback: "captchaSuccess",
|
||||||
@@ -126,7 +128,6 @@ function info(message: string | object) {
|
|||||||
|
|
||||||
async function watchHeight() {
|
async function watchHeight() {
|
||||||
const imagesDiv = document.body.lastChild as HTMLElement;
|
const imagesDiv = document.body.lastChild as HTMLElement;
|
||||||
// eslint-disable-next-line
|
|
||||||
while (true) {
|
while (true) {
|
||||||
info({
|
info({
|
||||||
height:
|
height:
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
export function getQsParam(name: string) {
|
export function getQsParam(name: string) {
|
||||||
const url = window.location.href;
|
const url = window.location.href;
|
||||||
// eslint-disable-next-line
|
|
||||||
name = name.replace(/[\[\]]/g, "\\$&");
|
name = name.replace(/[\[\]]/g, "\\$&");
|
||||||
const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)");
|
const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)");
|
||||||
const results = regex.exec(url);
|
const results = regex.exec(url);
|
||||||
@@ -15,11 +14,7 @@ export function getQsParam(name: string) {
|
|||||||
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function b64Decode(str: string, spaceAsPlus = false) {
|
export function b64Decode(str: string) {
|
||||||
if (spaceAsPlus) {
|
|
||||||
str = str.replace(/ /g, "+");
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeURIComponent(
|
return decodeURIComponent(
|
||||||
Array.prototype.map
|
Array.prototype.map
|
||||||
.call(atob(str), (c: string) => {
|
.call(atob(str), (c: string) => {
|
||||||
22
connectors/src/common/styles.scss
Normal file
22
connectors/src/common/styles.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
@import "~bootstrap/scss/_functions";
|
||||||
|
@import "~bootstrap/scss/_variables";
|
||||||
|
@import "~bootstrap/scss/_mixins";
|
||||||
|
@import "~bootstrap/scss/_root";
|
||||||
|
@import "~bootstrap/scss/_reboot";
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.theme_light body.layout_frontend {
|
||||||
|
background-color: #ecf0f5;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.logo {
|
||||||
|
display: block;
|
||||||
|
height: 43px;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
width: 284px;
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: #efeff4 url("../images/loading.svg") 0 0 no-repeat;
|
background: #efeff4 url("../../../src/images/loading.svg") 0 0 no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as DuoWebSDK from "duo_web_sdk";
|
import * as DuoWebSDK from "duo_web_sdk";
|
||||||
|
import { getQsParam } from "../common";
|
||||||
|
|
||||||
import { getQsParam } from "./common";
|
// tslint:disable-next-line
|
||||||
|
|
||||||
require("./duo.scss");
|
require("./duo.scss");
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
const frameElement = document.createElement("iframe");
|
const frameElement = document.createElement("iframe");
|
||||||
frameElement.setAttribute("id", "duo_iframe");
|
frameElement.setAttribute("id", "duo_iframe");
|
||||||
setFrameHeight();
|
setFrameHeight();
|
||||||
@@ -41,7 +41,7 @@ function invokeCSCode(data: string) {
|
|||||||
try {
|
try {
|
||||||
(window as any).invokeCSharpAction(data);
|
(window as any).invokeCSharpAction(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line
|
// tslint:disable-next-line
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
47
connectors/src/sso.html
Normal file
47
connectors/src/sso.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="theme_light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=1010" />
|
||||||
|
<meta name="theme-color" content="#175DDC" />
|
||||||
|
|
||||||
|
<title>Bitwarden</title>
|
||||||
|
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../../src/images/icons/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../../src/images/icons/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../../src/images/icons/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="mask-icon" href="../../src/images/icons/safari-pinned-tab.svg" color="#175DDC" />
|
||||||
|
<link rel="manifest" href="../../src/manifest.json" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="layout_frontend">
|
||||||
|
<div class="mt-5 d-flex justify-content-center">
|
||||||
|
<div>
|
||||||
|
<img src="../../src/images/logo-dark@2x.png" class="mb-4 logo" alt="Bitwarden" />
|
||||||
|
<div id="content">
|
||||||
|
<p class="text-center">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin bwi-2x text-muted"
|
||||||
|
title="Loading"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
82
connectors/src/sso/sso.scss
Normal file
82
connectors/src/sso/sso.scss
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
@import "../common/styles.scss";
|
||||||
|
|
||||||
|
.mt-5,
|
||||||
|
.my-5 {
|
||||||
|
margin-top: 3rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-flex {
|
||||||
|
display: -ms-flexbox !important;
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-content-center {
|
||||||
|
-ms-flex-pack: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-4,
|
||||||
|
.my-4 {
|
||||||
|
margin-bottom: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
$icomoon-font-family: "bwi-font" !default;
|
||||||
|
$icomoon-font-path: "~@bitwarden/jslib-angular/src/scss/bwicons/fonts/" !default;
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "#{$icomoon-font-family}";
|
||||||
|
src: url($icomoon-font-path + "bwi-font.svg") format("svg"),
|
||||||
|
url($icomoon-font-path + "bwi-font.ttf") format("truetype"),
|
||||||
|
url($icomoon-font-path + "bwi-font.woff") format("woff"),
|
||||||
|
url($icomoon-font-path + "bwi-font.woff2") format("woff2");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base Class
|
||||||
|
.bwi {
|
||||||
|
/* use !important to prevent issues with browser extensions that change fonts */
|
||||||
|
font-family: "#{$icomoon-font-family}" !important;
|
||||||
|
speak: never;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
text-transform: none;
|
||||||
|
line-height: 1;
|
||||||
|
display: inline-block;
|
||||||
|
/* Better Font Rendering */
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bwi-2x {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spin Animations
|
||||||
|
.bwi-spin {
|
||||||
|
animation: bwi-spin 2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bwi-spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(359deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotation
|
||||||
|
.bwi-rotate-270 {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bwi-spinner:before {
|
||||||
|
content: "\e937";
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { getQsParam } from "./common";
|
import { getQsParam } from "../common";
|
||||||
|
|
||||||
|
// tslint:disable-next-line
|
||||||
require("./sso.scss");
|
require("./sso.scss");
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
const code = getQsParam("code");
|
const code = getQsParam("code");
|
||||||
const state = getQsParam("state");
|
const state = getQsParam("state");
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html class="theme_light">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Bitwarden WebAuthn Connector</title>
|
<title>Bitwarden WebAuthn Connector</title>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center mt-5">
|
<div class="row justify-content-center mt-5">
|
||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<img src="../images/logo-dark@2x.png" class="mb-4 logo" alt="Bitwarden" />
|
<img src="../../src/images/logo-dark@2x.png" class="mb-4 logo" alt="Bitwarden" />
|
||||||
<div id="spinner">
|
<div id="spinner">
|
||||||
<p class="text-center">
|
<p class="text-center">
|
||||||
<i
|
<i
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html class="theme_light">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta
|
<meta
|
||||||
@@ -14,12 +14,12 @@
|
|||||||
<body style="background: transparent">
|
<body style="background: transparent">
|
||||||
<div class="row justify-content-md-center mt-5">
|
<div class="row justify-content-md-center mt-5">
|
||||||
<div>
|
<div>
|
||||||
<img src="../images/logo-dark@2x.png" class="logo mb-2" alt="Bitwarden" />
|
<img src="../../src/images/logo-dark@2x.png" class="logo mb-2" alt="Bitwarden" />
|
||||||
<p id="webauthn-header" class="lead text-center mx-4 mb-4"></p>
|
<p id="webauthn-header" class="lead text-center mx-4 mb-4"></p>
|
||||||
<picture>
|
<picture>
|
||||||
<source srcset="../images/u2fkey-mobile.avif" type="image/avif" />
|
<source srcset="../../src/images/u2fkey-mobile.avif" type="image/avif" />
|
||||||
<source srcset="../images/u2fkey-mobile.webp" type="image/webp" />
|
<source srcset="../../src/images/u2fkey-mobile.webp" type="image/webp" />
|
||||||
<img src="../images/u2fkey-mobile.jpg" class="rounded img-fluid" />
|
<img src="../../src/images/u2fkey-mobile.jpg" class="rounded img-fluid" />
|
||||||
</picture>
|
</picture>
|
||||||
<div class="text-center mt-4">
|
<div class="text-center mt-4">
|
||||||
<button id="webauthn-button" class="btn btn-primary btn-lg"></button>
|
<button id="webauthn-button" class="btn btn-primary btn-lg"></button>
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<body style="background: transparent">
|
<body style="background: transparent">
|
||||||
<picture>
|
<picture>
|
||||||
<source srcset="../images/u2fkey.avif" type="image/avif" />
|
<source srcset="../../src/images/u2fkey.avif" type="image/avif" />
|
||||||
<source srcset="../images/u2fkey.webp" type="image/webp" />
|
<source srcset="../../src/images/u2fkey.webp" type="image/webp" />
|
||||||
<img src="../images/u2fkey.jpg" class="rounded img-fluid mb-3" />
|
<img src="../../src/images/u2fkey.jpg" class="rounded img-fluid mb-3" />
|
||||||
</picture>
|
</picture>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<button id="webauthn-button" class="btn btn-primary"></button>
|
<button id="webauthn-button" class="btn btn-primary"></button>
|
||||||
@@ -28,7 +28,6 @@ export function parseWebauthnJson(jsonString: string) {
|
|||||||
json.challenge = Uint8Array.from(atob(challenge), (c) => c.charCodeAt(0));
|
json.challenge = Uint8Array.from(atob(challenge), (c) => c.charCodeAt(0));
|
||||||
|
|
||||||
json.allowCredentials.forEach((listItem: any) => {
|
json.allowCredentials.forEach((listItem: any) => {
|
||||||
// eslint-disable-next-line
|
|
||||||
const fixedId = listItem.id.replace(/\_/g, "/").replace(/\-/g, "+");
|
const fixedId = listItem.id.replace(/\_/g, "/").replace(/\-/g, "+");
|
||||||
listItem.id = Uint8Array.from(atob(fixedId), (c) => c.charCodeAt(0));
|
listItem.id = Uint8Array.from(atob(fixedId), (c) => c.charCodeAt(0));
|
||||||
});
|
});
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import { b64Decode, getQsParam } from "./common";
|
import { b64Decode, getQsParam } from "../common";
|
||||||
import { buildDataString, parseWebauthnJson } from "./common-webauthn";
|
import { buildDataString, parseWebauthnJson } from "./common-webauthn";
|
||||||
|
|
||||||
|
// tslint:disable-next-line
|
||||||
require("./webauthn.scss");
|
require("./webauthn.scss");
|
||||||
|
|
||||||
let parsed = false;
|
let parsed = false;
|
||||||
let webauthnJson: any;
|
let webauthnJson: any;
|
||||||
let parentUrl: string = null;
|
let parentUrl: string = null;
|
||||||
|
let parentOrigin: string = null;
|
||||||
let sentSuccess = false;
|
let sentSuccess = false;
|
||||||
let locale = "en";
|
let locale: string = "en";
|
||||||
|
|
||||||
let locales: any = {};
|
let locales: any = {};
|
||||||
|
|
||||||
@@ -22,6 +24,7 @@ function parseParameters() {
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
parentUrl = decodeURIComponent(parentUrl);
|
parentUrl = decodeURIComponent(parentUrl);
|
||||||
|
parentOrigin = new URL(parentUrl).origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
locale = getQsParam("locale").replace("-", "_");
|
locale = getQsParam("locale").replace("-", "_");
|
||||||
@@ -63,7 +66,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
try {
|
try {
|
||||||
locales = await loadLocales(locale);
|
locales = await loadLocales(locale);
|
||||||
} catch {
|
} catch {
|
||||||
// eslint-disable-next-line
|
// tslint:disable-next-line:no-console
|
||||||
console.error("Failed to load the locale", locale);
|
console.error("Failed to load the locale", locale);
|
||||||
locales = await loadLocales("en");
|
locales = await loadLocales("en");
|
||||||
}
|
}
|
||||||
@@ -82,7 +85,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function loadLocales(newLocale: string) {
|
async function loadLocales(newLocale: string) {
|
||||||
const filePath = `locales/${newLocale}/messages.json?cache=${process.env.CACHE_TAG}`;
|
const filePath = `/locales/${newLocale}/messages.json?cache=${process.env.CACHE_TAG}`;
|
||||||
const localesResult = await fetch(filePath);
|
const localesResult = await fetch(filePath);
|
||||||
return await localesResult.json();
|
return await localesResult.json();
|
||||||
}
|
}
|
||||||
197
connectors/src/webauthn/webauthn.scss
Normal file
197
connectors/src/webauthn/webauthn.scss
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
@import "../common/styles.scss";
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-width: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-3,
|
||||||
|
.my-3 {
|
||||||
|
margin-bottom: 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded {
|
||||||
|
border-radius: 0.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-fluid {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
user-select: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-top-color: transparent;
|
||||||
|
border-right-color: transparent;
|
||||||
|
border-bottom-color: transparent;
|
||||||
|
border-left-color: transparent;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
|
||||||
|
border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #175ddc;
|
||||||
|
border-color: #175ddc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:not(:disabled):not(.disabled) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover,
|
||||||
|
.swal2-popup .swal2-actions button:hover {
|
||||||
|
color: #333;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #134eb9;
|
||||||
|
border-color: #1249ae;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Mobile **/
|
||||||
|
|
||||||
|
.mt-5,
|
||||||
|
.my-5 {
|
||||||
|
margin-top: 3rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-content-center,
|
||||||
|
.justify-content-md-center {
|
||||||
|
justify-content: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-right: -10px;
|
||||||
|
margin-left: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-2,
|
||||||
|
.my-2 {
|
||||||
|
margin-bottom: 0.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-4,
|
||||||
|
.mx-4 {
|
||||||
|
margin-left: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-4,
|
||||||
|
.my-4 {
|
||||||
|
margin-bottom: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr-4,
|
||||||
|
.mx-4 {
|
||||||
|
margin-right: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fallback **/
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 980px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-5 {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
flex: 0 0 41.6666666667%;
|
||||||
|
max-width: 41.6666666667%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
word-wrap: break-word;
|
||||||
|
background-color: #fff;
|
||||||
|
background-clip: border-box;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-block {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 1px;
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
position: relative;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-top-color: transparent;
|
||||||
|
border-right-color: transparent;
|
||||||
|
border-bottom-color: transparent;
|
||||||
|
border-left-color: transparent;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
color: #73271e;
|
||||||
|
background-color: #f8dbd7;
|
||||||
|
border-color: #f5cdc8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
padding-left: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check-input {
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
margin-left: -1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"],
|
||||||
|
input[type="checkbox"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check-label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border: 0;
|
||||||
|
border-top-color: currentcolor;
|
||||||
|
border-top-style: none;
|
||||||
|
border-top-width: 0px;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { b64Decode, getQsParam } from "./common";
|
import { b64Decode, getQsParam } from "../common";
|
||||||
import { buildDataString, parseWebauthnJson } from "./common-webauthn";
|
import { buildDataString, parseWebauthnJson } from "./common-webauthn";
|
||||||
|
|
||||||
|
// tslint:disable-next-line
|
||||||
require("./webauthn.scss");
|
require("./webauthn.scss");
|
||||||
|
|
||||||
const mobileCallbackUri = "bitwarden://webauthn-callback";
|
const mobileCallbackUri = "bitwarden://webauthn-callback";
|
||||||
150
connectors/webpack.config.js
Normal file
150
connectors/webpack.config.js
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const webpack = require("webpack");
|
||||||
|
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
|
const HtmlWebpackInjector = require("html-webpack-injector");
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
|
||||||
|
const NODE_ENV = process.env.NODE_ENV == null ? "development" : process.env.NODE_ENV;
|
||||||
|
|
||||||
|
const moduleRules = [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
enforce: "pre",
|
||||||
|
loader: "tslint-loader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "ts-loader",
|
||||||
|
options: {
|
||||||
|
transpileOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(html)$/,
|
||||||
|
loader: "html-loader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
|
||||||
|
exclude: /loading(|-white).svg/,
|
||||||
|
generator: {
|
||||||
|
filename: "fonts/[name].[contenthash][ext]",
|
||||||
|
},
|
||||||
|
type: "asset/resource",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(jpe?g|png|gif|svg|webp|avif)$/i,
|
||||||
|
exclude: /.*(fontawesome-webfont)\.svg/,
|
||||||
|
generator: {
|
||||||
|
filename: "images/[name].[contenthash][ext]",
|
||||||
|
},
|
||||||
|
type: "asset/resource",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: MiniCssExtractPlugin.loader,
|
||||||
|
},
|
||||||
|
"css-loader",
|
||||||
|
"sass-loader",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
new HtmlWebpackInjector(),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./src/duo.html",
|
||||||
|
filename: "duo.html",
|
||||||
|
chunks: ["duo"],
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./src/webauthn.html",
|
||||||
|
filename: "webauthn.html",
|
||||||
|
chunks: ["webauthn"],
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./src/webauthn-mobile.html",
|
||||||
|
filename: "webauthn-mobile.html",
|
||||||
|
chunks: ["webauthn"],
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./src/webauthn-fallback.html",
|
||||||
|
filename: "webauthn-fallback.html",
|
||||||
|
chunks: ["webauthn-fallback"],
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./src/sso.html",
|
||||||
|
filename: "sso.html",
|
||||||
|
chunks: ["sso"],
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./src/captcha.html",
|
||||||
|
filename: "captcha.html",
|
||||||
|
chunks: ["captcha"],
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./src/captcha-mobile.html",
|
||||||
|
filename: "captcha-mobile.html",
|
||||||
|
chunks: ["captcha"],
|
||||||
|
}),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: "assets/[name].[contenthash].css",
|
||||||
|
chunkFilename: "assets/[id].[contenthash].css",
|
||||||
|
}),
|
||||||
|
new webpack.EnvironmentPlugin({
|
||||||
|
CACHE_TAG: Math.random().toString(36).substring(7),
|
||||||
|
}),
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
process: "process/browser",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const webpackConfig = {
|
||||||
|
mode: NODE_ENV,
|
||||||
|
devtool: "source-map",
|
||||||
|
entry: {
|
||||||
|
webauthn: "./src/webauthn/webauthn.ts",
|
||||||
|
"webauthn-fallback": "./src/webauthn/webauthn-fallback.ts",
|
||||||
|
duo: "./src/duo/duo.ts",
|
||||||
|
sso: "./src/sso/sso.ts",
|
||||||
|
captcha: "./src/captcha/captcha.ts",
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
splitChunks: {
|
||||||
|
cacheGroups: {
|
||||||
|
commons: {
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
name: "app/vendor",
|
||||||
|
chunks: (chunk) => {
|
||||||
|
return chunk.name === "app/main";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: [".ts", ".js"],
|
||||||
|
symlinks: false,
|
||||||
|
modules: [path.resolve("../", "node_modules")],
|
||||||
|
fallback: {
|
||||||
|
buffer: false,
|
||||||
|
util: require.resolve("util/"),
|
||||||
|
assert: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: "assets/[name].[contenthash].js",
|
||||||
|
path: path.resolve(__dirname, "build"),
|
||||||
|
publicPath: "/connectors/",
|
||||||
|
clean: true,
|
||||||
|
},
|
||||||
|
module: { rules: moduleRules },
|
||||||
|
plugins: plugins,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = webpackConfig;
|
||||||
2
jslib
2
jslib
Submodule jslib updated: a6fe5c7900...92a65b7b36
10083
package-lock.json
generated
10083
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/web-vault",
|
"name": "@bitwarden/web-vault",
|
||||||
"version": "2.28.1",
|
"version": "2.25.1",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"repository": "https://github.com/bitwarden/web",
|
"repository": "https://github.com/bitwarden/web",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
"dist:bit:selfhost": "npm run build:bit:selfhost:prod",
|
"dist:bit:selfhost": "npm run build:bit:selfhost:prod",
|
||||||
"deploy": "npm run dist:bit && gh-pages -d build",
|
"deploy": "npm run dist:bit && gh-pages -d build",
|
||||||
"deploy:dev": "npm run dist:bit && gh-pages -d build -r git@github.com:kspearrin/bitwarden-web-dev.git",
|
"deploy:dev": "npm run dist:bit && gh-pages -d build -r git@github.com:kspearrin/bitwarden-web-dev.git",
|
||||||
"lint": "eslint . && prettier --check .",
|
"lint": "tslint 'src/**/*.ts' 'bitwarden_license/src/**/*.ts' && prettier --check .",
|
||||||
"lint:fix": "eslint . --fix",
|
"lint:fix": "tslint 'src/**/*.ts' 'bitwarden_license/src/**/*.ts' --fix",
|
||||||
"prettier": "prettier --write .",
|
"prettier": "prettier --write .",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
@@ -41,18 +41,11 @@
|
|||||||
"@types/node": "^16.11.12",
|
"@types/node": "^16.11.12",
|
||||||
"@types/webcrypto": "^0.0.28",
|
"@types/webcrypto": "^0.0.28",
|
||||||
"@types/webpack": "^5.28.0",
|
"@types/webpack": "^5.28.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
|
||||||
"@typescript-eslint/parser": "^5.10.1",
|
|
||||||
"autoprefixer": "^10.4.2",
|
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"copy-webpack-plugin": "^10.0.0",
|
"copy-webpack-plugin": "^10.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "^6.5.1",
|
"css-loader": "^6.5.1",
|
||||||
"eslint": "^8.7.0",
|
|
||||||
"eslint-config-prettier": "^8.3.0",
|
|
||||||
"eslint-import-resolver-typescript": "^2.5.0",
|
|
||||||
"eslint-plugin-import": "^2.25.4",
|
|
||||||
"gh-pages": "^3.1.0",
|
"gh-pages": "^3.1.0",
|
||||||
"html-loader": "^3.0.1",
|
"html-loader": "^3.0.1",
|
||||||
"html-webpack-injector": "1.1.4",
|
"html-webpack-injector": "1.1.4",
|
||||||
@@ -60,17 +53,15 @@
|
|||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"lint-staged": "^12.1.2",
|
"lint-staged": "^12.1.2",
|
||||||
"mini-css-extract-plugin": "^2.4.5",
|
"mini-css-extract-plugin": "^2.4.5",
|
||||||
"postcss": "^8.4.6",
|
|
||||||
"postcss-loader": "^6.2.1",
|
|
||||||
"prettier": "2.5.1",
|
"prettier": "2.5.1",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"sass": "^1.32.10",
|
"sass": "^1.32.10",
|
||||||
"sass-loader": "^12.4.0",
|
"sass-loader": "^12.4.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"tailwindcss": "^3.0.18",
|
|
||||||
"terser-webpack-plugin": "^5.2.5",
|
"terser-webpack-plugin": "^5.2.5",
|
||||||
"ts-loader": "^9.2.5",
|
"ts-loader": "^9.2.5",
|
||||||
|
"tslint": "^6.1.3",
|
||||||
|
"tslint-loader": "^3.5.4",
|
||||||
"typescript": "4.3.5",
|
"typescript": "4.3.5",
|
||||||
"util": "^0.12.4",
|
"util": "^0.12.4",
|
||||||
"webpack": "^5.64.4",
|
"webpack": "^5.64.4",
|
||||||
@@ -90,30 +81,27 @@
|
|||||||
"@bitwarden/jslib-angular": "file:jslib/angular",
|
"@bitwarden/jslib-angular": "file:jslib/angular",
|
||||||
"@bitwarden/jslib-common": "file:jslib/common",
|
"@bitwarden/jslib-common": "file:jslib/common",
|
||||||
"bootstrap": "4.6.0",
|
"bootstrap": "4.6.0",
|
||||||
"braintree-web-drop-in": "1.33.1",
|
"braintree-web-drop-in": "1.30.1",
|
||||||
"browser-hrtime": "^1.1.8",
|
"browser-hrtime": "^1.1.8",
|
||||||
"core-js": "^3.11.0",
|
"core-js": "^3.11.0",
|
||||||
"date-input-polyfill": "^2.14.0",
|
"date-input-polyfill": "^2.14.0",
|
||||||
|
"font-awesome": "4.7.0",
|
||||||
"jquery": "3.6.0",
|
"jquery": "3.6.0",
|
||||||
"jszip": "^3.7.1",
|
|
||||||
"ngx-infinite-scroll": "^10.0.1",
|
"ngx-infinite-scroll": "^10.0.1",
|
||||||
"ngx-toastr": "14.1.4",
|
"ngx-toastr": "14.1.4",
|
||||||
"node-forge": "^1.3.1",
|
|
||||||
"popper.js": "1.16.1",
|
"popper.js": "1.16.1",
|
||||||
"qrious": "4.0.2",
|
"qrious": "4.0.2",
|
||||||
"rxjs": "^7.4.0",
|
"rxjs": "^7.4.0",
|
||||||
"sweetalert2": "^10.16.6",
|
"sweetalert2": "^10.16.6",
|
||||||
"webcrypto-shim": "0.1.7",
|
"webcrypto-shim": "0.1.7",
|
||||||
"whatwg-fetch": "3.6.2",
|
"whatwg-fetch": "3.6.2"
|
||||||
"zone.js": "0.11.4"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "~16",
|
"node": "~16",
|
||||||
"npm": "~8"
|
"npm": "~8"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"./!(jslib)**": "prettier --ignore-unknown --write",
|
"*": "prettier --ignore-unknown --write",
|
||||||
"*.ts": "eslint --fix",
|
|
||||||
"*.png": "node scripts/optimize.js"
|
"*.png": "node scripts/optimize.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
/* eslint-disable no-undef */
|
|
||||||
module.exports = {
|
|
||||||
plugins: [require("tailwindcss"), require("autoprefixer"), require("postcss-nested")],
|
|
||||||
};
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { StateService as BaseStateService } from "jslib-common/abstractions/state.service";
|
|
||||||
import { StorageOptions } from "jslib-common/models/domain/storageOptions";
|
|
||||||
|
|
||||||
import { Account } from "src/models/account";
|
|
||||||
|
|
||||||
export abstract class StateService extends BaseStateService<Account> {
|
|
||||||
getRememberEmail: (options?: StorageOptions) => Promise<boolean>;
|
|
||||||
setRememberEmail: (value: boolean, options?: StorageOptions) => Promise<void>;
|
|
||||||
}
|
|
||||||
@@ -23,11 +23,7 @@
|
|||||||
<p>{{ "acceptEmergencyAccess" | i18n }}</p>
|
<p>{{ "acceptEmergencyAccess" | i18n }}</p>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a
|
<a routerLink="/" [queryParams]="{ email: email }" class="btn btn-primary btn-block">
|
||||||
routerLink="/login"
|
|
||||||
[queryParams]="{ email: email }"
|
|
||||||
class="btn btn-primary btn-block"
|
|
||||||
>
|
|
||||||
{{ "logIn" | i18n }}
|
{{ "logIn" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { I18nService } from "jslib-common/abstractions/i18n.service";
|
|||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { EmergencyAccessAcceptRequest } from "jslib-common/models/request/emergencyAccessAcceptRequest";
|
import { EmergencyAccessAcceptRequest } from "jslib-common/models/request/emergencyAccessAcceptRequest";
|
||||||
|
|
||||||
import { BaseAcceptComponent } from "../common/base.accept.component";
|
import { BaseAcceptComponent } from "../common/base.accept.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -24,11 +24,7 @@
|
|||||||
<p>{{ "joinOrganizationDesc" | i18n }}</p>
|
<p>{{ "joinOrganizationDesc" | i18n }}</p>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a
|
<a routerLink="/" [queryParams]="{ email: email }" class="btn btn-primary btn-block">
|
||||||
routerLink="/login"
|
|
||||||
[queryParams]="{ email: email }"
|
|
||||||
class="btn btn-primary btn-block"
|
|
||||||
>
|
|
||||||
{{ "logIn" | i18n }}
|
{{ "logIn" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import { LogService } from "jslib-common/abstractions/log.service";
|
|||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { Utils } from "jslib-common/misc/utils";
|
|
||||||
import { Policy } from "jslib-common/models/domain/policy";
|
|
||||||
import { OrganizationUserAcceptRequest } from "jslib-common/models/request/organizationUserAcceptRequest";
|
import { OrganizationUserAcceptRequest } from "jslib-common/models/request/organizationUserAcceptRequest";
|
||||||
import { OrganizationUserResetPasswordEnrollmentRequest } from "jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest";
|
import { OrganizationUserResetPasswordEnrollmentRequest } from "jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest";
|
||||||
|
|
||||||
|
import { Utils } from "jslib-common/misc/utils";
|
||||||
|
import { Policy } from "jslib-common/models/domain/policy";
|
||||||
import { BaseAcceptComponent } from "../common/base.accept.component";
|
import { BaseAcceptComponent } from "../common/base.accept.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
|
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-hint",
|
selector: "app-hint",
|
||||||
templateUrl: "hint.component.html",
|
templateUrl: "hint.component.html",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Component, NgZone } from "@angular/core";
|
import { Component, NgZone } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
@@ -15,6 +14,8 @@ import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.serv
|
|||||||
|
|
||||||
import { RouterService } from "../services/router.service";
|
import { RouterService } from "../services/router.service";
|
||||||
|
|
||||||
|
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-lock",
|
selector: "app-lock",
|
||||||
templateUrl: "lock.component.html",
|
templateUrl: "lock.component.html",
|
||||||
@@ -55,10 +56,10 @@ export class LockComponent extends BaseLockComponent {
|
|||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
this.onSuccessfulSubmit = async () => {
|
this.onSuccessfulSubmit = async () => {
|
||||||
const previousUrl = this.routerService.getPreviousUrl();
|
const previousUrl = this.routerService.getPreviousUrl();
|
||||||
if (previousUrl && previousUrl !== "/" && previousUrl.indexOf("lock") === -1) {
|
if (previousUrl !== "/" && previousUrl.indexOf("lock") === -1) {
|
||||||
this.successRoute = previousUrl;
|
this.successRoute = previousUrl;
|
||||||
}
|
}
|
||||||
this.router.navigateByUrl(this.successRoute);
|
this.router.navigate([this.successRoute]);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
[queryParams]="{ email: email }"
|
[queryParams]="{ email: email }"
|
||||||
class="btn btn-outline-secondary btn-block ml-2 mt-0"
|
class="btn btn-outline-secondary btn-block ml-2 mt-0"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-pencil-square" aria-hidden="true"></i>
|
<i class="bwi bwi-pencil-square-o" aria-hidden="true"></i>
|
||||||
{{ "createAccount" | i18n }}
|
{{ "createAccount" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,26 +1,22 @@
|
|||||||
import { Component, NgZone } from "@angular/core";
|
import { Component, NgZone } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
|
||||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { PolicyData } from "jslib-common/models/data/policyData";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
|
|
||||||
import { Policy } from "jslib-common/models/domain/policy";
|
|
||||||
import { ListResponse } from "jslib-common/models/response/listResponse";
|
|
||||||
import { PolicyResponse } from "jslib-common/models/response/policyResponse";
|
|
||||||
|
|
||||||
import { StateService } from "../../abstractions/state.service";
|
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
|
||||||
import { RouterService } from "../services/router.service";
|
|
||||||
|
import { Policy } from "jslib-common/models/domain/policy";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-login",
|
selector: "app-login",
|
||||||
@@ -28,14 +24,13 @@ import { RouterService } from "../services/router.service";
|
|||||||
})
|
})
|
||||||
export class LoginComponent extends BaseLoginComponent {
|
export class LoginComponent extends BaseLoginComponent {
|
||||||
showResetPasswordAutoEnrollWarning = false;
|
showResetPasswordAutoEnrollWarning = false;
|
||||||
enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions;
|
|
||||||
policies: ListResponse<PolicyResponse>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
authService: AuthService,
|
authService: AuthService,
|
||||||
router: Router,
|
router: Router,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
stateService: StateService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
environmentService: EnvironmentService,
|
environmentService: EnvironmentService,
|
||||||
passwordGenerationService: PasswordGenerationService,
|
passwordGenerationService: PasswordGenerationService,
|
||||||
@@ -43,10 +38,7 @@ export class LoginComponent extends BaseLoginComponent {
|
|||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
ngZone: NgZone,
|
ngZone: NgZone
|
||||||
protected stateService: StateService,
|
|
||||||
private messagingService: MessagingService,
|
|
||||||
private routerService: RouterService
|
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
authService,
|
authService,
|
||||||
@@ -60,9 +52,6 @@ export class LoginComponent extends BaseLoginComponent {
|
|||||||
logService,
|
logService,
|
||||||
ngZone
|
ngZone
|
||||||
);
|
);
|
||||||
this.onSuccessfulLogin = async () => {
|
|
||||||
this.messagingService.send("setFullWidth");
|
|
||||||
};
|
|
||||||
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,108 +61,58 @@ export class LoginComponent extends BaseLoginComponent {
|
|||||||
this.email = qParams.email;
|
this.email = qParams.email;
|
||||||
}
|
}
|
||||||
if (qParams.premium != null) {
|
if (qParams.premium != null) {
|
||||||
this.routerService.setPreviousUrl("/settings/premium");
|
this.stateService.setLoginRedirect({ route: "/settings/premium" });
|
||||||
} else if (qParams.org != null) {
|
} else if (qParams.org != null) {
|
||||||
const route = this.router.createUrlTree(["create-organization"], {
|
this.stateService.setLoginRedirect({
|
||||||
queryParams: { plan: qParams.org },
|
route: "/settings/create-organization",
|
||||||
|
qParams: { plan: qParams.org },
|
||||||
});
|
});
|
||||||
this.routerService.setPreviousUrl(route.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are they coming from an email for sponsoring a families organization
|
// Are they coming from an email for sponsoring a families organization
|
||||||
if (qParams.sponsorshipToken != null) {
|
if (qParams.sponsorshipToken != null) {
|
||||||
const route = this.router.createUrlTree(["setup/families-for-enterprise"], {
|
// After logging in redirect them to setup the families sponsorship
|
||||||
queryParams: { token: qParams.sponsorshipToken },
|
this.stateService.setLoginRedirect({
|
||||||
|
route: "/setup/families-for-enterprise",
|
||||||
|
qParams: { token: qParams.sponsorshipToken },
|
||||||
});
|
});
|
||||||
this.routerService.setPreviousUrl(route.toString());
|
|
||||||
}
|
}
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
this.rememberEmail = await this.stateService.getRememberEmail();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const invite = await this.stateService.getOrganizationInvitation();
|
const invite = await this.stateService.getOrganizationInvitation();
|
||||||
if (invite != null) {
|
if (invite != null) {
|
||||||
let policyList: Policy[] = null;
|
let policyList: Policy[] = null;
|
||||||
try {
|
try {
|
||||||
this.policies = await this.apiService.getPoliciesByToken(
|
const policies = await this.apiService.getPoliciesByToken(
|
||||||
invite.organizationId,
|
invite.organizationId,
|
||||||
invite.token,
|
invite.token,
|
||||||
invite.email,
|
invite.email,
|
||||||
invite.organizationUserId
|
invite.organizationUserId
|
||||||
);
|
);
|
||||||
policyList = this.policyService.mapPoliciesFromToken(this.policies);
|
policyList = this.policyService.mapPoliciesFromToken(policies);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (policyList != null) {
|
if (policyList != null) {
|
||||||
const resetPasswordPolicy = this.policyService.getResetPasswordPolicyOptions(
|
const result = this.policyService.getResetPasswordPolicyOptions(
|
||||||
policyList,
|
policyList,
|
||||||
invite.organizationId
|
invite.organizationId
|
||||||
);
|
);
|
||||||
// Set to true if policy enabled and auto-enroll enabled
|
// Set to true if policy enabled and auto-enroll enabled
|
||||||
this.showResetPasswordAutoEnrollWarning =
|
this.showResetPasswordAutoEnrollWarning = result[1] && result[0].autoEnrollEnabled;
|
||||||
resetPasswordPolicy[1] && resetPasswordPolicy[0].autoEnrollEnabled;
|
|
||||||
|
|
||||||
this.enforcedPasswordPolicyOptions =
|
|
||||||
await this.policyService.getMasterPasswordPolicyOptions(policyList);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async goAfterLogIn() {
|
async goAfterLogIn() {
|
||||||
// Check master password against policy
|
const loginRedirect = await this.stateService.getLoginRedirect();
|
||||||
if (this.enforcedPasswordPolicyOptions != null) {
|
if (loginRedirect != null) {
|
||||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams });
|
||||||
this.masterPassword,
|
await this.stateService.setLoginRedirect(null);
|
||||||
this.getPasswordStrengthUserInput()
|
|
||||||
);
|
|
||||||
const masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
|
||||||
|
|
||||||
// If invalid, save policies and require update
|
|
||||||
if (
|
|
||||||
!this.policyService.evaluateMasterPassword(
|
|
||||||
masterPasswordScore,
|
|
||||||
this.masterPassword,
|
|
||||||
this.enforcedPasswordPolicyOptions
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
const policiesData: { [id: string]: PolicyData } = {};
|
|
||||||
this.policies.data.map((p) => (policiesData[p.id] = new PolicyData(p)));
|
|
||||||
await this.policyService.replace(policiesData);
|
|
||||||
this.router.navigate(["update-password"]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const previousUrl = this.routerService.getPreviousUrl();
|
|
||||||
if (previousUrl) {
|
|
||||||
this.router.navigateByUrl(previousUrl);
|
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate([this.successRoute]);
|
this.router.navigate([this.successRoute]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit() {
|
|
||||||
await this.stateService.setRememberEmail(this.rememberEmail);
|
|
||||||
if (!this.rememberEmail) {
|
|
||||||
await this.stateService.setRememberedEmail(null);
|
|
||||||
}
|
|
||||||
await super.submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPasswordStrengthUserInput() {
|
|
||||||
let userInput: string[] = [];
|
|
||||||
const atPosition = this.email.indexOf("@");
|
|
||||||
if (atPosition > -1) {
|
|
||||||
userInput = userInput.concat(
|
|
||||||
this.email
|
|
||||||
.substr(0, atPosition)
|
|
||||||
.trim()
|
|
||||||
.toLowerCase()
|
|
||||||
.split(/[^A-Za-z0-9]/)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return userInput;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ApiService } from "jslib-common/abstractions/api.service";
|
|||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import { DeleteRecoverRequest } from "jslib-common/models/request/deleteRecoverRequest";
|
import { DeleteRecoverRequest } from "jslib-common/models/request/deleteRecoverRequest";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
|||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
|
||||||
import { TwoFactorRecoveryRequest } from "jslib-common/models/request/twoFactorRecoveryRequest";
|
import { TwoFactorRecoveryRequest } from "jslib-common/models/request/twoFactorRecoveryRequest";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -101,7 +101,7 @@
|
|||||||
<div [ngClass]="{ 'col-5': layout, 'col-12': !layout }">
|
<div [ngClass]="{ 'col-5': layout, 'col-12': !layout }">
|
||||||
<div class="row justify-content-md-center mt-5">
|
<div class="row justify-content-md-center mt-5">
|
||||||
<div [ngClass]="{ 'col-5': !layout, 'col-12': layout }">
|
<div [ngClass]="{ 'col-5': !layout, 'col-12': layout }">
|
||||||
<h1 class="lead text-center mb-4" *ngIf="!layout">{{ "createAccount" | i18n }}</h1>
|
<p class="lead text-center mb-4" *ngIf="!layout">{{ "createAccount" | i18n }}</p>
|
||||||
<div class="card d-block">
|
<div class="card d-block">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<app-callout
|
<app-callout
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
@@ -13,12 +13,14 @@ import { PasswordGenerationService } from "jslib-common/abstractions/passwordGen
|
|||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { PolicyData } from "jslib-common/models/data/policyData";
|
|
||||||
|
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
|
||||||
|
|
||||||
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
|
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
|
||||||
import { Policy } from "jslib-common/models/domain/policy";
|
import { Policy } from "jslib-common/models/domain/policy";
|
||||||
import { ReferenceEventRequest } from "jslib-common/models/request/referenceEventRequest";
|
|
||||||
|
|
||||||
import { RouterService } from "../services/router.service";
|
import { PolicyData } from "jslib-common/models/data/policyData";
|
||||||
|
import { ReferenceEventRequest } from "jslib-common/models/request/referenceEventRequest";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-register",
|
selector: "app-register",
|
||||||
@@ -43,8 +45,7 @@ export class RegisterComponent extends BaseRegisterComponent {
|
|||||||
passwordGenerationService: PasswordGenerationService,
|
passwordGenerationService: PasswordGenerationService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
environmentService: EnvironmentService,
|
environmentService: EnvironmentService,
|
||||||
logService: LogService,
|
logService: LogService
|
||||||
private routerService: RouterService
|
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
authService,
|
authService,
|
||||||
@@ -67,14 +68,14 @@ export class RegisterComponent extends BaseRegisterComponent {
|
|||||||
this.email = qParams.email;
|
this.email = qParams.email;
|
||||||
}
|
}
|
||||||
if (qParams.premium != null) {
|
if (qParams.premium != null) {
|
||||||
this.routerService.setPreviousUrl("/settings/premium");
|
this.stateService.setLoginRedirect({ route: "/settings/premium" });
|
||||||
} else if (qParams.org != null) {
|
} else if (qParams.org != null) {
|
||||||
this.showCreateOrgMessage = true;
|
this.showCreateOrgMessage = true;
|
||||||
this.referenceData.flow = qParams.org;
|
this.referenceData.flow = qParams.org;
|
||||||
const route = this.router.createUrlTree(["create-organization"], {
|
this.stateService.setLoginRedirect({
|
||||||
queryParams: { plan: qParams.org },
|
route: "/settings/create-organization",
|
||||||
|
qParams: { plan: qParams.org },
|
||||||
});
|
});
|
||||||
this.routerService.setPreviousUrl(route.toString());
|
|
||||||
}
|
}
|
||||||
if (qParams.layout != null) {
|
if (qParams.layout != null) {
|
||||||
this.layout = this.referenceData.layout = qParams.layout;
|
this.layout = this.referenceData.layout = qParams.layout;
|
||||||
@@ -91,10 +92,10 @@ export class RegisterComponent extends BaseRegisterComponent {
|
|||||||
// Are they coming from an email for sponsoring a families organization
|
// Are they coming from an email for sponsoring a families organization
|
||||||
if (qParams.sponsorshipToken != null) {
|
if (qParams.sponsorshipToken != null) {
|
||||||
// After logging in redirect them to setup the families sponsorship
|
// After logging in redirect them to setup the families sponsorship
|
||||||
const route = this.router.createUrlTree(["setup/families-for-enterprise"], {
|
this.stateService.setLoginRedirect({
|
||||||
queryParams: { plan: qParams.sponsorshipToken },
|
route: "/setup/families-for-enterprise",
|
||||||
|
qParams: { token: qParams.sponsorshipToken },
|
||||||
});
|
});
|
||||||
this.routerService.setPreviousUrl(route.toString());
|
|
||||||
}
|
}
|
||||||
if (this.referenceData.id === "") {
|
if (this.referenceData.id === "") {
|
||||||
this.referenceData.id = null;
|
this.referenceData.id = null;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
@@ -12,6 +11,8 @@ import { PolicyService } from "jslib-common/abstractions/policy.service";
|
|||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
|
|
||||||
|
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-set-password",
|
selector: "app-set-password",
|
||||||
templateUrl: "set-password.component.html",
|
templateUrl: "set-password.component.html",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||||
@@ -13,6 +13,8 @@ import { PasswordGenerationService } from "jslib-common/abstractions/passwordGen
|
|||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
|
||||||
|
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-sso",
|
selector: "app-sso",
|
||||||
templateUrl: "sso.component.html",
|
templateUrl: "sso.component.html",
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
|
import { AuthService } from "jslib-common/abstractions/auth.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 { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
|
||||||
|
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-options",
|
selector: "app-two-factor-options",
|
||||||
@@ -12,11 +13,11 @@ import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
|||||||
})
|
})
|
||||||
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||||
constructor(
|
constructor(
|
||||||
twoFactorService: TwoFactorService,
|
authService: AuthService,
|
||||||
router: Router,
|
router: Router,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService
|
platformUtilsService: PlatformUtilsService
|
||||||
) {
|
) {
|
||||||
super(twoFactorService, router, i18nService, platformUtilsService, window);
|
super(authService, router, i18nService, platformUtilsService, window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,9 +114,6 @@
|
|||||||
<p>{{ "noTwoStepProviders2" | i18n }}</p>
|
<p>{{ "noTwoStepProviders2" | i18n }}</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<hr />
|
<hr />
|
||||||
<div [hidden]="!showCaptcha()">
|
|
||||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex mb-3">
|
<div class="d-flex mb-3">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -138,7 +135,7 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
|
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
|
|
||||||
import { ModalService } from "jslib-angular/services/modal.service";
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AppIdService } from "jslib-common/abstractions/appId.service";
|
|
||||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
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 { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
|
||||||
|
import { ModalService } from "jslib-angular/services/modal.service";
|
||||||
|
|
||||||
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
|
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
|
||||||
|
|
||||||
import { RouterService } from "../services/router.service";
|
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
|
||||||
|
|
||||||
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
|
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
|
||||||
|
|
||||||
@@ -36,10 +36,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
environmentService: EnvironmentService,
|
environmentService: EnvironmentService,
|
||||||
private modalService: ModalService,
|
private modalService: ModalService,
|
||||||
route: ActivatedRoute,
|
route: ActivatedRoute,
|
||||||
logService: LogService,
|
logService: LogService
|
||||||
twoFactorService: TwoFactorService,
|
|
||||||
appIdService: AppIdService,
|
|
||||||
private routerService: RouterService
|
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
authService,
|
authService,
|
||||||
@@ -51,9 +48,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
environmentService,
|
environmentService,
|
||||||
stateService,
|
stateService,
|
||||||
route,
|
route,
|
||||||
logService,
|
logService
|
||||||
twoFactorService,
|
|
||||||
appIdService
|
|
||||||
);
|
);
|
||||||
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
|
||||||
}
|
}
|
||||||
@@ -76,9 +71,10 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async goAfterLogIn() {
|
async goAfterLogIn() {
|
||||||
const previousUrl = this.routerService.getPreviousUrl();
|
const loginRedirect = await this.stateService.getLoginRedirect();
|
||||||
if (previousUrl) {
|
if (loginRedirect != null) {
|
||||||
this.router.navigateByUrl(previousUrl);
|
this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams });
|
||||||
|
await this.stateService.setLoginRedirect(null);
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate([this.successRoute], {
|
this.router.navigate([this.successRoute], {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user