Merge branch 'main' into next

# Conflicts:
#	internal/auth/repository/eventsourcing/eventstore/auth_request.go
This commit is contained in:
Livio Spring
2023-08-18 10:36:05 +02:00
247 changed files with 15640 additions and 8115 deletions
+24
View File
@@ -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]"
}
+22
View File
@@ -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:
+13 -7
View File
@@ -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"
+3 -3
View File
@@ -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
+31 -23
View File
@@ -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
View File
@@ -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
-5
View File
@@ -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 -6
View File
@@ -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
+9 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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",
}
],
},
],
],
};
+6 -2
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -21,7 +21,7 @@ import (
type FirstInstance struct {
InstanceName string
DefaultLanguage language.Tag
Org command.OrgSetup
Org command.InstanceOrgSetup
MachineKeyPath string
PatPath string
+26
View File
@@ -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"
}
+2
View File
@@ -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;
+1
View File
@@ -67,6 +67,7 @@ type Steps struct {
s9EventstoreIndexes2 *EventstoreIndexesNew
CorrectCreationDate *CorrectCreationDate
AddEventCreatedAt *AddEventCreatedAt
s12AddOTPColumns *AddOTPColumns
}
type encryptionKeyConfig struct {
+3
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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))
View File
+29 -29
View File
@@ -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();
}
@@ -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]],
});
}
@@ -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);
});
}
});
}
@@ -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>
@@ -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;
}
}
@@ -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);
});
}
}
@@ -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'">
@@ -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);
}
@@ -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>
@@ -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,
@@ -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>
+140
View File
@@ -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);
+92 -24
View File
@@ -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);
+13 -2
View File
@@ -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": "Персонализираните текстове са запазени."
+14 -3
View File
@@ -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."
+14 -3
View File
@@ -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."
+15 -4
View File
@@ -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."
+15 -4
View File
@@ -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."
+15 -4
View File
@@ -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."
+14 -3
View File
@@ -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": "カスタムテキストが保存されました。"
+14 -3
View File
@@ -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": "Прилагодените текстови се зачувани."
+15 -4
View File
@@ -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."
+14 -3
View File
@@ -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."
+14 -3
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -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
+1 -1
View File
@@ -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 |
+2
View File
@@ -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.
![user info inside id token](/img/guides/integrate/services/user-info-inside-id-token.png)
:::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.
+2 -2
View File
@@ -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])
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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.
+2 -3
View File
@@ -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
View File
@@ -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"
}
}
+2
View File
@@ -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
View File
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:
+14
View File
@@ -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"
+41 -37
View File
@@ -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 },
);
});
});
});
+3 -3
View File
@@ -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
+10 -5
View File
@@ -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=
+4
View File
@@ -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
}
+96
View File
@@ -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{
+54
View File
@@ -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 {
+14 -6
View File
@@ -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
}
+1 -5
View File
@@ -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{
+1
View File
@@ -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
}
+1 -5
View File
@@ -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
}
+83
View File
@@ -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())
}
}
+172
View File
@@ -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)
})
}
}
+55
View File
@@ -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 -24
View File
@@ -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)
+83 -5
View File
@@ -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)
})
}
}
+6
View File
@@ -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