mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
Merge branch 'main' into next
# Conflicts: # internal/auth/repository/eventsourcing/eventstore/auth_request.go
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "zitadel",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "devcontainer",
|
||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/go:1": {
|
||||
"version": "1.20"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "18"
|
||||
},
|
||||
"ghcr.io/guiyomh/features/golangci-lint:0": {},
|
||||
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
|
||||
"ghcr.io/devcontainers/features/github-cli:1": {},
|
||||
"ghcr.io/jungaretti/features/make:1": {}
|
||||
},
|
||||
"forwardPorts": [
|
||||
3000,
|
||||
4200,
|
||||
8080
|
||||
],
|
||||
"onCreateCommand": "npm install -g [email protected]"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
devcontainer:
|
||||
image: mcr.microsoft.com/devcontainers/base:ubuntu
|
||||
volumes:
|
||||
- ../..:/workspaces:cached
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
network_mode: service:db
|
||||
command: sleep infinity
|
||||
|
||||
db:
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: postgres
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
@@ -72,24 +72,30 @@ jobs:
|
||||
container:
|
||||
needs: [compile]
|
||||
uses: ./.github/workflows/container.yml
|
||||
secrets: inherit
|
||||
permissions:
|
||||
packages: write
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
with:
|
||||
image_name: 'ghcr.io/zitadel/zitadel'
|
||||
build_image_name: 'ghcr.io/zitadel/zitadel-build'
|
||||
|
||||
e2e:
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
needs: [container]
|
||||
with:
|
||||
image: ${{ needs.container.outputs.image }}-debug
|
||||
needs: [compile]
|
||||
|
||||
release:
|
||||
uses: ./.github/workflows/release.yml
|
||||
permissions:
|
||||
packages: write
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
needs: [version, core-unit-test, core-integration-test, lint, container, e2e]
|
||||
# TODO: trigger release on workflow_dispatch if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
if: ${{ needs.version.outputs.published == 'true' }}
|
||||
if: ${{ needs.version.outputs.published == 'true' && github.event_name == 'workflow_dispatch' }}
|
||||
secrets:
|
||||
GCR_JSON_KEY_BASE64: ${{ secrets.GCR_JSON_KEY_BASE64 }}
|
||||
with:
|
||||
image: ${{ needs.container.outputs.image }}
|
||||
build_image_name: ${{ needs.container.outputs.build_image }}
|
||||
semantic_version: '19.0.2'
|
||||
image_name: 'ghcr.io/zitadel/zitadel'
|
||||
google_image_name: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel"
|
||||
|
||||
@@ -70,12 +70,12 @@ jobs:
|
||||
mv zitadel zitadel-${{ matrix.goos }}-${{ matrix.goarch }}/
|
||||
cp LICENSE zitadel-${{ matrix.goos }}-${{ matrix.goarch }}/
|
||||
cp README.md zitadel-${{ matrix.goos }}-${{ matrix.goarch }}/
|
||||
tar -cvf zitadel-${{ matrix.goos }}-${{ matrix.goarch }}.tar zitadel-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
tar -czvf zitadel-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz zitadel-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
-
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: zitadel-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: zitadel-${{ matrix.goos }}-${{ matrix.goarch }}.tar
|
||||
path: zitadel-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
|
||||
|
||||
checksums:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
path: executables
|
||||
-
|
||||
name: move files one folder up
|
||||
run: mv */*.tar . && find . -type d -empty -delete
|
||||
run: mv */*.tar.gz . && find . -type d -empty -delete
|
||||
working-directory: executables
|
||||
-
|
||||
run: sha256sum * > checksums.txt
|
||||
|
||||
@@ -3,12 +3,15 @@ name: Container
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_name:
|
||||
build_image_name:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
image:
|
||||
value: '${{ inputs.image_name }}:${{ github.sha }}'
|
||||
build_image:
|
||||
value: '${{ inputs.build_image_name }}:${{ github.sha }}'
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
env:
|
||||
default_labels: |
|
||||
@@ -31,7 +34,7 @@ jobs:
|
||||
id: scratch-meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ inputs.image_name }}
|
||||
images: ${{ inputs.build_image_name }}
|
||||
labels: ${{ env.default_labels}}
|
||||
tags: |
|
||||
type=sha,prefix=,suffix=,format=long
|
||||
@@ -40,7 +43,7 @@ jobs:
|
||||
id: debug-meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ inputs.image_name }}
|
||||
images: ${{ inputs.build_image_name }}
|
||||
labels: ${{ env.default_labels}}
|
||||
tags: |
|
||||
type=sha,prefix=,suffix=-debug,format=long
|
||||
@@ -52,7 +55,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver-opts: 'image=moby/buildkit:v0.11.6'
|
||||
-
|
||||
-
|
||||
name: Login to Docker registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
@@ -67,7 +70,7 @@ jobs:
|
||||
-
|
||||
name: Unpack executable
|
||||
run: |
|
||||
tar -xvf .artifacts/zitadel-linux-${{ matrix.arch }}.tar
|
||||
tar -xvf .artifacts/zitadel-linux-${{ matrix.arch }}.tar.gz
|
||||
mv zitadel-linux-${{ matrix.arch }}/zitadel ./zitadel
|
||||
-
|
||||
name: Debug
|
||||
@@ -76,12 +79,14 @@ jobs:
|
||||
timeout-minutes: 3
|
||||
with:
|
||||
context: .
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: build/Dockerfile
|
||||
target: artifact
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
push: true
|
||||
labels: ${{ steps.debug-meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ inputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
||||
outputs: type=image,name=${{ inputs.build_image_name }},push-by-digest=true,name-canonical=true,push=true
|
||||
-
|
||||
name: Scratch
|
||||
id: build-scratch
|
||||
@@ -89,12 +94,14 @@ jobs:
|
||||
timeout-minutes: 3
|
||||
with:
|
||||
context: .
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
file: build/Dockerfile
|
||||
target: final
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
push: true
|
||||
labels: ${{ steps.scratch-meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ inputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
||||
outputs: type=image,name=${{ inputs.build_image_name }},push-by-digest=true,name-canonical=true,push=true
|
||||
-
|
||||
name: Export debug digest
|
||||
run: |
|
||||
@@ -142,27 +149,28 @@ jobs:
|
||||
with:
|
||||
driver-opts: 'image=moby/buildkit:v0.11.6'
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: '${{ inputs.image_name }}'
|
||||
tags: |
|
||||
type=sha,prefix=,suffix=${{ matrix.suffix }},format=long
|
||||
-
|
||||
name: Login to Docker registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Create manifest list and push
|
||||
-
|
||||
name: Docker meta
|
||||
id: build-meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: '${{ inputs.build_image_name }}'
|
||||
tags: |
|
||||
type=sha,prefix=,suffix=${{ matrix.suffix }},format=long
|
||||
-
|
||||
name: Create build manifest list and push
|
||||
working-directory: /tmp/digests/${{ matrix.image }}
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ inputs.image_name }}@sha256:%s ' *)
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.build-meta.outputs.json }}') \
|
||||
$(printf '${{ inputs.build_image_name }}@sha256:%s ' *)
|
||||
-
|
||||
name: Inspect image
|
||||
name: Inspect build image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ inputs.image_name }}:${{ github.sha }}${{ matrix.suffix }}
|
||||
docker buildx imagetools inspect ${{ inputs.build_image_name }}:${{ github.sha }}${{ matrix.suffix }}
|
||||
|
||||
|
||||
+24
-66
@@ -2,10 +2,6 @@ name: "ZITADEL e2e Tests"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -13,79 +9,41 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
browser: [firefox, chrome]
|
||||
services:
|
||||
# we currently use postgres because cockroach doesn't work
|
||||
postgres:
|
||||
image: postgres
|
||||
ports:
|
||||
- 5432:5432
|
||||
env:
|
||||
POSTGRES_USER: zitadel
|
||||
PGUSER: zitadel
|
||||
POSTGRES_DB: zitadel
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
--health-start-period 10s
|
||||
zitadel:
|
||||
image: ${{ inputs.image }}
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
options: >-
|
||||
--health-cmd "zitadel ready"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
--health-start-period 10s
|
||||
--add-host host.docker.internal:host-gateway
|
||||
ports:
|
||||
- 8080:8080
|
||||
env:
|
||||
ZITADEL_ARGS: "start-from-init --masterkeyFromEnv"
|
||||
ZITADEL_MASTERKEY: MasterkeyNeedsToHave32Characters
|
||||
ZITADEL_LOG_LEVEL: debug
|
||||
ZITADEL_EXTERNALDOMAIN: localhost
|
||||
ZITADEL_EXTERNALSECURE: "false"
|
||||
ZITADEL_TLS_ENABLED: "false"
|
||||
ZITADEL_DATABASE_POSTGRES_HOST: postgres
|
||||
ZITADEL_DATABASE_POSTGRES_PORT: "5432"
|
||||
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
|
||||
ZITADEL_DATABASE_POSTGRES_MAXOPENCONNS: "20"
|
||||
ZITADEL_DATABASE_POSTGRES_MAXIDLECONNS: "10"
|
||||
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
|
||||
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
|
||||
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: zitadel
|
||||
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
|
||||
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: "false"
|
||||
ZITADEL_LOGSTORE_ACCESS_DATABASE_ENABLED: "true"
|
||||
ZITADEL_LOGSTORE_ACCESS_DATABASE_DEBOUNCE_MINFREQUENCY: 0s
|
||||
ZITADEL_LOGSTORE_ACCESS_DATABASE_DEBOUNCE_MAXBULKSIZE: "0"
|
||||
ZITADEL_LOGSTORE_EXECUTION_DATABASE_ENABLED: "true"
|
||||
ZITADEL_LOGSTORE_EXECUTION_STDOUT_ENABLED: "false"
|
||||
ZITADEL_QUOTAS_ACCESS_EXHAUSTEDCOOKIEKEY: "zitadel.quota.limiting"
|
||||
ZITADEL_QUOTAS_ACCESS_EXHAUSTEDCOOKIEMAXAGE: 60s
|
||||
ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_REQUEUEEVERY: 1s
|
||||
ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_MFAINITSKIPLIFETIME: "0"
|
||||
ZITADEL_SYSTEMAPIUSERS: "{\"cypress\": {\"keyData\": \"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6aStGRlNKTDdmNXl3NEtUd3pnTQpQMzRlUEd5Y20vTStrVDBNN1Y0Q2d4NVYzRWFESXZUUUtUTGZCYUVCNDV6YjlMdGpJWHpEdzByWFJvUzJoTzZ0CmgrQ1lRQ3ozS0N2aDA5QzBJenhaaUIySVMzSC9hVCs1Qng5RUZZK3ZuQWtaamNjYnlHNVlOUnZtdE9sbnZJZUkKSDdxWjB0RXdrUGZGNUdFWk5QSlB0bXkzVUdWN2lvZmRWUVMxeFJqNzMrYU13NXJ2SDREOElkeWlBQzNWZWtJYgpwdDBWajBTVVgzRHdLdG9nMzM3QnpUaVBrM2FYUkYwc2JGaFFvcWRKUkk4TnFnWmpDd2pxOXlmSTV0eXhZc3duCitKR3pIR2RIdlczaWRPRGxtd0V0NUsycGFzaVJJV0syT0dmcSt3MEVjbHRRSGFidXFFUGdabG1oQ2tSZE5maXgKQndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==\"}}"
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
ZITADEL_IMAGE: ${{ inputs.image }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: .artifacts
|
||||
name: zitadel-linux-amd64
|
||||
-
|
||||
name: Unpack executable
|
||||
run: |
|
||||
tar -xvf .artifacts/zitadel-linux-amd64.tar.gz
|
||||
mv zitadel-linux-amd64/zitadel ./zitadel
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver-opts: 'image=moby/buildkit:v0.11.6'
|
||||
-
|
||||
name: Start DB and ZITADEL
|
||||
run: |
|
||||
cd ./e2e
|
||||
ZITADEL_IMAGE=zitadel:local docker compose up --detach --wait
|
||||
-
|
||||
name: Cypress run
|
||||
uses: cypress-io/github-action@v5
|
||||
env:
|
||||
CYPRESS_BASE_URL: http://localhost:8080/ui/console
|
||||
CYPRESS_WEBHOOK_HANDLER_HOST: host.docker.internal
|
||||
CYPRESS_DATABASE_CONNECTION_URL: 'postgresql://zitadel@localhost:5432/zitadel'
|
||||
CYPRESS_DATABASE_CONNECTION_URL: 'postgresql://root@localhost:26257/zitadel'
|
||||
CYPRESS_BACKEND_URL: http://localhost:8080
|
||||
with:
|
||||
working-directory: e2e
|
||||
|
||||
@@ -35,11 +35,6 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: tibdex/github-app-token@v1
|
||||
id: generate-token
|
||||
with:
|
||||
app_id: ${{ secrets.APP_ID }}
|
||||
private_key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
- name: Google Artifact Registry Login
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
semantic_version:
|
||||
required: true
|
||||
type: string
|
||||
image:
|
||||
build_image_name:
|
||||
required: true
|
||||
type: string
|
||||
image_name:
|
||||
@@ -56,20 +56,20 @@ jobs:
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.image_name }}:${{ needs.version.outputs.version }} \
|
||||
${{ inputs.image }}
|
||||
${{ inputs.build_image_name }}
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.image_name }}:${{ needs.version.outputs.version }}-debug \
|
||||
${{ inputs.image }}-debug
|
||||
${{ inputs.build_image_name }}-debug
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.google_image_name }}:${{ needs.version.outputs.version }} \
|
||||
${{ inputs.image }}
|
||||
${{ inputs.build_image_name }}
|
||||
-
|
||||
name: Publish latest
|
||||
if: ${{ github.ref_name == 'next' }}
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.image_name }}:latest \
|
||||
${{ inputs.image }}
|
||||
${{ inputs.build_image_name }}
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.image_name }}:latest-debug \
|
||||
${{ inputs.image }}-debug
|
||||
${{ inputs.build_image_name }}-debug
|
||||
|
||||
@@ -10,9 +10,9 @@ on:
|
||||
required: true
|
||||
type: boolean
|
||||
outputs:
|
||||
version:
|
||||
version:
|
||||
value: ${{ jobs.generate.outputs.version }}
|
||||
published:
|
||||
published:
|
||||
value: ${{jobs.generate.outputs.published }}
|
||||
|
||||
jobs:
|
||||
@@ -24,10 +24,15 @@ jobs:
|
||||
version: ${{ steps.output.outputs.VERSION }}
|
||||
published: ${{ steps.semantic.outputs.new_release_published }}
|
||||
steps:
|
||||
-
|
||||
-
|
||||
name: Source checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
-
|
||||
uses: actions/download-artifact@v3
|
||||
if: ${{ !inputs.dry_run }}
|
||||
with:
|
||||
path: .artifacts
|
||||
-
|
||||
name: Semantic Release
|
||||
uses: cycjimmy/semantic-release-action@v3
|
||||
id: semantic
|
||||
|
||||
+1
-1
@@ -64,7 +64,6 @@ docs/docs/apis/proto
|
||||
**/.sass-cache
|
||||
/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css
|
||||
/internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css.map
|
||||
zitadel
|
||||
zitadel-*-*
|
||||
|
||||
# local
|
||||
@@ -74,6 +73,7 @@ migrations/cockroach/migrate_cloud.go
|
||||
/.artifacts/*
|
||||
!/.artifacts/zitadel
|
||||
/zitadel
|
||||
node_modules/
|
||||
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
+36
-2
@@ -1,11 +1,45 @@
|
||||
module.exports = {
|
||||
branches: [
|
||||
{ name: "next" },
|
||||
{ name: "next-rc", prerelease: "rc" }
|
||||
{ name: "next-rc", prerelease: "rc" },
|
||||
],
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/github"
|
||||
[
|
||||
"@semantic-release/github",
|
||||
{
|
||||
assets: [
|
||||
{
|
||||
path: ".artifacts/zitadel-linux-amd64/zitadel-linux-amd64.tar.gz",
|
||||
label: "zitadel-linux-amd64.tar.gz",
|
||||
},
|
||||
{
|
||||
path: ".artifacts/zitadel-linux-arm64/zitadel-linux-arm64.tar.gz",
|
||||
label: "zitadel-linux-arm64.tar.gz",
|
||||
},
|
||||
{
|
||||
path: ".artifacts/zitadel-windows-amd64/zitadel-windows-amd64.tar.gz",
|
||||
label: "zitadel-windows-amd64.tar.gz",
|
||||
},
|
||||
{
|
||||
path: ".artifacts/zitadel-windows-arm64/zitadel-windows-arm64.tar.gz",
|
||||
label: "zitadel-windows-arm64.tar.gz",
|
||||
},
|
||||
{
|
||||
path: ".artifacts/zitadel-darwin-amd64/zitadel-darwin-amd64.tar.gz",
|
||||
label: "zitadel-darwin-amd64.tar.gz",
|
||||
},
|
||||
{
|
||||
path: ".artifacts/zitadel-darwin-arm64/zitadel-darwin-arm64.tar.gz",
|
||||
label: "zitadel-darwin-arm64.tar.gz",
|
||||
},
|
||||
{
|
||||
path: ".artifacts/checksums.txt",
|
||||
label: "checksums.txt",
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
@@ -49,6 +49,7 @@ core_grpc_dependencies:
|
||||
go install github.com/grpc-ecosystem/grpc-gateway/v2/[email protected]
|
||||
go install github.com/grpc-ecosystem/grpc-gateway/v2/[email protected]
|
||||
go install github.com/envoyproxy/[email protected]
|
||||
go install github.com/bufbuild/buf/cmd/[email protected]
|
||||
|
||||
.PHONY: core_api
|
||||
core_api: core_api_generator core_grpc_dependencies
|
||||
@@ -90,12 +91,15 @@ clean:
|
||||
core_unit_test:
|
||||
go test -race -coverprofile=profile.cov ./...
|
||||
|
||||
.PHONY: core_integration_test
|
||||
core_integration_test:
|
||||
.PHONY: core_integration_setup
|
||||
core_integration_setup:
|
||||
go build -o zitadel main.go
|
||||
./zitadel init --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml
|
||||
./zitadel setup --masterkeyFromEnv --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml
|
||||
$(RM) zitadel
|
||||
|
||||
.PHONY: core_integration_test
|
||||
core_integration_test: core_integration_setup
|
||||
go test -tags=integration -race -p 1 -v -coverprofile=profile.cov -coverpkg=./internal/...,./cmd/... ./internal/integration ./internal/api/grpc/... ./internal/notification/handlers/... ./internal/api/oidc/...
|
||||
|
||||
.PHONY: console_lint
|
||||
|
||||
+391
-360
File diff suppressed because one or more lines are too long
+1
-1
@@ -21,7 +21,7 @@ import (
|
||||
type FirstInstance struct {
|
||||
InstanceName string
|
||||
DefaultLanguage language.Tag
|
||||
Org command.OrgSetup
|
||||
Org command.InstanceOrgSetup
|
||||
MachineKeyPath string
|
||||
PatPath string
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 12/12_add_otp_columns.sql
|
||||
addOTPColumns string
|
||||
)
|
||||
|
||||
type AddOTPColumns struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddOTPColumns) Execute(ctx context.Context) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addOTPColumns)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddOTPColumns) String() string {
|
||||
return "12_auth_users_otp_columns"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE auth.users2 ADD COLUMN otp_sms_added BOOL DEFAULT false;
|
||||
ALTER TABLE auth.users2 ADD COLUMN otp_email_added BOOL DEFAULT false;
|
||||
@@ -67,6 +67,7 @@ type Steps struct {
|
||||
s9EventstoreIndexes2 *EventstoreIndexesNew
|
||||
CorrectCreationDate *CorrectCreationDate
|
||||
AddEventCreatedAt *AddEventCreatedAt
|
||||
s12AddOTPColumns *AddOTPColumns
|
||||
}
|
||||
|
||||
type encryptionKeyConfig struct {
|
||||
|
||||
@@ -94,6 +94,7 @@ func Setup(config *Config, steps *Steps, masterKey string) {
|
||||
steps.CorrectCreationDate.dbClient = dbClient
|
||||
steps.AddEventCreatedAt.dbClient = dbClient
|
||||
steps.AddEventCreatedAt.step10 = steps.CorrectCreationDate
|
||||
steps.s12AddOTPColumns = &AddOTPColumns{dbClient: dbClient}
|
||||
|
||||
err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil)
|
||||
logging.OnError(err).Fatal("unable to start projections")
|
||||
@@ -134,6 +135,8 @@ func Setup(config *Config, steps *Steps, masterKey string) {
|
||||
logging.OnError(err).Fatal("unable to migrate step 10")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.AddEventCreatedAt)
|
||||
logging.OnError(err).Fatal("unable to migrate step 11")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s12AddOTPColumns)
|
||||
logging.OnError(err).Fatal("unable to migrate step 12")
|
||||
|
||||
for _, repeatableStep := range repeatableSteps {
|
||||
err = migration.Migrate(ctx, eventstoreClient, repeatableStep)
|
||||
|
||||
+39
-27
@@ -1,41 +1,53 @@
|
||||
FirstInstance:
|
||||
MachineKeyPath:
|
||||
PatPath:
|
||||
InstanceName: ZITADEL
|
||||
DefaultLanguage: en
|
||||
# The machine key from the section FirstInstance.Org.Machine.MachineKey is written to the MachineKeyPath.
|
||||
MachineKeyPath: # ZITADEL_FIRSTINSTANCE_MACHINEKEYPATH
|
||||
# The personal access token from the section FirstInstance.Org.Machine.Pat is written to the PatPath.
|
||||
PatPath: # ZITADEL_FIRSTINSTANCE_PATPATH
|
||||
InstanceName: ZITADEL # ZITADEL_FIRSTINSTANCE_INSTANCENAME
|
||||
DefaultLanguage: en # ZITADEL_FIRSTINSTANCE_DEFAULTLANGUAGE
|
||||
Org:
|
||||
Name: ZITADEL
|
||||
Name: ZITADEL # ZITADEL_FIRSTINSTANCE_ORG_NAME
|
||||
# In the FirstInstance.Org.Human section, the initial organization's admin user with the role IAM_OWNER is defined.
|
||||
# ZITADEL either creates a human user or a machine user.
|
||||
# If FirstInstance.Org.Machine.Machine is defined, a service user is created with the IAM_OWNER role, not a human user.
|
||||
Human:
|
||||
# in case that UserLoginMustBeDomain is false (default) and you don't overwrite the username with an email,
|
||||
# In case UserLoginMustBeDomain is false (default) and you don't overwrite the username with an email,
|
||||
# it will be suffixed by the org domain (org-name + domain from config).
|
||||
# for example: zitadel-admin in org ZITADEL on domain.tld -> [email protected]
|
||||
UserName: zitadel-admin
|
||||
FirstName: ZITADEL
|
||||
LastName: Admin
|
||||
NickName:
|
||||
DisplayName:
|
||||
# for example zitadel-admin in org ZITADEL on domain.tld -> [email protected]
|
||||
UserName: zitadel-admin # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME
|
||||
FirstName: ZITADEL # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_FIRSTNAME
|
||||
LastName: Admin # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_LASTNAME
|
||||
NickName: # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_NICKNAME
|
||||
DisplayName: # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_DISPLAYNAME
|
||||
Email:
|
||||
Address: #uses the username if empty
|
||||
Verified: true
|
||||
PreferredLanguage: en
|
||||
Gender:
|
||||
# uses the username if empty
|
||||
Address: # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS
|
||||
Verified: true # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_VERIFIED
|
||||
PreferredLanguage: en # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PREFERREDLANGUAGE
|
||||
Gender: # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_GENDER
|
||||
Phone:
|
||||
Number:
|
||||
Verified:
|
||||
Password: Password1!
|
||||
PasswordChangeRequired: true
|
||||
Number: # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PHONE_NUMBER
|
||||
Verified: # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PHONE_VERIFIED
|
||||
Password: Password1! # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD
|
||||
PasswordChangeRequired: true # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED
|
||||
# In the FirstInstance.Org.Machine section, the initial organization's admin user with the role IAM_OWNER is defined.
|
||||
# ZITADEL either creates a human user or a machine user.
|
||||
# If FirstInstance.Org.Machine.Machine is defined, a service user is created with the IAM_OWNER role, not a human user.
|
||||
Machine:
|
||||
Machine:
|
||||
Username:
|
||||
Name:
|
||||
Username: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_USERNAME
|
||||
Name: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_NAME
|
||||
MachineKey:
|
||||
ExpirationDate:
|
||||
Type:
|
||||
# date format: 2023-01-01T00:00:00Z
|
||||
ExpirationDate: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINEKEY_EXPIRATIONDATE
|
||||
# Currently, the only supported value is 1 for JSON
|
||||
Type: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINEKEY_TYPE
|
||||
Pat:
|
||||
ExpirationDate:
|
||||
# date format: 2023-01-01T00:00:00Z
|
||||
ExpirationDate: # ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE
|
||||
|
||||
CorrectCreationDate:
|
||||
FailAfter: 5m
|
||||
FailAfter: 5m # ZITADEL_CORRECTCREATIONDATE_FAILAFTER
|
||||
|
||||
AddEventCreatedAt:
|
||||
BulkAmount: 100
|
||||
BulkAmount: 100 # ZITADEL_ADDEVENTCREATEDAT_BULKAMOUNT
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/auth"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/management"
|
||||
oidc_v2 "github.com/zitadel/zitadel/internal/api/grpc/oidc/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/org/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/session/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/settings/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/system"
|
||||
@@ -351,6 +352,9 @@ func startAPIs(
|
||||
if err := apis.RegisterService(ctx, settings.CreateServer(commands, queries, config.ExternalSecure)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := apis.RegisterService(ctx, org.CreateServer(commands, queries, permissionCheck)); err != nil {
|
||||
return err
|
||||
}
|
||||
instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, login.IgnoreInstanceEndpoints...)
|
||||
assetsCache := middleware.AssetsCacheInterceptor(config.AssetStorage.Cache.MaxAge, config.AssetStorage.Cache.SharedMaxAge)
|
||||
apis.RegisterHandlerOnPrefix(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, middleware.CallDurationHandler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.Handle))
|
||||
|
||||
Generated
+29
-29
@@ -12,18 +12,18 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^16.1.2",
|
||||
"@angular/cdk": "^16.1.2",
|
||||
"@angular/common": "^16.1.2",
|
||||
"@angular/compiler": "^16.1.2",
|
||||
"@angular/core": "^16.1.2",
|
||||
"@angular/forms": "^16.1.2",
|
||||
"@angular/material": "^16.1.2",
|
||||
"@angular/material-moment-adapter": "^16.1.2",
|
||||
"@angular/platform-browser": "^16.1.2",
|
||||
"@angular/platform-browser-dynamic": "^16.1.2",
|
||||
"@angular/router": "^16.1.2",
|
||||
"@angular/service-worker": "^16.1.2",
|
||||
"@angular/animations": "^16.2.0",
|
||||
"@angular/cdk": "^16.2.0",
|
||||
"@angular/common": "^16.2.0",
|
||||
"@angular/compiler": "^16.2.0",
|
||||
"@angular/core": "^16.2.0",
|
||||
"@angular/forms": "^16.2.0",
|
||||
"@angular/material": "^16.2.0",
|
||||
"@angular/material-moment-adapter": "^16.2.0",
|
||||
"@angular/platform-browser": "^16.2.0",
|
||||
"@angular/platform-browser-dynamic": "^16.2.0",
|
||||
"@angular/router": "^16.2.0",
|
||||
"@angular/service-worker": "^16.2.0",
|
||||
"@ctrl/ngx-codemirror": "^6.1.0",
|
||||
"@grpc/grpc-js": "^1.8.14",
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
@@ -33,7 +33,7 @@
|
||||
"codemirror": "^5.65.8",
|
||||
"cors": "^2.8.5",
|
||||
"file-saver": "^2.0.5",
|
||||
"flag-icons": "^6.6.6",
|
||||
"flag-icons": "^6.7.0",
|
||||
"google-proto-files": "^3.0.3",
|
||||
"google-protobuf": "^3.21.2",
|
||||
"grpc-web": "^1.4.1",
|
||||
@@ -47,19 +47,19 @@
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tslib": "^2.4.1",
|
||||
"uuid": "^9.0.0",
|
||||
"zone.js": "~0.13.0"
|
||||
"zone.js": "~0.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^16.1.1",
|
||||
"@angular-eslint/builder": "16.0.1",
|
||||
"@angular-eslint/eslint-plugin": "16.0.1",
|
||||
"@angular-eslint/eslint-plugin-template": "16.0.1",
|
||||
"@angular-eslint/schematics": "16.0.1",
|
||||
"@angular-eslint/template-parser": "16.0.1",
|
||||
"@angular/cli": "^16.1.1",
|
||||
"@angular/compiler-cli": "^16.1.2",
|
||||
"@angular/language-service": "^16.1.2",
|
||||
"@bufbuild/buf": "^1.18.0-1",
|
||||
"@angular-devkit/build-angular": "^16.2.0",
|
||||
"@angular-eslint/builder": "16.1.0",
|
||||
"@angular-eslint/eslint-plugin": "16.1.0",
|
||||
"@angular-eslint/eslint-plugin-template": "16.1.0",
|
||||
"@angular-eslint/schematics": "16.1.0",
|
||||
"@angular-eslint/template-parser": "16.1.0",
|
||||
"@angular/cli": "^16.2.0",
|
||||
"@angular/compiler-cli": "^16.2.0",
|
||||
"@angular/language-service": "^16.2.0",
|
||||
"@bufbuild/buf": "^1.23.1",
|
||||
"@types/file-saver": "^2.0.2",
|
||||
"@types/google-protobuf": "^3.15.3",
|
||||
"@types/jasmine": "~4.3.3",
|
||||
@@ -68,21 +68,21 @@
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/opentype.js": "^1.3.4",
|
||||
"@types/qrcode": "^1.5.0",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
||||
"@typescript-eslint/parser": "^5.59.5",
|
||||
"@typescript-eslint/parser": "^5.60.1",
|
||||
"codelyzer": "^6.0.2",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint": "^8.44.0",
|
||||
"jasmine-core": "~4.6.0",
|
||||
"jasmine-spec-reporter": "~7.0.0",
|
||||
"karma": "^6.4.2",
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
"karma-coverage-istanbul-reporter": "^3.0.3",
|
||||
"karma-jasmine": "^5.1.0",
|
||||
"karma-jasmine-html-reporter": "^2.0.0",
|
||||
"karma-jasmine-html-reporter": "^2.1.0",
|
||||
"prettier": "^2.8.7",
|
||||
"prettier-plugin-organize-imports": "^3.2.2",
|
||||
"protractor": "~7.0.0",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<span class="title" mat-dialog-title>{{ data.titleKey | translate : data.titleParam }}</span>
|
||||
<div mat-dialog-content>
|
||||
<div class="icon-wrapper" *ngIf="data.icon">
|
||||
<i class="icon {{ data.icon }}"></i>
|
||||
</div>
|
||||
<p class="desc cnsl-secondary-text">{{ data.descriptionKey | translate : data.descriptionParam }}</p>
|
||||
|
||||
<cnsl-info-section *ngIf="data.infoSectionKey" [type]="InfoSectionType.INFO">{{
|
||||
data.warnSectionKey | translate
|
||||
}}</cnsl-info-section>
|
||||
|
||||
<p *ngIf="data.hintKey" class="desc cnsl-secondary-text">{{ data.hintKey | translate : { value: data.confirmation } }}</p>
|
||||
|
||||
<cnsl-form-field *ngIf="data.confirmation && data.confirmationKey" class="formfield">
|
||||
<cnsl-label>{{ data.confirmationKey | translate : { value: data.confirmation } }}</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="confirm" data-e2e="confirm-dialog-input" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button *ngIf="data.cancelKey" mat-stroked-button (click)="closeDialog()">
|
||||
{{ data.cancelKey | translate }}
|
||||
</button>
|
||||
<span class="fill-space"></span>
|
||||
<button
|
||||
color="primary"
|
||||
[disabled]="data.confirmation && confirm !== data.confirmation"
|
||||
mat-raised-button
|
||||
class="ok-button"
|
||||
(click)="closeDialogWithSuccess()"
|
||||
data-e2e="confirm-dialog-button"
|
||||
>
|
||||
{{ data.confirmKey | translate }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
.title {
|
||||
font-size: 1.2rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
margin: 1rem;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.ok-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { InfoDialogComponent } from './info-dialog.component';
|
||||
|
||||
describe('InfoDialogComponent', () => {
|
||||
let component: InfoDialogComponent;
|
||||
let fixture: ComponentFixture<InfoDialogComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [InfoDialogComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(InfoDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import {
|
||||
MatLegacyDialogRef as MatDialogRef,
|
||||
MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA,
|
||||
} from '@angular/material/legacy-dialog';
|
||||
|
||||
import { InfoSectionType } from '../info-section/info-section.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-info-dialog',
|
||||
templateUrl: './info-dialog.component.html',
|
||||
styleUrls: ['./info-dialog.component.scss'],
|
||||
})
|
||||
export class InfoDialogComponent {
|
||||
public confirm: string = '';
|
||||
InfoSectionType: any = InfoSectionType;
|
||||
constructor(public dialogRef: MatDialogRef<InfoDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
public closeDialogWithSuccess(): void {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { InfoSectionModule } from '../info-section/info-section.module';
|
||||
import { InputModule } from '../input/input.module';
|
||||
import { InfoDialogComponent } from './info-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [InfoDialogComponent],
|
||||
imports: [CommonModule, FormsModule, TranslateModule, InfoSectionModule, MatButtonModule, InputModule],
|
||||
})
|
||||
export class InfoDialogModule {}
|
||||
@@ -31,7 +31,6 @@
|
||||
|
||||
.info-section-content {
|
||||
flex: 1;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
&.info {
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
[chips]="chips[currentType]"
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
label="one"
|
||||
[default$]="getDefaultInitMessageTextMap$"
|
||||
[current$]="getCustomInitMessageTextMap$"
|
||||
[default$]="getDefaultMessageTextMap$"
|
||||
[current$]="getCustomMessageTextMap$"
|
||||
(changedValues)="updateCurrentValues($event)"
|
||||
></cnsl-edit-text>
|
||||
</div>
|
||||
@@ -46,7 +46,7 @@
|
||||
<div class="message-text-actions">
|
||||
<button
|
||||
class="reset-button"
|
||||
*ngIf="(getCustomInitMessageTextMap$ | async) && (getCustomInitMessageTextMap$ | async)?.['isDefault'] === false"
|
||||
*ngIf="(getCustomMessageTextMap$ | async) && (getCustomMessageTextMap$ | async)?.['isDefault'] === false"
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
(click)="resetDefault()"
|
||||
color="message-text-warn"
|
||||
|
||||
@@ -9,14 +9,18 @@ import {
|
||||
GetDefaultPasswordlessRegistrationMessageTextRequest as AdminGetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
GetDefaultPasswordResetMessageTextRequest as AdminGetDefaultPasswordResetMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextRequest as AdminGetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyEmailOTPMessageTextRequest as AdminGetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextRequest as AdminGetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultVerifySMSOTPMessageTextRequest as AdminGetDefaultVerifySMSOTPMessageTextRequest,
|
||||
SetDefaultDomainClaimedMessageTextRequest,
|
||||
SetDefaultInitMessageTextRequest,
|
||||
SetDefaultPasswordChangeMessageTextRequest,
|
||||
SetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
SetDefaultPasswordResetMessageTextRequest,
|
||||
SetDefaultVerifyEmailMessageTextRequest,
|
||||
SetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
SetDefaultVerifyPhoneMessageTextRequest,
|
||||
SetDefaultVerifySMSOTPMessageTextRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
GetCustomDomainClaimedMessageTextRequest,
|
||||
@@ -25,21 +29,27 @@ import {
|
||||
GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
GetCustomPasswordResetMessageTextRequest,
|
||||
GetCustomVerifyEmailMessageTextRequest,
|
||||
GetCustomVerifyEmailOTPMessageTextRequest,
|
||||
GetCustomVerifyPhoneMessageTextRequest,
|
||||
GetCustomVerifySMSOTPMessageTextRequest,
|
||||
GetDefaultDomainClaimedMessageTextRequest,
|
||||
GetDefaultInitMessageTextRequest,
|
||||
GetDefaultPasswordChangeMessageTextRequest,
|
||||
GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
GetDefaultPasswordResetMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultVerifySMSOTPMessageTextRequest,
|
||||
SetCustomDomainClaimedMessageTextRequest,
|
||||
SetCustomInitMessageTextRequest,
|
||||
SetCustomPasswordChangeMessageTextRequest,
|
||||
SetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
SetCustomPasswordResetMessageTextRequest,
|
||||
SetCustomVerifyEmailMessageTextRequest,
|
||||
SetCustomVerifyEmailOTPMessageTextRequest,
|
||||
SetCustomVerifyPhoneMessageTextRequest,
|
||||
SetCustomVerifySMSOTPMessageTextRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { MessageCustomText } from 'src/app/proto/generated/zitadel/text_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
@@ -60,6 +70,8 @@ enum MESSAGETYPES {
|
||||
DOMAINCLAIMED = 'DC',
|
||||
PASSWORDLESS = 'PL',
|
||||
PASSWORDCHANGE = 'PC',
|
||||
VERIFYSMSOTP = 'VSO',
|
||||
VERIFYEMAILOTP = 'VEO',
|
||||
}
|
||||
|
||||
const REQUESTMAP = {
|
||||
@@ -132,6 +144,33 @@ const REQUESTMAP = {
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.VERIFYSMSOTP]: {
|
||||
get: new GetCustomVerifySMSOTPMessageTextRequest(),
|
||||
set: new SetCustomVerifySMSOTPMessageTextRequest(),
|
||||
getDefault: new GetDefaultVerifySMSOTPMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomVerifySMSOTPMessageTextRequest => {
|
||||
const req = new SetCustomVerifySMSOTPMessageTextRequest();
|
||||
req.setText(map.text ?? '');
|
||||
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.VERIFYEMAILOTP]: {
|
||||
get: new GetCustomVerifyEmailOTPMessageTextRequest(),
|
||||
set: new SetCustomVerifyEmailOTPMessageTextRequest(),
|
||||
getDefault: new GetDefaultVerifyEmailOTPMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetCustomVerifyEmailOTPMessageTextRequest => {
|
||||
const req = new SetCustomVerifyEmailOTPMessageTextRequest();
|
||||
req.setFooterText(map.footerText ?? '');
|
||||
req.setGreeting(map.greeting ?? '');
|
||||
req.setPreHeader(map.preHeader ?? '');
|
||||
req.setSubject(map.subject ?? '');
|
||||
req.setText(map.text ?? '');
|
||||
req.setTitle(map.title ?? '');
|
||||
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.PASSWORDRESET]: {
|
||||
get: new GetCustomPasswordResetMessageTextRequest(),
|
||||
set: new SetCustomPasswordResetMessageTextRequest(),
|
||||
@@ -255,6 +294,31 @@ const REQUESTMAP = {
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.VERIFYSMSOTP]: {
|
||||
get: new AdminGetDefaultVerifySMSOTPMessageTextRequest(),
|
||||
set: new SetDefaultVerifySMSOTPMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetDefaultVerifySMSOTPMessageTextRequest => {
|
||||
const req = new SetDefaultVerifySMSOTPMessageTextRequest();
|
||||
req.setText(map.text ?? '');
|
||||
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.VERIFYEMAILOTP]: {
|
||||
get: new AdminGetDefaultVerifyEmailOTPMessageTextRequest(),
|
||||
set: new SetDefaultVerifyEmailOTPMessageTextRequest(),
|
||||
setFcn: (map: Partial<MessageCustomText.AsObject>): SetDefaultVerifyEmailOTPMessageTextRequest => {
|
||||
const req = new SetDefaultVerifyEmailOTPMessageTextRequest();
|
||||
req.setFooterText(map.footerText ?? '');
|
||||
req.setGreeting(map.greeting ?? '');
|
||||
req.setPreHeader(map.preHeader ?? '');
|
||||
req.setSubject(map.subject ?? '');
|
||||
req.setText(map.text ?? '');
|
||||
req.setTitle(map.title ?? '');
|
||||
|
||||
return req;
|
||||
},
|
||||
},
|
||||
[MESSAGETYPES.PASSWORDRESET]: {
|
||||
get: new AdminGetDefaultPasswordResetMessageTextRequest(),
|
||||
set: new SetDefaultPasswordResetMessageTextRequest(),
|
||||
@@ -318,8 +382,8 @@ const REQUESTMAP = {
|
||||
})
|
||||
export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
public loading: boolean = false;
|
||||
public getDefaultInitMessageTextMap$: Observable<{ [key: string]: string }> = of({});
|
||||
public getCustomInitMessageTextMap$: BehaviorSubject<{ [key: string]: string | boolean }> = new BehaviorSubject({}); // boolean because of isDefault
|
||||
public getDefaultMessageTextMap$: Observable<{ [key: string]: string }> = of({});
|
||||
public getCustomMessageTextMap$: BehaviorSubject<{ [key: string]: string | boolean }> = new BehaviorSubject({}); // boolean because of isDefault
|
||||
|
||||
public currentType: MESSAGETYPES = MESSAGETYPES.INIT;
|
||||
|
||||
@@ -329,7 +393,7 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
|
||||
public MESSAGETYPES: any = MESSAGETYPES;
|
||||
|
||||
public updateRequest!: SetCustomInitMessageTextRequest | SetDefaultInitMessageTextRequest;
|
||||
public updateRequest!: any;
|
||||
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
public chips: {
|
||||
@@ -411,6 +475,38 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||
],
|
||||
[MESSAGETYPES.VERIFYSMSOTP]: [
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.otp', value: '{{.OTP}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifyUrl', value: '{{.VerifyURL}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.preferredLoginName', value: '{{.PreferredLoginName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.username', value: '{{.UserName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.firstname', value: '{{.FirstName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastname', value: '{{.Lastname}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.nickName', value: '{{.NickName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.displayName', value: '{{.DisplayName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastEmail', value: '{{.LastEmail}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedEmail', value: '{{.VerifiedEmail}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastPhone', value: '{{.LastPhone}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedPhone', value: '{{.VerifiedPhone}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||
],
|
||||
[MESSAGETYPES.VERIFYEMAILOTP]: [
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.otp', value: '{{.OTP}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifyUrl', value: '{{.VerifyURL}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.preferredLoginName', value: '{{.PreferredLoginName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.username', value: '{{.UserName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.firstname', value: '{{.FirstName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastname', value: '{{.Lastname}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.nickName', value: '{{.NickName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.displayName', value: '{{.DisplayName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastEmail', value: '{{.LastEmail}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedEmail', value: '{{.VerifiedEmail}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.lastPhone', value: '{{.LastPhone}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.verifiedPhone', value: '{{.VerifiedPhone}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.loginnames', value: '{{.LoginNames}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.changedate', value: '{{.ChangeDate}}' },
|
||||
],
|
||||
[MESSAGETYPES.PASSWORDLESS]: [
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.preferredLoginName', value: '{{.PreferredLoginName}}' },
|
||||
{ key: 'POLICY.MESSAGE_TEXTS.CHIPS.username', value: '{{.UserName}}' },
|
||||
@@ -481,64 +577,48 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
public getDefaultValues(type: MESSAGETYPES, req: any): Promise<any> {
|
||||
switch (type) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return this.stripDetails(this.service.getDefaultInitMessageText(req));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return this.stripDetails(this.service.getDefaultVerifyPhoneMessageText(req));
|
||||
return this.stripEmail(this.service.getDefaultInitMessageText(req));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return this.stripDetails(this.service.getDefaultVerifyEmailMessageText(req));
|
||||
return this.stripEmail(this.service.getDefaultVerifyEmailMessageText(req));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return this.stripSMS(this.service.getDefaultVerifyPhoneMessageText(req));
|
||||
case MESSAGETYPES.VERIFYSMSOTP:
|
||||
return this.stripSMS(this.service.getDefaultVerifySMSOTPMessageText(req));
|
||||
case MESSAGETYPES.VERIFYEMAILOTP:
|
||||
return this.stripEmail(this.service.getDefaultVerifyEmailOTPMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return this.stripDetails(this.service.getDefaultPasswordResetMessageText(req));
|
||||
return this.stripEmail(this.service.getDefaultPasswordResetMessageText(req));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return this.stripDetails(this.service.getDefaultDomainClaimedMessageText(req));
|
||||
return this.stripEmail(this.service.getDefaultDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails(this.service.getDefaultPasswordlessRegistrationMessageText(req));
|
||||
return this.stripEmail(this.service.getDefaultPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails(this.service.getDefaultPasswordChangeMessageText(req));
|
||||
return this.stripEmail(this.service.getDefaultPasswordChangeMessageText(req));
|
||||
}
|
||||
}
|
||||
|
||||
public getCurrentValues(type: MESSAGETYPES, req: any): Promise<any> | undefined {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
switch (type) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomInitMessageText(req));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomVerifyPhoneMessageText(req));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomVerifyEmailMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomPasswordResetMessageText(req));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomPasswordChangeMessageText(req));
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
switch (type) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return this.stripDetails((this.service as AdminService).getCustomInitMessageText(req));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return this.stripDetails((this.service as AdminService).getCustomVerifyPhoneMessageText(req));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return this.stripDetails((this.service as AdminService).getCustomVerifyEmailMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return this.stripDetails((this.service as AdminService).getCustomPasswordResetMessageText(req));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return this.stripDetails((this.service as AdminService).getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails((this.service as AdminService).getCustomPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripDetails((this.service as AdminService).getCustomPasswordChangeMessageText(req));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
switch (type) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return this.stripEmail(this.service.getCustomInitMessageText(req));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return this.stripEmail(this.service.getCustomVerifyEmailMessageText(req));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return this.stripSMS(this.service.getCustomVerifyPhoneMessageText(req));
|
||||
case MESSAGETYPES.VERIFYSMSOTP:
|
||||
return this.stripSMS(this.service.getCustomVerifySMSOTPMessageText(req));
|
||||
case MESSAGETYPES.VERIFYEMAILOTP:
|
||||
return this.stripEmail(this.service.getCustomVerifyEmailOTPMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return this.stripEmail(this.service.getCustomPasswordResetMessageText(req));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return this.stripEmail(this.service.getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripEmail(this.service.getCustomPasswordlessRegistrationMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return this.stripEmail(this.service.getCustomPasswordChangeMessageText(req));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,7 +632,7 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
const reqDefaultInit = REQUESTMAP[this.serviceType][type].getDefault;
|
||||
|
||||
reqDefaultInit.setLanguage(this.locale);
|
||||
this.getDefaultInitMessageTextMap$ = from(this.getDefaultValues(type, reqDefaultInit));
|
||||
this.getDefaultMessageTextMap$ = from(this.getDefaultValues(type, reqDefaultInit));
|
||||
}
|
||||
|
||||
const reqCustomInit = REQUESTMAP[this.serviceType][type].get.setLanguage(this.locale);
|
||||
@@ -560,7 +640,7 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return this.getCurrentValues(type, reqCustomInit)
|
||||
?.then((data) => {
|
||||
this.loading = false;
|
||||
this.getCustomInitMessageTextMap$.next(data);
|
||||
this.getCustomMessageTextMap$.next(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false;
|
||||
@@ -587,10 +667,14 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
switch (this.currentType) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return handler((this.service as ManagementService).setCustomInitMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return handler((this.service as ManagementService).setCustomVerifyPhoneMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return handler((this.service as ManagementService).setCustomVerifyEmailMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return handler((this.service as ManagementService).setCustomVerifyPhoneMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYSMSOTP:
|
||||
return handler((this.service as ManagementService).setCustomVerifySMSOTPMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYEMAILOTP:
|
||||
return handler((this.service as ManagementService).setCustomVerifyEmailOTPMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return handler((this.service as ManagementService).setCustomPasswordResetMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
@@ -608,6 +692,10 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
return handler((this.service as AdminService).setDefaultInitMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return handler((this.service as AdminService).setDefaultVerifyPhoneMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYSMSOTP:
|
||||
return handler((this.service as AdminService).setDefaultVerifySMSOTPMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYEMAILOTP:
|
||||
return handler((this.service as AdminService).setDefaultVerifyEmailOTPMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return handler((this.service as AdminService).setDefaultVerifyEmailMessageText(this.updateRequest));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
@@ -635,55 +723,68 @@ export class MessageTextsComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
const handler = (prom: Promise<any>): Promise<any> => {
|
||||
return prom
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.loadData(this.currentType);
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
};
|
||||
|
||||
switch (this.currentType) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return handler((this.service as ManagementService).resetCustomInitMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return handler((this.service as ManagementService).resetCustomVerifyPhoneMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return handler((this.service as ManagementService).resetCustomVerifyEmailMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return handler((this.service as ManagementService).resetCustomPasswordResetMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return handler((this.service as ManagementService).resetCustomDomainClaimedMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return handler(
|
||||
(this.service as ManagementService).resetCustomPasswordlessRegistrationMessageTextToDefault(this.locale),
|
||||
);
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return handler((this.service as ManagementService).resetCustomPasswordChangeMessageTextToDefault(this.locale));
|
||||
default:
|
||||
return Promise.reject();
|
||||
}
|
||||
} else {
|
||||
if (!resp) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
const handler = (prom: Promise<any>): Promise<any> => {
|
||||
return prom
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
this.loadData(this.currentType);
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
};
|
||||
|
||||
switch (this.currentType) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return handler(this.service.resetCustomInitMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return handler(this.service.resetCustomVerifyPhoneMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYSMSOTP:
|
||||
return handler(this.service.resetCustomVerifySMSOTPMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYEMAILOTP:
|
||||
return handler(this.service.resetCustomVerifyEmailOTPMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return handler(this.service.resetCustomVerifyEmailMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return handler(this.service.resetCustomPasswordResetMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return handler(this.service.resetCustomDomainClaimedMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return handler(this.service.resetCustomPasswordlessRegistrationMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.PASSWORDCHANGE:
|
||||
return handler(this.service.resetCustomPasswordChangeMessageTextToDefault(this.locale));
|
||||
default:
|
||||
return Promise.reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private stripDetails(prom: Promise<any>): Promise<any> {
|
||||
private strip(prom: Promise<any>, properties: Array<string>): Promise<any> {
|
||||
return prom.then((res) => {
|
||||
if (res.customText) {
|
||||
delete res.customText.details;
|
||||
properties.forEach((property) => {
|
||||
delete res.customText[property];
|
||||
});
|
||||
return Object.assign({}, res.customText as unknown as { [key: string]: string });
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private stripEmail(prom: Promise<any>): Promise<any> {
|
||||
return this.strip(prom, ['details']);
|
||||
}
|
||||
|
||||
private stripSMS(prom: Promise<any>): Promise<any> {
|
||||
return this.strip(prom, ['details', 'buttonText', 'footerText', 'greeting', 'preHeader', 'subject', 'title']);
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
+2
-2
@@ -32,8 +32,8 @@ export class DialogAddSecretGeneratorComponent {
|
||||
expiry: [exp, [requiredValidator]],
|
||||
length: [data.config?.length ?? 6, [requiredValidator]],
|
||||
includeDigits: [data.config?.includeDigits ?? true, [requiredValidator]],
|
||||
includeLowerLetters: [data.config?.includeSymbols ?? true, [requiredValidator]],
|
||||
includeSymbols: [data.config?.includeLowerLetters ?? true, [requiredValidator]],
|
||||
includeSymbols: [data.config?.includeSymbols ?? true, [requiredValidator]],
|
||||
includeLowerLetters: [data.config?.includeLowerLetters ?? true, [requiredValidator]],
|
||||
includeUpperLetters: [data.config?.includeUpperLetters ?? true, [requiredValidator]],
|
||||
});
|
||||
}
|
||||
|
||||
+4
-1
@@ -106,7 +106,10 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
|
||||
.subscribe((userresp: ListUsersResponse.AsObject | unknown) => {
|
||||
this.isLoading = false;
|
||||
if (this.target === UserTarget.SELF && userresp) {
|
||||
this.filteredUsers = (userresp as ListUsersResponse.AsObject).resultList;
|
||||
const results = (userresp as ListUsersResponse.AsObject).resultList;
|
||||
this.filteredUsers = results.filter((filteredUser) => {
|
||||
return !this.users.map((u) => u.id).includes(filteredUser.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+110
-7
@@ -7,27 +7,130 @@
|
||||
|
||||
<div class="type-selection">
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
mat-stroked-button
|
||||
[disabled]="data.otpDisabled$ | async"
|
||||
(click)="selectType(AuthFactorType.OTP)"
|
||||
data-e2e="add-factor-otp"
|
||||
>
|
||||
<div class="otp-btn">
|
||||
<mat-icon class="icon" svgIcon="mdi_radar"></mat-icon>
|
||||
<div class="icon-row">
|
||||
<svg
|
||||
class="authenticator-logo"
|
||||
version="1.1"
|
||||
baseProfile="basic"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 512 512"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<path
|
||||
fill="#1A73E8"
|
||||
d="M440,255.99997v0.00006C440,273.12085,426.12085,287,409.00003,287H302l-46-93.01001l49.6507-85.9951
|
||||
c8.56021-14.82629,27.51834-19.9065,42.34518-11.34724l0.00586,0.0034c14.82776,8.55979,19.90875,27.51928,11.34857,42.34682
|
||||
L309.70001,225h99.30002C426.12085,225,440,238.87917,440,255.99997z"
|
||||
/>
|
||||
<path
|
||||
fill="#EA4335"
|
||||
d="M348.00174,415.34897l-0.00586,0.00339c-14.82684,8.55927-33.78497,3.47903-42.34518-11.34723L256,318.01001
|
||||
l-49.65065,85.99509c-8.5602,14.82629-27.51834,19.90652-42.34517,11.34729l-0.00591-0.00342
|
||||
c-14.82777-8.55978-19.90875-27.51929-11.34859-42.34683L202.29999,287L256,285l53.70001,2l49.6503,86.00214
|
||||
C367.91049,387.82968,362.8295,406.78918,348.00174,415.34897z"
|
||||
/>
|
||||
<path
|
||||
fill="#FBBC04"
|
||||
d="M256,193.98999L242,232l-39.70001-7l-49.6503-86.00212
|
||||
c-8.56017-14.82755-3.47919-33.78705,11.34859-42.34684l0.00591-0.00341c14.82683-8.55925,33.78497-3.47903,42.34517,11.34726
|
||||
L256,193.98999z"
|
||||
/>
|
||||
<path
|
||||
fill="#34A853"
|
||||
d="M248,225l-36,62H102.99997C85.87916,287,72,273.12085,72,256.00003v-0.00006
|
||||
C72,238.87917,85.87916,225,102.99997,225H248z"
|
||||
/>
|
||||
<polygon fill="#185DB7" points="309.70001,287 202.29999,287 256,193.98999 " />
|
||||
</svg>
|
||||
</div>
|
||||
<span>{{ 'USER.MFA.OTP' | translate }}</span>
|
||||
</div>
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="selectType(AuthFactorType.U2F)">
|
||||
<button mat-stroked-button (click)="selectType(AuthFactorType.U2F)">
|
||||
<div class="u2f-btn">
|
||||
<div class="icon-row">
|
||||
<i matTooltip="Fingerprint" class="las la-fingerprint"></i>
|
||||
<i matTooltip="Security Key" class="lab la-usb"></i>
|
||||
<mat-icon matTooltip="NFC">nfc</mat-icon>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="fingerprint"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M7.864 4.243A7.5 7.5 0 0119.5 10.5c0 2.92-.556 5.709-1.568 8.268M5.742 6.364A7.465 7.465 0 004.5 10.5a7.464 7.464 0 01-1.15 3.993m1.989 3.559A11.209 11.209 0 008.25 10.5a3.75 3.75 0 117.5 0c0 .527-.021 1.049-.064 1.565M12 10.5a14.94 14.94 0 01-3.6 9.75m6.633-4.596a18.666 18.666 0 01-2.485 5.33"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span>{{ 'USER.MFA.U2F' | translate }}</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
[disabled]="!data.phoneVerified || (data.otpSmsDisabled$ | async)"
|
||||
mat-stroked-button
|
||||
(click)="selectType(AuthFactorType.OTPSMS)"
|
||||
>
|
||||
<div class="otp-btn">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="icon-row">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="sms"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M10.5 1.5H8.25A2.25 2.25 0 006 3.75v16.5a2.25 2.25 0 002.25 2.25h7.5A2.25 2.25 0 0018 20.25V3.75a2.25 2.25 0 00-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span>{{ 'USER.MFA.OTPSMS' | translate }}</span>
|
||||
</div>
|
||||
|
||||
<cnsl-info-section class="info" [type]="InfoSectionType.ALERT" *ngIf="!data.phoneVerified"
|
||||
><span>{{ 'USER.MFA.OTPSMSPHONEMUSTBEVERIFIED' | translate }}</span></cnsl-info-section
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<button [disabled]="data.otpEmailDisabled$ | async" mat-stroked-button (click)="selectType(AuthFactorType.OTPEMAIL)">
|
||||
<div class="otp-btn">
|
||||
<div class="icon-row">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="email"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span>{{ 'USER.MFA.OTPEMAIL' | translate }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
+50
-4
@@ -1,21 +1,67 @@
|
||||
.desc {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.type-selection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 -0.5rem;
|
||||
|
||||
.otp-btn,
|
||||
.u2f-btn {
|
||||
flex: 1;
|
||||
min-height: 100px;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: 1rem;
|
||||
box-sizing: border-box;
|
||||
white-space: normal;
|
||||
line-height: 1.5rem;
|
||||
padding: 0.5rem 0;
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.info {
|
||||
margin-left: calc(40px + 1rem);
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.authenticator-logo {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
* {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.icon-row {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 1rem;
|
||||
|
||||
.sms,
|
||||
.email,
|
||||
.fingerprint {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
text-align: start;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-12
@@ -8,12 +8,15 @@ import { take } from 'rxjs/operators';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
|
||||
import { _base64ToArrayBuffer } from '../../u2f-util';
|
||||
import { _arrayBufferToBase64 } from '../u2f_util';
|
||||
|
||||
export enum AuthFactorType {
|
||||
OTP,
|
||||
U2F,
|
||||
OTPSMS,
|
||||
OTPEMAIL,
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -32,10 +35,13 @@ export class AuthFactorDialogComponent {
|
||||
public u2fLoading: boolean = false;
|
||||
public u2fError: string = '';
|
||||
|
||||
public phoneVerified: boolean = false;
|
||||
|
||||
AuthFactorType: any = AuthFactorType;
|
||||
selectedType!: AuthFactorType;
|
||||
|
||||
public copied: string = '';
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private toast: ToastService,
|
||||
@@ -52,18 +58,19 @@ export class AuthFactorDialogComponent {
|
||||
this.selectedType = type;
|
||||
|
||||
if (type === AuthFactorType.OTP) {
|
||||
this.authService.addMyMultiFactorOTP().then(
|
||||
(otpresp) => {
|
||||
this.authService
|
||||
.addMyMultiFactorOTP()
|
||||
.then((otpresp) => {
|
||||
this.otpurl = otpresp.url;
|
||||
this.otpsecret = otpresp.secret;
|
||||
},
|
||||
(error) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
} else if (type === AuthFactorType.U2F) {
|
||||
this.authService.addMyMultiFactorU2F().then(
|
||||
(u2fresp) => {
|
||||
this.authService
|
||||
.addMyMultiFactorU2F()
|
||||
.then((u2fresp) => {
|
||||
if (u2fresp.key) {
|
||||
const credOptions: CredentialCreationOptions = JSON.parse(atob(u2fresp.key?.publicKey as string));
|
||||
|
||||
@@ -79,11 +86,42 @@ export class AuthFactorDialogComponent {
|
||||
this.u2fCredentialOptions = credOptions;
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
} else if (type === AuthFactorType.OTPSMS) {
|
||||
this.authService
|
||||
.addMyAuthFactorOTPSMS()
|
||||
.then(() => {
|
||||
this.dialogRef.close(true);
|
||||
this.translate
|
||||
.get('USER.MFA.OTPSMSSUCCESS')
|
||||
.pipe(take(1))
|
||||
.subscribe((msg) => {
|
||||
this.toast.showInfo(msg);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.dialogRef.close(false);
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (type === AuthFactorType.OTPEMAIL) {
|
||||
this.authService
|
||||
.addMyAuthFactorOTPEmail()
|
||||
.then(() => {
|
||||
this.dialogRef.close(true);
|
||||
this.translate
|
||||
.get('USER.MFA.OTPEMAILSUCCESS')
|
||||
.pipe(take(1))
|
||||
.subscribe((msg) => {
|
||||
this.toast.showInfo(msg);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.dialogRef.close(false);
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -115,7 +115,11 @@
|
||||
|
||||
<cnsl-auth-passwordless *ngIf="user" #mfaComponent></cnsl-auth-passwordless>
|
||||
|
||||
<cnsl-auth-user-mfa *ngIf="user" #mfaComponent></cnsl-auth-user-mfa>
|
||||
<cnsl-auth-user-mfa
|
||||
[phoneVerified]="user.human?.phone?.isPhoneVerified ?? false"
|
||||
*ngIf="user"
|
||||
#mfaComponent
|
||||
></cnsl-auth-user-mfa>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'grants'">
|
||||
|
||||
+27
@@ -9,6 +9,7 @@ import { Buffer } from 'buffer';
|
||||
import { Subscription, take } from 'rxjs';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { phoneValidator, requiredValidator } from 'src/app/modules/form-field/validators/validators';
|
||||
import { InfoDialogComponent } from 'src/app/modules/info-dialog/info-dialog.component';
|
||||
import { MetadataDialogComponent } from 'src/app/modules/metadata/metadata-dialog/metadata-dialog.component';
|
||||
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
|
||||
@@ -223,12 +224,38 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
this.refreshUser();
|
||||
this.promptSetupforSMSOTP();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public promptSetupforSMSOTP(): void {
|
||||
const dialogRef = this.dialog.open(InfoDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.CONTINUE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'USER.MFA.OTPSMS',
|
||||
descriptionKey: 'USER.MFA.SETUPOTPSMSDESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
this.userService.addMyAuthFactorOTPSMS().then(() => {
|
||||
this.translate
|
||||
.get('USER.MFA.OTPSMSSUCCESS')
|
||||
.pipe(take(1))
|
||||
.subscribe((msg) => {
|
||||
this.toast.showInfo(msg);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public changedLanguage(language: string): void {
|
||||
this.translate.use(language);
|
||||
}
|
||||
|
||||
+3
-1
@@ -31,8 +31,10 @@
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MFA.TABLETYPE' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<span *ngIf="mfa.otp !== undefined">OTP (One-Time Password)</span>
|
||||
<span *ngIf="mfa.otp !== undefined">TOTP (Time-based One-Time Password)</span>
|
||||
<span *ngIf="mfa.u2f !== undefined">U2F (Universal 2nd Factor)</span>
|
||||
<span *ngIf="mfa.otpSms !== undefined">One-Time Password SMS</span>
|
||||
<span *ngIf="mfa.otpEmail !== undefined">One-Time Password Email</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
||||
+60
-9
@@ -1,4 +1,4 @@
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
|
||||
import { MatLegacyTable as MatTable, MatLegacyTableDataSource as MatTableDataSource } from '@angular/material/legacy-table';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
@@ -32,12 +32,15 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild(MatTable) public table!: MatTable<AuthFactor.AsObject>;
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
@Input() public phoneVerified: boolean = false;
|
||||
public dataSource: MatTableDataSource<AuthFactor.AsObject> = new MatTableDataSource<AuthFactor.AsObject>([]);
|
||||
|
||||
public AuthFactorState: any = AuthFactorState;
|
||||
|
||||
public error: string = '';
|
||||
public otpDisabled$ = new BehaviorSubject<boolean>(true);
|
||||
public otpSmsDisabled$ = new BehaviorSubject<boolean>(true);
|
||||
public otpEmailDisabled$ = new BehaviorSubject<boolean>(true);
|
||||
|
||||
constructor(private service: GrpcAuthService, private toast: ToastService, private dialog: MatDialog) {}
|
||||
|
||||
@@ -53,6 +56,9 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
const dialogRef = this.dialog.open(AuthFactorDialogComponent, {
|
||||
data: {
|
||||
otpDisabled$: this.otpDisabled$,
|
||||
otpSmsDisabled$: this.otpSmsDisabled$,
|
||||
otpEmailDisabled$: this.otpEmailDisabled$,
|
||||
phoneVerified: this.phoneVerified,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -73,12 +79,39 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
if (index === -1) {
|
||||
this.otpDisabled$.next(false);
|
||||
}
|
||||
|
||||
const sms = list.findIndex((mfa) => mfa.otpSms);
|
||||
if (sms === -1) {
|
||||
this.otpSmsDisabled$.next(false);
|
||||
}
|
||||
|
||||
const email = list.findIndex((mfa) => mfa.otpEmail);
|
||||
if (email === -1) {
|
||||
this.otpEmailDisabled$.next(false);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.error = error.message;
|
||||
});
|
||||
}
|
||||
|
||||
private cleanupList(): void {
|
||||
const totp = this.dataSource.data.findIndex((mfa) => !!mfa.otp);
|
||||
if (totp > -1) {
|
||||
this.dataSource.data.splice(totp, 1);
|
||||
}
|
||||
|
||||
const sms = this.dataSource.data.findIndex((mfa) => !!mfa.otpSms);
|
||||
if (sms > -1) {
|
||||
this.dataSource.data.splice(sms, 1);
|
||||
}
|
||||
|
||||
const email = this.dataSource.data.findIndex((mfa) => !!mfa.otpEmail);
|
||||
if (email > -1) {
|
||||
this.dataSource.data.splice(email, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public deleteMFA(factor: AuthFactor.AsObject): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
@@ -98,10 +131,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.OTPREMOVED', true);
|
||||
|
||||
const index = this.dataSource.data.findIndex((mfa) => !!mfa.otp);
|
||||
if (index > -1) {
|
||||
this.dataSource.data.splice(index, 1);
|
||||
}
|
||||
this.cleanupList();
|
||||
this.getMFAs();
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -113,10 +143,31 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.U2FREMOVED', true);
|
||||
|
||||
const index = this.dataSource.data.findIndex((mfa) => !!mfa.u2f);
|
||||
if (index > -1) {
|
||||
this.dataSource.data.splice(index, 1);
|
||||
}
|
||||
this.cleanupList();
|
||||
this.getMFAs();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (factor.otpEmail) {
|
||||
this.service
|
||||
.removeMyAuthFactorOTPEmail()
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.U2FREMOVED', true);
|
||||
|
||||
this.cleanupList();
|
||||
this.getMFAs();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (factor.otpSms) {
|
||||
this.service
|
||||
.removeMyAuthFactorOTPSMS()
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.U2FREMOVED', true);
|
||||
|
||||
this.cleanupList();
|
||||
this.getMFAs();
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { Human, UserState } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { CodeDialogComponent } from '../auth-user-detail/code-dialog/code-dialog.component';
|
||||
import { EditDialogType } from '../auth-user-detail/edit-dialog/edit-dialog.component';
|
||||
|
||||
@@ -25,15 +26,19 @@ export class ContactComponent {
|
||||
public UserState: any = UserState;
|
||||
|
||||
public EditDialogType: any = EditDialogType;
|
||||
constructor(private dialog: MatDialog) {}
|
||||
constructor(private dialog: MatDialog, private authService: GrpcAuthService) {}
|
||||
|
||||
async emitDeletePhone(): Promise<void> {
|
||||
const { resultList } = await this.authService.listMyMultiFactors();
|
||||
const hasSMSOTP = !!resultList.find((mfa) => mfa.otpSms);
|
||||
|
||||
emitDeletePhone(): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'USER.LOGINMETHODS.PHONE.DELETETITLE',
|
||||
descriptionKey: 'USER.LOGINMETHODS.PHONE.DELETEDESC',
|
||||
warnSectionKey: hasSMSOTP ? 'USER.LOGINMETHODS.PHONE.OTPSMSREMOVALWARNING' : '',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/
|
||||
|
||||
import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
|
||||
import { QRCodeModule } from 'angularx-qrcode';
|
||||
import { InfoDialogModule } from 'src/app/modules/info-dialog/info-dialog.module';
|
||||
import { MetadataModule } from 'src/app/modules/metadata/metadata.module';
|
||||
import { CountryCallingCodesService } from 'src/app/services/country-calling-codes.service';
|
||||
import { InfoRowModule } from '../../../modules/info-row/info-row.module';
|
||||
@@ -86,6 +87,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
||||
ChangesModule,
|
||||
CommonModule,
|
||||
SidenavModule,
|
||||
InfoDialogModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MembershipsTableModule,
|
||||
|
||||
+3
-1
@@ -18,8 +18,10 @@
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MFA.TABLETYPE' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<span *ngIf="mfa.otp !== undefined">OTP (One-Time Password)</span>
|
||||
<span *ngIf="mfa.otp !== undefined">TOTP (Time-based One-Time Password)</span>
|
||||
<span *ngIf="mfa.u2f !== undefined">U2F (Universal 2nd Factor)</span>
|
||||
<span *ngIf="mfa.otpSms !== undefined">One-Time Password SMS</span>
|
||||
<span *ngIf="mfa.otpEmail !== undefined">One-Time Password Email</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@@ -66,8 +66,12 @@ import {
|
||||
GetCustomPasswordResetMessageTextResponse,
|
||||
GetCustomVerifyEmailMessageTextRequest,
|
||||
GetCustomVerifyEmailMessageTextResponse,
|
||||
GetCustomVerifyEmailOTPMessageTextRequest,
|
||||
GetCustomVerifyEmailOTPMessageTextResponse,
|
||||
GetCustomVerifyPhoneMessageTextRequest,
|
||||
GetCustomVerifyPhoneMessageTextResponse,
|
||||
GetCustomVerifySMSOTPMessageTextRequest,
|
||||
GetCustomVerifySMSOTPMessageTextResponse,
|
||||
GetDefaultDomainClaimedMessageTextRequest,
|
||||
GetDefaultDomainClaimedMessageTextResponse,
|
||||
GetDefaultInitMessageTextRequest,
|
||||
@@ -84,8 +88,12 @@ import {
|
||||
GetDefaultPasswordResetMessageTextResponse,
|
||||
GetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextResponse,
|
||||
GetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
GetDefaultVerifyEmailOTPMessageTextResponse,
|
||||
GetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextResponse,
|
||||
GetDefaultVerifySMSOTPMessageTextRequest,
|
||||
GetDefaultVerifySMSOTPMessageTextResponse,
|
||||
GetDomainPolicyRequest,
|
||||
GetDomainPolicyResponse,
|
||||
GetFileSystemNotificationProviderRequest,
|
||||
@@ -196,8 +204,12 @@ import {
|
||||
SetDefaultPasswordResetMessageTextResponse,
|
||||
SetDefaultVerifyEmailMessageTextRequest,
|
||||
SetDefaultVerifyEmailMessageTextResponse,
|
||||
SetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
SetDefaultVerifyEmailOTPMessageTextResponse,
|
||||
SetDefaultVerifyPhoneMessageTextRequest,
|
||||
SetDefaultVerifyPhoneMessageTextResponse,
|
||||
SetDefaultVerifySMSOTPMessageTextRequest,
|
||||
SetDefaultVerifySMSOTPMessageTextResponse,
|
||||
SetSecurityPolicyRequest,
|
||||
SetSecurityPolicyResponse,
|
||||
SetUpOrgRequest,
|
||||
@@ -256,6 +268,26 @@ import {
|
||||
UpdateSMTPConfigResponse,
|
||||
} from '../proto/generated/zitadel/admin_pb';
|
||||
import { Event } from '../proto/generated/zitadel/event_pb';
|
||||
import {
|
||||
ResetCustomDomainClaimedMessageTextToDefaultRequest,
|
||||
ResetCustomDomainClaimedMessageTextToDefaultResponse,
|
||||
ResetCustomInitMessageTextToDefaultRequest,
|
||||
ResetCustomInitMessageTextToDefaultResponse,
|
||||
ResetCustomPasswordChangeMessageTextToDefaultRequest,
|
||||
ResetCustomPasswordChangeMessageTextToDefaultResponse,
|
||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest,
|
||||
ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse,
|
||||
ResetCustomPasswordResetMessageTextToDefaultRequest,
|
||||
ResetCustomPasswordResetMessageTextToDefaultResponse,
|
||||
ResetCustomVerifyEmailMessageTextToDefaultRequest,
|
||||
ResetCustomVerifyEmailMessageTextToDefaultResponse,
|
||||
ResetCustomVerifyEmailOTPMessageTextToDefaultRequest,
|
||||
ResetCustomVerifyEmailOTPMessageTextToDefaultResponse,
|
||||
ResetCustomVerifyPhoneMessageTextToDefaultRequest,
|
||||
ResetCustomVerifyPhoneMessageTextToDefaultResponse,
|
||||
ResetCustomVerifySMSOTPMessageTextToDefaultRequest,
|
||||
ResetCustomVerifySMSOTPMessageTextToDefaultResponse,
|
||||
} from '../proto/generated/zitadel/management_pb';
|
||||
import { SearchQuery } from '../proto/generated/zitadel/member_pb';
|
||||
import { ListQuery } from '../proto/generated/zitadel/object_pb';
|
||||
import { GrpcService } from './grpc.service';
|
||||
@@ -415,6 +447,12 @@ export class AdminService {
|
||||
return this.grpcService.admin.getCustomInitMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomInitMessageTextToDefault(lang: string): Promise<ResetCustomInitMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomInitMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin.resetCustomInitMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setDefaultInitMessageText(
|
||||
req: SetDefaultInitMessageTextRequest,
|
||||
): Promise<SetDefaultInitMessageTextResponse.AsObject> {
|
||||
@@ -439,6 +477,14 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultVerifyEmailMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomVerifyEmailMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomVerifyEmailMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomVerifyEmailMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin.resetCustomVerifyEmailMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultVerifyPhoneMessageText(
|
||||
req: GetDefaultVerifyPhoneMessageTextRequest,
|
||||
): Promise<GetDefaultVerifyPhoneMessageTextResponse.AsObject> {
|
||||
@@ -457,6 +503,66 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultVerifyPhoneMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomVerifyPhoneMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomVerifyPhoneMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomVerifyPhoneMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin.resetCustomVerifyPhoneMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultVerifySMSOTPMessageText(
|
||||
req: GetDefaultVerifySMSOTPMessageTextRequest,
|
||||
): Promise<GetDefaultVerifySMSOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.getDefaultVerifySMSOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomVerifySMSOTPMessageText(
|
||||
req: GetCustomVerifySMSOTPMessageTextRequest,
|
||||
): Promise<GetCustomVerifySMSOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.getCustomVerifySMSOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setDefaultVerifySMSOTPMessageText(
|
||||
req: SetDefaultVerifySMSOTPMessageTextRequest,
|
||||
): Promise<SetDefaultVerifySMSOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.setDefaultVerifySMSOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomVerifySMSOTPMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomVerifySMSOTPMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomVerifySMSOTPMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin.resetCustomVerifySMSOTPMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultVerifyEmailOTPMessageText(
|
||||
req: GetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
): Promise<GetDefaultVerifyEmailOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.getDefaultVerifyEmailOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomVerifyEmailOTPMessageText(
|
||||
req: GetCustomVerifyEmailOTPMessageTextRequest,
|
||||
): Promise<GetCustomVerifyEmailOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.getCustomVerifyEmailOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setDefaultVerifyEmailOTPMessageText(
|
||||
req: SetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
): Promise<SetDefaultVerifyEmailOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.admin.setDefaultVerifyEmailOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomVerifyEmailOTPMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomVerifyEmailOTPMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomVerifyEmailOTPMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin.resetCustomVerifyEmailOTPMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordResetMessageText(
|
||||
req: GetDefaultPasswordResetMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordResetMessageTextResponse.AsObject> {
|
||||
@@ -475,6 +581,14 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultPasswordResetMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomPasswordResetMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomPasswordResetMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomPasswordResetMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin.resetCustomPasswordResetMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultDomainClaimedMessageText(
|
||||
req: GetDefaultDomainClaimedMessageTextRequest,
|
||||
): Promise<GetDefaultDomainClaimedMessageTextResponse.AsObject> {
|
||||
@@ -493,6 +607,14 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultDomainClaimedMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomDomainClaimedMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomDomainClaimedMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomDomainClaimedMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin.resetCustomDomainClaimedMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordlessRegistrationMessageText(
|
||||
req: GetDefaultPasswordlessRegistrationMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordlessRegistrationMessageTextResponse.AsObject> {
|
||||
@@ -511,6 +633,16 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultPasswordlessRegistrationMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomPasswordlessRegistrationMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin
|
||||
.resetCustomPasswordlessRegistrationMessageTextToDefault(req, null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordChangeMessageText(
|
||||
req: GetDefaultPasswordChangeMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordChangeMessageTextResponse.AsObject> {
|
||||
@@ -529,6 +661,14 @@ export class AdminService {
|
||||
return this.grpcService.admin.setDefaultPasswordChangeMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomPasswordChangeMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomPasswordChangeMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomPasswordChangeMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.admin.resetCustomPasswordChangeMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public SetUpOrg(org: SetUpOrgRequest.Org, human: SetUpOrgRequest.Human): Promise<SetUpOrgResponse.AsObject> {
|
||||
const req = new SetUpOrgRequest();
|
||||
|
||||
|
||||
@@ -16,8 +16,12 @@ import {
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
AddMyAuthFactorOTPEmailRequest,
|
||||
AddMyAuthFactorOTPEmailResponse,
|
||||
AddMyAuthFactorOTPRequest,
|
||||
AddMyAuthFactorOTPResponse,
|
||||
AddMyAuthFactorOTPSMSRequest,
|
||||
AddMyAuthFactorOTPSMSResponse,
|
||||
AddMyAuthFactorU2FRequest,
|
||||
AddMyAuthFactorU2FResponse,
|
||||
AddMyPasswordlessLinkRequest,
|
||||
@@ -61,8 +65,12 @@ import {
|
||||
ListMyUserSessionsResponse,
|
||||
ListMyZitadelPermissionsRequest,
|
||||
ListMyZitadelPermissionsResponse,
|
||||
RemoveMyAuthFactorOTPEmailRequest,
|
||||
RemoveMyAuthFactorOTPEmailResponse,
|
||||
RemoveMyAuthFactorOTPRequest,
|
||||
RemoveMyAuthFactorOTPResponse,
|
||||
RemoveMyAuthFactorOTPSMSRequest,
|
||||
RemoveMyAuthFactorOTPSMSResponse,
|
||||
RemoveMyAuthFactorU2FRequest,
|
||||
RemoveMyAuthFactorU2FResponse,
|
||||
RemoveMyAvatarRequest,
|
||||
@@ -557,6 +565,18 @@ export class GrpcAuthService {
|
||||
return this.grpcService.auth.addMyAuthFactorOTP(new AddMyAuthFactorOTPRequest(), null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addMyAuthFactorOTPSMS(): Promise<AddMyAuthFactorOTPSMSResponse.AsObject> {
|
||||
return this.grpcService.auth
|
||||
.addMyAuthFactorOTPSMS(new AddMyAuthFactorOTPSMSRequest(), null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addMyAuthFactorOTPEmail(): Promise<AddMyAuthFactorOTPEmailResponse.AsObject> {
|
||||
return this.grpcService.auth
|
||||
.addMyAuthFactorOTPEmail(new AddMyAuthFactorOTPEmailRequest(), null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public addMyMultiFactorU2F(): Promise<AddMyAuthFactorU2FResponse.AsObject> {
|
||||
return this.grpcService.auth.addMyAuthFactorU2F(new AddMyAuthFactorU2FRequest(), null).then((resp) => resp.toObject());
|
||||
}
|
||||
@@ -617,6 +637,18 @@ export class GrpcAuthService {
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public removeMyAuthFactorOTPSMS(): Promise<RemoveMyAuthFactorOTPSMSResponse.AsObject> {
|
||||
return this.grpcService.auth
|
||||
.removeMyAuthFactorOTPSMS(new RemoveMyAuthFactorOTPSMSRequest(), null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public removeMyAuthFactorOTPEmail(): Promise<RemoveMyAuthFactorOTPEmailResponse.AsObject> {
|
||||
return this.grpcService.auth
|
||||
.removeMyAuthFactorOTPEmail(new RemoveMyAuthFactorOTPEmailRequest(), null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public verifyMyMultiFactorOTP(code: string): Promise<VerifyMyAuthFactorOTPResponse.AsObject> {
|
||||
const req = new VerifyMyAuthFactorOTPRequest();
|
||||
req.setCode(code);
|
||||
|
||||
@@ -135,8 +135,12 @@ import {
|
||||
GetCustomPasswordResetMessageTextResponse,
|
||||
GetCustomVerifyEmailMessageTextRequest,
|
||||
GetCustomVerifyEmailMessageTextResponse,
|
||||
GetCustomVerifyEmailOTPMessageTextRequest,
|
||||
GetCustomVerifyEmailOTPMessageTextResponse,
|
||||
GetCustomVerifyPhoneMessageTextRequest,
|
||||
GetCustomVerifyPhoneMessageTextResponse,
|
||||
GetCustomVerifySMSOTPMessageTextRequest,
|
||||
GetCustomVerifySMSOTPMessageTextResponse,
|
||||
GetDefaultDomainClaimedMessageTextRequest,
|
||||
GetDefaultDomainClaimedMessageTextResponse,
|
||||
GetDefaultInitMessageTextRequest,
|
||||
@@ -155,8 +159,12 @@ import {
|
||||
GetDefaultPasswordResetMessageTextResponse,
|
||||
GetDefaultVerifyEmailMessageTextRequest,
|
||||
GetDefaultVerifyEmailMessageTextResponse,
|
||||
GetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
GetDefaultVerifyEmailOTPMessageTextResponse,
|
||||
GetDefaultVerifyPhoneMessageTextRequest,
|
||||
GetDefaultVerifyPhoneMessageTextResponse,
|
||||
GetDefaultVerifySMSOTPMessageTextRequest,
|
||||
GetDefaultVerifySMSOTPMessageTextResponse,
|
||||
GetDomainPolicyRequest,
|
||||
GetDomainPolicyResponse,
|
||||
GetFlowRequest,
|
||||
@@ -375,8 +383,12 @@ import {
|
||||
ResetCustomPasswordResetMessageTextToDefaultResponse,
|
||||
ResetCustomVerifyEmailMessageTextToDefaultRequest,
|
||||
ResetCustomVerifyEmailMessageTextToDefaultResponse,
|
||||
ResetCustomVerifyEmailOTPMessageTextToDefaultRequest,
|
||||
ResetCustomVerifyEmailOTPMessageTextToDefaultResponse,
|
||||
ResetCustomVerifyPhoneMessageTextToDefaultRequest,
|
||||
ResetCustomVerifyPhoneMessageTextToDefaultResponse,
|
||||
ResetCustomVerifySMSOTPMessageTextToDefaultRequest,
|
||||
ResetCustomVerifySMSOTPMessageTextToDefaultResponse,
|
||||
ResetLabelPolicyToDefaultRequest,
|
||||
ResetLabelPolicyToDefaultResponse,
|
||||
ResetLockoutPolicyToDefaultRequest,
|
||||
@@ -408,8 +420,12 @@ import {
|
||||
SetCustomPasswordResetMessageTextResponse,
|
||||
SetCustomVerifyEmailMessageTextRequest,
|
||||
SetCustomVerifyEmailMessageTextResponse,
|
||||
SetCustomVerifyEmailOTPMessageTextRequest,
|
||||
SetCustomVerifyEmailOTPMessageTextResponse,
|
||||
SetCustomVerifyPhoneMessageTextRequest,
|
||||
SetCustomVerifyPhoneMessageTextResponse,
|
||||
SetCustomVerifySMSOTPMessageTextRequest,
|
||||
SetCustomVerifySMSOTPMessageTextResponse,
|
||||
SetHumanInitialPasswordRequest,
|
||||
SetOrgMetadataRequest,
|
||||
SetOrgMetadataResponse,
|
||||
@@ -620,6 +636,58 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.resetCustomVerifyPhoneMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultVerifySMSOTPMessageText(
|
||||
req: GetDefaultVerifySMSOTPMessageTextRequest,
|
||||
): Promise<GetDefaultVerifySMSOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getDefaultVerifySMSOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomVerifySMSOTPMessageText(
|
||||
req: GetCustomVerifySMSOTPMessageTextRequest,
|
||||
): Promise<GetCustomVerifySMSOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getCustomVerifySMSOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setCustomVerifySMSOTPMessageText(
|
||||
req: SetCustomVerifySMSOTPMessageTextRequest,
|
||||
): Promise<SetCustomVerifySMSOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.setCustomVerifySMSOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomVerifySMSOTPMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomVerifySMSOTPMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomVerifySMSOTPMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.mgmt.resetCustomVerifySMSOTPMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultVerifyEmailOTPMessageText(
|
||||
req: GetDefaultVerifyEmailOTPMessageTextRequest,
|
||||
): Promise<GetDefaultVerifyEmailOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getDefaultVerifyEmailOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomVerifyEmailOTPMessageText(
|
||||
req: GetCustomVerifyEmailOTPMessageTextRequest,
|
||||
): Promise<GetCustomVerifyEmailOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getCustomVerifyEmailOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setCustomVerifyEmailOTPMessageText(
|
||||
req: SetCustomVerifyEmailOTPMessageTextRequest,
|
||||
): Promise<SetCustomVerifyEmailOTPMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.setCustomVerifyEmailOTPMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomVerifyEmailOTPMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomVerifyEmailOTPMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomVerifyEmailOTPMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.mgmt.resetCustomVerifyEmailOTPMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordResetMessageText(
|
||||
req: GetDefaultPasswordResetMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordResetMessageTextResponse.AsObject> {
|
||||
@@ -678,6 +746,30 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.getDefaultPasswordlessRegistrationMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomPasswordlessRegistrationMessageText(
|
||||
req: GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
): Promise<GetCustomPasswordlessRegistrationMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getCustomPasswordlessRegistrationMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setCustomPasswordlessRegistrationMessageCustomText(
|
||||
req: SetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
): Promise<SetCustomPasswordlessRegistrationMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt
|
||||
.setCustomPasswordlessRegistrationMessageCustomText(req, null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomPasswordlessRegistrationMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.mgmt
|
||||
.resetCustomPasswordlessRegistrationMessageTextToDefault(req, null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getDefaultPasswordChangeMessageText(
|
||||
req: GetDefaultPasswordChangeMessageTextRequest,
|
||||
): Promise<GetDefaultPasswordChangeMessageTextResponse.AsObject> {
|
||||
@@ -704,30 +796,6 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.resetCustomPasswordChangeMessageTextToDefault(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getCustomPasswordlessRegistrationMessageText(
|
||||
req: GetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
): Promise<GetCustomPasswordlessRegistrationMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt.getCustomPasswordlessRegistrationMessageText(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public setCustomPasswordlessRegistrationMessageCustomText(
|
||||
req: SetCustomPasswordlessRegistrationMessageTextRequest,
|
||||
): Promise<SetCustomPasswordlessRegistrationMessageTextResponse.AsObject> {
|
||||
return this.grpcService.mgmt
|
||||
.setCustomPasswordlessRegistrationMessageCustomText(req, null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public resetCustomPasswordlessRegistrationMessageTextToDefault(
|
||||
lang: string,
|
||||
): Promise<ResetCustomPasswordlessRegistrationMessageTextToDefaultResponse.AsObject> {
|
||||
const req = new ResetCustomPasswordlessRegistrationMessageTextToDefaultRequest();
|
||||
req.setLanguage(lang);
|
||||
return this.grpcService.mgmt
|
||||
.resetCustomPasswordlessRegistrationMessageTextToDefault(req, null)
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public updateUserName(userId: string, username: string): Promise<UpdateUserNameResponse.AsObject> {
|
||||
const req = new UpdateUserNameRequest();
|
||||
req.setUserId(userId);
|
||||
|
||||
@@ -414,6 +414,12 @@
|
||||
"U2F_SUCCESS": "Факторът е добавен успешно!",
|
||||
"U2F_ERROR": "Възникна грешка по време на настройката!",
|
||||
"U2F_NAME": "Име на автентификатора",
|
||||
"OTPSMS": "OTP (One-Time password) c SMS",
|
||||
"OTPEMAIL": "OTP (еднократна парола) с имейл",
|
||||
"SETUPOTPSMSDESCRIPTION": "Искате ли да настроите този телефонен номер като OTP (еднократна парола) втори фактор?",
|
||||
"OTPSMSSUCCESS": "OTP Factor е настроен успешно.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Вашият телефон трябва да бъде потвърден, за да използвате този метод.",
|
||||
"OTPEMAILSUCCESS": "OTP Factor е настроен успешно.",
|
||||
"TYPE": {
|
||||
"0": "Няма дефинирани MFA",
|
||||
"1": "Еднократна парола (OTP)",
|
||||
@@ -580,7 +586,8 @@
|
||||
"EDITVALUE": "Телефонен номер",
|
||||
"EDITDESC": "Въведете новия телефонен номер в полето по-долу.",
|
||||
"DELETETITLE": "Изтриване на телефонен номер",
|
||||
"DELETEDESC": "Наистина ли искате да изтриете телефонния номер"
|
||||
"DELETEDESC": "Наистина ли искате да изтриете телефонния номер",
|
||||
"OTPSMSREMOVALWARNING": "Този акаунт използва този телефонен номер като втори фактор. Няма да можете да го използвате, след като продължите."
|
||||
},
|
||||
"RESENDCODE": "Код за препращане",
|
||||
"ENTERCODE": "Проверете",
|
||||
@@ -1287,6 +1294,8 @@
|
||||
"INIT": "Инициализация",
|
||||
"VE": "Потвърди Имейл",
|
||||
"VP": "Потвърдете телефона",
|
||||
"VSO": "Проверка на SMS OTP",
|
||||
"VEO": "Проверка на електронна поща OTP",
|
||||
"PR": "Нулиране на парола",
|
||||
"DC": "Заявка за домейн",
|
||||
"PL": "Без парола",
|
||||
@@ -1307,7 +1316,9 @@
|
||||
"verifiedPhone": "Потвърден телефон",
|
||||
"changedate": "Промяна на датата",
|
||||
"username": "Потребителско име",
|
||||
"tempUsername": "Временно потребителско име"
|
||||
"tempUsername": "Временно потребителско име",
|
||||
"otp": "Еднократна парола",
|
||||
"verifyUrl": "URL за потвърждаване на еднократна парола"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Персонализираните текстове са запазени."
|
||||
|
||||
@@ -411,7 +411,7 @@
|
||||
"DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um Dein Konto optimal zu schützen.",
|
||||
"MANAGE_DESCRIPTION": "Verwalte die Multifaktor-Merkmale Deiner Benutzer.",
|
||||
"ADD": "Faktor hinzufügen",
|
||||
"OTP": "Authentikator App für OTP (One-Time Password)",
|
||||
"OTP": "Authentikator App für TOTP (Time-based One-Time Password)",
|
||||
"OTP_DIALOG_TITLE": "OTP hinzufügen",
|
||||
"OTP_DIALOG_DESCRIPTION": "Scanne den QR-Code mit einer Authenticator App und verifiziere den erhaltenen Code, um OTP zu aktivieren.",
|
||||
"U2F": "Fingerabdruck, Security Key, Face ID oder andere",
|
||||
@@ -420,6 +420,12 @@
|
||||
"U2F_SUCCESS": "Faktor erfolgreich hinzugefügt!",
|
||||
"U2F_ERROR": "Ein Fehler ist aufgetreten!",
|
||||
"U2F_NAME": "Authenticator Name",
|
||||
"OTPSMS": "OTP (One-Time Password) mit SMS",
|
||||
"OTPEMAIL": "OTP (One-Time Password) mit E-Mail",
|
||||
"SETUPOTPSMSDESCRIPTION": "Möchten Sie diese Telefonnummer als zweiten OTP-Faktor (Einmalpasswort) einrichten?",
|
||||
"OTPSMSSUCCESS": "OTP Faktor erfolgrech hinzugefügt.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Um diese Methode nutzen zu können, muss Ihr Telefon verifiziert werden.",
|
||||
"OTPEMAILSUCCESS": "OTP Faktor erfolgrech hinzugefügt.",
|
||||
"TYPE": {
|
||||
"0": "Keine MFA definiert",
|
||||
"1": "OTP",
|
||||
@@ -586,7 +592,8 @@
|
||||
"EDITVALUE": "Telefonnummer",
|
||||
"EDITDESC": "Geben Sie die neue Nummer in dem darunterliegenden Feld ein!",
|
||||
"DELETETITLE": "Telefonnummer löschen",
|
||||
"DELETEDESC": "Wollen Sie die Telefonnummer wirklich löschen?"
|
||||
"DELETEDESC": "Wollen Sie die Telefonnummer wirklich löschen?",
|
||||
"OTPSMSREMOVALWARNING": "Dieses Konto verwendet diese Telefonnummer als zweiten Faktor. Wenn Sie fortfahren können Sie nicht mehr darauf zugreifen."
|
||||
},
|
||||
"RESENDCODE": "Code erneut senden",
|
||||
"ENTERCODE": "Verifizieren",
|
||||
@@ -1293,6 +1300,8 @@
|
||||
"INIT": "Initialisierung",
|
||||
"VE": "Emailverifikation",
|
||||
"VP": "Telefonnummerverifikation",
|
||||
"VSO": "SMS-OTP überprüfen",
|
||||
"VEO": "E-Mail-OTP überprüfen",
|
||||
"PR": "Passwort Wiederherstellung",
|
||||
"DC": "Domainbeanspruchung",
|
||||
"PL": "Passwortlos",
|
||||
@@ -1313,7 +1322,9 @@
|
||||
"verifiedPhone": "Verifizierte Telefonnummer",
|
||||
"changedate": "Änderungsdatum",
|
||||
"username": "Username",
|
||||
"tempUsername": "Temp. Username"
|
||||
"tempUsername": "Temp. Username",
|
||||
"otp": "Einmalpasswort",
|
||||
"verifyUrl": "URL zur Überprüfung des Einmalpassworts"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Benutzerdefinierte Texte gespeichert."
|
||||
|
||||
@@ -412,7 +412,7 @@
|
||||
"DESCRIPTION": "Add a second factor to ensure optimal security for your account.",
|
||||
"MANAGE_DESCRIPTION": "Manage the second factor methods of your users.",
|
||||
"ADD": "Add Factor",
|
||||
"OTP": "Authenticator App for OTP (One-Time Password)",
|
||||
"OTP": "Authenticator App for TOTP (Time-based One-Time Password)",
|
||||
"OTP_DIALOG_TITLE": "Add OTP",
|
||||
"OTP_DIALOG_DESCRIPTION": "Scan the QR code with an authenticator app and enter the code below to verify and activate the OTP method.",
|
||||
"U2F": "Fingerprint, Security Keys, Face ID and other",
|
||||
@@ -421,6 +421,12 @@
|
||||
"U2F_SUCCESS": "Factor added successfully!",
|
||||
"U2F_ERROR": "An error during setup occurred!",
|
||||
"U2F_NAME": "Authenticator Name",
|
||||
"OTPSMS": "OTP (One-Time Password) with SMS",
|
||||
"OTPEMAIL": "OTP (One-Time Password) with Email",
|
||||
"SETUPOTPSMSDESCRIPTION": "Do you want to setup this phonenumber as OTP (One-Time password) second factor?",
|
||||
"OTPSMSSUCCESS": "OTP factor set up with success.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Your phone must be verified in order to use this method.",
|
||||
"OTPEMAILSUCCESS": "OTP factor set up with success.",
|
||||
"TYPE": {
|
||||
"0": "No MFA defined",
|
||||
"1": "One Time Password (OTP)",
|
||||
@@ -587,7 +593,8 @@
|
||||
"EDITVALUE": "Phone number",
|
||||
"EDITDESC": "Enter the new phone number in the field below.",
|
||||
"DELETETITLE": "Delete phone number",
|
||||
"DELETEDESC": "Do you really want to delete the phone number"
|
||||
"DELETEDESC": "Do you really want to delete the phone number",
|
||||
"OTPSMSREMOVALWARNING": "This account uses this phone number as second factor. You won't be able to use it after you proceed."
|
||||
},
|
||||
"RESENDCODE": "Resend Code",
|
||||
"ENTERCODE": "Verify",
|
||||
@@ -1294,6 +1301,8 @@
|
||||
"INIT": "Initialization",
|
||||
"VE": "Verify Email",
|
||||
"VP": "Verify Phone",
|
||||
"VSO": "Verify SMS OTP",
|
||||
"VEO": "Verify Email OTP",
|
||||
"PR": "Password Reset",
|
||||
"DC": "Domain Claim",
|
||||
"PL": "Passwordless",
|
||||
@@ -1314,7 +1323,9 @@
|
||||
"verifiedPhone": "Verified phone",
|
||||
"changedate": "Change date",
|
||||
"username": "Username",
|
||||
"tempUsername": "Temp username"
|
||||
"tempUsername": "Temp username",
|
||||
"otp": "One-time password",
|
||||
"verifyUrl": "Verify One-time-password URL"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Custom Texts saved."
|
||||
|
||||
@@ -412,8 +412,8 @@
|
||||
"DESCRIPTION": "Añade un doble factor para configurar una seguridad óptima de tu cuenta.",
|
||||
"MANAGE_DESCRIPTION": "Gestiona el método de doble factor para tus usuarios.",
|
||||
"ADD": "Añadir factor",
|
||||
"OTP": "App autenticadora para OTP (One-Time Password)",
|
||||
"OTP_DIALOG_TITLE": "Añadir OTP",
|
||||
"OTP": "App autenticadora para TOTP (Time-based One-Time Password)",
|
||||
"OTP_DIALOG_TITLE": "Añadir TOTP",
|
||||
"OTP_DIALOG_DESCRIPTION": "Escanea el código QR con una app autenticadora introduciendo el siguiente código para verificar y activar el método OTP.",
|
||||
"U2F": "Huella dactilar, claves de seguridad, Face ID y otros",
|
||||
"U2F_DIALOG_TITLE": "Verificar factor",
|
||||
@@ -421,6 +421,12 @@
|
||||
"U2F_SUCCESS": "¡Factor añadido con éxito!",
|
||||
"U2F_ERROR": "¡Se produjo un error durante la configuración!",
|
||||
"U2F_NAME": "Nombre del autenticador",
|
||||
"OTPSMS": "OTP (One-Time Password) con SMS",
|
||||
"OTPEMAIL": "OTP (contraseña de un solo uso) con correo electrónico",
|
||||
"SETUPOTPSMSDESCRIPTION": "¿Desea configurar este número de teléfono como segundo factor OTP (contraseña de un solo uso)?",
|
||||
"OTPSMSSUCCESS": "Factor OTP establecido con éxito.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Su teléfono debe estar verificado para usar este método.",
|
||||
"OTPEMAILSUCCESS": "Factor OTP establecido con éxito.",
|
||||
"TYPE": {
|
||||
"0": "No se ha definido MFA",
|
||||
"1": "One Time Password (OTP)",
|
||||
@@ -587,7 +593,8 @@
|
||||
"EDITVALUE": "Número de teléfono",
|
||||
"EDITDESC": "Introduce el número de teléfono en el siguiente campo.",
|
||||
"DELETETITLE": "Borrar número de teléfono",
|
||||
"DELETEDESC": "Estás seguro que quieres borrar el número de teléfono"
|
||||
"DELETEDESC": "Estás seguro que quieres borrar el número de teléfono",
|
||||
"OTPSMSREMOVALWARNING": "Esta cuenta utiliza este número de teléfono como segundo factor. No podrá usarlo después de continuar."
|
||||
},
|
||||
"RESENDCODE": "Reenviar el código",
|
||||
"ENTERCODE": "Verificar",
|
||||
@@ -1294,6 +1301,8 @@
|
||||
"INIT": "Inicialización",
|
||||
"VE": "Verificación de email",
|
||||
"VP": "Verificación de teléfono",
|
||||
"VSO": "Verificar OTP de SMS",
|
||||
"VEO": "Verificar OTP de correo electrónico",
|
||||
"PR": "Restablecimiento de contraseña",
|
||||
"DC": "Reclamar un dominio",
|
||||
"PL": "Acceso sin contraseña",
|
||||
@@ -1314,7 +1323,9 @@
|
||||
"verifiedPhone": "Teléfono verificado",
|
||||
"changedate": "Cambiar fecha",
|
||||
"username": "Nombre de usuario",
|
||||
"tempUsername": "Nombre de usuario temporal"
|
||||
"tempUsername": "Nombre de usuario temporal",
|
||||
"otp": "Contraseña de un solo uso",
|
||||
"verifyUrl": "URL para verificar la contraseña de un solo uso"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Textos personalizados guardados."
|
||||
|
||||
@@ -411,8 +411,8 @@
|
||||
"DESCRIPTION": "Ajoutez un second facteur pour garantir une sécurité optimale de votre compte.",
|
||||
"MANAGE_DESCRIPTION": "Gérez les méthodes de second facteur de vos utilisateurs.",
|
||||
"ADD": "Ajouter un facteur",
|
||||
"OTP": "Application d'authentification pour OTP (One-time password)",
|
||||
"OTP_DIALOG_TITLE": "Ajouter un OTP",
|
||||
"OTP": "Application d'authentification pour TOTP (Time-based One-time password)",
|
||||
"OTP_DIALOG_TITLE": "Ajouter un TOTP",
|
||||
"OTP_DIALOG_DESCRIPTION": "Scannez le code QR avec une application d'authentification et saisissez le code ci-dessous pour vérifier et activer la méthode OTP.",
|
||||
"U2F": "Empreinte digitale, clés de sécurité, Face ID et autres",
|
||||
"U2F_DIALOG_TITLE": "Vérifier le facteur",
|
||||
@@ -420,6 +420,12 @@
|
||||
"U2F_SUCCESS": "Facteur ajouté avec succès !",
|
||||
"U2F_ERROR": "Une erreur s'est produite pendant l'installation !",
|
||||
"U2F_NAME": "Nom de l'authentificateur",
|
||||
"OTPSMS": "OTP (One-Time Password) avec SMS",
|
||||
"OTPEMAIL": "OTP (password monouso) con e-mail",
|
||||
"SETUPOTPSMSDESCRIPTION": "Voulez-vous configurer ce numéro de téléphone comme deuxième facteur OTP (mot de passe à usage unique) ?",
|
||||
"OTPSMSSUCCESS": "OTP Factor mis en place avec succès.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Votre téléphone doit être vérifié pour pouvoir utiliser cette méthode.",
|
||||
"OTPEMAILSUCCESS": "OTP Factor mis en place avec succès.",
|
||||
"TYPE": {
|
||||
"0": "Pas de MFA défini",
|
||||
"1": "Mot de passe à usage unique (OTP)",
|
||||
@@ -586,7 +592,8 @@
|
||||
"EDITVALUE": "Numéro de téléphone",
|
||||
"EDITDESC": "Entrez le nouveau numéro de téléphone dans le champ ci-dessous.",
|
||||
"DELETETITLE": "Supprimer le numéro de téléphone",
|
||||
"DELETEDESC": "Voulez-vous vraiment supprimer le numéro de téléphone ?"
|
||||
"DELETEDESC": "Voulez-vous vraiment supprimer le numéro de téléphone ?",
|
||||
"OTPSMSREMOVALWARNING": "Ce compte utilise ce numéro de téléphone comme deuxième facteur. Vous ne pourrez plus l'utiliser après avoir continué."
|
||||
},
|
||||
"RESENDCODE": "Renvoyer le code",
|
||||
"ENTERCODE": "Vérifier",
|
||||
@@ -1293,6 +1300,8 @@
|
||||
"INIT": "Initialisation",
|
||||
"VE": "Vérifier l'email",
|
||||
"VP": "Vérifier le téléphone",
|
||||
"VSO": "Vérifier SMS OTP",
|
||||
"VEO": "Vérifier l'OTP par courrier électronique",
|
||||
"PR": "Réinitialisation du mot de passe",
|
||||
"DC": "Réclamation de domaine",
|
||||
"PL": "Sans mot de passe",
|
||||
@@ -1313,7 +1322,9 @@
|
||||
"verifiedPhone": "Téléphone vérifié",
|
||||
"changedate": "Date de changement",
|
||||
"username": "Nom d'utilisateur",
|
||||
"tempUsername": "Nom d'utilisateur temporaire"
|
||||
"tempUsername": "Nom d'utilisateur temporaire",
|
||||
"otp": "Mot de passe à usage unique",
|
||||
"verifyUrl": "URL pour vérifier le mot de passe à usage unique"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Textes personnalisés enregistrés."
|
||||
|
||||
@@ -410,8 +410,8 @@
|
||||
"DESCRIPTION": "Aggiungi un secondo fattore per garantire la sicurezza ottimale del tuo account.",
|
||||
"MANAGE_DESCRIPTION": "Gestite i metodi del secondo fattore dei vostri utenti.",
|
||||
"ADD": "Aggiungi fattore",
|
||||
"OTP": "App di autenticazione per OTP (One-Time Password)",
|
||||
"OTP_DIALOG_TITLE": "Aggiungi OTP",
|
||||
"OTP": "App di autenticazione per TOTP (Time-based One-Time Password)",
|
||||
"OTP_DIALOG_TITLE": "Aggiungi TOTP",
|
||||
"OTP_DIALOG_DESCRIPTION": "Scansiona il codice QR con un'app di autenticazione e inserisci il codice nel campo sottostante per verificare e attivare il metodo OTP.",
|
||||
"U2F": "Impronta digitale, chiave di sicurezza, Face ID e altri",
|
||||
"U2F_DIALOG_TITLE": "Verifica Fattore",
|
||||
@@ -419,6 +419,12 @@
|
||||
"U2F_SUCCESS": "Fattore aggiunto con successo!",
|
||||
"U2F_ERROR": "Si \u00e8 verificato un errore durante la configurazione!",
|
||||
"U2F_NAME": "Nome dell'autenticatore",
|
||||
"OTPSMS": "OTP (One-Time Password) con SMS",
|
||||
"OTPEMAIL": "OTP (One-Time Password) with Email",
|
||||
"SETUPOTPSMSDESCRIPTION": "Vuoi impostare questo numero di telefono come secondo fattore OTP (One-Time password)?",
|
||||
"OTPSMSSUCCESS": "Fattore OTP impostato con successo.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Il tuo telefono deve essere verificato per utilizzare questo metodo.",
|
||||
"OTPEMAILSUCCESS": "Fattore OTP impostato con successo.",
|
||||
"TYPE": {
|
||||
"0": "Nessun altro fattore definito",
|
||||
"1": "One Time Password (OTP)",
|
||||
@@ -585,7 +591,8 @@
|
||||
"EDITVALUE": "Numero di telefono",
|
||||
"EDITDESC": "Inserisci il nuovo numero di telefono nel campo sottostante.",
|
||||
"DELETETITLE": "Rimuovi il numero di telefono",
|
||||
"DELETEDESC": "Vuoi davvero rimuovere il numero di telefono"
|
||||
"DELETEDESC": "Vuoi davvero rimuovere il numero di telefono",
|
||||
"OTPSMSREMOVALWARNING": "Questo account utilizza questo numero di telefono come secondo fattore. Non sarai in grado di usarlo dopo aver proceduto."
|
||||
},
|
||||
"RESENDCODE": "Reinvia il codice",
|
||||
"ENTERCODE": "Verifica",
|
||||
@@ -1293,6 +1300,8 @@
|
||||
"INIT": "Inizializzazione",
|
||||
"VE": "Verificazione l'e-mail",
|
||||
"VP": "Verificazione del telefono",
|
||||
"VSO": "Verifica OTP SMS",
|
||||
"VEO": "Verifica OTP Email",
|
||||
"PR": "Ripristino della password",
|
||||
"DC": "Rivendicazione del dominio",
|
||||
"PL": "Autenticazione Passwordless",
|
||||
@@ -1313,7 +1322,9 @@
|
||||
"verifiedPhone": "Telefono verificato",
|
||||
"changedate": "Cambia data",
|
||||
"username": "Nome utente",
|
||||
"tempUsername": "Nome utente temporaneo"
|
||||
"tempUsername": "Nome utente temporaneo",
|
||||
"otp": "Password monouso",
|
||||
"verifyUrl": "URL per verificare la password monouso"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Testi personalizzati salvati."
|
||||
|
||||
@@ -412,7 +412,7 @@
|
||||
"DESCRIPTION": "二要素認証を追加して、アカウントに最適なセキュリティを確保します。",
|
||||
"MANAGE_DESCRIPTION": "ユーザーの二要素認証を管理する。",
|
||||
"ADD": "二要素認証を追加する",
|
||||
"OTP": "OTP用認証アプリ(ワンタイム・パスワード)",
|
||||
"OTP": "TOTP用認証アプリ(ワンタイム・パスワード)",
|
||||
"OTP_DIALOG_TITLE": "OTPの追加",
|
||||
"OTP_DIALOG_DESCRIPTION": "認証アプリでQRコードを読み取り、下記のコードを入力することで、OTP方式の確認と有効化ができます。",
|
||||
"U2F": "指紋、セキュリティキー、フェイスIDなど",
|
||||
@@ -421,6 +421,12 @@
|
||||
"U2F_SUCCESS": "二要素認証が正常に追加されました!",
|
||||
"U2F_ERROR": "セットアップ中にエラーが発生しました!",
|
||||
"U2F_NAME": "認証者名",
|
||||
"OTPSMS": "OTP(ワンタイムパスワード)とSMS",
|
||||
"OTPEMAIL": "電子メールによる OTP (ワンタイム パスワード)",
|
||||
"SETUPOTPSMSDESCRIPTION": "この電話番号を OTP (ワンタイム パスワード) の第 2 要素として設定しますか?",
|
||||
"OTPSMSSUCCESS": "OTP 係数の設定が成功しました。",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "この方法を使用するには、電話を認証する必要があります。",
|
||||
"OTPEMAILSUCCESS": "OTP 係数の設定が成功しました。",
|
||||
"TYPE": {
|
||||
"0": "MFAは定義されていません",
|
||||
"1": "ワンタイムパスワード(OTP)",
|
||||
@@ -587,7 +593,8 @@
|
||||
"EDITVALUE": "電話番号",
|
||||
"EDITDESC": "下のフィールドに新しい電話番号を入力してください。",
|
||||
"DELETETITLE": "電話番号の削除",
|
||||
"DELETEDESC": "本当に電話番号を削除してよろしいですか?"
|
||||
"DELETEDESC": "本当に電話番号を削除してよろしいですか?",
|
||||
"OTPSMSREMOVALWARNING": "このアカウントは、この電話番号を 2 番目の要素として使用します。続行すると使用できなくなります。"
|
||||
},
|
||||
"RESENDCODE": "再送信コード",
|
||||
"ENTERCODE": "認証",
|
||||
@@ -1289,6 +1296,8 @@
|
||||
"INIT": "初期セットアップ",
|
||||
"VE": "メールの認証",
|
||||
"VP": "電話番号の認証",
|
||||
"VSO": "SMS OTPを確認する",
|
||||
"VEO": "メールOTPを確認する",
|
||||
"PR": "パスワードのリセット",
|
||||
"DC": "ドメインクレーム",
|
||||
"PL": "パスワードレス",
|
||||
@@ -1309,7 +1318,9 @@
|
||||
"verifiedPhone": "認証済みの電話番号",
|
||||
"changedate": "変更日",
|
||||
"username": "ユーザー名",
|
||||
"tempUsername": "一時ユーザー名"
|
||||
"tempUsername": "一時ユーザー名",
|
||||
"otp": "ワンタイムパスワード",
|
||||
"verifyUrl": "ワンタイムパスワードを確認するURL"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "カスタムテキストが保存されました。"
|
||||
|
||||
@@ -412,7 +412,7 @@
|
||||
"DESCRIPTION": "Додадете втор фактор за оптимална безбедност на вашиот профил.",
|
||||
"MANAGE_DESCRIPTION": "Управувајте со методите за втор фактор за вашите корисници.",
|
||||
"ADD": "Додади фактор",
|
||||
"OTP": "Апликација за автентикација со OTP (Еднократна Лозинка)",
|
||||
"OTP": "Апликација за автентикација со TOTP (Еднократна Лозинка)",
|
||||
"OTP_DIALOG_TITLE": "Додади OTP",
|
||||
"OTP_DIALOG_DESCRIPTION": "Скенирајте го QR-кодот со автентикатор апликација и внесете го кодот подолу за да го верификувате и активирате методот OTP.",
|
||||
"U2F": "Отпечаток на прст, безбедносни клучеви, Face ID и други",
|
||||
@@ -421,6 +421,12 @@
|
||||
"U2F_SUCCESS": "Факторот е успешно додаден!",
|
||||
"U2F_ERROR": "Се појави грешка при подесувањето!",
|
||||
"U2F_NAME": "Име на автентикатор",
|
||||
"OTPSMS": "ОТП (еднократна лозинка) со СМС",
|
||||
"OTPEMAIL": "OTP (еднократна лозинка) со е-пошта",
|
||||
"SETUPOTPSMSDESCRIPTION": "Дали сакате да го поставите овој телефонски број како втор фактор OTP (еднократна лозинка)?",
|
||||
"OTPSMSSUCCESS": "OTP Factor е поставен со успех.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Вашиот телефон мора да биде потврден за да го користите овој метод.",
|
||||
"OTPEMAILSUCCESS": "OTP Factor е поставен со успех.",
|
||||
"TYPE": {
|
||||
"0": "Нема дефинирана MFA",
|
||||
"1": "Еднократна лозинка (OTP)",
|
||||
@@ -587,7 +593,8 @@
|
||||
"EDITVALUE": "Телефонски број",
|
||||
"EDITDESC": "Внесете го новиот телефонски број во полето подолу.",
|
||||
"DELETETITLE": "Избриши телефонски број",
|
||||
"DELETEDESC": "Дали навистина сакате да го избришете телефонскиот број"
|
||||
"DELETEDESC": "Дали навистина сакате да го избришете телефонскиот број",
|
||||
"OTPSMSREMOVALWARNING": "Оваа сметка го користи овој телефонски број како втор фактор. Нема да можете да го користите откако ќе продолжите."
|
||||
},
|
||||
"RESENDCODE": "Повторно испрати код",
|
||||
"ENTERCODE": "Верифицирај",
|
||||
@@ -1294,6 +1301,8 @@
|
||||
"INIT": "Иницијализација",
|
||||
"VE": "Верификација на е-пошта",
|
||||
"VP": "Верификација на телефонски број",
|
||||
"VSO": "Провери SMS OTP",
|
||||
"VEO": "Провери Email OTP",
|
||||
"PR": "Ресетирање на лозинка",
|
||||
"DC": "Зафатница на домен",
|
||||
"PL": "Лозинка без лозинка",
|
||||
@@ -1314,7 +1323,9 @@
|
||||
"verifiedPhone": "Верифициран телефонски број",
|
||||
"changedate": "Датум на промена",
|
||||
"username": "Корисничко име",
|
||||
"tempUsername": "Привремено корисничко име"
|
||||
"tempUsername": "Привремено корисничко име",
|
||||
"otp": "Еднократна лозинка",
|
||||
"verifyUrl": "URL за потврдување на еднократна лозинка"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Прилагодените текстови се зачувани."
|
||||
|
||||
@@ -411,8 +411,8 @@
|
||||
"DESCRIPTION": "Dodaj drugi czynnik, aby zapewnić optymalne bezpieczeństwo twojego konta.",
|
||||
"MANAGE_DESCRIPTION": "Zarządzaj metodami drugiego czynnika swoich użytkowników.",
|
||||
"ADD": "Dodaj czynnik",
|
||||
"OTP": "Aplikacja uwierzytelniająca OTP (jednorazowe hasło)",
|
||||
"OTP_DIALOG_TITLE": "Dodaj OTP",
|
||||
"OTP": "Aplikacja uwierzytelniająca TOTP (jednorazowe hasło)",
|
||||
"OTP_DIALOG_TITLE": "Dodaj TOTP",
|
||||
"OTP_DIALOG_DESCRIPTION": "Zeskanuj kod QR za pomocą aplikacji uwierzytelniającej i wprowadź poniższy kod, aby zweryfikować i aktywować metodę OTP.",
|
||||
"U2F": "Odcisk palca, klucze bezpieczeństwa, Face ID i inne",
|
||||
"U2F_DIALOG_TITLE": "Zweryfikuj czynnik",
|
||||
@@ -420,6 +420,12 @@
|
||||
"U2F_SUCCESS": "Czynnik został pomyślnie dodany!",
|
||||
"U2F_ERROR": "Wystąpił błąd podczas konfiguracji!",
|
||||
"U2F_NAME": "Nazwa uwierzytelniacza",
|
||||
"OTPSMS": "OTP (hasło jednorazowe) z SMS-em",
|
||||
"OTPEMAIL": "OTP (hasło jednorazowe) z e-mailem",
|
||||
"SETUPOTPSMSDESCRIPTION": "Czy chcesz ustawić ten numer telefonu jako drugi czynnik OTP (hasło jednorazowe)?",
|
||||
"OTPSMSSUCCESS": "Pomyślnie skonfigurowano współczynnik OTP.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Aby skorzystać z tej metody, Twój telefon musi zostać zweryfikowany.",
|
||||
"OTPEMAILSUCCESS": "Pomyślnie skonfigurowano współczynnik OTP.",
|
||||
"TYPE": {
|
||||
"0": "Nie zdefiniowano MFA",
|
||||
"1": "Jednorazowe hasło (OTP)",
|
||||
@@ -586,7 +592,8 @@
|
||||
"EDITVALUE": "Numer telefonu",
|
||||
"EDITDESC": "Wprowadź nowy numer telefonu poniżej.",
|
||||
"DELETETITLE": "Usuń numer telefonu",
|
||||
"DELETEDESC": "Czy na pewno chcesz usunąć numer telefonu"
|
||||
"DELETEDESC": "Czy na pewno chcesz usunąć numer telefonu",
|
||||
"OTPSMSREMOVALWARNING": "To konto używa tego numeru telefonu jako drugiego czynnika. Po przejściu dalej nie będziesz mógł z niego korzystać."
|
||||
},
|
||||
"RESENDCODE": "Wyślij kod ponownie",
|
||||
"ENTERCODE": "Zweryfikuj",
|
||||
@@ -1293,6 +1300,8 @@
|
||||
"INIT": "Inicjalizacja",
|
||||
"VE": "Weryfikacja e-mail",
|
||||
"VP": "Weryfikacja telefonu",
|
||||
"VSO": "Zweryfikuj SMS OTP",
|
||||
"VEO": "Zweryfikuj Email OTP",
|
||||
"PR": "Resetowanie hasła",
|
||||
"DC": "Rejestracja domeny",
|
||||
"PL": "Bez hasła",
|
||||
@@ -1313,7 +1322,9 @@
|
||||
"verifiedPhone": "Zweryfikowany telefon",
|
||||
"changedate": "Data zmiany",
|
||||
"username": "Nazwa użytkownika",
|
||||
"tempUsername": "Tymczasowa nazwa użytkownika"
|
||||
"tempUsername": "Tymczasowa nazwa użytkownika",
|
||||
"otp": "Hasło jednorazowe",
|
||||
"verifyUrl": "URL do weryfikacji hasła jednorazowego"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Teksty niestandardowe zapisane."
|
||||
|
||||
@@ -412,7 +412,7 @@
|
||||
"DESCRIPTION": "Adicione um segundo fator para garantir a segurança ideal da sua conta.",
|
||||
"MANAGE_DESCRIPTION": "Gerencie os métodos de segundo fator dos seus usuários.",
|
||||
"ADD": "Adicionar Fator",
|
||||
"OTP": "Aplicativo de Autenticação para OTP (One-Time Password)",
|
||||
"OTP": "Aplicativo de Autenticação para TOTP (One-Time Password)",
|
||||
"OTP_DIALOG_TITLE": "Adicionar OTP",
|
||||
"OTP_DIALOG_DESCRIPTION": "Digitalize o código QR com um aplicativo autenticador e insira o código abaixo para verificar e ativar o método OTP.",
|
||||
"U2F": "Impressão digital, Chaves de Segurança, Face ID e outros",
|
||||
@@ -421,6 +421,12 @@
|
||||
"U2F_SUCCESS": "Fator adicionado com sucesso!",
|
||||
"U2F_ERROR": "Ocorreu um erro durante a configuração!",
|
||||
"U2F_NAME": "Nome do Autenticador",
|
||||
"OTPSMS": "OTP (senha de uso único) com SMS",
|
||||
"OTPEMAIL": "OTP (senha de uso único) com e-mail",
|
||||
"SETUPOTPSMSDESCRIPTION": "Deseja configurar este número de telefone como segundo fator OTP (senha de uso único)?",
|
||||
"OTPSMSSUCCESS": "Fator OTP configurado com sucesso.",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "Seu telefone deve ser verificado para usar este método.",
|
||||
"OTPEMAILSUCCESS": "Fator OTP configurado com sucesso.",
|
||||
"TYPE": {
|
||||
"0": "Nenhum MFA definido",
|
||||
"1": "One Time Password (OTP)",
|
||||
@@ -587,7 +593,8 @@
|
||||
"EDITVALUE": "Número de Telefone",
|
||||
"EDITDESC": "Digite o novo número de telefone no campo abaixo.",
|
||||
"DELETETITLE": "Excluir número de telefone",
|
||||
"DELETEDESC": "Você realmente deseja excluir o número de telefone?"
|
||||
"DELETEDESC": "Você realmente deseja excluir o número de telefone?",
|
||||
"OTPSMSREMOVALWARNING": "Esta conta usa este número de telefone como segundo fator. Você não poderá usá-lo depois de continuar."
|
||||
},
|
||||
"RESENDCODE": "Reenviar Código",
|
||||
"ENTERCODE": "Verificar",
|
||||
@@ -1294,6 +1301,8 @@
|
||||
"INIT": "Inicialização",
|
||||
"VE": "Verificar Email",
|
||||
"VP": "Verificar Telefone",
|
||||
"VSO": "Verificar OTP de SMS",
|
||||
"VEO": "Verificar OTP de Email",
|
||||
"PR": "Redefinição de Senha",
|
||||
"DC": "Reivindicação de Domínio",
|
||||
"PL": "Sem senha",
|
||||
@@ -1314,7 +1323,9 @@
|
||||
"verifiedPhone": "Telefone verificado",
|
||||
"changedate": "Data de alteração",
|
||||
"username": "Nome de usuário",
|
||||
"tempUsername": "Nome de usuário temporário"
|
||||
"tempUsername": "Nome de usuário temporário",
|
||||
"otp": "Senha de uso único",
|
||||
"verifyUrl": "URL para verificar a senha de uso único"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "Textos personalizados salvos."
|
||||
|
||||
@@ -411,7 +411,7 @@
|
||||
"DESCRIPTION": "添加第二个因素以确保您帐户的最佳安全性。",
|
||||
"MANAGE_DESCRIPTION": "管理用户的第二因素身份认证方式。",
|
||||
"ADD": "添加因子",
|
||||
"OTP": "用于 OTP 的身份验证器应用程序",
|
||||
"OTP": "用于 TOTP 的身份验证器应用程序",
|
||||
"OTP_DIALOG_TITLE": "添加 OTP",
|
||||
"OTP_DIALOG_DESCRIPTION": "使用验证器应用程序扫描二维码并输入下面的代码以验证并激活 OTP 方法。",
|
||||
"U2F": "指纹、安全密钥、Face ID 等",
|
||||
@@ -420,6 +420,12 @@
|
||||
"U2F_SUCCESS": "身份验证因子添加成功!",
|
||||
"U2F_ERROR": "安装过程中发生错误!",
|
||||
"U2F_NAME": "身份验证器名称",
|
||||
"OTPSMS": "带短信的 OTP(一次性密码)",
|
||||
"OTPEMAIL": "带电子邮件的 OTP(一次性密码)",
|
||||
"SETUPOTPSMSDESCRIPTION": "您想将此电话号码设置为 OTP(一次性密码)第二因素吗?",
|
||||
"OTPSMSSUCCESS": "OTP 因子设置成功。",
|
||||
"OTPSMSPHONEMUSTBEVERIFIED": "您的手机必须经过验证才能使用此方法。",
|
||||
"OTPEMAILSUCCESS": "OTP 因子设置成功。",
|
||||
"TYPE": {
|
||||
"0": "未定义 MFA",
|
||||
"1": "一次性密码 (OTP)",
|
||||
@@ -586,7 +592,8 @@
|
||||
"EDITVALUE": "手机号码",
|
||||
"EDITDESC": "在下面的字段中输入新的手机号码。",
|
||||
"DELETETITLE": "删除手机号码",
|
||||
"DELETEDESC": "你真的要删除手机号码吗"
|
||||
"DELETEDESC": "你真的要删除手机号码吗",
|
||||
"OTPSMSREMOVALWARNING": "此帐户使用此电话号码作为第二因素。继续后您将无法使用它。"
|
||||
},
|
||||
"RESENDCODE": "重新发送验证码",
|
||||
"ENTERCODE": "验证",
|
||||
@@ -1292,6 +1299,8 @@
|
||||
"INIT": "初始化",
|
||||
"VE": "验证电子邮箱",
|
||||
"VP": "验证手机号码",
|
||||
"VSO": "验证短信一次性密码",
|
||||
"VEO": "验证电子邮件一次性密码",
|
||||
"PR": "重置密码",
|
||||
"DC": "域名声明",
|
||||
"PL": "无密码身份验证",
|
||||
@@ -1312,7 +1321,9 @@
|
||||
"verifiedPhone": "已验证的手机号码",
|
||||
"changedate": "修改于",
|
||||
"username": "用户名",
|
||||
"tempUsername": "临时用户名"
|
||||
"tempUsername": "临时用户名",
|
||||
"otp": "一次性密码",
|
||||
"verifyUrl": "验证一次性密码的URL"
|
||||
},
|
||||
"TOAST": {
|
||||
"UPDATED": "自定义文本已保存。"
|
||||
|
||||
+3026
-2455
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,9 @@
|
||||
# Generated by docusaurus-plugin-openapi-docs
|
||||
docs/apis/resources
|
||||
|
||||
# Generated markedown from config
|
||||
/uitls/generate_config/node_modules
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Complement SAMLResponse
|
||||
---
|
||||
|
||||
This flow is executed before the return of the SAMLResponse.
|
||||
|
||||
## Pre SAMLResponse creation
|
||||
|
||||
This trigger is called before attributes are set in the SAMLResponse.
|
||||
|
||||
### Parameters of Pre SAMLResponse creation
|
||||
|
||||
- `ctx`
|
||||
The first parameter contains the following fields:
|
||||
- `v1`
|
||||
- `getUser()` [*User*](./objects#user)
|
||||
- `user`
|
||||
- `getMetadata()` [*metadataResult*](./objects#metadata-result)
|
||||
- `grants` [*UserGrantList*](./objects#user-grant-list)
|
||||
- `api`
|
||||
The second parameter contains the following fields:
|
||||
- `v1`
|
||||
- `attributes`
|
||||
- `setCustomAttribute(string, string, ...string)`
|
||||
Sets any value as attribute in addition to the default attributes, if the key is not already present. The parameters represent the key, nameFormat and the attributeValue(s).
|
||||
- `user`
|
||||
- `setMetadata(string, Any)`
|
||||
Key of the metadata and any value
|
||||
@@ -42,7 +42,7 @@ Please check below the matrix for an overview where which scope is asserted.
|
||||
| Claims | Example | Description |
|
||||
|:-------------------|:-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| acr | TBA | TBA |
|
||||
| address | `Teufener Strasse 19, 9000 St. Gallen` | TBA |
|
||||
| address | `Lerchenfeldstrasse 3, 9014 St. Gallen` | TBA |
|
||||
| amr | `pwd mfa` | Authentication Method References as defined in [RFC8176](https://tools.ietf.org/html/rfc8176) <br/> `password` value is deprecated, please check `pwd` |
|
||||
| aud | `69234237810729019` | The audience of the token, by default all client id's and the project id are included |
|
||||
| auth_time | `1311280969` | Unix time of the authentication |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -39,7 +39,6 @@ export default function CreateApp(props) {
|
||||
/>
|
||||
<h3>Review your configuration</h3>
|
||||
<ReviewConfig appType={props.appType} authType={props.authType} />
|
||||
<h3>Create key for private key JWT</h3>
|
||||
<GenerateKey appType={props.appType} authType={props.authType} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import ThemedImage from "@theme/ThemedImage";
|
||||
export default function GenerateKey(props) {
|
||||
return props.appType == "api" || props.authType == "jwt" ? (
|
||||
<div>
|
||||
<h3>Create key for private key JWT</h3>
|
||||
<p>
|
||||
After you successfully created your application with authentication type
|
||||
JWT your can create keys in the Configuration section and Keys Card of
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Configure as OIDC Identity Provider for Cloudflare Zero Trust
|
||||
sidebar_label: Cloudflare Zero Trust
|
||||
---
|
||||
|
||||
import CreateApp from "../application/_application.mdx";
|
||||
|
||||
This guide shows how to configure ZITADEL as OpenID Connect identity provider for Cloudflare Zero Trust.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Existing ZITADEL instance, organization, and project. Follow our [get started](/guides/start/quickstart) guide to get started. If not present follow [this guide](/guides/start/quickstart)
|
||||
- Existing Cloudflare account and [team domain](https://developers.cloudflare.com/cloudflare-one/glossary/#team-domain)
|
||||
|
||||
## Create the client in ZITADEL
|
||||
|
||||
<CreateApp appType="web" authType="code" appName="Cloudflare" redirectURI="https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback"/>
|
||||
|
||||
## Send user info in tokens
|
||||
|
||||
Make sure to enable "User Info inside ID Token" on your application settings.
|
||||
|
||||

|
||||
|
||||
:::info
|
||||
Cloudflare will return an error "User email was not returned. API permissions are likely incorrect". Enable to send the user information inside the token on your client settings.
|
||||
:::
|
||||
|
||||
## Configure Cloudflare Zero Trust Authentication
|
||||
|
||||
1. On the Cloudflare dashboard go to Zero Trust, click settings, and then select "Authentication"
|
||||
2. Add a new login method with the type "OpenID Connect"
|
||||
3. Fill in the required information. Check the discovery endpoint of your instance (https://{your_domain}/.well-known/openid-configuration) for the urls. As mentioned in the Cloudflare docs the Certificate Url is jwks_uri.
|
||||
4. Disable PKCE (Cloudflare requires a client secret for PKCE, which is currently not supported)
|
||||
5. Add the following claims: "openid", "profile", "email"
|
||||
6. Test the connection
|
||||
|
||||
### Example configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"client_id": "<your client id>",
|
||||
"client_secret": "<your client secret>",
|
||||
"auth_url": "https://{your_domain}.zitadel.cloud/oauth/v2/authorize",
|
||||
"token_url": "https://{your_domain}.zitadel.cloud/oauth/v2/token",
|
||||
"certs_url": "https://{your_domain}.zitadel.cloud/oauth/v2/keys",
|
||||
"scopes": ["openid", "email", "profile"],
|
||||
"pkce_enabled": false,
|
||||
},
|
||||
"type": "oidc",
|
||||
"name": "Generic Google"
|
||||
}
|
||||
```
|
||||
@@ -41,6 +41,7 @@ At the moment ZITADEL offers four client types:
|
||||
- [**Native**](#native) (native, mobile or desktop applications)
|
||||
- [**User Agent**](#user-agent) (single page applications / SPA, generally JavaScript executed in the browser)
|
||||
- [**API**](#api) (OAuth Resource Server)
|
||||
- [**SAML**](#saml)
|
||||
|
||||
The first three options (Web, Native and User Agent) require user interaction, the fourth option (API) has no direct user-interaction.
|
||||
Depending on the app type, there are small differences in the possible settings.
|
||||
@@ -84,6 +85,17 @@ Following authentication types can be used:
|
||||
|
||||
After selecting your Apps Type and Authentication Method, you may need to specify redirect URIs.
|
||||
|
||||
### SAML
|
||||
|
||||
If your application doesn't support the OIDC standard, but SAML this is the right application type to choose. SAML is an open standard used for authentication based on XML.
|
||||
|
||||
After entering a name and choosing the application type "SAML", you will get to the SAML configuration screen.
|
||||
|
||||
You can either add a link to a SAML metadata file or upload it directly.
|
||||
SAML metadata is an XML document which contains information necessary for interaction with SAML-enabled identity or service providers.
|
||||
|
||||
You can find some example configurations for SAML 2.0 in our [integrate services](/docs/guides/integrate/services) guides
|
||||
|
||||
## Redirect URIs
|
||||
|
||||
App Types with User interaction (Web, Native and User Agent) require redirect URIs.
|
||||
|
||||
@@ -14,8 +14,8 @@ The responsible party for the data processing described in this privacy policy
|
||||
|
||||
**CAOS AG**
|
||||
Data Protection Officer
|
||||
Teufener Strasse 19
|
||||
9000 St. Gallen
|
||||
Lerchenfeldstrasse 3
|
||||
9014 St. Gallen
|
||||
Switzerland
|
||||
[[email protected]](mailto:[email protected])
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ custom_edit_url: null
|
||||
|
||||
### Introduction
|
||||
|
||||
CAOS Ltd. (**"We"**, **CAOS AG**, or simply **CAOS**), with head office in Teufener Strasse 19, 9000 St. Gallen, Switzerland, offers "Identity and Access Management as service" with the brand name "ZITADEL Cloud Services" and all of our Websites (**Services** or **ZITADEL Cloud**).
|
||||
CAOS Ltd. (**"We"**, **CAOS AG**, or simply **CAOS**), with head office in Lerchenfeldstrasse 3, 9014 St. Gallen, Switzerland, offers "Identity and Access Management as service" with the brand name "ZITADEL Cloud Services" and all of our Websites (**Services** or **ZITADEL Cloud**).
|
||||
|
||||
The customer relationship (**Framework Agreement** or **The Agreement**) is created by the **Customer** (**"you"**) by creating a user or organization within the ZITADEL Cloud Service. On the basis of this Framework Agreement you may then choose to make use of payable services (**Subscription**) as you wish, i.e. you may book services, options and packages yourself at any time (**Booking**, **Purchase Order**) and subsequently terminate them.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ custom_edit_url: null
|
||||
|
||||
### Introduction
|
||||
|
||||
CAOS Ltd. (**"We"**, **CAOS AG**, or simply **CAOS**), with head office located at Teufener Strasse 19, 9000 St. Gallen, Switzerland, offers amongst other services and websites (**Services**) as well commercial support services (**Support Services**) for units of CAOS software products (**Unit**), if not otherwise defined a Unit refers to a is a single, dedicated setup of an application or service.
|
||||
CAOS Ltd. (**"We"**, **CAOS AG**, or simply **CAOS**), with head office located at Lerchenfeldstrasse 3, 9014 St. Gallen, Switzerland, offers amongst other services and websites (**Services**) as well commercial support services (**Support Services**) for units of CAOS software products (**Unit**), if not otherwise defined a Unit refers to a is a single, dedicated setup of an application or service.
|
||||
|
||||
The customer relationship (**Framework Agreement** or **The Agreement**) is created by the **Customer** (**"you"**) by accepting a **Purchase Order** (**"PO"**) for the specified Support Services (**Booking**). Jointly you and CAOS will be referred to as **the Parties**. The terms of service (**"TOS"**) outlined in this document establish the most important points of this Framework Agreement – independently of the use of any other services.
|
||||
|
||||
|
||||
@@ -69,13 +69,12 @@ helm install crdb cockroachdb/cockroachdb \
|
||||
--set statefulset.replicas=1
|
||||
|
||||
# Install ZITADEL
|
||||
helm install --namespace zitadel --create-namespace my-zitadel zitadel/zitadel \
|
||||
helm install my-zitadel zitadel/zitadel \
|
||||
--set zitadel.masterkey="MasterkeyNeedsToHave32Characters" \
|
||||
--set zitadel.configmapConfig.ExternalSecure=false \
|
||||
--set zitadel.configmapConfig.TLS.Enabled=false \
|
||||
--set zitadel.secretConfig.Database.cockroach.User.Password="a-zitadel-db-user-password" \
|
||||
--set replicaCount=1 \
|
||||
--set zitadel.configmapConfig.FirstInstance.MachineKeyPath="/machinekey/zitadel-admin-sa.json" \
|
||||
--set zitadel.configmapConfig.FirstInstance.Org.Machine.Machine.Username="zitadel-admin-sa" \
|
||||
--set zitadel.configmapConfig.FirstInstance.Org.Machine.Machine.Name="Admin" \
|
||||
--set zitadel.configmapConfig.FirstInstance.Org.Machine.MachineKey.Type=1
|
||||
@@ -86,7 +85,7 @@ kubectl port-forward svc/my-zitadel 8080
|
||||
|
||||
When Helm is done, you can print your service account key from a Kubernetes secret:
|
||||
```bash
|
||||
kubectl -n zitadel get secret zitadel-admin-sa -o jsonpath='{ .data.zitadel-admin-sa\.json }' | base64 -D
|
||||
kubectl get secret zitadel-admin-sa -o jsonpath='{ .data.zitadel-admin-sa\.json }' | base64 --decode
|
||||
```
|
||||
|
||||
<Next components={props.components} />
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
defaults.yaml
|
||||
steps.yaml
|
||||
@@ -4,44 +4,57 @@ title: Configuration Options
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
import LinuxUnix from './_linuxunix.mdx'
|
||||
import Compose from './_compose.mdx'
|
||||
import Helm from './_helm.mdx'
|
||||
|
||||
# Configure ZITADEL
|
||||
import LinuxUnix from "./_linuxunix.mdx";
|
||||
import Compose from "./_compose.mdx";
|
||||
import Helm from "./_helm.mdx";
|
||||
import CodeBlock from "@theme/CodeBlock";
|
||||
import DefaultsYamlSource from "!!raw-loader!./defaults.yaml";
|
||||
import StepsYamlSource from "!!raw-loader!./steps.yaml";
|
||||
|
||||
This guide assumes you are familiar with [running ZITADEL using the least amount of configuration possible](/docs/self-hosting/deploy/overview).
|
||||
|
||||
## Configuration Files
|
||||
## Configuration files
|
||||
|
||||
### Runtime Configuration File
|
||||
### Runtime configuration file
|
||||
|
||||
You can configure the runtime using the `--config` flag of the `zitadel` binary.
|
||||
For a description of all _runtime configuration options_ and their defaults, read the [ZITADEL source code](https://github.com/zitadel/zitadel/blob/main/cmd/defaults.yaml).
|
||||
Also, you can use the environment variables listed in the defaults.yaml.
|
||||
|
||||
### Database Initialization File
|
||||
<details>
|
||||
<summary>defaults.yaml</summary>
|
||||
<CodeBlock language="yaml">{DefaultsYamlSource}</CodeBlock>
|
||||
</details>
|
||||
|
||||
### Database initialization file
|
||||
|
||||
ZITADEL uses a [different configuration file](https://github.com/zitadel/zitadel/blob/main/cmd/setup/steps.yaml) for _database initialization steps_.
|
||||
Use the `--steps` flag of the `zitadel` binary to provide this configuration file.
|
||||
Also, you can use the environment variables listed in the steps.yaml.
|
||||
|
||||
### Multiple Configuration Files
|
||||
<details>
|
||||
<summary>steps.yaml</summary>
|
||||
<CodeBlock language="yaml">{StepsYamlSource}</CodeBlock>
|
||||
</details>
|
||||
|
||||
### Multiple configuration files
|
||||
|
||||
ZITADEL merges configuration files when multiple `--config` and `--steps` flags are provided.
|
||||
You can use these flags to handle standard configuration files differently from secret configuration files.
|
||||
For example, standard configuration files stored in git may contain public information such as a database hostname.
|
||||
To use private information — such as a database admin credential — without storing it in git, use an extra `--config` or `--steps` flag that requests the private information from a secret manager.
|
||||
|
||||
## Environment Variables
|
||||
## Environment variables
|
||||
|
||||
All configuration properties are configurable using environment variables.
|
||||
ZITADEL environment variable keys are prefixed with `ZITADEL_`.
|
||||
For example, to configure the default ZITADEL IAM admin username and password, set the `zitadel` binary runtime environment variables `ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME` and `ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD`.
|
||||
For example, to configure the default ZITADEL IAM admin username and password set the `zitadel` binary runtime environment variables `ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME` and `ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD`.
|
||||
All supported environment variables are listed in the [runtime configuration file](#runtime-configuration-file) and the [database initialization file](#database-initialization-file).
|
||||
|
||||
### Proxy Configuration
|
||||
### Proxy configuration
|
||||
|
||||
A proxy for outgoing connections can be configured using the environment variables: Use `HTTP_PROXY` for outgoing HTTP requests, and `HTTPS_PROXY` for outgoing HTTPS requests.
|
||||
These environment variables are used as a proxy URL.
|
||||
To exclude specific hosts from proxying, set the `NO_PROXY` environment variable: The value is interpreted as a comma separated string.
|
||||
To exclude specific hosts from proxying, set the `NO_PROXY` environment variable: The value is interpreted as a comma-separated string.
|
||||
For more information on the `NO_PROXY` environment variable, read the [`httpproxy` Go doc](https://pkg.go.dev/golang.org/x/net/http/httpproxy#Config).
|
||||
|
||||
## Masterkey
|
||||
@@ -49,6 +62,7 @@ For more information on the `NO_PROXY` environment variable, read the [`httpprox
|
||||
The masterkey is used to AES256-encrypt other generated encryption keys.
|
||||
It must be 32 bytes.
|
||||
There are three ways to pass the masterkey to the `zitadel` binary:
|
||||
|
||||
- By value: Use the flag `--masterkey My_Master_Key_Which_Has_32_Bytes`
|
||||
- By environment variable `ZITADEL_MASTERKEY`: Use the flag `--masterkeyFromEnv`
|
||||
- By file: Use the flag `--masterkeyFile /path/to/file`
|
||||
@@ -56,29 +70,30 @@ There are three ways to pass the masterkey to the `zitadel` binary:
|
||||
## Passing the configuration
|
||||
|
||||
<Tabs
|
||||
groupId="installation-target"
|
||||
default="linuxunix"
|
||||
values={[
|
||||
{'label': 'Linux and Unix', 'value': 'linuxunix'},
|
||||
{'label': 'Docker Compose', 'value': 'compose'},
|
||||
{'label': 'Kubernetes (Helm)', 'value': 'k8s'}
|
||||
]}
|
||||
groupId="installation-target"
|
||||
default="linuxunix"
|
||||
values={[
|
||||
{ label: "Linux and Unix", value: "linuxunix" },
|
||||
{ label: "Docker Compose", value: "compose" },
|
||||
{ label: "Kubernetes (Helm)", value: "k8s" },
|
||||
]}
|
||||
>
|
||||
<TabItem value="linuxunix">
|
||||
<LinuxUnix/>
|
||||
</TabItem>
|
||||
<TabItem value="compose">
|
||||
<Compose/>
|
||||
</TabItem>
|
||||
<TabItem value="k8s">
|
||||
<Helm/>
|
||||
</TabItem>
|
||||
<TabItem value="linuxunix">
|
||||
<LinuxUnix />
|
||||
</TabItem>
|
||||
<TabItem value="compose">
|
||||
<Compose />
|
||||
</TabItem>
|
||||
<TabItem value="k8s">
|
||||
<Helm />
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Open your favorite internet browser at [http://localhost:8080/ui/console](http://localhost:8080/ui/console).
|
||||
This is the IAM admin users login according to your configuration in the [example-zitadel-init-steps.yaml](./example-zitadel-init-steps.yaml):
|
||||
- **username**: *root@<span></span>zitadel.localhost*
|
||||
- **password**: *RootPassword1!*
|
||||
|
||||
- **username**: _root@<span></span>zitadel.localhost_
|
||||
- **password**: _RootPassword1!_
|
||||
|
||||
## What's next
|
||||
|
||||
@@ -90,4 +105,5 @@ This is the IAM admin users login according to your configuration in the [exampl
|
||||
:::caution
|
||||
|
||||
<!-- TODO: Better mark the link in the UI -->
|
||||
|
||||
The ZITADEL management console [requires end-to-end HTTP/2 support](/docs/self-hosting/manage/http2)
|
||||
|
||||
@@ -21,6 +21,9 @@ Read more about separating the init and setup phases on the [Updating and Scalin
|
||||
## Configuration
|
||||
|
||||
Read [on the configure page](/docs/self-hosting/manage/configure) about the available options you have to configure ZITADEL.
|
||||
Prefer passing .yaml files to the ZITADEL binary instead of environment variables.
|
||||
Restricting access to these files to avoid leaking sensitive information is easier than restricting access to environment variables.
|
||||
Also, not all configuration options are available as environment variables.
|
||||
|
||||
## Networking
|
||||
|
||||
|
||||
+13
-155
@@ -6,184 +6,41 @@
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"start:api": "yarn generate && docusaurus start",
|
||||
"build": "yarn generate && docusaurus build --no-minify",
|
||||
"build": "yarn generate && docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids",
|
||||
"generate": "yarn generate:grpc && yarn generate:apidocs",
|
||||
"generate": "yarn generate:grpc && yarn generate:apidocs && yarn generate:configdocs",
|
||||
"generate:grpc": "buf generate ../proto",
|
||||
"generate:apidocs": "docusaurus clean-api-docs all && docusaurus gen-api-docs all"
|
||||
"generate:apidocs": "docusaurus clean-api-docs all && docusaurus gen-api-docs all",
|
||||
"generate:configdocs": "cp -r ../cmd/defaults.yaml ./docs/self-hosting/manage/configure/ && cp -r ../cmd/setup/steps.yaml ./docs/self-hosting/manage/configure/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": "1.5.2",
|
||||
"@algolia/autocomplete-preset-algolia": "1.5.2",
|
||||
"@ampproject/remapping": "2.2.0",
|
||||
"@babel/core": "7.17.10",
|
||||
"@babel/plugin-proposal-async-generator-functions": "7.16.8",
|
||||
"@babel/plugin-proposal-class-static-block": "7.17.6",
|
||||
"@babel/plugin-proposal-object-rest-spread": "7.17.3",
|
||||
"@babel/plugin-proposal-private-methods": "7.16.11",
|
||||
"@babel/plugin-transform-async-to-generator": "7.16.8",
|
||||
"@babel/plugin-transform-destructuring": "7.17.7",
|
||||
"@babel/plugin-transform-modules-commonjs": "7.17.9",
|
||||
"@babel/plugin-transform-modules-systemjs": "7.17.8",
|
||||
"@babel/plugin-transform-named-capturing-groups-regex": "7.17.10",
|
||||
"@babel/plugin-transform-regenerator": "7.17.9",
|
||||
"@babel/plugin-transform-runtime": "7.17.10",
|
||||
"@babel/preset-env": "7.17.10",
|
||||
"@babel/preset-react": "7.16.7",
|
||||
"@babel/preset-typescript": "7.16.7",
|
||||
"@babel/runtime-corejs3": "7.17.9",
|
||||
"@bufbuild/buf": "^1.14.0",
|
||||
"@colors/colors": "1.5.0",
|
||||
"@docsearch/css": "3.0.0",
|
||||
"@docsearch/react": "3.0.0",
|
||||
"@docusaurus/core": "2.2.0",
|
||||
"@docusaurus/cssnano-preset": "2.2.0",
|
||||
"@docusaurus/module-type-aliases": "2.2.0",
|
||||
"@docusaurus/plugin-debug": "2.2.0",
|
||||
"@docusaurus/plugin-google-analytics": "2.2.0",
|
||||
"@docusaurus/plugin-google-gtag": "2.2.0",
|
||||
"@docusaurus/plugin-sitemap": "2.2.0",
|
||||
"@docusaurus/preset-classic": "2.2.0",
|
||||
"@docusaurus/theme-classic": "2.2.0",
|
||||
"@docusaurus/theme-search-algolia": "2.2.0",
|
||||
"@docusaurus/types": "2.2.0",
|
||||
"@headlessui/react": "^1.7.4",
|
||||
"@heroicons/react": "^2.0.13",
|
||||
"@jridgewell/resolve-uri": "3.0.7",
|
||||
"@jridgewell/set-array": "1.1.1",
|
||||
"@jridgewell/trace-mapping": "0.3.11",
|
||||
"@leichtgewicht/ip-codec": "2.0.4",
|
||||
"@mdx-js/mdx": "1.6.22",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@saucelabs/theme-github-codeblock": "^0.1.1",
|
||||
"@slorber/static-site-generator-webpack-plugin": "4.0.4",
|
||||
"@svgr/core": "6.2.1",
|
||||
"@svgr/hast-util-to-babel-ast": "6.2.1",
|
||||
"@svgr/plugin-svgo": "6.2.0",
|
||||
"@swc/core": "^1.3.36",
|
||||
"@types/eslint-scope": "3.7.3",
|
||||
"@types/http-proxy": "1.17.9",
|
||||
"@types/react-router-config": "5.0.6",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/ws": "8.5.3",
|
||||
"@saucelabs/theme-github-codeblock": "^0.2.3",
|
||||
"@swc/core": "^1.3.74",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"axios": "0.25.0",
|
||||
"babel-loader": "8.2.5",
|
||||
"body-parser": "1.20.0",
|
||||
"bonjour-service": "1.0.12",
|
||||
"boxen": "6.2.1",
|
||||
"buffer": "^6.0.3",
|
||||
"clean-css": "5.3.0",
|
||||
"cli-boxes": "3.0.0",
|
||||
"cli-table3": "0.6.2",
|
||||
"clsx": "^1.2.1",
|
||||
"cookie": "0.5.0",
|
||||
"copy-webpack-plugin": "10.2.4",
|
||||
"core-js": "3.22.5",
|
||||
"core-js-pure": "3.22.5",
|
||||
"css-declaration-sorter": "6.2.2",
|
||||
"css-loader": "6.7.1",
|
||||
"css-minimizer-webpack-plugin": "3.4.1",
|
||||
"cssnano": "5.1.7",
|
||||
"cssnano-preset-advanced": "5.3.3",
|
||||
"dns-packet": "5.3.1",
|
||||
"docusaurus-plugin-image-zoom": "^0.1.1",
|
||||
"docusaurus-plugin-openapi-docs": "1.5.2",
|
||||
"docusaurus-theme-openapi-docs": "1.5.2",
|
||||
"eastasianwidth": "0.2.0",
|
||||
"enhanced-resolve": "5.9.3",
|
||||
"eval": "0.1.8",
|
||||
"express": "4.18.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"follow-redirects": "1.15.0",
|
||||
"fraction.js": "4.2.0",
|
||||
"html-minifier-terser": "6.1.0",
|
||||
"html-tags": "3.2.0",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"http-proxy-middleware": "2.0.6",
|
||||
"infima": "0.2.0-alpha.39",
|
||||
"invariant": "2.2.4",
|
||||
"docusaurus-plugin-image-zoom": "^1.0.1",
|
||||
"docusaurus-plugin-openapi-docs": "^1.7.3",
|
||||
"docusaurus-theme-openapi-docs": "^1.7.3",
|
||||
"mdx-mermaid": "^1.1.0",
|
||||
"mermaid": "^9.1.2",
|
||||
"mini-css-extract-plugin": "2.6.0",
|
||||
"multicast-dns": "7.2.4",
|
||||
"nanoid": "3.3.4",
|
||||
"node-forge": "1.3.1",
|
||||
"object-inspect": "1.12.0",
|
||||
"plugin-image-zoom": "ataft/plugin-image-zoom",
|
||||
"postcss": "^8.4.19",
|
||||
"postcss-calc": "8.2.4",
|
||||
"postcss-colormin": "5.3.0",
|
||||
"postcss-convert-values": "5.1.0",
|
||||
"postcss-discard-comments": "5.1.1",
|
||||
"postcss-discard-duplicates": "5.1.0",
|
||||
"postcss-discard-empty": "5.1.1",
|
||||
"postcss-discard-overridden": "5.1.0",
|
||||
"postcss-discard-unused": "5.1.0",
|
||||
"postcss-loader": "6.2.1",
|
||||
"postcss-merge-idents": "5.1.1",
|
||||
"postcss-merge-longhand": "5.1.4",
|
||||
"postcss-merge-rules": "5.1.1",
|
||||
"postcss-minify-font-values": "5.1.0",
|
||||
"postcss-minify-gradients": "5.1.1",
|
||||
"postcss-minify-params": "5.1.2",
|
||||
"postcss-minify-selectors": "5.2.0",
|
||||
"postcss-normalize-charset": "5.1.0",
|
||||
"postcss-normalize-display-values": "5.1.0",
|
||||
"postcss-normalize-positions": "5.1.0",
|
||||
"postcss-normalize-repeat-style": "5.1.0",
|
||||
"postcss-normalize-string": "5.1.0",
|
||||
"postcss-normalize-timing-functions": "5.1.0",
|
||||
"postcss-normalize-unicode": "5.1.0",
|
||||
"postcss-normalize-url": "5.1.0",
|
||||
"postcss-normalize-whitespace": "5.1.1",
|
||||
"postcss-ordered-values": "5.1.1",
|
||||
"postcss-reduce-idents": "5.2.0",
|
||||
"postcss-reduce-initial": "5.1.0",
|
||||
"postcss-reduce-transforms": "5.1.0",
|
||||
"postcss-sort-media-queries": "4.2.1",
|
||||
"postcss-svgo": "5.1.0",
|
||||
"postcss-unique-selectors": "5.1.1",
|
||||
"postcss-zindex": "5.1.0",
|
||||
"prismjs": "1.28.0",
|
||||
"raw-body": "2.5.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "^17.0.2",
|
||||
"react": "17.0.2",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dev-utils": "12.0.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-error-overlay": "6.0.11",
|
||||
"react-fast-compare": "3.2.0",
|
||||
"regenerator-transform": "0.15.0",
|
||||
"remark-emoji": "2.2.0",
|
||||
"rtlcss": "3.5.0",
|
||||
"rxjs": "7.5.5",
|
||||
"selfsigned": "2.0.1",
|
||||
"serve-static": "1.15.0",
|
||||
"shallowequal": "1.1.0",
|
||||
"side-channel": "1.0.4",
|
||||
"react-dom": "17.0.2",
|
||||
"sitemap": "7.1.1",
|
||||
"source-map-js": "1.0.2",
|
||||
"stylehacks": "5.1.0",
|
||||
"swc-loader": "^0.2.3",
|
||||
"terser-webpack-plugin": "5.3.1",
|
||||
"type-fest": "2.12.2",
|
||||
"url": "^0.11.0",
|
||||
"wait-on": "6.0.1",
|
||||
"webpack-bundle-analyzer": "4.5.0",
|
||||
"webpack-dev-middleware": "5.3.1",
|
||||
"webpack-dev-server": "4.9.0",
|
||||
"widest-line": "4.0.1",
|
||||
"wrap-ansi": "8.0.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"@docusaurus/theme-common": "2.2.0",
|
||||
"@paloaltonetworks/postman-code-generators": "1.1.13"
|
||||
"wait-on": "6.0.1"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
@@ -198,6 +55,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.2.0",
|
||||
"tailwindcss": "^3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +276,7 @@ module.exports = {
|
||||
collapsed: true,
|
||||
items: [
|
||||
"guides/integrate/services/gitlab-self-hosted",
|
||||
"guides/integrate/services/cloudflare-oidc",
|
||||
"guides/integrate/services/aws-saml",
|
||||
"guides/integrate/services/google-cloud",
|
||||
"guides/integrate/services/atlassian-saml",
|
||||
@@ -565,6 +566,7 @@ module.exports = {
|
||||
"apis/actions/internal-authentication",
|
||||
"apis/actions/external-authentication",
|
||||
"apis/actions/complement-token",
|
||||
"apis/actions/customize-samlresponse",
|
||||
"apis/actions/objects",
|
||||
]
|
||||
},
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 220 KiB |
+2351
-3442
File diff suppressed because it is too large
Load Diff
@@ -16,24 +16,13 @@ services:
|
||||
service: 'zitadel'
|
||||
volumes:
|
||||
- ./zitadel.yaml:/zitadel.yaml
|
||||
|
||||
prepare:
|
||||
image: node:18-alpine3.15
|
||||
working_dir: /e2e
|
||||
user: '$UID'
|
||||
volumes:
|
||||
- ../../:/e2e
|
||||
command: 'sh -c "npm ci --omit=dev && npm run lint && npx wait-on http://zitadel:8080/debug/ready"'
|
||||
|
||||
e2e:
|
||||
image: cypress/included:12.2.0
|
||||
depends_on:
|
||||
zitadel:
|
||||
condition: 'service_started'
|
||||
condition: 'service_healthy'
|
||||
db:
|
||||
condition: 'service_healthy'
|
||||
prepare:
|
||||
condition: 'service_completed_successfully'
|
||||
working_dir: /e2e
|
||||
user: '$UID'
|
||||
volumes:
|
||||
|
||||
@@ -5,6 +5,14 @@ services:
|
||||
user: '$UID'
|
||||
restart: 'always'
|
||||
image: '${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:latest}'
|
||||
build:
|
||||
context: ../../..
|
||||
dockerfile: ./build/Dockerfile
|
||||
target: artifact
|
||||
cache_from:
|
||||
- type=gha
|
||||
cache_to:
|
||||
- type=gha,mode=max
|
||||
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled --config /zitadel.yaml --steps /zitadel.yaml'
|
||||
depends_on:
|
||||
db:
|
||||
@@ -13,6 +21,12 @@ services:
|
||||
- ./zitadel.yaml:/zitadel.yaml
|
||||
ports:
|
||||
- "8080:8080"
|
||||
healthcheck:
|
||||
test: ["CMD", "zitadel", "ready"]
|
||||
interval: '10s'
|
||||
timeout: '5s'
|
||||
retries: 5
|
||||
start_period: '10s'
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ describe('quotas', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('notifications', () => {
|
||||
describe.skip('notifications', () => {
|
||||
const callURL = `http://${Cypress.env('WEBHOOK_HANDLER_HOST')}:${Cypress.env('WEBHOOK_HANDLER_PORT')}/do_something`;
|
||||
|
||||
beforeEach(() => cy.task('resetWebhookEvents'));
|
||||
@@ -185,21 +185,23 @@ describe('quotas', () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
cy.waitUntil(() =>
|
||||
cy.task<Array<ZITADELWebhookEvent>>('handledWebhookEvents').then((events) => {
|
||||
if (events.length < 1) {
|
||||
return false;
|
||||
}
|
||||
return Cypress._.matches(<ZITADELWebhookEvent>{
|
||||
sentStatus: 200,
|
||||
payload: {
|
||||
callURL: callURL,
|
||||
threshold: percent,
|
||||
unit: 1,
|
||||
usage: percent,
|
||||
},
|
||||
})(events[0]);
|
||||
}),
|
||||
cy.waitUntil(
|
||||
() =>
|
||||
cy.task<Array<ZITADELWebhookEvent>>('handledWebhookEvents').then((events) => {
|
||||
if (events.length < 1) {
|
||||
return false;
|
||||
}
|
||||
return Cypress._.matches(<ZITADELWebhookEvent>{
|
||||
sentStatus: 200,
|
||||
payload: {
|
||||
callURL: callURL,
|
||||
threshold: percent,
|
||||
unit: 1,
|
||||
usage: percent,
|
||||
},
|
||||
})(events[0]);
|
||||
}),
|
||||
{ timeout: 60_000 },
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -284,29 +286,31 @@ describe('quotas', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
cy.waitUntil(() =>
|
||||
cy.task<Array<ZITADELWebhookEvent>>('handledWebhookEvents').then((events) => {
|
||||
let foundExpected = 0;
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
for (let expect = 10; expect <= 30; expect += 10) {
|
||||
if (
|
||||
Cypress._.matches(<ZITADELWebhookEvent>{
|
||||
sentStatus: 200,
|
||||
payload: {
|
||||
callURL: callURL,
|
||||
threshold: expect,
|
||||
unit: 1,
|
||||
usage: expect,
|
||||
},
|
||||
})(events[i])
|
||||
) {
|
||||
foundExpected++;
|
||||
cy.waitUntil(
|
||||
() =>
|
||||
cy.task<Array<ZITADELWebhookEvent>>('handledWebhookEvents').then((events) => {
|
||||
let foundExpected = 0;
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
for (let expect = 10; expect <= 30; expect += 10) {
|
||||
if (
|
||||
Cypress._.matches(<ZITADELWebhookEvent>{
|
||||
sentStatus: 200,
|
||||
payload: {
|
||||
callURL: callURL,
|
||||
threshold: expect,
|
||||
unit: 1,
|
||||
usage: expect,
|
||||
},
|
||||
})(events[i])
|
||||
) {
|
||||
foundExpected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundExpected >= 3;
|
||||
}),
|
||||
), { timeout: 60_000 };
|
||||
return foundExpected >= 3;
|
||||
}),
|
||||
{ timeout: 60_000 },
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,7 +61,7 @@ require (
|
||||
github.com/zitadel/logging v0.3.4
|
||||
github.com/zitadel/oidc/v2 v2.7.0
|
||||
github.com/zitadel/passwap v0.3.0
|
||||
github.com/zitadel/saml v0.0.11
|
||||
github.com/zitadel/saml v0.1.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0
|
||||
go.opentelemetry.io/otel v1.14.0
|
||||
@@ -115,7 +115,7 @@ require (
|
||||
cloud.google.com/go/trace v1.9.0 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/amdonov/xmlsig v0.1.0 // indirect
|
||||
github.com/beevik/etree v1.1.0 // indirect
|
||||
github.com/beevik/etree v1.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
@@ -181,7 +181,7 @@ require (
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/russellhaering/goxmldsig v1.3.0 // indirect
|
||||
github.com/russellhaering/goxmldsig v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
|
||||
@@ -105,8 +105,9 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
||||
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||
github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw=
|
||||
github.com/beevik/etree v1.2.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
@@ -280,6 +281,7 @@ github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXg
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -789,8 +791,8 @@ github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russellhaering/goxmldsig v1.3.0 h1:DllIWUgMy0cRUMfGiASiYEa35nsieyD3cigIwLonTPM=
|
||||
github.com/russellhaering/goxmldsig v1.3.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||
github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys=
|
||||
github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -900,8 +902,10 @@ github.com/zitadel/oidc/v2 v2.7.0 h1:IGX4EDk6tegTjUSsZDWeTfLseFU0BdJ/Glf1tgys2lU
|
||||
github.com/zitadel/oidc/v2 v2.7.0/go.mod h1:zkUkVJS0sDVy9m0UA9RgO3f8i/C0rtjvXU36UJj7T+0=
|
||||
github.com/zitadel/passwap v0.3.0 h1:kC/vzN9xQlEQjUAZs0z2P5nKrZs9AuTqprteSQ2S4Ag=
|
||||
github.com/zitadel/passwap v0.3.0/go.mod h1:sIpG6HfmnP28qwxu8kf+ot53ERbLwU9fOITstAwZSms=
|
||||
github.com/zitadel/saml v0.0.11 h1:kObucnBrcu1PHCO7RGT0iVeuJL/5I50gUgr40S41nMs=
|
||||
github.com/zitadel/saml v0.0.11/go.mod h1:YGWAvPZRv4DbEZ78Ht/2P0AWzGn+6WGhFf90PMXl0Po=
|
||||
github.com/zitadel/saml v0.0.12-0.20230809152136-3bdfd7ad13fe h1:mcdiFREwddgcJjK+KDl/J29Cikr0nymiFhYdp3DBKB4=
|
||||
github.com/zitadel/saml v0.0.12-0.20230809152136-3bdfd7ad13fe/go.mod h1:M+X+3vMUulpoLofKeH/W1/qjQQ3owitc2GuGDu3oYpM=
|
||||
github.com/zitadel/saml v0.1.0 h1:FZKKFRCamoKmFH3kGOW0ObcDozaJz7NdHn+WPm8PcXc=
|
||||
github.com/zitadel/saml v0.1.0/go.mod h1:M+X+3vMUulpoLofKeH/W1/qjQQ3owitc2GuGDu3oYpM=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
@@ -1009,6 +1013,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
|
||||
@@ -19,6 +19,8 @@ func FlowTypeToDomain(flowType string) domain.FlowType {
|
||||
return domain.FlowTypeCustomiseToken
|
||||
case domain.FlowTypeInternalAuthentication.ID():
|
||||
return domain.FlowTypeInternalAuthentication
|
||||
case domain.FlowTypeCustomizeSAMLResponse.ID():
|
||||
return domain.FlowTypeCustomizeSAMLResponse
|
||||
default:
|
||||
return domain.FlowTypeUnspecified
|
||||
}
|
||||
@@ -47,6 +49,8 @@ func TriggerTypeToDomain(triggerType string) domain.TriggerType {
|
||||
return domain.TriggerTypePreAccessTokenCreation
|
||||
case domain.TriggerTypePreUserinfoCreation.ID():
|
||||
return domain.TriggerTypePreUserinfoCreation
|
||||
case domain.TriggerTypePreSAMLResponseCreation.ID():
|
||||
return domain.TriggerTypePreSAMLResponseCreation
|
||||
default:
|
||||
return domain.TriggerTypeUnspecified
|
||||
}
|
||||
|
||||
@@ -204,6 +204,102 @@ func (s *Server) ResetCustomVerifyPhoneMessageTextToDefault(ctx context.Context,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultVerifySMSOTPMessageText(ctx context.Context, req *admin_pb.GetDefaultVerifySMSOTPMessageTextRequest) (*admin_pb.GetDefaultVerifySMSOTPMessageTextResponse, error) {
|
||||
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.VerifySMSOTPMessageType, req.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetDefaultVerifySMSOTPMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomVerifySMSOTPMessageText(ctx context.Context, req *admin_pb.GetCustomVerifySMSOTPMessageTextRequest) (*admin_pb.GetCustomVerifySMSOTPMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetInstance(ctx).InstanceID(), domain.VerifySMSOTPMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetCustomVerifySMSOTPMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetDefaultVerifySMSOTPMessageText(ctx context.Context, req *admin_pb.SetDefaultVerifySMSOTPMessageTextRequest) (*admin_pb.SetDefaultVerifySMSOTPMessageTextResponse, error) {
|
||||
result, err := s.command.SetDefaultMessageText(ctx, authz.GetInstance(ctx).InstanceID(), SetVerifySMSOTPCustomTextToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.SetDefaultVerifySMSOTPMessageTextResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetCustomVerifySMSOTPMessageTextToDefault(ctx context.Context, req *admin_pb.ResetCustomVerifySMSOTPMessageTextToDefaultRequest) (*admin_pb.ResetCustomVerifySMSOTPMessageTextToDefaultResponse, error) {
|
||||
result, err := s.command.RemoveInstanceMessageTexts(ctx, domain.VerifySMSOTPMessageType, language.Make(req.Language))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.ResetCustomVerifySMSOTPMessageTextToDefaultResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultVerifyEmailOTPMessageText(ctx context.Context, req *admin_pb.GetDefaultVerifyEmailOTPMessageTextRequest) (*admin_pb.GetDefaultVerifyEmailOTPMessageTextResponse, error) {
|
||||
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.VerifyEmailOTPMessageType, req.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetDefaultVerifyEmailOTPMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomVerifyEmailOTPMessageText(ctx context.Context, req *admin_pb.GetCustomVerifyEmailOTPMessageTextRequest) (*admin_pb.GetCustomVerifyEmailOTPMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetInstance(ctx).InstanceID(), domain.VerifyEmailOTPMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetCustomVerifyEmailOTPMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetDefaultVerifyEmailOTPMessageText(ctx context.Context, req *admin_pb.SetDefaultVerifyEmailOTPMessageTextRequest) (*admin_pb.SetDefaultVerifyEmailOTPMessageTextResponse, error) {
|
||||
result, err := s.command.SetDefaultMessageText(ctx, authz.GetInstance(ctx).InstanceID(), SetVerifyEmailOTPCustomTextToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.SetDefaultVerifyEmailOTPMessageTextResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetCustomVerifyEmailOTPMessageTextToDefault(ctx context.Context, req *admin_pb.ResetCustomVerifyEmailOTPMessageTextToDefaultRequest) (*admin_pb.ResetCustomVerifyEmailOTPMessageTextToDefaultResponse, error) {
|
||||
result, err := s.command.RemoveInstanceMessageTexts(ctx, domain.VerifyEmailOTPMessageType, language.Make(req.Language))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.ResetCustomVerifyEmailOTPMessageTextToDefaultResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultDomainClaimedMessageText(ctx context.Context, req *admin_pb.GetDefaultDomainClaimedMessageTextRequest) (*admin_pb.GetDefaultDomainClaimedMessageTextResponse, error) {
|
||||
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.DomainClaimedMessageType, req.Language)
|
||||
if err != nil {
|
||||
|
||||
@@ -68,6 +68,30 @@ func SetVerifyPhoneCustomTextToDomain(msg *admin_pb.SetDefaultVerifyPhoneMessage
|
||||
}
|
||||
}
|
||||
|
||||
func SetVerifySMSOTPCustomTextToDomain(msg *admin_pb.SetDefaultVerifySMSOTPMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
MessageTextType: domain.VerifySMSOTPMessageType,
|
||||
Language: langTag,
|
||||
Text: msg.Text,
|
||||
}
|
||||
}
|
||||
|
||||
func SetVerifyEmailOTPCustomTextToDomain(msg *admin_pb.SetDefaultVerifyEmailOTPMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
MessageTextType: domain.VerifyEmailOTPMessageType,
|
||||
Language: langTag,
|
||||
Title: msg.Title,
|
||||
PreHeader: msg.PreHeader,
|
||||
Subject: msg.Subject,
|
||||
Greeting: msg.Greeting,
|
||||
Text: msg.Text,
|
||||
ButtonText: msg.ButtonText,
|
||||
FooterText: msg.FooterText,
|
||||
}
|
||||
}
|
||||
|
||||
func SetDomainClaimedCustomTextToDomain(msg *admin_pb.SetDefaultDomainClaimedMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
|
||||
@@ -152,6 +152,16 @@ func (s *Server) ExportData(ctx context.Context, req *admin_pb.ExportDataRequest
|
||||
return nil, err
|
||||
}
|
||||
|
||||
org.VerifySmsOtpMessages, err = s.getCustomVerifySMSOTPMessageTexts(ctx, org.GetOrgId(), langResp.Languages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
org.VerifyEmailOtpMessages, err = s.getCustomVerifyEmailOTPMessageTexts(ctx, org.GetOrgId(), langResp.Languages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
org.DomainClaimedMessages, err = s.getCustomDomainClaimedMessageTexts(ctx, org.GetOrgId(), langResp.Languages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1160,6 +1170,50 @@ func (s *Server) getCustomVerifyPhoneMessageTexts(ctx context.Context, org strin
|
||||
return customTexts, nil
|
||||
}
|
||||
|
||||
func (s *Server) getCustomVerifySMSOTPMessageTexts(ctx context.Context, org string, languages []string) ([]*management_pb.SetCustomVerifySMSOTPMessageTextRequest, error) {
|
||||
customTexts := make([]*management_pb.SetCustomVerifySMSOTPMessageTextRequest, 0, len(languages))
|
||||
for _, lang := range languages {
|
||||
text, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, org, domain.VerifySMSOTPMessageType, lang, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !text.IsDefault {
|
||||
customTexts = append(customTexts, &management_pb.SetCustomVerifySMSOTPMessageTextRequest{
|
||||
Language: lang,
|
||||
Text: text.Text,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return customTexts, nil
|
||||
}
|
||||
|
||||
func (s *Server) getCustomVerifyEmailOTPMessageTexts(ctx context.Context, org string, languages []string) ([]*management_pb.SetCustomVerifyEmailOTPMessageTextRequest, error) {
|
||||
customTexts := make([]*management_pb.SetCustomVerifyEmailOTPMessageTextRequest, 0, len(languages))
|
||||
for _, lang := range languages {
|
||||
text, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, org, domain.VerifyEmailOTPMessageType, lang, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !text.IsDefault {
|
||||
customTexts = append(customTexts, &management_pb.SetCustomVerifyEmailOTPMessageTextRequest{
|
||||
Language: lang,
|
||||
Title: text.Title,
|
||||
PreHeader: text.PreHeader,
|
||||
Subject: text.Subject,
|
||||
Greeting: text.Greeting,
|
||||
Text: text.Text,
|
||||
ButtonText: text.ButtonText,
|
||||
FooterText: text.Footer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return customTexts, nil
|
||||
}
|
||||
|
||||
func (s *Server) getCustomDomainClaimedMessageTexts(ctx context.Context, org string, languages []string) ([]*management_pb.SetCustomDomainClaimedMessageTextRequest, error) {
|
||||
customTexts := make([]*management_pb.SetCustomDomainClaimedMessageTextRequest, 0, len(languages))
|
||||
for _, lang := range languages {
|
||||
|
||||
@@ -72,18 +72,26 @@ func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (*
|
||||
}
|
||||
human := setUpOrgHumanToCommand(req.User.(*admin_pb.SetUpOrgRequest_Human_).Human) //TODO: handle machine
|
||||
|
||||
userID, objectDetails, err := s.command.SetUpOrg(ctx, &command.OrgSetup{
|
||||
createdOrg, err := s.command.SetUpOrg(ctx, &command.OrgSetup{
|
||||
Name: req.Org.Name,
|
||||
CustomDomain: req.Org.Domain,
|
||||
Human: human,
|
||||
Roles: req.Roles,
|
||||
}, userIDs...)
|
||||
Admins: []*command.OrgSetupAdmin{
|
||||
{
|
||||
Human: human,
|
||||
Roles: req.Roles,
|
||||
},
|
||||
},
|
||||
}, true, userIDs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var userID string
|
||||
if len(createdOrg.CreatedAdmins) == 1 {
|
||||
userID = createdOrg.CreatedAdmins[0].ID
|
||||
}
|
||||
return &admin_pb.SetUpOrgResponse{
|
||||
Details: object.DomainToAddDetailsPb(objectDetails),
|
||||
OrgId: objectDetails.ResourceOwner,
|
||||
Details: object.DomainToAddDetailsPb(createdOrg.ObjectDetails),
|
||||
OrgId: createdOrg.ObjectDetails.ResourceOwner,
|
||||
UserId: userID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -62,11 +62,7 @@ func (s *Server) VerifyMyPhone(ctx context.Context, req *auth_pb.VerifyMyPhoneRe
|
||||
|
||||
func (s *Server) ResendMyPhoneVerification(ctx context.Context, _ *auth_pb.ResendMyPhoneVerificationRequest) (*auth_pb.ResendMyPhoneVerificationResponse, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objectDetails, err := s.command.CreateHumanPhoneVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner, phoneCodeGenerator)
|
||||
objectDetails, err := s.command.CreateHumanPhoneVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -204,6 +204,102 @@ func (s *Server) ResetCustomVerifyPhoneMessageTextToDefault(ctx context.Context,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomVerifySMSOTPMessageText(ctx context.Context, req *mgmt_pb.GetCustomVerifySMSOTPMessageTextRequest) (*mgmt_pb.GetCustomVerifySMSOTPMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetCtxData(ctx).OrgID, domain.VerifySMSOTPMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetCustomVerifySMSOTPMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultVerifySMSOTPMessageText(ctx context.Context, req *mgmt_pb.GetDefaultVerifySMSOTPMessageTextRequest) (*mgmt_pb.GetDefaultVerifySMSOTPMessageTextResponse, error) {
|
||||
msg, err := s.query.IAMMessageTextByTypeAndLanguage(ctx, domain.VerifySMSOTPMessageType, req.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetDefaultVerifySMSOTPMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetCustomVerifySMSOTPMessageText(ctx context.Context, req *mgmt_pb.SetCustomVerifySMSOTPMessageTextRequest) (*mgmt_pb.SetCustomVerifySMSOTPMessageTextResponse, error) {
|
||||
result, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, SetVerifySMSOTPCustomTextToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.SetCustomVerifySMSOTPMessageTextResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetCustomVerifySMSOTPMessageTextToDefault(ctx context.Context, req *mgmt_pb.ResetCustomVerifySMSOTPMessageTextToDefaultRequest) (*mgmt_pb.ResetCustomVerifySMSOTPMessageTextToDefaultResponse, error) {
|
||||
result, err := s.command.RemoveOrgMessageTexts(ctx, authz.GetCtxData(ctx).OrgID, domain.VerifySMSOTPMessageType, language.Make(req.Language))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.ResetCustomVerifySMSOTPMessageTextToDefaultResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomVerifyEmailOTPMessageText(ctx context.Context, req *mgmt_pb.GetCustomVerifyEmailOTPMessageTextRequest) (*mgmt_pb.GetCustomVerifyEmailOTPMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetCtxData(ctx).OrgID, domain.VerifyEmailOTPMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetCustomVerifyEmailOTPMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultVerifyEmailOTPMessageText(ctx context.Context, req *mgmt_pb.GetDefaultVerifyEmailOTPMessageTextRequest) (*mgmt_pb.GetDefaultVerifyEmailOTPMessageTextResponse, error) {
|
||||
msg, err := s.query.IAMMessageTextByTypeAndLanguage(ctx, domain.VerifyEmailOTPMessageType, req.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetDefaultVerifyEmailOTPMessageTextResponse{
|
||||
CustomText: text_grpc.ModelCustomMessageTextToPb(msg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetCustomVerifyEmailOTPMessageText(ctx context.Context, req *mgmt_pb.SetCustomVerifyEmailOTPMessageTextRequest) (*mgmt_pb.SetCustomVerifyEmailOTPMessageTextResponse, error) {
|
||||
result, err := s.command.SetOrgMessageText(ctx, authz.GetCtxData(ctx).OrgID, SetVerifyEmailOTPCustomTextToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.SetCustomVerifyEmailOTPMessageTextResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetCustomVerifyEmailOTPMessageTextToDefault(ctx context.Context, req *mgmt_pb.ResetCustomVerifyEmailOTPMessageTextToDefaultRequest) (*mgmt_pb.ResetCustomVerifyEmailOTPMessageTextToDefaultResponse, error) {
|
||||
result, err := s.command.RemoveOrgMessageTexts(ctx, authz.GetCtxData(ctx).OrgID, domain.VerifyEmailOTPMessageType, language.Make(req.Language))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.ResetCustomVerifyEmailOTPMessageTextToDefaultResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
result.Sequence,
|
||||
result.EventDate,
|
||||
result.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomDomainClaimedMessageText(ctx context.Context, req *mgmt_pb.GetCustomDomainClaimedMessageTextRequest) (*mgmt_pb.GetCustomDomainClaimedMessageTextResponse, error) {
|
||||
msg, err := s.query.CustomMessageTextByTypeAndLanguage(ctx, authz.GetCtxData(ctx).OrgID, domain.DomainClaimedMessageType, req.Language, false)
|
||||
if err != nil {
|
||||
|
||||
@@ -68,6 +68,30 @@ func SetVerifyPhoneCustomTextToDomain(msg *mgmt_pb.SetCustomVerifyPhoneMessageTe
|
||||
}
|
||||
}
|
||||
|
||||
func SetVerifySMSOTPCustomTextToDomain(msg *mgmt_pb.SetCustomVerifySMSOTPMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
MessageTextType: domain.VerifySMSOTPMessageType,
|
||||
Language: langTag,
|
||||
Text: msg.Text,
|
||||
}
|
||||
}
|
||||
|
||||
func SetVerifyEmailOTPCustomTextToDomain(msg *mgmt_pb.SetCustomVerifyEmailOTPMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
MessageTextType: domain.VerifyEmailOTPMessageType,
|
||||
Language: langTag,
|
||||
Title: msg.Title,
|
||||
PreHeader: msg.PreHeader,
|
||||
Subject: msg.Subject,
|
||||
Greeting: msg.Greeting,
|
||||
Text: msg.Text,
|
||||
ButtonText: msg.ButtonText,
|
||||
FooterText: msg.FooterText,
|
||||
}
|
||||
}
|
||||
|
||||
func SetDomainClaimedCustomTextToDomain(msg *mgmt_pb.SetCustomDomainClaimedMessageTextRequest) *domain.CustomMessageText {
|
||||
langTag := language.Make(msg.Language)
|
||||
return &domain.CustomMessageText{
|
||||
|
||||
@@ -18,6 +18,7 @@ func (s *Server) ListFlowTypes(ctx context.Context, _ *mgmt_pb.ListFlowTypesRequ
|
||||
action_grpc.FlowTypeToPb(domain.FlowTypeExternalAuthentication),
|
||||
action_grpc.FlowTypeToPb(domain.FlowTypeCustomiseToken),
|
||||
action_grpc.FlowTypeToPb(domain.FlowTypeInternalAuthentication),
|
||||
action_grpc.FlowTypeToPb(domain.FlowTypeCustomizeSAMLResponse),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -549,11 +549,7 @@ func (s *Server) RemoveHumanPhone(ctx context.Context, req *mgmt_pb.RemoveHumanP
|
||||
}
|
||||
|
||||
func (s *Server) ResendHumanPhoneVerification(ctx context.Context, req *mgmt_pb.ResendHumanPhoneVerificationRequest) (*mgmt_pb.ResendHumanPhoneVerificationResponse, error) {
|
||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objectDetails, err := s.command.CreateHumanPhoneVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, phoneCodeGenerator)
|
||||
objectDetails, err := s.command.CreateHumanPhoneVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package org
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/user/v2"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
|
||||
)
|
||||
|
||||
func (s *Server) AddOrganization(ctx context.Context, request *org.AddOrganizationRequest) (*org.AddOrganizationResponse, error) {
|
||||
orgSetup, err := addOrganizationRequestToCommand(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
createdOrg, err := s.command.SetUpOrg(ctx, orgSetup, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return createdOrganizationToPb(createdOrg)
|
||||
}
|
||||
|
||||
func addOrganizationRequestToCommand(request *org.AddOrganizationRequest) (*command.OrgSetup, error) {
|
||||
admins, err := addOrganizationRequestAdminsToCommand(request.GetAdmins())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &command.OrgSetup{
|
||||
Name: request.GetName(),
|
||||
CustomDomain: "",
|
||||
Admins: admins,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func addOrganizationRequestAdminsToCommand(requestAdmins []*org.AddOrganizationRequest_Admin) (admins []*command.OrgSetupAdmin, err error) {
|
||||
admins = make([]*command.OrgSetupAdmin, len(requestAdmins))
|
||||
for i, admin := range requestAdmins {
|
||||
admins[i], err = addOrganizationRequestAdminToCommand(admin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return admins, nil
|
||||
}
|
||||
|
||||
func addOrganizationRequestAdminToCommand(admin *org.AddOrganizationRequest_Admin) (*command.OrgSetupAdmin, error) {
|
||||
switch a := admin.GetUserType().(type) {
|
||||
case *org.AddOrganizationRequest_Admin_UserId:
|
||||
return &command.OrgSetupAdmin{
|
||||
ID: a.UserId,
|
||||
Roles: admin.GetRoles(),
|
||||
}, nil
|
||||
case *org.AddOrganizationRequest_Admin_Human:
|
||||
human, err := user.AddUserRequestToAddHuman(a.Human)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &command.OrgSetupAdmin{
|
||||
Human: human,
|
||||
Roles: admin.GetRoles(),
|
||||
}, nil
|
||||
default:
|
||||
return nil, caos_errs.ThrowUnimplementedf(nil, "ORGv2-SD2r1", "userType oneOf %T in method AddOrganization not implemented", a)
|
||||
}
|
||||
}
|
||||
|
||||
func createdOrganizationToPb(createdOrg *command.CreatedOrg) (_ *org.AddOrganizationResponse, err error) {
|
||||
admins := make([]*org.AddOrganizationResponse_CreatedAdmin, len(createdOrg.CreatedAdmins))
|
||||
for i, admin := range createdOrg.CreatedAdmins {
|
||||
admins[i] = &org.AddOrganizationResponse_CreatedAdmin{
|
||||
UserId: admin.ID,
|
||||
EmailCode: admin.EmailCode,
|
||||
PhoneCode: admin.PhoneCode,
|
||||
}
|
||||
}
|
||||
return &org.AddOrganizationResponse{
|
||||
Details: object.DomainToDetailsPb(createdOrg.ObjectDetails),
|
||||
OrganizationId: createdOrg.ObjectDetails.ResourceOwner,
|
||||
CreatedAdmins: admins,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
//go:build integration
|
||||
|
||||
package org_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha"
|
||||
)
|
||||
|
||||
var (
|
||||
CTX context.Context
|
||||
Tester *integration.Tester
|
||||
Client org.OrganizationServiceClient
|
||||
User *user.AddHumanUserResponse
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
ctx, errCtx, cancel := integration.Contexts(5 * time.Minute)
|
||||
defer cancel()
|
||||
|
||||
Tester = integration.NewTester(ctx)
|
||||
defer Tester.Done()
|
||||
Client = Tester.Client.OrgV2
|
||||
|
||||
CTX, _ = Tester.WithAuthorization(ctx, integration.IAMOwner), errCtx
|
||||
User = Tester.CreateHumanUser(CTX)
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
||||
|
||||
func TestServer_AddOrganization(t *testing.T) {
|
||||
idpID := Tester.AddGenericOAuthProvider(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
req *org.AddOrganizationRequest
|
||||
want *org.AddOrganizationResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner),
|
||||
req: &org.AddOrganizationRequest{
|
||||
Name: "name",
|
||||
Admins: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty name",
|
||||
ctx: CTX,
|
||||
req: &org.AddOrganizationRequest{
|
||||
Name: "",
|
||||
Admins: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid admin type",
|
||||
ctx: CTX,
|
||||
req: &org.AddOrganizationRequest{
|
||||
Name: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
Admins: []*org.AddOrganizationRequest_Admin{
|
||||
{},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "admin with init",
|
||||
ctx: CTX,
|
||||
req: &org.AddOrganizationRequest{
|
||||
Name: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
Admins: []*org.AddOrganizationRequest_Admin{
|
||||
{
|
||||
UserType: &org.AddOrganizationRequest_Admin_Human{
|
||||
Human: &user.AddHumanUserRequest{
|
||||
Profile: &user.SetHumanProfile{
|
||||
FirstName: "firstname",
|
||||
LastName: "lastname",
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: fmt.Sprintf("%[email protected]", time.Now().UnixNano()),
|
||||
Verification: &user.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user.ReturnEmailVerificationCode{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &org.AddOrganizationResponse{
|
||||
OrganizationId: integration.NotEmpty,
|
||||
CreatedAdmins: []*org.AddOrganizationResponse_CreatedAdmin{
|
||||
{
|
||||
UserId: integration.NotEmpty,
|
||||
EmailCode: gu.Ptr(integration.NotEmpty),
|
||||
PhoneCode: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing user and new human with idp",
|
||||
ctx: CTX,
|
||||
req: &org.AddOrganizationRequest{
|
||||
Name: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
Admins: []*org.AddOrganizationRequest_Admin{
|
||||
{
|
||||
UserType: &org.AddOrganizationRequest_Admin_UserId{UserId: User.GetUserId()},
|
||||
},
|
||||
{
|
||||
UserType: &org.AddOrganizationRequest_Admin_Human{
|
||||
Human: &user.AddHumanUserRequest{
|
||||
Profile: &user.SetHumanProfile{
|
||||
FirstName: "firstname",
|
||||
LastName: "lastname",
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: fmt.Sprintf("%[email protected]", time.Now().UnixNano()),
|
||||
Verification: &user.SetHumanEmail_IsVerified{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
IdpLinks: []*user.IDPLink{
|
||||
{
|
||||
IdpId: idpID,
|
||||
UserId: "userID",
|
||||
UserName: "username",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &org.AddOrganizationResponse{
|
||||
CreatedAdmins: []*org.AddOrganizationResponse_CreatedAdmin{
|
||||
// a single admin is expected, because the first provided already exists
|
||||
{
|
||||
UserId: integration.NotEmpty,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.AddOrganization(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// check details
|
||||
assert.NotZero(t, got.GetDetails().GetSequence())
|
||||
gotCD := got.GetDetails().GetChangeDate().AsTime()
|
||||
now := time.Now()
|
||||
assert.WithinRange(t, gotCD, now.Add(-time.Minute), now.Add(time.Minute))
|
||||
assert.NotEmpty(t, got.GetDetails().GetResourceOwner())
|
||||
|
||||
// organization id must be the same as the resourceOwner
|
||||
assert.Equal(t, got.GetDetails().GetResourceOwner(), got.GetOrganizationId())
|
||||
|
||||
// check the admins
|
||||
require.Len(t, got.GetCreatedAdmins(), len(tt.want.GetCreatedAdmins()))
|
||||
for i, admin := range tt.want.GetCreatedAdmins() {
|
||||
gotAdmin := got.GetCreatedAdmins()[i]
|
||||
assertCreatedAdmin(t, admin, gotAdmin)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertCreatedAdmin(t *testing.T, expected, got *org.AddOrganizationResponse_CreatedAdmin) {
|
||||
if expected.GetUserId() != "" {
|
||||
assert.NotEmpty(t, got.GetUserId())
|
||||
} else {
|
||||
assert.Empty(t, got.GetUserId())
|
||||
}
|
||||
if expected.GetEmailCode() != "" {
|
||||
assert.NotEmpty(t, got.GetEmailCode())
|
||||
} else {
|
||||
assert.Empty(t, got.GetEmailCode())
|
||||
}
|
||||
if expected.GetPhoneCode() != "" {
|
||||
assert.NotEmpty(t, got.GetPhoneCode())
|
||||
} else {
|
||||
assert.Empty(t, got.GetPhoneCode())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package org
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha"
|
||||
org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha"
|
||||
)
|
||||
|
||||
func Test_addOrganizationRequestToCommand(t *testing.T) {
|
||||
type args struct {
|
||||
request *org.AddOrganizationRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *command.OrgSetup
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "nil user",
|
||||
args: args{
|
||||
request: &org.AddOrganizationRequest{
|
||||
Name: "name",
|
||||
Admins: []*org.AddOrganizationRequest_Admin{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: caos_errs.ThrowUnimplementedf(nil, "ORGv2-SD2r1", "userType oneOf %T in method AddOrganization not implemented", nil),
|
||||
},
|
||||
{
|
||||
name: "user ID",
|
||||
args: args{
|
||||
request: &org.AddOrganizationRequest{
|
||||
Name: "name",
|
||||
Admins: []*org.AddOrganizationRequest_Admin{
|
||||
{
|
||||
UserType: &org.AddOrganizationRequest_Admin_UserId{
|
||||
UserId: "userID",
|
||||
},
|
||||
Roles: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &command.OrgSetup{
|
||||
Name: "name",
|
||||
CustomDomain: "",
|
||||
Admins: []*command.OrgSetupAdmin{
|
||||
{
|
||||
ID: "userID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human user",
|
||||
args: args{
|
||||
request: &org.AddOrganizationRequest{
|
||||
Name: "name",
|
||||
Admins: []*org.AddOrganizationRequest_Admin{
|
||||
{
|
||||
UserType: &org.AddOrganizationRequest_Admin_Human{
|
||||
Human: &user.AddHumanUserRequest{
|
||||
Profile: &user.SetHumanProfile{
|
||||
FirstName: "firstname",
|
||||
LastName: "lastname",
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: "[email protected]",
|
||||
},
|
||||
},
|
||||
},
|
||||
Roles: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &command.OrgSetup{
|
||||
Name: "name",
|
||||
CustomDomain: "",
|
||||
Admins: []*command.OrgSetupAdmin{
|
||||
{
|
||||
Human: &command.AddHuman{
|
||||
Username: "[email protected]",
|
||||
FirstName: "firstname",
|
||||
LastName: "lastname",
|
||||
Email: command.Email{
|
||||
Address: "[email protected]",
|
||||
},
|
||||
Metadata: make([]*command.AddMetadataEntry, 0),
|
||||
Links: make([]*command.AddLink, 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := addOrganizationRequestToCommand(tt.args.request)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_createdOrganizationToPb(t *testing.T) {
|
||||
now := time.Now()
|
||||
type args struct {
|
||||
createdOrg *command.CreatedOrg
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *org.AddOrganizationResponse
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "human user with phone and email code",
|
||||
args: args{
|
||||
createdOrg: &command.CreatedOrg{
|
||||
ObjectDetails: &domain.ObjectDetails{
|
||||
Sequence: 1,
|
||||
EventDate: now,
|
||||
ResourceOwner: "orgID",
|
||||
},
|
||||
CreatedAdmins: []*command.CreatedOrgAdmin{
|
||||
{
|
||||
ID: "id",
|
||||
EmailCode: gu.Ptr("emailCode"),
|
||||
PhoneCode: gu.Ptr("phoneCode"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &org.AddOrganizationResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.New(now),
|
||||
ResourceOwner: "orgID",
|
||||
},
|
||||
OrganizationId: "orgID",
|
||||
CreatedAdmins: []*org.AddOrganizationResponse_CreatedAdmin{
|
||||
{
|
||||
UserId: "id",
|
||||
EmailCode: gu.Ptr("emailCode"),
|
||||
PhoneCode: gu.Ptr("phoneCode"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := createdOrganizationToPb(tt.args.createdOrg)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
|
||||
)
|
||||
|
||||
var _ org.OrganizationServiceServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
org.UnimplementedOrganizationServiceServer
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
checkPermission domain.PermissionCheck,
|
||||
) *Server {
|
||||
return &Server{
|
||||
command: command,
|
||||
query: query,
|
||||
checkPermission: checkPermission,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
|
||||
org.RegisterOrganizationServiceServer(grpcServer, s)
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return org.OrganizationService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return org.OrganizationService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return org.OrganizationService_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
|
||||
return org.RegisterOrganizationServiceHandler
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func (s *Server) CreateSession(ctx context.Context, req *session.CreateSessionRe
|
||||
}
|
||||
challengeResponse, cmds := s.challengesToCommand(req.GetChallenges(), checks)
|
||||
|
||||
set, err := s.command.CreateSession(ctx, cmds, req.GetDomain(), metadata)
|
||||
set, err := s.command.CreateSession(ctx, cmds, metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,7 +107,6 @@ func sessionToPb(s *query.Session) *session.Session {
|
||||
Sequence: s.Sequence,
|
||||
Factors: factorsToPb(s),
|
||||
Metadata: s.Metadata,
|
||||
Domain: s.Domain,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +118,9 @@ func factorsToPb(s *query.Session) *session.Factors {
|
||||
return &session.Factors{
|
||||
User: user,
|
||||
Password: passwordFactorToPb(s.PasswordFactor),
|
||||
Passkey: passkeyFactorToPb(s.PasskeyFactor),
|
||||
WebAuthN: webAuthNFactorToPb(s.WebAuthNFactor),
|
||||
Intent: intentFactorToPb(s.IntentFactor),
|
||||
Totp: totpFactorToPb(s.TOTPFactor),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,12 +142,22 @@ func intentFactorToPb(factor query.SessionIntentFactor) *session.IntentFactor {
|
||||
}
|
||||
}
|
||||
|
||||
func passkeyFactorToPb(factor query.SessionPasskeyFactor) *session.PasskeyFactor {
|
||||
if factor.PasskeyCheckedAt.IsZero() {
|
||||
func webAuthNFactorToPb(factor query.SessionWebAuthNFactor) *session.WebAuthNFactor {
|
||||
if factor.WebAuthNCheckedAt.IsZero() {
|
||||
return nil
|
||||
}
|
||||
return &session.PasskeyFactor{
|
||||
VerifiedAt: timestamppb.New(factor.PasskeyCheckedAt),
|
||||
return &session.WebAuthNFactor{
|
||||
VerifiedAt: timestamppb.New(factor.WebAuthNCheckedAt),
|
||||
UserVerified: factor.UserVerified,
|
||||
}
|
||||
}
|
||||
|
||||
func totpFactorToPb(factor query.SessionTOTPFactor) *session.TOTPFactor {
|
||||
if factor.TOTPCheckedAt.IsZero() {
|
||||
return nil
|
||||
}
|
||||
return &session.TOTPFactor{
|
||||
VerifiedAt: timestamppb.New(factor.TOTPCheckedAt),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,36 +254,49 @@ func (s *Server) checksToCommand(ctx context.Context, checks *session.Checks) ([
|
||||
if intent := checks.GetIntent(); intent != nil {
|
||||
sessionChecks = append(sessionChecks, command.CheckIntent(intent.GetIntentId(), intent.GetToken()))
|
||||
}
|
||||
if passkey := checks.GetPasskey(); passkey != nil {
|
||||
sessionChecks = append(sessionChecks, s.command.CheckPasskey(passkey.GetCredentialAssertionData()))
|
||||
if passkey := checks.GetWebAuthN(); passkey != nil {
|
||||
sessionChecks = append(sessionChecks, s.command.CheckWebAuthN(passkey.GetCredentialAssertionData()))
|
||||
}
|
||||
if totp := checks.GetTotp(); totp != nil {
|
||||
sessionChecks = append(sessionChecks, command.CheckTOTP(totp.GetTotp()))
|
||||
}
|
||||
|
||||
return sessionChecks, nil
|
||||
}
|
||||
|
||||
func (s *Server) challengesToCommand(challenges []session.ChallengeKind, cmds []command.SessionCommand) (*session.Challenges, []command.SessionCommand) {
|
||||
if len(challenges) == 0 {
|
||||
func (s *Server) challengesToCommand(challenges *session.RequestChallenges, cmds []command.SessionCommand) (*session.Challenges, []command.SessionCommand) {
|
||||
if challenges == nil {
|
||||
return nil, cmds
|
||||
}
|
||||
resp := new(session.Challenges)
|
||||
for _, c := range challenges {
|
||||
switch c {
|
||||
case session.ChallengeKind_CHALLENGE_KIND_UNSPECIFIED:
|
||||
continue
|
||||
case session.ChallengeKind_CHALLENGE_KIND_PASSKEY:
|
||||
passkeyChallenge, cmd := s.createPasskeyChallengeCommand()
|
||||
resp.Passkey = passkeyChallenge
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
if req := challenges.GetWebAuthN(); req != nil {
|
||||
challenge, cmd := s.createWebAuthNChallengeCommand(req)
|
||||
resp.WebAuthN = challenge
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
return resp, cmds
|
||||
}
|
||||
|
||||
func (s *Server) createPasskeyChallengeCommand() (*session.Challenges_Passkey, command.SessionCommand) {
|
||||
challenge := &session.Challenges_Passkey{
|
||||
func (s *Server) createWebAuthNChallengeCommand(req *session.RequestChallenges_WebAuthN) (*session.Challenges_WebAuthN, command.SessionCommand) {
|
||||
challenge := &session.Challenges_WebAuthN{
|
||||
PublicKeyCredentialRequestOptions: new(structpb.Struct),
|
||||
}
|
||||
return challenge, s.command.CreatePasskeyChallenge(domain.UserVerificationRequirementRequired, challenge.PublicKeyCredentialRequestOptions)
|
||||
userVerification := userVerificationRequirementToDomain(req.GetUserVerificationRequirement())
|
||||
return challenge, s.command.CreateWebAuthNChallenge(userVerification, req.GetDomain(), challenge.PublicKeyCredentialRequestOptions)
|
||||
}
|
||||
|
||||
func userVerificationRequirementToDomain(req session.UserVerificationRequirement) domain.UserVerificationRequirement {
|
||||
switch req {
|
||||
case session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_UNSPECIFIED:
|
||||
return domain.UserVerificationRequirementUnspecified
|
||||
case session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED:
|
||||
return domain.UserVerificationRequirementRequired
|
||||
case session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_PREFERRED:
|
||||
return domain.UserVerificationRequirementPreferred
|
||||
case session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_DISCOURAGED:
|
||||
return domain.UserVerificationRequirementDiscouraged
|
||||
default:
|
||||
return domain.UserVerificationRequirementUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
func userCheck(user *session.CheckUser) (userSearch, error) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -45,6 +46,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, window time.Duration, metadata map[string][]byte, factors ...wantFactor) *session.Session {
|
||||
t.Helper()
|
||||
require.NotEmpty(t, id)
|
||||
require.NotEmpty(t, token)
|
||||
|
||||
@@ -69,7 +71,9 @@ type wantFactor int
|
||||
const (
|
||||
wantUserFactor wantFactor = iota
|
||||
wantPasswordFactor
|
||||
wantPasskeyFactor
|
||||
wantWebAuthNFactor
|
||||
wantWebAuthNFactorUserVerified
|
||||
wantTOTPFactor
|
||||
wantIntentFactor
|
||||
)
|
||||
|
||||
@@ -85,8 +89,18 @@ func verifyFactors(t testing.TB, factors *session.Factors, window time.Duration,
|
||||
pf := factors.GetPassword()
|
||||
assert.NotNil(t, pf)
|
||||
assert.WithinRange(t, pf.GetVerifiedAt().AsTime(), time.Now().Add(-window), time.Now().Add(window))
|
||||
case wantPasskeyFactor:
|
||||
pf := factors.GetPasskey()
|
||||
case wantWebAuthNFactor:
|
||||
pf := factors.GetWebAuthN()
|
||||
assert.NotNil(t, pf)
|
||||
assert.WithinRange(t, pf.GetVerifiedAt().AsTime(), time.Now().Add(-window), time.Now().Add(window))
|
||||
assert.False(t, pf.GetUserVerified())
|
||||
case wantWebAuthNFactorUserVerified:
|
||||
pf := factors.GetWebAuthN()
|
||||
assert.NotNil(t, pf)
|
||||
assert.WithinRange(t, pf.GetVerifiedAt().AsTime(), time.Now().Add(-window), time.Now().Add(window))
|
||||
assert.True(t, pf.GetUserVerified())
|
||||
case wantTOTPFactor:
|
||||
pf := factors.GetTotp()
|
||||
assert.NotNil(t, pf)
|
||||
assert.WithinRange(t, pf.GetVerifiedAt().AsTime(), time.Now().Add(-window), time.Now().Add(window))
|
||||
case wantIntentFactor:
|
||||
@@ -127,7 +141,6 @@ func TestServer_CreateSession(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Metadata: map[string][]byte{"foo": []byte("bar")},
|
||||
Domain: "domain",
|
||||
},
|
||||
want: &session.CreateSessionResponse{
|
||||
Details: &object.Details{
|
||||
@@ -150,8 +163,11 @@ func TestServer_CreateSession(t *testing.T) {
|
||||
{
|
||||
name: "passkey without user error",
|
||||
req: &session.CreateSessionRequest{
|
||||
Challenges: []session.ChallengeKind{
|
||||
session.ChallengeKind_CHALLENGE_KIND_PASSKEY,
|
||||
Challenges: &session.RequestChallenges{
|
||||
WebAuthN: &session.RequestChallenges_WebAuthN{
|
||||
Domain: Tester.Config.ExternalDomain,
|
||||
UserVerificationRequirement: session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@@ -166,8 +182,10 @@ func TestServer_CreateSession(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Challenges: []session.ChallengeKind{
|
||||
session.ChallengeKind_CHALLENGE_KIND_PASSKEY,
|
||||
Challenges: &session.RequestChallenges{
|
||||
WebAuthN: &session.RequestChallenges_WebAuthN{
|
||||
UserVerificationRequirement: session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@@ -188,8 +206,8 @@ func TestServer_CreateSession(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_CreateSession_passkey(t *testing.T) {
|
||||
// create new session with user and request the passkey challenge
|
||||
func TestServer_CreateSession_webauthn(t *testing.T) {
|
||||
// create new session with user and request the webauthn challenge
|
||||
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{
|
||||
Checks: &session.Checks{
|
||||
User: &session.CheckUser{
|
||||
@@ -198,29 +216,31 @@ func TestServer_CreateSession_passkey(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Challenges: []session.ChallengeKind{
|
||||
session.ChallengeKind_CHALLENGE_KIND_PASSKEY,
|
||||
Challenges: &session.RequestChallenges{
|
||||
WebAuthN: &session.RequestChallenges_WebAuthN{
|
||||
Domain: Tester.Config.ExternalDomain,
|
||||
UserVerificationRequirement: session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED,
|
||||
},
|
||||
},
|
||||
Domain: Tester.Config.ExternalDomain,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil)
|
||||
|
||||
assertionData, err := Tester.WebAuthN.CreateAssertionResponse(createResp.GetChallenges().GetPasskey().GetPublicKeyCredentialRequestOptions())
|
||||
assertionData, err := Tester.WebAuthN.CreateAssertionResponse(createResp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), true)
|
||||
require.NoError(t, err)
|
||||
|
||||
// update the session with passkey assertion data
|
||||
// update the session with webauthn assertion data
|
||||
updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: createResp.GetSessionToken(),
|
||||
Checks: &session.Checks{
|
||||
Passkey: &session.CheckPasskey{
|
||||
WebAuthN: &session.CheckWebAuthN{
|
||||
CredentialAssertionData: assertionData,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantPasskeyFactor)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactorUserVerified)
|
||||
}
|
||||
|
||||
func TestServer_CreateSession_successfulIntent(t *testing.T) {
|
||||
@@ -238,7 +258,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil)
|
||||
|
||||
intentID, token, _, _ := Tester.CreateSuccessfulIntent(t, idpID, User.GetUserId(), "id")
|
||||
intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, User.GetUserId(), "id")
|
||||
updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: createResp.GetSessionToken(),
|
||||
@@ -269,7 +289,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) {
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil)
|
||||
|
||||
idpUserID := "id"
|
||||
intentID, token, _, _ := Tester.CreateSuccessfulIntent(t, idpID, "", idpUserID)
|
||||
intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, "", idpUserID)
|
||||
updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: createResp.GetSessionToken(),
|
||||
@@ -325,17 +345,31 @@ func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func registerTOTP(ctx context.Context, t *testing.T, userID string) (secret string) {
|
||||
resp, err := Tester.Client.UserV2.RegisterTOTP(ctx, &user.RegisterTOTPRequest{
|
||||
UserId: userID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
secret = resp.GetSecret()
|
||||
code, err := totp.GenerateCode(secret, time.Now())
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = Tester.Client.UserV2.VerifyTOTPRegistration(ctx, &user.VerifyTOTPRegistrationRequest{
|
||||
UserId: userID,
|
||||
Code: code,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return secret
|
||||
}
|
||||
|
||||
func TestServer_SetSession_flow(t *testing.T) {
|
||||
var wantFactors []wantFactor
|
||||
|
||||
// create new, empty session
|
||||
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{Domain: Tester.Config.ExternalDomain})
|
||||
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, wantFactors...)
|
||||
sessionToken := createResp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, createResp.GetDetails().GetSequence(), time.Minute, nil)
|
||||
|
||||
t.Run("check user", func(t *testing.T) {
|
||||
wantFactors = append(wantFactors, wantUserFactor)
|
||||
resp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: sessionToken,
|
||||
@@ -348,43 +382,107 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, wantFactors...)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor)
|
||||
})
|
||||
|
||||
t.Run("check passkey", func(t *testing.T) {
|
||||
t.Run("check webauthn, user verified (passkey)", func(t *testing.T) {
|
||||
resp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: sessionToken,
|
||||
Challenges: []session.ChallengeKind{
|
||||
session.ChallengeKind_CHALLENGE_KIND_PASSKEY,
|
||||
Challenges: &session.RequestChallenges{
|
||||
WebAuthN: &session.RequestChallenges_WebAuthN{
|
||||
Domain: Tester.Config.ExternalDomain,
|
||||
UserVerificationRequirement: session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, wantFactors...)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
|
||||
wantFactors = append(wantFactors, wantPasskeyFactor)
|
||||
assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetPasskey().GetPublicKeyCredentialRequestOptions())
|
||||
assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), true)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err = Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: sessionToken,
|
||||
Checks: &session.Checks{
|
||||
Passkey: &session.CheckPasskey{
|
||||
WebAuthN: &session.CheckWebAuthN{
|
||||
CredentialAssertionData: assertionData,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, wantFactors...)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactorUserVerified)
|
||||
})
|
||||
|
||||
userAuthCtx := Tester.WithAuthorizationToken(CTX, sessionToken)
|
||||
Tester.RegisterUserU2F(userAuthCtx, User.GetUserId())
|
||||
totpSecret := registerTOTP(userAuthCtx, t, User.GetUserId())
|
||||
|
||||
t.Run("check webauthn, user not verified (U2F)", func(t *testing.T) {
|
||||
|
||||
for _, userVerificationRequirement := range []session.UserVerificationRequirement{
|
||||
session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_PREFERRED,
|
||||
session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
|
||||
} {
|
||||
t.Run(userVerificationRequirement.String(), func(t *testing.T) {
|
||||
resp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: sessionToken,
|
||||
Challenges: &session.RequestChallenges{
|
||||
WebAuthN: &session.RequestChallenges_WebAuthN{
|
||||
Domain: Tester.Config.ExternalDomain,
|
||||
UserVerificationRequirement: userVerificationRequirement,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
|
||||
assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), false)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err = Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: sessionToken,
|
||||
Checks: &session.Checks{
|
||||
WebAuthN: &session.CheckWebAuthN{
|
||||
CredentialAssertionData: assertionData,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("check TOTP", func(t *testing.T) {
|
||||
code, err := totp.GenerateCode(totpSecret, time.Now())
|
||||
require.NoError(t, err)
|
||||
resp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: sessionToken,
|
||||
Checks: &session.Checks{
|
||||
Totp: &session.CheckTOTP{
|
||||
Totp: code,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, wantUserFactor, wantWebAuthNFactor, wantTOTPFactor)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ZITADEL_API_missing_authentication(t *testing.T) {
|
||||
// create new, empty session
|
||||
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{Domain: Tester.Config.ExternalDomain})
|
||||
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := metadata.AppendToOutgoingContext(context.Background(), "Authorization", fmt.Sprintf("Bearer %s", createResp.GetSessionToken()))
|
||||
@@ -403,16 +501,19 @@ func Test_ZITADEL_API_missing_mfa(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ZITADEL_API_success(t *testing.T) {
|
||||
id, token, _, _ := Tester.CreatePasskeySession(t, CTX, User.GetUserId())
|
||||
id, token, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, User.GetUserId())
|
||||
|
||||
ctx := Tester.WithAuthorizationToken(context.Background(), token)
|
||||
sessionResp, err := Tester.Client.SessionV2.GetSession(ctx, &session.GetSessionRequest{SessionId: id})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, id, sessionResp.GetSession().GetFactors().GetPasskey().GetVerifiedAt().AsTime())
|
||||
|
||||
webAuthN := sessionResp.GetSession().GetFactors().GetWebAuthN()
|
||||
require.NotNil(t, id, webAuthN.GetVerifiedAt().AsTime())
|
||||
require.True(t, webAuthN.GetUserVerified())
|
||||
}
|
||||
|
||||
func Test_ZITADEL_API_session_not_found(t *testing.T) {
|
||||
id, token, _, _ := Tester.CreatePasskeySession(t, CTX, User.GetUserId())
|
||||
id, token, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, User.GetUserId())
|
||||
|
||||
// test session token works
|
||||
ctx := Tester.WithAuthorizationToken(context.Background(), token)
|
||||
|
||||
@@ -70,7 +70,7 @@ func Test_sessionsToPb(t *testing.T) {
|
||||
},
|
||||
Metadata: map[string][]byte{"hello": []byte("world")},
|
||||
},
|
||||
{ // passkey factor
|
||||
{ // webAuthN factor
|
||||
ID: "999",
|
||||
CreationDate: now,
|
||||
ChangeDate: now,
|
||||
@@ -85,8 +85,29 @@ func Test_sessionsToPb(t *testing.T) {
|
||||
DisplayName: "donald duck",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
PasskeyFactor: query.SessionPasskeyFactor{
|
||||
PasskeyCheckedAt: past,
|
||||
WebAuthNFactor: query.SessionWebAuthNFactor{
|
||||
WebAuthNCheckedAt: past,
|
||||
UserVerified: true,
|
||||
},
|
||||
Metadata: map[string][]byte{"hello": []byte("world")},
|
||||
},
|
||||
{ // totp factor
|
||||
ID: "999",
|
||||
CreationDate: now,
|
||||
ChangeDate: now,
|
||||
Sequence: 123,
|
||||
State: domain.SessionStateActive,
|
||||
ResourceOwner: "me",
|
||||
Creator: "he",
|
||||
UserFactor: query.SessionUserFactor{
|
||||
UserID: "345",
|
||||
UserCheckedAt: past,
|
||||
LoginName: "donald",
|
||||
DisplayName: "donald duck",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
TOTPFactor: query.SessionTOTPFactor{
|
||||
TOTPCheckedAt: past,
|
||||
},
|
||||
Metadata: map[string][]byte{"hello": []byte("world")},
|
||||
},
|
||||
@@ -136,7 +157,7 @@ func Test_sessionsToPb(t *testing.T) {
|
||||
},
|
||||
Metadata: map[string][]byte{"hello": []byte("world")},
|
||||
},
|
||||
{ // passkey factor
|
||||
{ // webAuthN factor
|
||||
Id: "999",
|
||||
CreationDate: timestamppb.New(now),
|
||||
ChangeDate: timestamppb.New(now),
|
||||
@@ -149,7 +170,27 @@ func Test_sessionsToPb(t *testing.T) {
|
||||
DisplayName: "donald duck",
|
||||
OrganisationId: "org1",
|
||||
},
|
||||
Passkey: &session.PasskeyFactor{
|
||||
WebAuthN: &session.WebAuthNFactor{
|
||||
VerifiedAt: timestamppb.New(past),
|
||||
UserVerified: true,
|
||||
},
|
||||
},
|
||||
Metadata: map[string][]byte{"hello": []byte("world")},
|
||||
},
|
||||
{ // totp factor
|
||||
Id: "999",
|
||||
CreationDate: timestamppb.New(now),
|
||||
ChangeDate: timestamppb.New(now),
|
||||
Sequence: 123,
|
||||
Factors: &session.Factors{
|
||||
User: &session.UserFactor{
|
||||
VerifiedAt: timestamppb.New(past),
|
||||
Id: "345",
|
||||
LoginName: "donald",
|
||||
DisplayName: "donald duck",
|
||||
OrganisationId: "org1",
|
||||
},
|
||||
Totp: &session.TOTPFactor{
|
||||
VerifiedAt: timestamppb.New(past),
|
||||
},
|
||||
},
|
||||
@@ -432,3 +473,40 @@ func Test_userCheck(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_userVerificationRequirementToDomain(t *testing.T) {
|
||||
type args struct {
|
||||
req session.UserVerificationRequirement
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
want domain.UserVerificationRequirement
|
||||
}{
|
||||
{
|
||||
args: args{session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_UNSPECIFIED},
|
||||
want: domain.UserVerificationRequirementUnspecified,
|
||||
},
|
||||
{
|
||||
args: args{session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED},
|
||||
want: domain.UserVerificationRequirementRequired,
|
||||
},
|
||||
{
|
||||
args: args{session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_PREFERRED},
|
||||
want: domain.UserVerificationRequirementPreferred,
|
||||
},
|
||||
{
|
||||
args: args{session.UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_DISCOURAGED},
|
||||
want: domain.UserVerificationRequirementDiscouraged,
|
||||
},
|
||||
{
|
||||
args: args{999},
|
||||
want: domain.UserVerificationRequirementUnspecified,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.args.req.String(), func(t *testing.T) {
|
||||
got := userVerificationRequirementToDomain(tt.args.req)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ func UserQueryToQuery(query *user_pb.SearchQuery) (query.SearchQuery, error) {
|
||||
return LoginNameQueryToQuery(q.LoginNameQuery)
|
||||
case *user_pb.SearchQuery_ResourceOwner:
|
||||
return ResourceOwnerQueryToQuery(q.ResourceOwner)
|
||||
case *user_pb.SearchQuery_InUserIdsQuery:
|
||||
return InUserIdsQueryToQuery(q.InUserIdsQuery)
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgument(nil, "GRPC-vR9nC", "List.Query.Invalid")
|
||||
}
|
||||
@@ -84,3 +86,7 @@ func LoginNameQueryToQuery(q *user_pb.LoginNameQuery) (query.SearchQuery, error)
|
||||
func ResourceOwnerQueryToQuery(q *user_pb.ResourceOwnerQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserResourceOwnerSearchQuery(q.OrgID, query.TextEquals)
|
||||
}
|
||||
|
||||
func InUserIdsQueryToQuery(q *user_pb.InUserIDQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserInUserIdsSearchQuery(q.UserIds)
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ import (
|
||||
func TestServer_AddOTPSMS(t *testing.T) {
|
||||
userID := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreatePasskeySession(t, CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
// TODO: add when phone can be added to user
|
||||
/*
|
||||
userIDPhone := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userIDPhone)
|
||||
_, sessionTokenPhone, _, _ := Tester.CreatePasskeySession(t, CTX, userIDPhone)
|
||||
_, sessionTokenPhone, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userIDPhone)
|
||||
*/
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -99,7 +99,7 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
|
||||
/*
|
||||
userID := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreatePasskeySession(t, CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userID)
|
||||
*/
|
||||
|
||||
type args struct {
|
||||
@@ -157,7 +157,7 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
|
||||
func TestServer_AddOTPEmail(t *testing.T) {
|
||||
userID := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreatePasskeySession(t, CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
userVerified := Tester.CreateHumanUser(CTX)
|
||||
_, err := Tester.Client.UserV2.VerifyEmail(CTX, &user.VerifyEmailRequest{
|
||||
@@ -166,7 +166,7 @@ func TestServer_AddOTPEmail(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
Tester.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Tester.CreatePasskeySession(t, CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -238,11 +238,11 @@ func TestServer_AddOTPEmail(t *testing.T) {
|
||||
func TestServer_RemoveOTPEmail(t *testing.T) {
|
||||
userID := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreatePasskeySession(t, CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
userVerified := Tester.CreateHumanUser(CTX)
|
||||
Tester.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Tester.CreatePasskeySession(t, CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
userVerifiedCtx := Tester.WithAuthorizationToken(context.Background(), sessionTokenVerified)
|
||||
_, err := Tester.Client.UserV2.VerifyEmail(userVerifiedCtx, &user.VerifyEmailRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user