mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 19:47:23 +00:00
Merge branch 'main' into next
# Conflicts: # .releaserc.js
This commit is contained in:
commit
e2644cf076
3
.artifacts/zitadel/.gitignore
vendored
3
.artifacts/zitadel/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
!.gitkeep
|
@ -1,6 +1,6 @@
|
||||
/.git/
|
||||
# .git
|
||||
.codecov
|
||||
/.github/
|
||||
.github
|
||||
.gitignore
|
||||
.dockerignore
|
||||
**/Dockerfile
|
||||
@ -15,8 +15,14 @@ CONTRIBUTING.md
|
||||
LICENSE
|
||||
README.md
|
||||
SECURITY.md
|
||||
**/Dockerfile
|
||||
**/*.Dockerfile
|
||||
pkg/grpc/*/*.pb.*
|
||||
pkg/grpc/*/*.swagger.json
|
||||
.goreleaser.yaml
|
||||
.artifacts/
|
||||
**/.artifacts
|
||||
console/.angular
|
||||
console/node_modules
|
||||
console/src/app/proto/generated/
|
||||
console/tmp/
|
||||
.vscode
|
||||
build/*.Dockerfile
|
99
.github/workflows/build.yml
vendored
Normal file
99
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
name: ZITADEL CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- "*"
|
||||
branches:
|
||||
- "**"
|
||||
pull_request:
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
|
||||
core:
|
||||
uses: ./.github/workflows/core.yml
|
||||
with:
|
||||
node_version: '18'
|
||||
buf_version: 'latest'
|
||||
go_version: '1.20'
|
||||
|
||||
console:
|
||||
uses: ./.github/workflows/console.yml
|
||||
with:
|
||||
node_version: '18'
|
||||
buf_version: 'latest'
|
||||
|
||||
version:
|
||||
uses: ./.github/workflows/version.yml
|
||||
with:
|
||||
semantic_version: '19.0.2'
|
||||
dry_run: true
|
||||
|
||||
compile:
|
||||
needs: [core, console, version]
|
||||
uses: ./.github/workflows/compile.yml
|
||||
with:
|
||||
go_version: '1.20'
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
console_cache_key: ${{ needs.console.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
console_cache_path: ${{ needs.console.outputs.cache_path }}
|
||||
version: ${{ needs.version.outputs.version }}
|
||||
|
||||
core-unit-test:
|
||||
needs: core
|
||||
uses: ./.github/workflows/core-unit-test.yml
|
||||
with:
|
||||
go_version: '1.20'
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
|
||||
core-integration-test:
|
||||
needs: core
|
||||
uses: ./.github/workflows/core-integration-test.yml
|
||||
with:
|
||||
go_version: '1.20'
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
|
||||
lint:
|
||||
needs: [core, console]
|
||||
uses: ./.github/workflows/lint.yml
|
||||
with:
|
||||
go_version: '1.20'
|
||||
node_version: '18'
|
||||
buf_version: 'latest'
|
||||
go_lint_version: 'v1.53.2'
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
|
||||
container:
|
||||
needs: [compile]
|
||||
uses: ./.github/workflows/container.yml
|
||||
with:
|
||||
image_name: 'ghcr.io/zitadel/zitadel'
|
||||
|
||||
e2e:
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
needs: [container]
|
||||
with:
|
||||
image: ${{ needs.container.outputs.image }}-debug
|
||||
|
||||
release:
|
||||
uses: ./.github/workflows/release.yml
|
||||
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' }}
|
||||
secrets:
|
||||
GCR_JSON_KEY_BASE64: ${{ secrets.GCR_JSON_KEY_BASE64 }}
|
||||
with:
|
||||
image: ${{ needs.container.outputs.image }}
|
||||
semantic_version: '19.0.2'
|
||||
image_name: 'ghcr.io/zitadel/zitadel'
|
||||
google_image_name: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel"
|
@ -1,4 +1,4 @@
|
||||
name: "Code scanning - action"
|
||||
name: "Code Scanning"
|
||||
|
||||
on:
|
||||
push:
|
99
.github/workflows/compile.yml
vendored
Normal file
99
.github/workflows/compile.yml
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
name: Compile
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
go_version:
|
||||
required: true
|
||||
type: string
|
||||
core_cache_key:
|
||||
required: true
|
||||
type: string
|
||||
core_cache_path:
|
||||
required: true
|
||||
type: string
|
||||
console_cache_key:
|
||||
required: true
|
||||
type: string
|
||||
console_cache_path:
|
||||
required: true
|
||||
type: string
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
executable:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goos: [linux, darwin, windows]
|
||||
goarch: [amd64, arm64]
|
||||
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
timeout-minutes: 1
|
||||
name: restore console
|
||||
with:
|
||||
path: ${{ inputs.console_cache_path }}
|
||||
key: ${{ inputs.console_cache_key }}
|
||||
fail-on-cache-miss: true
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
timeout-minutes: 1
|
||||
name: restore core
|
||||
with:
|
||||
path: ${{ inputs.core_cache_path }}
|
||||
key: ${{ inputs.core_cache_key }}
|
||||
fail-on-cache-miss: true
|
||||
-
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ inputs.go_version }}
|
||||
-
|
||||
name: compile
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
GOOS="${{matrix.goos}}" \
|
||||
GOARCH="${{matrix.goarch}}" \
|
||||
VERSION="${{ inputs.version }}" \
|
||||
COMMIT_SHA="${{ github.sha }}" \
|
||||
make compile_pipeline
|
||||
-
|
||||
name: create folder
|
||||
run: |
|
||||
mkdir zitadel-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
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 }}
|
||||
-
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: zitadel-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: zitadel-${{ matrix.goos }}-${{ matrix.goarch }}.tar
|
||||
|
||||
checksums:
|
||||
runs-on: ubuntu-latest
|
||||
needs: executable
|
||||
steps:
|
||||
-
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: executables
|
||||
-
|
||||
name: move files one folder up
|
||||
run: mv */*.tar . && find . -type d -empty -delete
|
||||
working-directory: executables
|
||||
-
|
||||
run: sha256sum * > checksums.txt
|
||||
working-directory: executables
|
||||
-
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: checksums.txt
|
||||
path: executables/checksums.txt
|
61
.github/workflows/console.yml
vendored
Normal file
61
.github/workflows/console.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
name: Build console
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
node_version:
|
||||
required: true
|
||||
type: string
|
||||
buf_version:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
cache_key:
|
||||
value: ${{ jobs.build.outputs.cache_key }}
|
||||
cache_path:
|
||||
value: ${{ jobs.build.outputs.cache_path }}
|
||||
|
||||
env:
|
||||
cache_path: console/dist/console
|
||||
|
||||
jobs:
|
||||
build:
|
||||
outputs:
|
||||
cache_key: ${{ steps.cache.outputs.cache-primary-key }}
|
||||
cache_path: ${{ env.cache_path }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
timeout-minutes: 1
|
||||
id: cache
|
||||
with:
|
||||
key: console-${{ hashFiles('console', 'proto') }}
|
||||
restore-keys: |
|
||||
console-
|
||||
path: ${{ env.cache_path }}
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
uses: bufbuild/buf-setup-action@v1
|
||||
with:
|
||||
github_token: ${{ github.token }}
|
||||
version: ${{ inputs.buf_version }}
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ inputs.node_version }}
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: console/yarn.lock
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
run: make console_build
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: ${{ env.cache_path }}
|
||||
key: ${{ steps.cache.outputs.cache-primary-key }}
|
||||
|
168
.github/workflows/container.yml
vendored
Normal file
168
.github/workflows/container.yml
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
name: Container
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
image:
|
||||
value: '${{ inputs.image_name }}:${{ github.sha }}'
|
||||
|
||||
env:
|
||||
default_labels: |
|
||||
org.opencontainers.image.documentation=https://zitadel.com/docs
|
||||
org.opencontainers.image.vendor=CAOS AG
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: zitadel
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [amd64,arm64]
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Scratch meta
|
||||
id: scratch-meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ inputs.image_name }}
|
||||
labels: ${{ env.default_labels}}
|
||||
tags: |
|
||||
type=sha,prefix=,suffix=,format=long
|
||||
-
|
||||
name: Debug meta
|
||||
id: debug-meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ inputs.image_name }}
|
||||
labels: ${{ env.default_labels}}
|
||||
tags: |
|
||||
type=sha,prefix=,suffix=-debug,format=long
|
||||
-
|
||||
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: Login to Docker registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: .artifacts
|
||||
name: zitadel-linux-${{ matrix.arch }}
|
||||
-
|
||||
name: Unpack executable
|
||||
run: |
|
||||
tar -xvf .artifacts/zitadel-linux-${{ matrix.arch }}.tar
|
||||
mv zitadel-linux-${{ matrix.arch }}/zitadel ./zitadel
|
||||
-
|
||||
name: Debug
|
||||
id: build-debug
|
||||
uses: docker/build-push-action@v4
|
||||
timeout-minutes: 3
|
||||
with:
|
||||
context: .
|
||||
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
|
||||
-
|
||||
name: Scratch
|
||||
id: build-scratch
|
||||
uses: docker/build-push-action@v4
|
||||
timeout-minutes: 3
|
||||
with:
|
||||
context: .
|
||||
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
|
||||
-
|
||||
name: Export debug digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests/debug
|
||||
digest="${{ steps.build-debug.outputs.digest }}"
|
||||
touch "/tmp/digests/debug/${digest#sha256:}"
|
||||
-
|
||||
name: Export scratch digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests/scratch
|
||||
digest="${{ steps.build-scratch.outputs.digest }}"
|
||||
touch "/tmp/digests/scratch/${digest#sha256:}"
|
||||
-
|
||||
name: Upload digest
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: digests
|
||||
path: /tmp/digests
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [scratch, debug]
|
||||
include:
|
||||
- image: scratch
|
||||
suffix: ''
|
||||
- image: debug
|
||||
suffix: '-debug'
|
||||
steps:
|
||||
-
|
||||
name: Download digests
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: digests
|
||||
path: /tmp/digests
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
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
|
||||
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 ' *)
|
||||
-
|
||||
name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ inputs.image_name }}:${{ github.sha }}${{ matrix.suffix }}
|
153
.github/workflows/core-integration-test.yml
vendored
Normal file
153
.github/workflows/core-integration-test.yml
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
name: Integration test core
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
go_version:
|
||||
required: true
|
||||
type: string
|
||||
core_cache_key:
|
||||
required: true
|
||||
type: string
|
||||
core_cache_path:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
postgres:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
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
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ inputs.go_version }}
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
timeout-minutes: 1
|
||||
name: restore core
|
||||
id: restore-core
|
||||
with:
|
||||
path: ${{ inputs.core_cache_path }}
|
||||
key: ${{ inputs.core_cache_key }}
|
||||
fail-on-cache-miss: true
|
||||
-
|
||||
id: go-cache-path
|
||||
name: set cache path
|
||||
run: echo "GO_CACHE_PATH=$(go env GOCACHE)" >> $GITHUB_OUTPUT
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache
|
||||
timeout-minutes: 1
|
||||
name: restore previous results
|
||||
with:
|
||||
key: integration-test-postgres-${{ inputs.core_cache_key }}
|
||||
restore-keys: |
|
||||
integration-test-postgres-core-
|
||||
path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}
|
||||
-
|
||||
name: test
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
env:
|
||||
ZITADEL_MASTERKEY: MasterkeyNeedsToHave32Characters
|
||||
INTEGRATION_DB_FLAVOR: postgres
|
||||
run: make core_integration_test
|
||||
-
|
||||
name: publish coverage
|
||||
uses: codecov/codecov-action@v3.1.4
|
||||
with:
|
||||
file: profile.cov
|
||||
name: core-integration-tests-postgres
|
||||
flags: core-integration-tests-postgres
|
||||
-
|
||||
uses: actions/cache/save@v3
|
||||
name: cache results
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
with:
|
||||
key: integration-test-postgres-${{ inputs.core_cache_key }}
|
||||
path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}
|
||||
|
||||
# TODO: enable as soon as cockroach allows `COCKROACH_ARGS`
|
||||
# cockroach:
|
||||
# runs-on: ubuntu-latest
|
||||
# services:
|
||||
# cockroach:
|
||||
# image: cockroachdb/cockroach:latest
|
||||
# ports:
|
||||
# - 26257:26257
|
||||
# - 9090:9090
|
||||
# env:
|
||||
# COCKROACH_ARGS: start-single-node --insecure --http-addr :9090
|
||||
# options: >-
|
||||
# --health-cmd "curl http://localhost:9090/health?ready=1 || exit 1"
|
||||
# --health-interval 10s
|
||||
# --health-timeout 5s
|
||||
# --health-retries 5
|
||||
# --health-start-period 10s
|
||||
# steps:
|
||||
# -
|
||||
# uses: actions/checkout@v3
|
||||
# -
|
||||
# uses: actions/setup-go@v4
|
||||
# with:
|
||||
# go-version: ${{ inputs.go_version }}
|
||||
# -
|
||||
# uses: actions/cache/restore@v3
|
||||
# timeout-minutes: 1
|
||||
# name: restore core
|
||||
# with:
|
||||
# path: ${{ inputs.core_cache_path }}
|
||||
# key: ${{ inputs.core_cache_key }}
|
||||
# fail-on-cache-miss: true
|
||||
# -
|
||||
# id: go-cache-path
|
||||
# name: set cache path
|
||||
# run: echo "GO_CACHE_PATH=$(go env GOCACHE)" >> $GITHUB_OUTPUT
|
||||
# -
|
||||
# uses: actions/cache/restore@v3
|
||||
# timeout-minutes: 1
|
||||
# name: restore previous results
|
||||
# cache: id
|
||||
# with:
|
||||
# key: integration-test-crdb-${{ inputs.core_cache_key }}
|
||||
# restore-keys: |
|
||||
# integration-test-crdb-core-
|
||||
# path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}
|
||||
# -
|
||||
# name: test
|
||||
# if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
# env:
|
||||
# ZITADEL_MASTERKEY: MasterkeyNeedsToHave32Characters
|
||||
# INTEGRATION_DB_FLAVOR: cockroach
|
||||
# run: make core_integration_test
|
||||
# -
|
||||
# name: publish coverage
|
||||
# uses: codecov/codecov-action@v3.1.4
|
||||
# with:
|
||||
# file: profile.cov
|
||||
# name: core-integration-tests-cockroach
|
||||
# flags: core-integration-tests-cockroach
|
||||
# -
|
||||
# uses: actions/cache/save@v3
|
||||
# name: cache results
|
||||
# if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
# with:
|
||||
# key: integration-test-crdb-${{ inputs.core_cache_key }}
|
||||
# path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}
|
67
.github/workflows/core-unit-test.yml
vendored
Normal file
67
.github/workflows/core-unit-test.yml
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
name: Unit test core
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
go_version:
|
||||
required: true
|
||||
type: string
|
||||
core_cache_key:
|
||||
required: true
|
||||
type: string
|
||||
core_cache_path:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ inputs.go_version }}
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
timeout-minutes: 1
|
||||
name: restore core
|
||||
id: restore-core
|
||||
with:
|
||||
path: ${{ inputs.core_cache_path }}
|
||||
key: ${{ inputs.core_cache_key }}
|
||||
fail-on-cache-miss: true
|
||||
-
|
||||
id: go-cache-path
|
||||
name: set cache path
|
||||
run: echo "GO_CACHE_PATH=$(go env GOCACHE)" >> $GITHUB_OUTPUT
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache
|
||||
timeout-minutes: 1
|
||||
name: restore previous results
|
||||
with:
|
||||
key: unit-test-${{ inputs.core_cache_key }}
|
||||
restore-keys: |
|
||||
unit-test-core-
|
||||
path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}
|
||||
-
|
||||
name: test
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
run: make core_unit_test
|
||||
-
|
||||
name: publish coverage
|
||||
uses: codecov/codecov-action@v3.1.4
|
||||
with:
|
||||
file: profile.cov
|
||||
name: core-unit-tests
|
||||
flags: core-unit-tests
|
||||
-
|
||||
uses: actions/cache/save@v3
|
||||
name: cache results
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
with:
|
||||
key: unit-test-${{ inputs.core_cache_key }}
|
||||
path: ${{ steps.go-cache-path.outputs.GO_CACHE_PATH }}
|
||||
|
81
.github/workflows/core.yml
vendored
Normal file
81
.github/workflows/core.yml
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
name: Build core
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
go_version:
|
||||
required: true
|
||||
type: string
|
||||
buf_version:
|
||||
required: true
|
||||
type: string
|
||||
node_version:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
cache_key:
|
||||
value: ${{ jobs.build.outputs.cache_key }}
|
||||
cache_path:
|
||||
value: ${{ jobs.build.outputs.cache_path }}
|
||||
|
||||
env:
|
||||
cache_path: |
|
||||
internal/statik/statik.go
|
||||
internal/notification/statik/statik.go
|
||||
internal/api/ui/login/static/resources/themes/zitadel/css/zitadel.css*
|
||||
internal/api/ui/login/statik/statik.go
|
||||
internal/api/assets/authz.go
|
||||
internal/api/assets/router.go
|
||||
openapi/v2
|
||||
pkg/grpc/**/*.pb.*
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache_key: ${{ steps.cache.outputs.cache-primary-key }}
|
||||
cache_path: ${{ env.cache_path }}
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
timeout-minutes: 1
|
||||
id: cache
|
||||
with:
|
||||
key: core-${{ hashFiles( 'go.*', 'openapi', 'cmd', 'pkg/grpc/**/*.go', 'proto', 'internal') }}
|
||||
restore-keys: |
|
||||
core-
|
||||
path: ${{ env.cache_path }}
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
uses: bufbuild/buf-setup-action@v1
|
||||
with:
|
||||
github_token: ${{ github.token }}
|
||||
version: ${{ inputs.buf_version }}
|
||||
|
||||
-
|
||||
# node to install sass
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ inputs.node_version }}
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
run: npm install -g sass
|
||||
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ inputs.go_version }}
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
run: make core_build
|
||||
-
|
||||
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
key: ${{ steps.cache.outputs.cache-primary-key }}
|
||||
path: ${{ env.cache_path }}
|
||||
|
128
.github/workflows/e2e.yml
vendored
128
.github/workflows/e2e.yml
vendored
@ -1,66 +1,104 @@
|
||||
name: "ZITADEL e2e Tests"
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ZITADEL Release]
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
releaseversion:
|
||||
description: 'Release version to test'
|
||||
image:
|
||||
required: true
|
||||
default: 'latest'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
browser: [firefox, chrome]
|
||||
runs-on: ubuntu-20.04
|
||||
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_REGISTRY: 'ghcr.io/zitadel/zitadel'
|
||||
ZITADEL_IMAGE: ${{ inputs.image }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
-
|
||||
name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Set TAG env manual trigger
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: echo "ZITADEL_IMAGE=${ZITADEL_IMAGE_REGISTRY}:${{ github.event.inputs.releaseversion }}" >> $GITHUB_ENV
|
||||
- name: get latest tag
|
||||
uses: actions-ecosystem/action-get-latest-tag@v1
|
||||
id: get-latest-tag
|
||||
with:
|
||||
semver_only: true
|
||||
- name: Set TAG env on ZITADEL release
|
||||
if: github.event_name == 'workflow_run'
|
||||
run: echo "ZITADEL_IMAGE=${ZITADEL_IMAGE_REGISTRY}:${{ steps.get-latest-tag.outputs.tag }}" >> $GITHUB_ENV
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
-
|
||||
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_BACKEND_URL: http://localhost:8080
|
||||
with:
|
||||
driver: docker
|
||||
install: true
|
||||
- name: Test ${{ matrix.browser }}
|
||||
run: docker compose run --service-ports e2e --browser ${{ matrix.browser }}
|
||||
working-directory: e2e/config/host.docker.internal
|
||||
- name: Ensure Artifacts Directory Exists
|
||||
run: mkdir -p ./.artifacts
|
||||
- name: Save ZITADEL Logs
|
||||
if: always()
|
||||
run: docker compose logs zitadel > ../../../.artifacts/e2e-compose-zitadel.log
|
||||
working-directory: e2e/config/host.docker.internal
|
||||
- name: Save Prepare Logs
|
||||
if: always()
|
||||
run: docker compose logs prepare > ../../../.artifacts/e2e-compose-prepare.log
|
||||
working-directory: e2e/config/host.docker.internal
|
||||
- name: Archive production tests ${{ matrix.browser }}
|
||||
if: always()
|
||||
working-directory: e2e
|
||||
browser: ${{ matrix.browser }}
|
||||
command: npm run e2e
|
||||
config-file: cypress.config.ts
|
||||
-
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: production-tests-${{ matrix.browser }}
|
||||
path: |
|
||||
e2e/cypress/results
|
||||
e2e/cypress/videos
|
||||
e2e/cypress/screenshots
|
||||
.artifacts/e2e-compose-zitadel.log
|
||||
.artifacts/e2e-compose-prepare.log
|
||||
e2e/cypress/videos
|
||||
e2e/cypress/results
|
||||
retention-days: 30
|
||||
|
74
.github/workflows/homebrew.yml
vendored
Normal file
74
.github/workflows/homebrew.yml
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
name: ZITADEL Update Homebrew Formula
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
homebrew-releaser:
|
||||
runs-on: ubuntu-latest
|
||||
name: homebrew-releaser
|
||||
steps:
|
||||
- name: Release my project to my Homebrew tap
|
||||
uses: Justintime50/homebrew-releaser@v1
|
||||
with:
|
||||
# The name of the homebrew tap to publish your formula to as it appears on GitHub.
|
||||
# Required - strings
|
||||
homebrew_owner: zitadel
|
||||
homebrew_tap: homebrew-tap
|
||||
|
||||
# The name of the folder in your homebrew tap where formula will be committed to.
|
||||
# Default is shown - string
|
||||
formula_folder: Formula
|
||||
|
||||
# The Personal Access Token (saved as a repo secret) that has `repo` permissions for the repo running the action AND Homebrew tap you want to release to.
|
||||
# Required - string
|
||||
github_token: ${{ secrets.CR_PAT }}
|
||||
|
||||
# Git author info used to commit to the homebrew tap.
|
||||
# Defaults are shown - strings
|
||||
commit_owner: homebrew-releaser
|
||||
commit_email: homebrew-releaser@zitadel.com
|
||||
|
||||
# Custom dependencies in case other formulas are needed to build the current one.
|
||||
# Optional - multiline string
|
||||
depends_on: |
|
||||
"go" => :optional
|
||||
"git"
|
||||
|
||||
# Custom install command for your formula.
|
||||
# Required - string
|
||||
install: 'bin.install "zitadel"'
|
||||
|
||||
# Custom test command for your formula so you can run `brew test`.
|
||||
# Optional - string
|
||||
test: 'ystem "#{bin}/zitadel -v"'
|
||||
|
||||
# Allows you to set a custom download strategy. Note that you'll need
|
||||
# to implement the strategy and add it to your tap repository.
|
||||
# Example: https://docs.brew.sh/Formula-Cookbook#specifying-the-download-strategy-explicitly
|
||||
# Optional - string
|
||||
# download_strategy: CurlDownloadStrategy
|
||||
|
||||
# Allows you to add a custom require_relative at the top of the formula template.
|
||||
# Optional - string
|
||||
# custom_require: custom_download_strategy
|
||||
|
||||
# Adds URL and checksum targets for different OS and architecture pairs. Using this option assumes
|
||||
# a tar archive exists on your GitHub repo with the following URL pattern (this cannot be customized):
|
||||
# https://github.com/{GITHUB_OWNER}/{REPO_NAME}/releases/download/{TAG}/{REPO_NAME}-{VERSION}-{OPERATING_SYSTEM}-{ARCHITECTURE}.tar.gz'
|
||||
# Darwin AMD pre-existing path example: https://github.com/justintime50/myrepo/releases/download/v1.2.0/myrepo-1.2.0-darwin-amd64.tar.gz
|
||||
# Linux ARM pre-existing path example: https://github.com/justintime50/myrepo/releases/download/v1.2.0/myrepo-1.2.0-linux-arm64.tar.gz
|
||||
# Optional - booleans
|
||||
target_darwin_amd64: true
|
||||
target_darwin_arm64: true
|
||||
target_linux_amd64: true
|
||||
target_linux_arm64: true
|
||||
|
||||
# Skips committing the generated formula to a homebrew tap (useful for local testing).
|
||||
# Default is shown - boolean
|
||||
skip_commit: true
|
||||
|
||||
# Logs debugging info to console.
|
||||
# Default is shown - boolean
|
||||
debug: true
|
51
.github/workflows/integration.yml
vendored
51
.github/workflows/integration.yml
vendored
@ -1,51 +0,0 @@
|
||||
name: Integration tests
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- '**'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
run:
|
||||
strategy:
|
||||
matrix:
|
||||
db: [cockroach, postgres]
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
INTEGRATION_DB_FLAVOR: ${{ matrix.db }}
|
||||
ZITADEL_MASTERKEY: MasterkeyNeedsToHave32Characters
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
- name: Source checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver: docker
|
||||
install: true
|
||||
- name: Generate gRPC definitions
|
||||
run: docker build -f build/grpc/Dockerfile -t zitadel-base:local .
|
||||
- name: Copy gRPC definitions
|
||||
run: docker build -f build/zitadel/Dockerfile . -t zitadel-go-base --target go-copy -o .
|
||||
- name: Download Go modules
|
||||
run: go mod download
|
||||
- name: Start ${{ matrix.db }} database
|
||||
run: docker compose -f internal/integration/config/docker-compose.yaml up --wait ${INTEGRATION_DB_FLAVOR}
|
||||
- name: Run zitadel init and setup
|
||||
run: |
|
||||
go run main.go init --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml
|
||||
go run main.go setup --masterkeyFromEnv --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml
|
||||
- name: Run integration tests
|
||||
run: go test -tags=integration -race -p 1 -v -coverprofile=profile.cov -coverpkg=./internal/...,./cmd/... ./internal/integration ./internal/api/grpc/...
|
||||
- name: Publish go coverage
|
||||
uses: codecov/codecov-action@v3.1.0
|
||||
with:
|
||||
file: profile.cov
|
||||
name: integration-tests
|
104
.github/workflows/lint.yml
vendored
Normal file
104
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
node_version:
|
||||
required: true
|
||||
type: string
|
||||
go_version:
|
||||
required: true
|
||||
type: string
|
||||
buf_version:
|
||||
required: true
|
||||
type: string
|
||||
go_lint_version:
|
||||
required: true
|
||||
type: string
|
||||
core_cache_key:
|
||||
required: true
|
||||
type: string
|
||||
core_cache_path:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
lint-skip:
|
||||
name: lint skip
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
steps:
|
||||
- name: Lint skip
|
||||
run: |
|
||||
echo "Linting outside of pull requests is skipped"
|
||||
|
||||
api:
|
||||
name: api
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: bufbuild/buf-setup-action@v1
|
||||
with:
|
||||
version: ${{ inputs.buf_version }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: lint
|
||||
uses: bufbuild/buf-lint-action@v1
|
||||
-
|
||||
uses: bufbuild/buf-breaking-action@v1
|
||||
with:
|
||||
against: "https://github.com/${{ github.repository }}.git#branch=main"
|
||||
|
||||
console:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
name: console
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ inputs.node_version }}
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: console/yarn.lock
|
||||
-
|
||||
run: cd console && yarn install
|
||||
-
|
||||
name: lint
|
||||
run: make console_lint
|
||||
|
||||
core:
|
||||
name: core
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ github.event.inputs.go_version }}
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
timeout-minutes: 1
|
||||
name: restore core
|
||||
with:
|
||||
path: ${{ inputs.core_cache_path }}
|
||||
key: ${{ inputs.core_cache_key }}
|
||||
fail-on-cache-miss: true
|
||||
-
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: ${{ inputs.go_lint_version }}
|
||||
github-token: ${{ github.token }}
|
||||
only-new-issues: true
|
||||
skip-pkg-cache: true
|
||||
skip-build-cache: true
|
75
.github/workflows/release.yml
vendored
Normal file
75
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
semantic_version:
|
||||
required: true
|
||||
type: string
|
||||
image:
|
||||
required: true
|
||||
type: string
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
google_image_name:
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
GCR_JSON_KEY_BASE64:
|
||||
description: 'base64 endcrypted key to connect to Google'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
version:
|
||||
uses: ./.github/workflows/version.yml
|
||||
with:
|
||||
semantic_version: ${{ inputs.semantic_version }}
|
||||
dry_run: true
|
||||
|
||||
docker:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [ version ]
|
||||
steps:
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
-
|
||||
name: Login to Docker registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Login to Google Artifact Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: europe-docker.pkg.dev
|
||||
username: _json_key_base64
|
||||
password: ${{ secrets.GCR_JSON_KEY_BASE64 }}
|
||||
-
|
||||
name: Publish ${{ needs.version.outputs.version }}
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.image_name }}:${{ needs.version.outputs.version }} \
|
||||
${{ inputs.image }}
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.image_name }}:${{ needs.version.outputs.version }}-debug \
|
||||
${{ inputs.image }}-debug
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.google_image_name }}:${{ needs.version.outputs.version }} \
|
||||
${{ inputs.image }}
|
||||
-
|
||||
name: Publish latest
|
||||
if: ${{ github.ref_name == 'main' }}
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.image_name }}:latest \
|
||||
${{ inputs.image }}
|
||||
docker buildx imagetools create \
|
||||
--tag ${{ inputs.image_name }}:latest-debug \
|
||||
${{ inputs.image }}-debug
|
75
.github/workflows/test-code.yml
vendored
75
.github/workflows/test-code.yml
vendored
@ -1,75 +0,0 @@
|
||||
name: ZITADEL PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'guides/**'
|
||||
- '**.md'
|
||||
- 'release-channels.yaml'
|
||||
|
||||
jobs:
|
||||
Build-ZITADEL:
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
- name: Source checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver: docker
|
||||
install: true
|
||||
- name: Install GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v3
|
||||
with:
|
||||
install-only: true
|
||||
version: v1.10.3
|
||||
- name: Build and Unit Test
|
||||
run: GOOS="linux" GOARCH="amd64" goreleaser build --id prod --snapshot --single-target --rm-dist --output .artifacts/zitadel/zitadel
|
||||
- name: linting
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.52
|
||||
only-new-issues: true
|
||||
skip-pkg-cache: true
|
||||
- name: Publish go coverage
|
||||
uses: codecov/codecov-action@v3.1.0
|
||||
with:
|
||||
file: .artifacts/codecov/profile.cov
|
||||
name: unit-tests
|
||||
# As goreleaser doesn't build a dockerfile in snapshot mode, we have to build it here
|
||||
- name: Build Docker Image
|
||||
run: docker build -t zitadel:pr --file build/Dockerfile .artifacts/zitadel
|
||||
- name: Run E2E Tests
|
||||
run: docker compose run --service-ports e2e --browser chrome
|
||||
working-directory: e2e/config/host.docker.internal
|
||||
env:
|
||||
ZITADEL_IMAGE: zitadel:pr
|
||||
- name: Save ZITADEL Logs
|
||||
if: always()
|
||||
run: docker compose logs zitadel > ../../../.artifacts/e2e-compose-zitadel.log
|
||||
working-directory: e2e/config/host.docker.internal
|
||||
- name: Save Prepare Logs
|
||||
if: always()
|
||||
run: docker compose logs prepare > ../../../.artifacts/e2e-compose-prepare.log
|
||||
working-directory: e2e/config/host.docker.internal
|
||||
- name: Archive Test Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: pull-request-tests
|
||||
path: |
|
||||
e2e/cypress/results
|
||||
e2e/cypress/videos
|
||||
e2e/cypress/screenshots
|
||||
.artifacts/e2e-compose-zitadel.log
|
||||
.artifacts/e2e-compose-prepare.log
|
||||
retention-days: 30
|
21
.github/workflows/test-docs.yml
vendored
21
.github/workflows/test-docs.yml
vendored
@ -1,21 +0,0 @@
|
||||
# ATTENTION: Although this workflow doesn't do much, it is still important.
|
||||
# It is complementary to the workflow in the file test-code.yml.
|
||||
# It enables to exclude files for the workflow and still mark the Test job as required without having pending PRs.
|
||||
# GitHub recommends this solution here:
|
||||
# https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
|
||||
|
||||
name: ZITADEL PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'guides/**'
|
||||
- '**.md'
|
||||
- 'release-channels.yaml'
|
||||
|
||||
jobs:
|
||||
Build-ZITADEL:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- run: 'echo "No tests for docs are implemented, yet"'
|
45
.github/workflows/version.yml
vendored
Normal file
45
.github/workflows/version.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: Version
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
semantic_version:
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
required: true
|
||||
type: boolean
|
||||
outputs:
|
||||
version:
|
||||
value: ${{ jobs.generate.outputs.version }}
|
||||
published:
|
||||
value: ${{jobs.generate.outputs.published }}
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
outputs:
|
||||
version: ${{ steps.output.outputs.VERSION }}
|
||||
published: ${{ steps.semantic.outputs.new_release_published }}
|
||||
steps:
|
||||
-
|
||||
name: Source checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Semantic Release
|
||||
uses: cycjimmy/semantic-release-action@v3
|
||||
id: semantic
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
dry_run: ${{ inputs.dry_run }}
|
||||
semantic_version: ${{ inputs.semantic_version }}
|
||||
extra_plugins: |
|
||||
@semantic-release/exec@6.0.3
|
||||
-
|
||||
name: output
|
||||
id: output
|
||||
run:
|
||||
if [[ ! -z "${{ steps.semantic.outputs.new_release_version }}" ]]; then echo "VERSION=v${{ steps.semantic.outputs.new_release_version }}" >> "$GITHUB_OUTPUT"; else echo "VERSION=" >> "$GITHUB_OUTPUT";fi
|
88
.github/workflows/zitadel.yml
vendored
88
.github/workflows/zitadel.yml
vendored
@ -1,88 +0,0 @@
|
||||
name: ZITADEL Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- "*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
- name: Source checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Fetch all tags
|
||||
run: git fetch --force --tags
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver: docker
|
||||
install: true
|
||||
- name: Tag
|
||||
id: semantic
|
||||
uses: cycjimmy/semantic-release-action@v2
|
||||
with:
|
||||
dry_run: false
|
||||
semantic_version: 19.0.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: GitHub Container Registry Login
|
||||
if: steps.semantic.outputs.new_release_published == 'true' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
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
|
||||
if: steps.semantic.outputs.new_release_published == 'true' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: europe-docker.pkg.dev
|
||||
username: _json_key_base64
|
||||
password: ${{ secrets.GCR_JSON_KEY_BASE64 }}
|
||||
- uses: goreleaser/goreleaser-action@v3
|
||||
name: Publish ZITADEL
|
||||
if: steps.semantic.outputs.new_release_published == 'true' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: v1.11.0
|
||||
args: release --timeout 50m
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_TOKEN_TAP: ${{ steps.generate-token.outputs.token }}
|
||||
RELEASE_VERSION: ${{ steps.semantic.outputs.release-version }} # I think this line is not needed. Nevertheless, it's explicit
|
||||
DISCORD_WEBHOOK_ID: "976058224484687932"
|
||||
DISCORD_WEBHOOK_TOKEN: "${{ secrets.DISCORD_WEBHOOK_TOKEN }}"
|
||||
- name: Publish go coverage
|
||||
uses: codecov/codecov-action@v3.1.0
|
||||
with:
|
||||
file: .artifacts/codecov/profile.cov
|
||||
name: go-codecov
|
||||
- name: Bump Chart Version
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
if: steps.semantic.outputs.new_release_published == 'true' && github.ref == 'refs/heads/next'
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
repository: zitadel/zitadel-charts
|
||||
event-type: zitadel-released
|
||||
client-payload: '{"semanticoutputs": "${{ steps.semantic.outputs }}"}'
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -55,10 +55,17 @@ openapi/**/*.json
|
||||
/internal/api/assets/authz.go
|
||||
/internal/api/assets/router.go
|
||||
/internal/api/ui/console/static/*
|
||||
!/internal/api/ui/console/static/gitkeep
|
||||
docs/docs/apis/auth
|
||||
docs/docs/apis/admin
|
||||
docs/docs/apis/mgmt
|
||||
docs/docs/apis/system
|
||||
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
|
||||
build/local/*.env
|
||||
|
@ -6,7 +6,7 @@ issues:
|
||||
max-same-issues: 0
|
||||
|
||||
run:
|
||||
concurrency: 2
|
||||
concurrency: 4
|
||||
timeout: 10m
|
||||
go: '1.19'
|
||||
skip-dirs:
|
||||
|
126
.goreleaser.yaml
126
.goreleaser.yaml
@ -1,126 +0,0 @@
|
||||
project_name: zitadel
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: zitadel
|
||||
name: zitadel
|
||||
draft: false
|
||||
# If set to auto, will mark the release as not ready for production
|
||||
# in case there is an indicator for this in the tag e.g. v1.0.0-rc1
|
||||
# If set to true, will mark the release as not ready for production.
|
||||
# Default is false.
|
||||
prerelease: auto
|
||||
|
||||
before:
|
||||
hooks:
|
||||
# this file would invalidate go source caches
|
||||
- sh -c "rm openapi/statik/statik.go || true"
|
||||
- docker build -f build/grpc/Dockerfile -t zitadel-base:local .
|
||||
- docker build -f build/zitadel/Dockerfile . -t zitadel-go-test --target go-codecov -o .artifacts/codecov
|
||||
- docker build -f build/zitadel/Dockerfile . -t zitadel-go-base --target go-copy -o .artifacts/grpc/go-client
|
||||
- sh -c "find pkg/grpc -name '*.pb*.go' -delete"
|
||||
- sh -c "cp -r .artifacts/grpc/go-client/* ."
|
||||
- docker build -f build/console/Dockerfile . -t zitadel-npm-console --target angular-export -o .artifacts/console
|
||||
- sh -c "cp -r .artifacts/console/* internal/api/ui/console/static/"
|
||||
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags: -s -w -X github.com/zitadel/zitadel/cmd/build.version={{.Version}} -X github.com/zitadel/zitadel/cmd/build.commit={{.Commit}} -X github.com/zitadel/zitadel/cmd/build.date={{.Date}}
|
||||
|
||||
dist: .artifacts/goreleaser
|
||||
|
||||
dockers:
|
||||
- image_templates:
|
||||
- ghcr.io/zitadel/zitadel:{{ .Tag }}-amd64
|
||||
- europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel:{{ .Tag }}-amd64
|
||||
goarch: amd64
|
||||
use: buildx
|
||||
dockerfile: build/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- image_templates:
|
||||
- ghcr.io/zitadel/zitadel:{{ .Tag }}-arm64
|
||||
- ghcr.io/zitadel/zitadel:{{ .ShortCommit }}-arm64
|
||||
goarch: arm64
|
||||
use: buildx
|
||||
dockerfile: build/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64"
|
||||
|
||||
docker_manifests:
|
||||
- id: zitadel-latest
|
||||
name_template: ghcr.io/zitadel/zitadel:latest
|
||||
image_templates:
|
||||
- ghcr.io/zitadel/zitadel:{{ .Tag }}-amd64
|
||||
- ghcr.io/zitadel/zitadel:{{ .Tag }}-arm64
|
||||
# Skips can and shall be set for individual manifests same as in dockers
|
||||
skip_push: auto
|
||||
- id: zitadel-Tag
|
||||
name_template: ghcr.io/zitadel/zitadel:{{ .Tag }}
|
||||
image_templates:
|
||||
- ghcr.io/zitadel/zitadel:{{ .Tag }}-amd64
|
||||
- ghcr.io/zitadel/zitadel:{{ .Tag }}-arm64
|
||||
|
||||
archives:
|
||||
- name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
|
||||
gomod:
|
||||
proxy: false
|
||||
|
||||
checksum:
|
||||
name_template: "checksums.txt"
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
|
||||
brews:
|
||||
- tap:
|
||||
owner: zitadel
|
||||
name: homebrew-tap
|
||||
token: "{{ .Env.GORELEASER_TOKEN_TAP }}"
|
||||
folder: Formula
|
||||
goarm: "7"
|
||||
homepage: https://zitadel.com
|
||||
description: Open source identity solution built for the container and cloud era
|
||||
license: Apache 2.0
|
||||
test: |
|
||||
system "#{bin}/zitadel -v"
|
||||
dependencies:
|
||||
- name: go
|
||||
type: optional
|
||||
- name: git
|
||||
install: |-
|
||||
bin.install "zitadel"
|
||||
# If set to auto, the release will not be uploaded to the homebrew tap
|
||||
# in case there is an indicator for prerelease in the tag e.g. v1.0.0-rc1
|
||||
# Default is false.
|
||||
skip_upload: auto
|
||||
|
||||
announce:
|
||||
discord:
|
||||
enabled: true
|
||||
message_template: "ZITADEL {{ .Tag }} is ready! Check the notes: https://github.com/zitadel/zitadel/releases/tag/{{ .Tag }}"
|
@ -1,10 +1,12 @@
|
||||
module.exports = {
|
||||
branches: [
|
||||
{ name: 'main' },
|
||||
{ name: 'next' },
|
||||
{ name: 'rc', prerelease: true },
|
||||
],
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer"
|
||||
]
|
||||
branches: [
|
||||
{ name: "main" },
|
||||
{ name: "next" },
|
||||
{ name: "rc", prerelease: true },
|
||||
],
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/github"
|
||||
],
|
||||
};
|
||||
|
@ -34,26 +34,30 @@ Follow [@zitadel](https://twitter.com/zitadel) on twitter
|
||||
|
||||
We strongly recommend to [talk to us](https://zitadel.com/contact) before you start contributing to streamline our and your work.
|
||||
|
||||
We accept contributions through pull requests. You need a github account for that. If you are unfamiliar with git have a look at Github's documentation on [creating forks](https://help.github.com/articles/fork-a-repo) and [creating pull requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork). Please draft the pull request as soon as possible. Go through the following checklist before you submit the final pull request:
|
||||
We accept contributions through pull requests.
|
||||
You need a github account for that.
|
||||
If you are unfamiliar with git have a look at Github's documentation on [creating forks](https://help.github.com/articles/fork-a-repo) and [creating pull requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork).
|
||||
Please draft the pull request as soon as possible.
|
||||
Go through the following checklist before you submit the final pull request:
|
||||
|
||||
### Submit a pull request (PR)
|
||||
|
||||
1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the [zitadel/zitadel](https://github.com/zitadel/zitadel) repository on GitHub
|
||||
2. On your fork, commit your changes to a new branch
|
||||
|
||||
`git checkout -b my-fix-branch main`
|
||||
`git checkout -b my-fix-branch main`
|
||||
|
||||
3. Make your changes following the [guidelines](#contribute) in this guide. Make sure that all tests pass.
|
||||
|
||||
4. Commit the changes on the new branch
|
||||
|
||||
`git commit --all`
|
||||
`git commit --all`
|
||||
|
||||
5. [Merge](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging) the latest commit of the `main`-branch
|
||||
|
||||
6. Push the changes to your branch on Github
|
||||
|
||||
`git push origin my-fix-branch`
|
||||
`git push origin my-fix-branch`
|
||||
|
||||
7. Use [Semantic Release commit messages](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#type) to simplify creation of release notes. In the title of the pull request [correct tagging](#commit-messages) is required and will be requested by the reviewers.
|
||||
|
||||
@ -61,7 +65,8 @@ We accept contributions through pull requests. You need a github account for tha
|
||||
|
||||
### Review a pull request
|
||||
|
||||
The reviewers will provide you feedback and approve your changes as soon as they are satisfied. If we ask you for changes in the code, you can follow the [GitHub Guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/incorporating-feedback-in-your-pull-request) to incorporate feedback in your pull request.
|
||||
The reviewers will provide you feedback and approve your changes as soon as they are satisfied.
|
||||
If we ask you for changes in the code, you can follow the [GitHub Guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/incorporating-feedback-in-your-pull-request) to incorporate feedback in your pull request.
|
||||
|
||||
<!-- TODO: how to do this via git -->
|
||||
<!-- TODO: change commit message via git -->
|
||||
@ -88,6 +93,16 @@ This is optional to indicate which component is affected. In doubt, leave blank
|
||||
|
||||
Provide a brief description of the change.
|
||||
|
||||
### Quality assurance
|
||||
|
||||
Please make sure you cover your changes with tests before marking a Pull Request as ready for review:
|
||||
|
||||
- [ ] Integration tests against the gRPC server ensure that one or multiple API calls that belong together return the expected results.
|
||||
- [ ] Integration tests against the gRPC server ensure that probable good and bad read and write permissions are tested.
|
||||
- [ ] Integration tests against the gRPC server ensure that the API is easily usable despite eventual consistency.
|
||||
- [ ] Integration tests against the gRPC server ensure that all probable login and registration flows are covered."
|
||||
- [ ] Integration tests ensure that certain commands send expected notifications.
|
||||
|
||||
## Contribute
|
||||
|
||||
The code consists of the following parts:
|
||||
@ -111,7 +126,7 @@ We add the label "good first issue" for problems we think are a good starting po
|
||||
|
||||
By executing the commands from this section, you run everything you need to develop the ZITADEL backend locally.
|
||||
Using [Docker Compose](https://docs.docker.com/compose/), you run a [CockroachDB](https://www.cockroachlabs.com/docs/stable/start-a-local-cluster-in-docker-mac.html) on your local machine.
|
||||
With [goreleaser](https://opencollective.com/goreleaser), you build a debuggable ZITADEL binary and run it using [delve](https://github.com/go-delve/delve).
|
||||
With [make](https://www.gnu.org/software/make/), you build a debuggable ZITADEL binary and run it using [delve](https://github.com/go-delve/delve).
|
||||
Then, you test your changes via the console your binary is serving at http://<span because="breaks the link"></span>localhost:8080 and by verifying the database.
|
||||
Once you are happy with your changes, you run end-to-end tests and tear everything down.
|
||||
|
||||
@ -120,8 +135,7 @@ ZITADEL uses [golangci-lint](https://golangci-lint.run) for code quality checks.
|
||||
The commands in this section are tested against the following software versions:
|
||||
|
||||
- [Docker version 20.10.17](https://docs.docker.com/engine/install/)
|
||||
- [Goreleaser version v1.8.3](https://goreleaser.com/install/)
|
||||
- [Go version 1.19](https://go.dev/doc/install)
|
||||
- [Go version 1.20](https://go.dev/doc/install)
|
||||
- [Delve 1.9.1](https://github.com/go-delve/delve/tree/v1.9.1/Documentation/installation)
|
||||
|
||||
Make some changes to the source code, then run the database locally.
|
||||
@ -134,16 +148,15 @@ docker compose --file ./e2e/docker-compose.yaml up --detach db
|
||||
Build the binary. This takes some minutes, but you can speed up rebuilds.
|
||||
|
||||
```bash
|
||||
# You just need goreleasers build part (--snapshot) and you just need to target your current platform (--single-target)
|
||||
goreleaser build --id dev --snapshot --single-target --rm-dist --output .artifacts/zitadel/zitadel
|
||||
make compile
|
||||
```
|
||||
|
||||
> Note: With this command, several steps are executed.
|
||||
> For speeding up rebuilds, you can reexecute only specific steps you think are necessary based on your changes.
|
||||
> Generating gRPC stubs: `DOCKER_BUILDKIT=1 docker build -f build/zitadel/Dockerfile . --target go-copy -o .`
|
||||
> Running unit tests: `DOCKER_BUILDKIT=1 docker build -f build/zitadel/Dockerfile . --target go-codecov`
|
||||
> Generating the console: `DOCKER_BUILDKIT=1 docker build -f build/console/Dockerfile . --target angular-export -o internal/api/ui/console/static/`
|
||||
> Build the binary: `goreleaser build --id dev --snapshot --single-target --rm-dist --output .artifacts/zitadel/zitadel --skip-before`
|
||||
> Generating gRPC stubs: `make core_api`
|
||||
> Running unit tests: `make core_unit_test`
|
||||
> Generating the console: `make console_build console_move`
|
||||
> Build the binary: `make compile`
|
||||
|
||||
You can now run and debug the binary in .artifacts/zitadel/zitadel using your favourite IDE, for example GoLand.
|
||||
You can test if ZITADEL does what you expect by using the UI at http://localhost:8080/ui/console.
|
||||
@ -151,19 +164,20 @@ Also, you can verify the data by running `cockroach sql --database zitadel --ins
|
||||
|
||||
As soon as you are ready to battle test your changes, run the end-to-end tests.
|
||||
|
||||
#### Running the tests with docker
|
||||
#### Running the tests
|
||||
|
||||
Running the tests with docker doesn't require you to take care of other dependencies than docker and goreleaser.
|
||||
Running the tests with docker doesn't require you to take care of other dependencies than docker and make.
|
||||
|
||||
```bash
|
||||
# Build the production binary (unit tests are executed, too)
|
||||
goreleaser build --id prod --snapshot --single-target --rm-dist --output .artifacts/zitadel/zitadel
|
||||
make core_build console_build
|
||||
GOOS=linux make compile_pipeline
|
||||
|
||||
# Pack the binary into a docker image
|
||||
DOCKER_BUILDKIT=1 docker build --file build/Dockerfile .artifacts/zitadel -t zitadel:local
|
||||
DOCKER_BUILDKIT=1 docker build --file build/Dockerfile . -t zitadel:local
|
||||
|
||||
# If you made changes in the e2e directory, make sure you reformat the files
|
||||
(cd ./e2e && npm run lint:fix)
|
||||
make console_lint
|
||||
|
||||
# Run the tests
|
||||
ZITADEL_IMAGE=zitadel:local docker compose --file ./e2e/config/host.docker.internal/docker-compose.yaml run --service-ports e2e
|
||||
@ -206,9 +220,7 @@ In order to run the integrations tests for the gRPC API, PostgreSQL and Cockroac
|
||||
```bash
|
||||
export INTEGRATION_DB_FLAVOR="cockroach" ZITADEL_MASTERKEY="MasterkeyNeedsToHave32Characters"
|
||||
docker compose -f internal/integration/config/docker-compose.yaml up --wait ${INTEGRATION_DB_FLAVOR}
|
||||
go run main.go init --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml
|
||||
go run main.go setup --masterkeyFromEnv --config internal/integration/config/zitadel.yaml --config internal/integration/config/${INTEGRATION_DB_FLAVOR}.yaml
|
||||
go test -count 1 -tags=integration -race -p 1 ./internal/integration ./internal/api/grpc/...
|
||||
make core_integration_test
|
||||
docker compose -f internal/integration/config/docker-compose.yaml down
|
||||
```
|
||||
|
||||
@ -221,7 +233,7 @@ Using [Docker Compose](https://docs.docker.com/compose/), you run [CockroachDB](
|
||||
You use the ZITADEL container as backend for your console.
|
||||
The console is run in your [Node](https://nodejs.org/en/about/) environment using [a local development server for Angular](https://angular.io/cli/serve#ng-serve), so you have fast feedback about your changes.
|
||||
|
||||
We use angular-eslint/Prettier for linting/formatting, so please run `npm run lint:fix` before committing. (VSCode users, check out [this ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [this Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to fix lint and formatting issues in development)
|
||||
We use angular-eslint/Prettier for linting/formatting, so please run `yarn lint:fix` before committing. (VSCode users, check out [this ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [this Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to fix lint and formatting issues in development)
|
||||
|
||||
Once you are happy with your changes, you run end-to-end tests and tear everything down.
|
||||
|
||||
@ -269,16 +281,16 @@ You can run the local console development server now.
|
||||
|
||||
```bash
|
||||
# Install npm dependencies
|
||||
npm install
|
||||
yarn install
|
||||
|
||||
# Generate source files from Protos
|
||||
npm run generate
|
||||
yarn generate
|
||||
|
||||
# Start the server
|
||||
npm start
|
||||
yarn start
|
||||
|
||||
# If you don't want to develop against http://localhost:8080, you can use another environment
|
||||
ENVIRONMENT_JSON_URL=https://my-cloud-instance-abcdef.zitadel.cloud/ui/console/assets/environment.json npm start
|
||||
ENVIRONMENT_JSON_URL=https://my-cloud-instance-abcdef.zitadel.cloud/ui/console/assets/environment.json yarn start
|
||||
```
|
||||
|
||||
Navigate to http://localhost:4200/.
|
||||
@ -288,7 +300,7 @@ Open another shell.
|
||||
|
||||
```bash
|
||||
# Reformat your console code
|
||||
npm run lint:fix
|
||||
yarn lint:fix
|
||||
|
||||
# Change to the e2e directory
|
||||
cd .. && cd e2e/
|
||||
@ -335,13 +347,13 @@ Please refer to the [README](./docs/README.md) for more information and local te
|
||||
### Style guide
|
||||
|
||||
- **Code with variables**: Make sure that code snippets can be used by setting environment variables, instead of manually replacing a placeholder.
|
||||
- **Embedded files**: When embedding mdx files, make sure the template ist prefixed by "_" (lowdash). The content will be rendered inside the parent page, but is not accessible individually (eg, by search).
|
||||
- **Embedded files**: When embedding mdx files, make sure the template ist prefixed by "\_" (lowdash). The content will be rendered inside the parent page, but is not accessible individually (eg, by search).
|
||||
- **Don't repeat yourself**: When using the same content in multiple places, save and manage the content as separate file and make use of embedded files to import it into other docs pages.
|
||||
- **Embedded code**: You can embed code snippets from a repository. See the [plugin](https://github.com/saucelabs/docusaurus-theme-github-codeblock#usage) for usage.
|
||||
|
||||
Following the [Google style guide](https://developers.google.com/style) is highly recommended. Its clear and concise guidelines ensure consistency and effective communication within the wider developer community.
|
||||
|
||||
The style guide covers a lot of material, so their [highlights](https://developers.google.com/style/highlights) page provides an overview of its most important points. Some of the points stated in the highlights that we care about most are given below:
|
||||
The style guide covers a lot of material, so their [highlights](https://developers.google.com/style/highlights) page provides an overview of its most important points. Some of the points stated in the highlights that we care about most are given below:
|
||||
|
||||
- Be conversational and friendly without being frivolous.
|
||||
- Use sentence case for document titles and section headings.
|
||||
@ -414,7 +426,6 @@ Priority shows you the priority the ZITADEL team has given this issue. In genera
|
||||
- **🏕 Medium**: After all the high issues are done these will be next.
|
||||
- **🏝 Low**: This is low in priority and will probably not be implemented in the next time or just if someone has some time in between.
|
||||
|
||||
|
||||
#### Complexity
|
||||
|
||||
This should give you an indication how complex the issue is. It's not about the hours or effort it takes.
|
||||
@ -448,4 +459,3 @@ The language shows you in which programming language the affected part is writte
|
||||
- **lang: angular**
|
||||
- **lang: go**
|
||||
- **lang: javascript**
|
||||
|
||||
|
112
Makefile
Normal file
112
Makefile
Normal file
@ -0,0 +1,112 @@
|
||||
go_bin := "$$(go env GOPATH)/bin"
|
||||
gen_authopt_path := "$(go_bin)/protoc-gen-authoption"
|
||||
gen_zitadel_path := "$(go_bin)/protoc-gen-zitadel"
|
||||
|
||||
now := $(shell date --rfc-3339=seconds | sed 's/ /T/')
|
||||
VERSION ?= development
|
||||
COMMIT_SHA ?= $(shell git rev-parse HEAD)
|
||||
|
||||
.PHONY: compile
|
||||
compile: core_build console_build compile_pipeline
|
||||
|
||||
.PHONY: compile_pipeline
|
||||
compile_pipeline: console_move
|
||||
CGO_ENABLED=0 go build -o zitadel -v -ldflags="-s -w -X 'github.com/zitadel/zitadel/cmd/build.commit=$(COMMIT_SHA)' -X 'github.com/zitadel/zitadel/cmd/build.date=$(now)' -X 'github.com/zitadel/zitadel/cmd/build.version=$(VERSION)' "
|
||||
chmod +x zitadel
|
||||
|
||||
.PHONY: core_dependencies
|
||||
core_dependencies:
|
||||
go mod download
|
||||
|
||||
.PHONY: core_static
|
||||
core_static:
|
||||
go install github.com/rakyll/statik@v0.1.7
|
||||
go generate internal/api/ui/login/statik/generate.go
|
||||
go generate internal/api/ui/login/static/resources/generate.go
|
||||
go generate internal/notification/statik/generate.go
|
||||
go generate internal/statik/generate.go
|
||||
|
||||
.PHONY: core_assets
|
||||
core_assets:
|
||||
mkdir -p docs/apis/assets
|
||||
go run internal/api/assets/generator/asset_generator.go -directory=internal/api/assets/generator/ -assets=docs/apis/assets/assets.md
|
||||
|
||||
.PHONY: core_api_generator
|
||||
core_api_generator:
|
||||
ifeq (,$(wildcard $(gen_authopt_path)))
|
||||
go install internal/protoc/protoc-gen-authoption/main.go \
|
||||
&& mv $$(go env GOPATH)/bin/main $(gen_authopt_path)
|
||||
endif
|
||||
ifeq (,$(wildcard $(gen_zitadel_path)))
|
||||
go install internal/protoc/protoc-gen-zitadel/main.go \
|
||||
&& mv $$(go env GOPATH)/bin/main $(gen_zitadel_path)
|
||||
endif
|
||||
|
||||
.PHONY: core_grpc_dependencies
|
||||
core_grpc_dependencies:
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3
|
||||
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v2.15.2
|
||||
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@v2.15.2
|
||||
go install github.com/envoyproxy/protoc-gen-validate@v0.10.1
|
||||
|
||||
.PHONY: core_api
|
||||
core_api: core_api_generator core_grpc_dependencies
|
||||
buf generate
|
||||
mkdir -p pkg/grpc
|
||||
cp -r .artifacts/grpc/github.com/zitadel/zitadel/pkg/grpc/* pkg/grpc/
|
||||
mkdir -p openapi/v2/zitadel
|
||||
cp -r .artifacts/grpc/zitadel/ openapi/v2/zitadel
|
||||
|
||||
.PHONY: core_build
|
||||
core_build: core_dependencies core_api core_static core_assets
|
||||
|
||||
.PHONY: console_move
|
||||
console_move:
|
||||
cp -r console/dist/console/* internal/api/ui/console/static
|
||||
|
||||
.PHONY: console_dependencies
|
||||
console_dependencies:
|
||||
cd console && \
|
||||
yarn install --immutable
|
||||
|
||||
.PHONY: console_client
|
||||
console_client:
|
||||
cd console && \
|
||||
yarn generate
|
||||
|
||||
.PHONY: console_build
|
||||
console_build: console_dependencies console_client
|
||||
cd console && \
|
||||
yarn build
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) .artifacts/grpc
|
||||
$(RM) $(gen_authopt_path)
|
||||
$(RM) $(gen_zitadel_path)
|
||||
|
||||
.PHONY: core_unit_test
|
||||
core_unit_test:
|
||||
go test -race -coverprofile=profile.cov ./...
|
||||
|
||||
.PHONY: core_integration_test
|
||||
core_integration_test:
|
||||
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
|
||||
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
|
||||
console_lint:
|
||||
cd console && \
|
||||
yarn lint
|
||||
|
||||
.PHONE: core_lint
|
||||
core_lint:
|
||||
golangci-lint run \
|
||||
--timeout 10m \
|
||||
--config ./.golangci.yaml \
|
||||
--out-format=github-actions \
|
||||
--concurrency=$$(getconf _NPROCESSORS_ONLN)
|
21
buf.gen.yaml
Normal file
21
buf.gen.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
version: v1
|
||||
plugins:
|
||||
- plugin: go
|
||||
out: .artifacts/grpc
|
||||
- plugin: go-grpc
|
||||
out: .artifacts/grpc
|
||||
- plugin: grpc-gateway
|
||||
out: .artifacts/grpc
|
||||
opt:
|
||||
- allow_delete_body=true
|
||||
- plugin: openapiv2
|
||||
out: .artifacts/grpc
|
||||
opt:
|
||||
- allow_delete_body=true
|
||||
- plugin: validate
|
||||
out: .artifacts/grpc
|
||||
opt: lang=go
|
||||
- plugin: authoption
|
||||
out: .artifacts/grpc
|
||||
- plugin: zitadel
|
||||
out: .artifacts/grpc
|
@ -1,20 +1,32 @@
|
||||
#######################
|
||||
## Final Production Image
|
||||
#######################
|
||||
FROM alpine:3 as artifact
|
||||
COPY zitadel /app/zitadel
|
||||
RUN adduser -D zitadel && \
|
||||
chown zitadel /app/zitadel && \
|
||||
chmod +x /app/zitadel
|
||||
FROM --platform=$TARGETPLATFORM debian:latest as artifact
|
||||
ENV ZITADEL_ARGS=
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN apt-get update && apt-get install ca-certificates -y
|
||||
|
||||
COPY build/entrypoint.sh /app/entrypoint.sh
|
||||
COPY zitadel /app/zitadel
|
||||
|
||||
RUN useradd -s "" --home / zitadel && \
|
||||
chown zitadel /app/zitadel && \
|
||||
chmod +x /app/zitadel && \
|
||||
chown zitadel /app/entrypoint.sh && \
|
||||
chmod +x /app/entrypoint.sh
|
||||
|
||||
WORKDIR /app
|
||||
ENV PATH="/app:${PATH}"
|
||||
|
||||
USER zitadel
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
|
||||
FROM --platform=$TARGETPLATFORM scratch as final
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
#######################
|
||||
## Scratch Image
|
||||
#######################
|
||||
FROM scratch as final
|
||||
COPY --from=artifact /etc/passwd /etc/passwd
|
||||
COPY --from=artifact /etc/ssl/certs /etc/ssl/certs
|
||||
COPY --from=artifact /app /
|
||||
USER zitadel
|
||||
HEALTHCHECK NONE
|
||||
ENTRYPOINT ["/zitadel"]
|
||||
COPY --from=artifact /app/zitadel /app/zitadel
|
||||
|
||||
HEALTHCHECK NONE
|
||||
|
||||
USER zitadel
|
||||
ENTRYPOINT ["/app/zitadel"]
|
@ -1,30 +0,0 @@
|
||||
ARG NODE_VERSION=18
|
||||
|
||||
#######################
|
||||
## With this step we prepare all node_modules, this helps caching the build
|
||||
## Speed up this step by mounting your local node_modules directory
|
||||
## We also copy and generate the source code
|
||||
#######################
|
||||
FROM node:${NODE_VERSION} as npm-base
|
||||
WORKDIR /console
|
||||
|
||||
# Dependencies
|
||||
COPY console/package.json console/package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Sources
|
||||
COPY console .
|
||||
COPY proto/ /proto/
|
||||
|
||||
#######################
|
||||
## angular lint workspace and prod build
|
||||
#######################
|
||||
FROM npm-base as angular-build
|
||||
RUN npm run lint
|
||||
RUN npm run build
|
||||
|
||||
#######################
|
||||
## Only Copy Assets
|
||||
#######################
|
||||
FROM scratch as angular-export
|
||||
COPY --from=angular-build /console/dist/console .
|
17
build/entrypoint.sh
Executable file
17
build/entrypoint.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
case $@ in
|
||||
sh*)
|
||||
${@:3}
|
||||
;;
|
||||
bash*)
|
||||
${@:5}
|
||||
;;
|
||||
*)
|
||||
if [[ ! -z "$@" ]]
|
||||
then
|
||||
ZITADEL_ARGS="$@"
|
||||
fi
|
||||
/app/zitadel ${ZITADEL_ARGS}
|
||||
;;
|
||||
esac
|
@ -1,47 +0,0 @@
|
||||
#ARG BUILDARCH=x86_64
|
||||
|
||||
#######################
|
||||
## These steps set platform / arch type specific variables
|
||||
#######################
|
||||
FROM alpine:3 AS arm64-base
|
||||
ENV PROTOC_ARCH aarch_64
|
||||
|
||||
FROM alpine:3 AS amd64-base
|
||||
ENV PROTOC_ARCH x86_64
|
||||
|
||||
#######################
|
||||
## This step sets up the folder structure,
|
||||
## initalices go mods,
|
||||
## downloads the protofiles,
|
||||
## protoc and protoc-gen-grpc-web for later use
|
||||
#######################
|
||||
FROM ${BUILDARCH}-base
|
||||
ARG PROTOC_VERSION=22.3
|
||||
ARG PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip
|
||||
ARG GRPC_WEB_VERSION=1.3.0
|
||||
ARG GATEWAY_VERSION=2.15.2
|
||||
ARG VALIDATOR_VERSION=0.10.1
|
||||
# no arm specific version available and x86 works fine at the moment:
|
||||
ARG GRPC_WEB=protoc-gen-grpc-web-${GRPC_WEB_VERSION}-linux-x86_64
|
||||
|
||||
RUN apk add tar curl
|
||||
WORKDIR /proto
|
||||
|
||||
#protoc
|
||||
RUN apk add tar curl \
|
||||
&& curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/$PROTOC_ZIP \
|
||||
&& unzip -o $PROTOC_ZIP -d /usr/local bin/protoc \
|
||||
&& unzip -o $PROTOC_ZIP -d /proto include/* \
|
||||
&& rm -f $PROTOC_ZIP \
|
||||
&& curl -OL https://github.com/grpc/grpc-web/releases/download/${GRPC_WEB_VERSION}/${GRPC_WEB} \
|
||||
&& mv ${GRPC_WEB} /usr/local/bin/protoc-gen-grpc-web \
|
||||
&& chmod +x /usr/local/bin/protoc-gen-grpc-web \
|
||||
&& curl https://raw.githubusercontent.com/envoyproxy/protoc-gen-validate/v${VALIDATOR_VERSION}/validate/validate.proto --create-dirs -o include/validate/validate.proto \
|
||||
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v${GATEWAY_VERSION}/protoc-gen-openapiv2/options/annotations.proto --create-dirs -o include/protoc-gen-openapiv2/options/annotations.proto \
|
||||
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v${GATEWAY_VERSION}/protoc-gen-openapiv2/options/openapiv2.proto --create-dirs -o include/protoc-gen-openapiv2/options/openapiv2.proto \
|
||||
&& curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto --create-dirs -o include/google/api/annotations.proto \
|
||||
&& curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto --create-dirs -o include/google/api/http.proto \
|
||||
&& curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/field_behavior.proto --create-dirs -o include/google/api/field_behavior.proto
|
||||
|
||||
#zitadel protos
|
||||
COPY proto/ include/.
|
302
build/workflow.Dockerfile
Normal file
302
build/workflow.Dockerfile
Normal file
@ -0,0 +1,302 @@
|
||||
# ##############################################################################
|
||||
# core
|
||||
# ##############################################################################
|
||||
|
||||
# #######################################
|
||||
# download dependencies
|
||||
# #######################################
|
||||
FROM golang:buster AS core-deps
|
||||
|
||||
WORKDIR /go/src/github.com/zitadel/zitadel
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
|
||||
RUN go mod download
|
||||
|
||||
# #######################################
|
||||
# compile custom protoc plugins
|
||||
# #######################################
|
||||
FROM golang:buster AS core-api-generator
|
||||
|
||||
WORKDIR /go/src/github.com/zitadel/zitadel
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
COPY internal/protoc internal/protoc
|
||||
COPY pkg/grpc/protoc/v2 pkg/grpc/protoc/v2
|
||||
|
||||
RUN go install internal/protoc/protoc-gen-authoption/main.go \
|
||||
&& mv $(go env GOPATH)/bin/main $(go env GOPATH)/bin/protoc-gen-authoption \
|
||||
&& go install internal/protoc/protoc-gen-zitadel/main.go \
|
||||
&& mv $(go env GOPATH)/bin/main $(go env GOPATH)/bin/protoc-gen-zitadel
|
||||
|
||||
# #######################################
|
||||
# build backend stub
|
||||
# #######################################
|
||||
FROM golang:buster AS core-api
|
||||
|
||||
WORKDIR /go/src/github.com/zitadel/zitadel
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
COPY proto proto
|
||||
COPY buf.*.yaml .
|
||||
COPY Makefile Makefile
|
||||
COPY --from=core-api-generator /go/bin /usr/local/bin
|
||||
|
||||
RUN make grpc
|
||||
|
||||
# #######################################
|
||||
# generate code for login ui
|
||||
# #######################################
|
||||
FROM golang:buster AS core-login
|
||||
|
||||
WORKDIR /go/src/github.com/zitadel/zitadel
|
||||
|
||||
COPY Makefile Makefile
|
||||
COPY internal/api/ui/login/static internal/api/ui/login/static
|
||||
COPY internal/api/ui/login/statik internal/api/ui/login/statik
|
||||
COPY internal/notification/static internal/notification/static
|
||||
COPY internal/notification/statik internal/notification/statik
|
||||
COPY internal/static internal/static
|
||||
COPY internal/statik internal/statik
|
||||
|
||||
RUN make static
|
||||
|
||||
# #######################################
|
||||
# generate code for assets
|
||||
# #######################################
|
||||
FROM golang:buster AS core-assets
|
||||
WORKDIR /go/src/github.com/zitadel/zitadel
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
COPY Makefile Makefile
|
||||
COPY internal/api/assets/generator internal/api/assets/generator
|
||||
COPY internal/config internal/config
|
||||
COPY internal/errors internal/errors
|
||||
COPY --from=core-api /go/src/github.com/zitadel/zitadel/openapi/v2 openapi/v2
|
||||
|
||||
RUN make assets
|
||||
|
||||
# #######################################
|
||||
# Gather all core files
|
||||
# #######################################
|
||||
FROM core-deps AS core-gathered
|
||||
|
||||
COPY cmd cmd
|
||||
COPY internal internal
|
||||
COPY pkg pkg
|
||||
COPY proto proto
|
||||
COPY openapi openapi
|
||||
COPY statik statik
|
||||
COPY main.go main.go
|
||||
COPY --from=core-api /go/src/github.com/zitadel/zitadel .
|
||||
COPY --from=core-login /go/src/github.com/zitadel/zitadel .
|
||||
COPY --from=core-assets /go/src/github.com/zitadel/zitadel/internal ./internal
|
||||
|
||||
# ##############################################################################
|
||||
# build console
|
||||
# ##############################################################################
|
||||
|
||||
# #######################################
|
||||
# download console dependencies
|
||||
# #######################################
|
||||
FROM node:18-buster AS console-deps
|
||||
|
||||
WORKDIR /zitadel/console
|
||||
|
||||
COPY console/package.json .
|
||||
COPY console/yarn.lock .
|
||||
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# #######################################
|
||||
# generate console client
|
||||
# #######################################
|
||||
FROM node:18-buster AS console-client
|
||||
|
||||
WORKDIR /zitadel/console
|
||||
|
||||
# install buf
|
||||
COPY --from=bufbuild/buf:latest /usr/local/bin/* /usr/local/bin/
|
||||
ENV PATH="/usr/local/bin:${PATH}"
|
||||
|
||||
COPY console/package.json .
|
||||
COPY console/buf.*.yaml .
|
||||
COPY proto ../proto
|
||||
|
||||
RUN yarn generate
|
||||
|
||||
# #######################################
|
||||
# Gather all console files
|
||||
# #######################################
|
||||
FROM console-deps as console-gathered
|
||||
|
||||
COPY --from=console-client /zitadel/console/src/app/proto/generated src/app/proto/generated
|
||||
|
||||
COPY console/src src
|
||||
COPY console/angular.json .
|
||||
COPY console/ngsw-config.json .
|
||||
COPY console/tsconfig* .
|
||||
|
||||
# #######################################
|
||||
# Build console
|
||||
# #######################################
|
||||
FROM console-gathered AS console
|
||||
RUN yarn build
|
||||
|
||||
# ##############################################################################
|
||||
# build the executable
|
||||
# ##############################################################################
|
||||
|
||||
# #######################################
|
||||
# build executable
|
||||
# #######################################
|
||||
FROM core-gathered AS compile
|
||||
|
||||
ARG GOOS
|
||||
ARG GOARCH
|
||||
|
||||
COPY --from=console /zitadel/console/dist/console internal/api/ui/console/static/
|
||||
|
||||
RUN go build -o zitadel -ldflags="-s -w -race" \
|
||||
&& chmod +x zitadel
|
||||
|
||||
ENTRYPOINT [ "./zitadel" ]
|
||||
|
||||
# #######################################
|
||||
# copy executable
|
||||
# #######################################
|
||||
FROM scratch AS copy-executable
|
||||
ARG GOOS
|
||||
ARG GOARCH
|
||||
|
||||
COPY --from=compile /go/src/github.com/zitadel/zitadel/zitadel /.artifacts/zitadel
|
||||
|
||||
# ##############################################################################
|
||||
# tests
|
||||
# ##############################################################################
|
||||
FROM ubuntu/postgres:latest AS test-core-base
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
make \
|
||||
ca-certificates \
|
||||
gcc \
|
||||
&& \
|
||||
update-ca-certificates; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# install go
|
||||
COPY --from=golang:latest /usr/local/go/ /usr/local/go/
|
||||
ENV PATH="/go/bin:/usr/local/go/bin:${PATH}"
|
||||
|
||||
WORKDIR /go/src/github.com/zitadel/zitadel
|
||||
|
||||
# default vars
|
||||
ENV DB_FLAVOR=postgres
|
||||
ENV POSTGRES_USER=zitadel
|
||||
ENV POSTGRES_DB=zitadel
|
||||
ENV POSTGRES_PASSWORD=postgres
|
||||
ENV POSTGRES_HOST_AUTH_METHOD=trust
|
||||
|
||||
ENV PGUSER=zitadel
|
||||
ENV PGDATABASE=zitadel
|
||||
ENV PGPASSWORD=postgres
|
||||
|
||||
ENV CGO_ENABLED=1
|
||||
|
||||
# copy zitadel files
|
||||
COPY --from=core-deps /go/pkg/mod /root/go/pkg/mod
|
||||
COPY --from=core-gathered /go/src/github.com/zitadel/zitadel .
|
||||
|
||||
# #######################################
|
||||
# unit test core
|
||||
# #######################################
|
||||
FROM test-core-base AS test-core-unit
|
||||
RUN go test -race -v -coverprofile=profile.cov ./...
|
||||
|
||||
# #######################################
|
||||
# coverage output
|
||||
# #######################################
|
||||
FROM scratch AS coverage-core-unit
|
||||
COPY --from=test-core-unit /go/src/github.com/zitadel/zitadel/profile.cov /coverage/
|
||||
|
||||
# #######################################
|
||||
# integration test core
|
||||
# #######################################
|
||||
FROM test-core-base AS test-core-integration
|
||||
ENV DB_FLAVOR=cockroach
|
||||
|
||||
# install cockroach
|
||||
COPY --from=cockroachdb/cockroach:latest /cockroach/cockroach /usr/local/bin/
|
||||
ENV COCKROACH_BINARY=/cockroach/cockroach
|
||||
|
||||
ENV ZITADEL_MASTERKEY=MasterkeyNeedsToHave32Characters
|
||||
|
||||
COPY build/core-integration-test.sh /usr/local/bin/run-tests.sh
|
||||
RUN chmod +x /usr/local/bin/run-tests.sh
|
||||
|
||||
RUN run-tests.sh
|
||||
|
||||
# #######################################
|
||||
# coverage output
|
||||
# #######################################
|
||||
FROM scratch AS coverage-core-integration
|
||||
COPY --from=test-core-integration /go/src/github.com/zitadel/zitadel/profile.cov /coverage/
|
||||
|
||||
# ##############################################################################
|
||||
# linting
|
||||
# ##############################################################################
|
||||
|
||||
# #######################################
|
||||
# api
|
||||
# #######################################
|
||||
FROM bufbuild/buf:latest AS lint-api
|
||||
|
||||
COPY proto proto
|
||||
COPY buf.*.yaml .
|
||||
|
||||
RUN buf lint
|
||||
|
||||
# #######################################
|
||||
# console
|
||||
# #######################################
|
||||
FROM console-gathered AS lint-console
|
||||
|
||||
COPY console/.eslintrc.js .
|
||||
COPY console/.prettier* .
|
||||
RUN yarn lint
|
||||
|
||||
# #######################################
|
||||
# core
|
||||
# #######################################
|
||||
FROM golangci/golangci-lint:latest AS lint-core
|
||||
ARG LINT_EXIT_CODE=1
|
||||
|
||||
WORKDIR /go/src/github.com/zitadel/zitadel
|
||||
|
||||
COPY .golangci.yaml .
|
||||
COPY .git/ .git/
|
||||
COPY --from=core-deps /go/pkg/mod /go/pkg/mod
|
||||
COPY --from=core-gathered /go/src/github.com/zitadel/zitadel .
|
||||
|
||||
RUN git fetch https://github.com/zitadel/zitadel main:main
|
||||
|
||||
RUN golangci-lint run \
|
||||
--timeout 10m \
|
||||
--config ./.golangci.yaml \
|
||||
--out-format=github-actions:report,colored-line-number \
|
||||
--issues-exit-code=${LINT_EXIT_CODE} \
|
||||
--concurrency=$(getconf _NPROCESSORS_ONLN)
|
||||
|
||||
# #######################################
|
||||
# report output
|
||||
# #######################################
|
||||
FROM scratch AS lint-core-report
|
||||
COPY --from=lint-core /go/src/github.com/zitadel/zitadel/report .
|
@ -1,107 +0,0 @@
|
||||
ARG GO_VERSION=1.19
|
||||
|
||||
#######################
|
||||
## Go dependencies
|
||||
## Speed up this step by mounting your local go mod pkg directory
|
||||
#######################
|
||||
FROM golang:${GO_VERSION} as go-dep
|
||||
WORKDIR /go/src/github.com/zitadel/zitadel
|
||||
|
||||
#download modules
|
||||
COPY go.mod ./
|
||||
COPY go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# install tools
|
||||
COPY tools ./tools
|
||||
RUN ./tools/install.sh
|
||||
|
||||
#######################
|
||||
## generates static files
|
||||
#######################
|
||||
FROM go-dep AS go-static
|
||||
COPY internal/api/ui/login/static internal/api/ui/login/static
|
||||
COPY internal/api/ui/login/statik internal/api/ui/login/statik
|
||||
COPY internal/notification/static internal/notification/static
|
||||
COPY internal/notification/statik internal/notification/statik
|
||||
COPY internal/static internal/static
|
||||
COPY internal/statik internal/statik
|
||||
|
||||
RUN go generate internal/api/ui/login/statik/generate.go \
|
||||
&& go generate internal/api/ui/login/static/generate.go \
|
||||
&& go generate internal/notification/statik/generate.go \
|
||||
&& go generate internal/statik/generate.go
|
||||
|
||||
#######################
|
||||
## generates grpc stub
|
||||
#######################
|
||||
FROM go-static AS go-stub
|
||||
COPY --from=zitadel-base:local /proto /proto
|
||||
COPY --from=zitadel-base:local /usr/local/bin /usr/local/bin/.
|
||||
|
||||
COPY build/zitadel/generate-grpc.sh build/zitadel/generate-grpc.sh
|
||||
COPY internal/protoc internal/protoc
|
||||
COPY openapi/statik openapi/statik
|
||||
COPY internal/api/assets/generator internal/api/assets/generator
|
||||
COPY internal/config internal/config
|
||||
COPY internal/errors internal/errors
|
||||
|
||||
RUN build/zitadel/generate-grpc.sh && \
|
||||
go generate openapi/statik/generate.go && \
|
||||
mkdir -p docs/apis/assets/ && \
|
||||
go run internal/api/assets/generator/asset_generator.go -directory=internal/api/assets/generator/ -assets=docs/apis/assets/assets.md
|
||||
|
||||
#######################
|
||||
## Go base build
|
||||
#######################
|
||||
FROM go-stub as go-base
|
||||
# copy remaining zitadel files
|
||||
COPY cmd cmd
|
||||
COPY internal internal
|
||||
COPY pkg pkg
|
||||
COPY openapi openapi
|
||||
|
||||
#######################
|
||||
## copy for local dev
|
||||
#######################
|
||||
FROM scratch as go-copy
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/internal/api/ui/login/statik/statik.go internal/api/ui/login/statik/statik.go
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/internal/notification/statik/statik.go internal/notification/statik/statik.go
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/internal/statik/statik.go internal/statik/statik.go
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/openapi/statik/statik.go openapi/statik/statik.go
|
||||
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/pkg/grpc pkg/grpc
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/openapi/v2/zitadel openapi/v2/zitadel
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/openapi/statik/statik.go openapi/statik/statik.go
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/internal/protoc/protoc-gen-authoption/authoption/options.pb.go internal/protoc/protoc-gen-authoption/authoption/options.pb.go
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/docs/apis/proto docs/docs/apis/proto
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/docs/apis/assets docs/docs/apis/assets
|
||||
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/internal/api/assets/authz.go ./internal/api/assets/authz.go
|
||||
COPY --from=go-stub /go/src/github.com/zitadel/zitadel/internal/api/assets/router.go ./internal/api/assets/router.go
|
||||
|
||||
#######################
|
||||
## Go test
|
||||
#######################
|
||||
FROM go-base as go-test
|
||||
|
||||
ARG COCKROACH_BINARY=/usr/local/bin/cockroach
|
||||
ARG COCKROACH_VERSION=v22.2.2
|
||||
RUN apt install openssl tzdata tar
|
||||
|
||||
# cockroach binary used to backup database
|
||||
RUN mkdir /usr/local/lib/cockroach
|
||||
RUN wget -qO- https://binaries.cockroachdb.com/cockroach-${COCKROACH_VERSION}.linux-amd64.tgz \
|
||||
| tar xvz && cp -i cockroach-${COCKROACH_VERSION}.linux-amd64/cockroach /usr/local/bin/
|
||||
RUN rm -r cockroach-${COCKROACH_VERSION}.linux-amd64
|
||||
|
||||
# Migrations for cockroach-secure
|
||||
RUN go install github.com/rakyll/statik \
|
||||
&& go test -race -v -coverprofile=profile.cov $(go list ./... | grep -v /operator/)
|
||||
|
||||
#######################
|
||||
## Go test results
|
||||
#######################
|
||||
FROM scratch as go-codecov
|
||||
COPY --from=go-test /go/src/github.com/zitadel/zitadel/profile.cov profile.cov
|
||||
|
@ -1,111 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eux
|
||||
|
||||
echo "Generate grpc"
|
||||
|
||||
OPENAPI_PATH=${GOPATH}/src/github.com/zitadel/zitadel/openapi/v2
|
||||
ZITADEL_PATH=${GOPATH}/src/github.com/zitadel/zitadel
|
||||
GRPC_PATH=${ZITADEL_PATH}/pkg/grpc
|
||||
PROTO_PATH=/proto/include/zitadel
|
||||
DOCS_PATH=${ZITADEL_PATH}/docs/apis/proto
|
||||
|
||||
# generate go stub and grpc code for all files
|
||||
protoc \
|
||||
-I=/proto/include/ \
|
||||
--go_out $GOPATH/src \
|
||||
--go-grpc_out $GOPATH/src \
|
||||
--validate_out=lang=go:${GOPATH}/src \
|
||||
$(find ${PROTO_PATH} -iname *.proto)
|
||||
|
||||
# install authoption and zitadel proto compiler
|
||||
go install ${ZITADEL_PATH}/internal/protoc/protoc-gen-auth
|
||||
go install ${ZITADEL_PATH}/internal/protoc/protoc-gen-zitadel
|
||||
|
||||
# output folder for openapi v2
|
||||
mkdir -p ${OPENAPI_PATH}
|
||||
mkdir -p ${DOCS_PATH}
|
||||
|
||||
# generate additional output
|
||||
|
||||
protoc \
|
||||
-I=/proto/include \
|
||||
--grpc-gateway_out ${GOPATH}/src \
|
||||
--grpc-gateway_opt logtostderr=true \
|
||||
--openapiv2_out ${OPENAPI_PATH} \
|
||||
--openapiv2_opt logtostderr=true \
|
||||
--auth_out ${GOPATH}/src \
|
||||
--validate_out=lang=go:${GOPATH}/src \
|
||||
${PROTO_PATH}/system.proto
|
||||
|
||||
protoc \
|
||||
-I=/proto/include \
|
||||
--grpc-gateway_out ${GOPATH}/src \
|
||||
--grpc-gateway_opt logtostderr=true \
|
||||
--openapiv2_out ${OPENAPI_PATH} \
|
||||
--openapiv2_opt logtostderr=true \
|
||||
--auth_out ${GOPATH}/src \
|
||||
--validate_out=lang=go:${GOPATH}/src \
|
||||
${PROTO_PATH}/admin.proto
|
||||
|
||||
protoc \
|
||||
-I=/proto/include \
|
||||
--grpc-gateway_out ${GOPATH}/src \
|
||||
--grpc-gateway_opt logtostderr=true \
|
||||
--grpc-gateway_opt allow_delete_body=true \
|
||||
--openapiv2_out ${OPENAPI_PATH} \
|
||||
--openapiv2_opt logtostderr=true \
|
||||
--openapiv2_opt allow_delete_body=true \
|
||||
--auth_out ${GOPATH}/src \
|
||||
--validate_out=lang=go:${GOPATH}/src \
|
||||
${PROTO_PATH}/management.proto
|
||||
|
||||
protoc \
|
||||
-I=/proto/include \
|
||||
--grpc-gateway_out ${GOPATH}/src \
|
||||
--grpc-gateway_opt logtostderr=true \
|
||||
--grpc-gateway_opt allow_delete_body=true \
|
||||
--openapiv2_out ${OPENAPI_PATH} \
|
||||
--openapiv2_opt logtostderr=true \
|
||||
--openapiv2_opt allow_delete_body=true \
|
||||
--auth_out=${GOPATH}/src \
|
||||
--validate_out=lang=go:${GOPATH}/src \
|
||||
${PROTO_PATH}/auth.proto
|
||||
|
||||
protoc \
|
||||
-I=/proto/include \
|
||||
--grpc-gateway_out ${GOPATH}/src \
|
||||
--grpc-gateway_opt logtostderr=true \
|
||||
--grpc-gateway_opt allow_delete_body=true \
|
||||
--openapiv2_out ${OPENAPI_PATH} \
|
||||
--openapiv2_opt logtostderr=true \
|
||||
--openapiv2_opt allow_delete_body=true \
|
||||
--zitadel_out=${GOPATH}/src \
|
||||
--validate_out=lang=go:${GOPATH}/src \
|
||||
${PROTO_PATH}/user/v2alpha/user_service.proto
|
||||
|
||||
protoc \
|
||||
-I=/proto/include \
|
||||
--grpc-gateway_out ${GOPATH}/src \
|
||||
--grpc-gateway_opt logtostderr=true \
|
||||
--grpc-gateway_opt allow_delete_body=true \
|
||||
--openapiv2_out ${OPENAPI_PATH} \
|
||||
--openapiv2_opt logtostderr=true \
|
||||
--openapiv2_opt allow_delete_body=true \
|
||||
--zitadel_out=${GOPATH}/src \
|
||||
--validate_out=lang=go:${GOPATH}/src \
|
||||
${PROTO_PATH}/session/v2alpha/session_service.proto
|
||||
|
||||
protoc \
|
||||
-I=/proto/include \
|
||||
--grpc-gateway_out ${GOPATH}/src \
|
||||
--grpc-gateway_opt logtostderr=true \
|
||||
--grpc-gateway_opt allow_delete_body=true \
|
||||
--openapiv2_out ${OPENAPI_PATH} \
|
||||
--openapiv2_opt logtostderr=true \
|
||||
--openapiv2_opt allow_delete_body=true \
|
||||
--zitadel_out=${GOPATH}/src \
|
||||
--validate_out=lang=go:${GOPATH}/src \
|
||||
${PROTO_PATH}/settings/v2alpha/settings_service.proto
|
||||
|
||||
echo "done generating grpc"
|
@ -14,6 +14,29 @@ Tracing:
|
||||
Fraction: 1.0
|
||||
MetricPrefix: zitadel
|
||||
|
||||
Telemetry:
|
||||
# As long as Enabled is true, ZITADEL tries to send usage data to the configured Telemetry.Endpoints.
|
||||
# Data is projected by ZITADEL even if Enabled is false.
|
||||
# This means that switching this to true makes ZITADEL try to send past data.
|
||||
Enabled: false
|
||||
# Push telemetry data to all these endpoints at least once using an HTTP POST request.
|
||||
# If one endpoint returns an unsuccessful response code or times out,
|
||||
# ZITADEL retries to push the data point to all configured endpoints until it succeeds.
|
||||
# Configure delivery guarantees and intervals in the section Projections.Customizations.Telemetry
|
||||
# The endpoints can be reconfigured at runtime.
|
||||
# Ten redirects are followed.
|
||||
# If you change this configuration at runtime, remaining data that is not successfully delivered to the old endpoints is sent to the new endpoints.
|
||||
Endpoints:
|
||||
- https://httpbin.org/post
|
||||
# These headers are sent with every request to the configured endpoints.
|
||||
Headers:
|
||||
# single-value: "single-value"
|
||||
# multi-value:
|
||||
# - "multi-value-1"
|
||||
# - "multi-value-2"
|
||||
# The maximum number of data points that are queried before they are sent to the configured endpoints.
|
||||
Limit: 100 # ZITADEL_TELEMETRY_LIMIT
|
||||
|
||||
# Port ZITADEL will listen on
|
||||
Port: 8080
|
||||
# Port ZITADEL is exposed on, it can differ from port e.g. if you proxy the traffic
|
||||
@ -169,17 +192,29 @@ Projections:
|
||||
BulkLimit: 2000
|
||||
# The Notifications projection is used for sending emails and SMS to users
|
||||
Notifications:
|
||||
# As notification projections don't result in database statements, retries don't have an effect
|
||||
# As notification projections don't result in database statements, retries don't have any effects
|
||||
MaxFailureCount: 0
|
||||
# The NotificationsQuotas projection is used for calling quota webhooks
|
||||
NotificationsQuotas:
|
||||
# Delivery guarantee requirements are probably higher for quota webhooks
|
||||
# In case of failed deliveries, ZITADEL retries to send the data points to the configured endpoints, but only for active instances.
|
||||
# An instance is active, as long as there are projected events on the instance, that are not older than the HandleActiveInstances duration.
|
||||
# Delivery guarantee requirements are higher for quota webhooks
|
||||
# Defaults to 45 days
|
||||
HandleActiveInstances: 1080h
|
||||
# As quota notification projections don't result in database statements, retries don't have an effect
|
||||
# As quota notification projections don't result in database statements, retries don't have any effects
|
||||
MaxFailureCount: 0
|
||||
# Quota notifications are not so time critical. Setting RequeueEvery every five minutes doesn't annoy the db too much.
|
||||
# Quota notifications are not so time critical. Setting RequeueEvery every five minutes doesn't annoy the database too much.
|
||||
RequeueEvery: 300s
|
||||
Telemetry:
|
||||
# In case of failed deliveries, ZITADEL retries to send the data points to the configured endpoints, but only for active instances.
|
||||
# An instance is active, as long as there are projected events on the instance, that are not older than the HandleActiveInstances duration.
|
||||
# Telemetry delivery guarantee requirements are a bit higher than normal data projections, as they are not interactively retryable.
|
||||
# Defaults to 15 days
|
||||
HandleActiveInstances: 360h
|
||||
# As sending telemetry data doesn't result in database statements, retries don't have any effects
|
||||
MaxFailureCount: 0
|
||||
# Telemetry data synchronization is not time critical. Setting RequeueEvery to 55 minutes doesn't annoy the database too much.
|
||||
RequeueEvery: 3300s
|
||||
|
||||
Auth:
|
||||
SearchLimit: 1000
|
||||
@ -235,6 +270,8 @@ OIDC:
|
||||
Path: /oauth/v2/keys
|
||||
DeviceAuth:
|
||||
Path: /oauth/v2/device_authorization
|
||||
DefaultLoginURLV2: "/login?authRequest="
|
||||
DefaultLogoutURLV2: "/logout?post_logout_redirect="
|
||||
|
||||
SAML:
|
||||
ProviderConfig:
|
||||
@ -320,6 +357,51 @@ SystemDefaults:
|
||||
PasswordSaltCost: 14
|
||||
MachineKeySize: 2048
|
||||
ApplicationKeySize: 2048
|
||||
PasswordHasher:
|
||||
# Set hasher configuration for user passwords.
|
||||
# Passwords previously hashed with a different algorithm
|
||||
# or cost are automatically re-hashed using this config,
|
||||
# upon password validation or update.
|
||||
Hasher:
|
||||
Algorithm: "bcrypt"
|
||||
Cost: 14
|
||||
|
||||
# Other supported Hasher configs:
|
||||
|
||||
# Hasher:
|
||||
# Algorithm: "argon2i"
|
||||
# Time: 3
|
||||
# Memory: 32768
|
||||
# Threads: 4
|
||||
|
||||
# Hasher:
|
||||
# Algorithm: "argon2id"
|
||||
# Time: 1
|
||||
# Memory: 65536
|
||||
# Threads: 4
|
||||
|
||||
# Hasher:
|
||||
# Algorithm: "scrypt"
|
||||
# Cost: 15
|
||||
|
||||
# Verifiers enable the possibility of verifying
|
||||
# passwords that are previously hashed using another
|
||||
# algorithm then the Hasher.
|
||||
# This can be used when migrating from one algorithm to another,
|
||||
# or when importing users with hashed passwords.
|
||||
# There is no need to enable a Verifier of the same algorithm
|
||||
# as the Hasher.
|
||||
#
|
||||
# The format of the encoded hash strings must comply
|
||||
# with https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md
|
||||
# https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
|
||||
#
|
||||
# Supported verifiers: (uncomment to enable)
|
||||
# Verifiers:
|
||||
# - "argon2" # verifier for both argon2i and argon2id.
|
||||
# - "bcrypt"
|
||||
# - "md5"
|
||||
# - "scrypt"
|
||||
Multifactors:
|
||||
OTP:
|
||||
# If this is empty, the issuer is the requested domain
|
||||
@ -1037,6 +1119,10 @@ InternalAuthZ:
|
||||
- "org.create"
|
||||
- "policy.read"
|
||||
- "user.self.delete"
|
||||
- Role: "ORG_USER_SELF_MANAGER"
|
||||
Permissions:
|
||||
- "policy.read"
|
||||
- "user.self.delete"
|
||||
- Role: "PROJECT_OWNER_GLOBAL"
|
||||
Permissions:
|
||||
- "org.global.read"
|
||||
|
34
cmd/ready/config.go
Normal file
34
cmd/ready/config.go
Normal file
@ -0,0 +1,34 @@
|
||||
package ready
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/config/hook"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Log *logging.Config
|
||||
Port uint16
|
||||
}
|
||||
|
||||
func MustNewConfig(v *viper.Viper) *Config {
|
||||
config := new(Config)
|
||||
err := v.Unmarshal(config,
|
||||
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
)),
|
||||
)
|
||||
logging.OnError(err).Fatal("unable to read default config")
|
||||
|
||||
err = config.Log.SetLogger()
|
||||
logging.OnError(err).Fatal("unable to set logger")
|
||||
|
||||
return config
|
||||
}
|
37
cmd/ready/ready.go
Normal file
37
cmd/ready/ready.go
Normal file
@ -0,0 +1,37 @@
|
||||
package ready
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/zitadel/logging"
|
||||
)
|
||||
|
||||
func New() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "ready",
|
||||
Short: "Checks if zitadel is ready",
|
||||
Long: "Checks if zitadel is ready",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := MustNewConfig(viper.GetViper())
|
||||
if !ready(config) {
|
||||
os.Exit(1)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ready(config *Config) bool {
|
||||
res, err := http.Get("http://" + net.JoinHostPort("localhost", strconv.Itoa(int(config.Port))) + "/debug/ready")
|
||||
if err != nil {
|
||||
logging.WithError(err).Warn("ready check failed")
|
||||
return false
|
||||
}
|
||||
defer res.Body.Close()
|
||||
logging.WithFields("status", res.StatusCode).Warn("ready check failed")
|
||||
return res.StatusCode == 200
|
||||
}
|
@ -88,6 +88,9 @@ func (mig *FirstInstance) Execute(ctx context.Context) error {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -17,6 +17,7 @@ type externalConfigChange struct {
|
||||
currentExternalDomain string
|
||||
currentExternalSecure bool
|
||||
currentExternalPort uint16
|
||||
defaults systemdefaults.SystemDefaults
|
||||
}
|
||||
|
||||
func (mig *externalConfigChange) SetLastExecution(lastRun map[string]interface{}) {
|
||||
@ -35,7 +36,7 @@ func (mig *externalConfigChange) Check() bool {
|
||||
func (mig *externalConfigChange) Execute(ctx context.Context) error {
|
||||
cmd, err := command.StartCommands(
|
||||
mig.es,
|
||||
systemdefaults.SystemDefaults{},
|
||||
mig.defaults,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
@ -53,6 +54,9 @@ func (mig *externalConfigChange) Execute(ctx context.Context) error {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -104,6 +104,7 @@ func Setup(config *Config, steps *Steps, masterKey string) {
|
||||
ExternalDomain: config.ExternalDomain,
|
||||
ExternalPort: config.ExternalPort,
|
||||
ExternalSecure: config.ExternalSecure,
|
||||
defaults: config.SystemDefaults,
|
||||
},
|
||||
&projectionTables{
|
||||
es: eventstoreClient,
|
||||
|
@ -1,6 +1,8 @@
|
||||
package start
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
@ -25,6 +27,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/logstore"
|
||||
"github.com/zitadel/zitadel/internal/notification/handlers"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
static_config "github.com/zitadel/zitadel/internal/static/config"
|
||||
metrics "github.com/zitadel/zitadel/internal/telemetry/metrics/config"
|
||||
@ -58,13 +61,14 @@ type Config struct {
|
||||
EncryptionKeys *encryptionKeyConfig
|
||||
DefaultInstance command.InstanceSetup
|
||||
AuditLogRetention time.Duration
|
||||
SystemAPIUsers map[string]*internal_authz.SystemAPIUser
|
||||
SystemAPIUsers SystemAPIUsers
|
||||
CustomerPortal string
|
||||
Machine *id.Config
|
||||
Actions *actions.Config
|
||||
Eventstore *eventstore.Config
|
||||
LogStore *logstore.Configs
|
||||
Quotas *QuotasConfig
|
||||
Telemetry *handlers.TelemetryPusherConfig
|
||||
}
|
||||
|
||||
type QuotasConfig struct {
|
||||
@ -83,6 +87,7 @@ func MustNewConfig(v *viper.Viper) *Config {
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
database.DecodeHook,
|
||||
actions.HTTPConfigDecodeHook,
|
||||
systemAPIUsersDecodeHook,
|
||||
)),
|
||||
)
|
||||
logging.OnError(err).Fatal("unable to read config")
|
||||
@ -114,3 +119,22 @@ type encryptionKeyConfig struct {
|
||||
CSRFCookieKeyID string
|
||||
UserAgentCookieKeyID string
|
||||
}
|
||||
|
||||
type SystemAPIUsers map[string]*internal_authz.SystemAPIUser
|
||||
|
||||
func systemAPIUsersDecodeHook(from, to reflect.Value) (any, error) {
|
||||
if to.Type() != reflect.TypeOf(SystemAPIUsers{}) {
|
||||
return from.Interface(), nil
|
||||
}
|
||||
|
||||
data, ok := from.Interface().(string)
|
||||
if !ok {
|
||||
return from.Interface(), nil
|
||||
}
|
||||
users := make(SystemAPIUsers)
|
||||
err := json.Unmarshal([]byte(data), &users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import (
|
||||
"time"
|
||||
|
||||
clockpkg "github.com/benbjohnson/clock"
|
||||
"github.com/common-nighthawk/go-figure"
|
||||
"github.com/fatih/color"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@ -22,6 +24,7 @@ import (
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
|
||||
"github.com/zitadel/zitadel/cmd/build"
|
||||
"github.com/zitadel/zitadel/cmd/key"
|
||||
cmd_tls "github.com/zitadel/zitadel/cmd/tls"
|
||||
"github.com/zitadel/zitadel/internal/actions"
|
||||
@ -32,6 +35,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/admin"
|
||||
"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/session/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/settings/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/system"
|
||||
@ -110,6 +114,8 @@ type Server struct {
|
||||
}
|
||||
|
||||
func startZitadel(config *Config, masterKey string, server chan<- *Server) error {
|
||||
showBasicInformation(config)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
dbClient, err := database.Connect(config.Database, false)
|
||||
@ -192,6 +198,9 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
&http.Client{},
|
||||
permissionCheck,
|
||||
sessionTokenVerifier,
|
||||
config.OIDC.DefaultAccessTokenLifetime,
|
||||
config.OIDC.DefaultRefreshTokenExpiration,
|
||||
config.OIDC.DefaultRefreshTokenIdleExpiration,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start commands: %w", err)
|
||||
@ -207,14 +216,14 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
return err
|
||||
}
|
||||
|
||||
usageReporter := logstore.UsageReporterFunc(commands.ReportUsage)
|
||||
usageReporter := logstore.UsageReporterFunc(commands.ReportQuotaUsage)
|
||||
actionsLogstoreSvc := logstore.New(queries, usageReporter, actionsExecutionDBEmitter, actionsExecutionStdoutEmitter)
|
||||
if actionsLogstoreSvc.Enabled() {
|
||||
logging.Warn("execution logs are currently in beta")
|
||||
}
|
||||
actions.SetLogstoreService(actionsLogstoreSvc)
|
||||
|
||||
notification.Start(ctx, config.Projections.Customizations["notifications"], config.Projections.Customizations["notificationsquotas"], config.ExternalPort, config.ExternalSecure, commands, queries, eventstoreClient, assets.AssetAPIFromDomain(config.ExternalSecure, config.ExternalPort), config.SystemDefaults.Notifications.FileSystemPath, keys.User, keys.SMTP, keys.SMS)
|
||||
notification.Start(ctx, config.Projections.Customizations["notifications"], config.Projections.Customizations["notificationsquotas"], config.Projections.Customizations["telemetry"], *config.Telemetry, config.ExternalDomain, config.ExternalPort, config.ExternalSecure, commands, queries, eventstoreClient, assets.AssetAPIFromDomain(config.ExternalSecure, config.ExternalPort), config.SystemDefaults.Notifications.FileSystemPath, keys.User, keys.SMTP, keys.SMS)
|
||||
|
||||
router := mux.NewRouter()
|
||||
tlsConfig, err := config.TLS.Config()
|
||||
@ -344,6 +353,7 @@ func startAPIs(
|
||||
if err := apis.RegisterService(ctx, session.CreateServer(commands, queries, permissionCheck)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := apis.RegisterService(ctx, settings.CreateServer(commands, queries, config.ExternalSecure)); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -397,6 +407,11 @@ func startAPIs(
|
||||
apis.RegisterHandlerOnPrefix(login.HandlerPrefix, l.Handler())
|
||||
apis.HandleFunc(login.EndpointDeviceAuth, login.RedirectDeviceAuthToPrefix)
|
||||
|
||||
// After OIDC provider so that the callback endpoint can be used
|
||||
if err := apis.RegisterService(ctx, oidc_v2.CreateServer(commands, queries, oidcProvider, config.ExternalSecure)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// handle grpc at last to be able to handle the root, because grpc and gateway require a lot of different prefixes
|
||||
apis.RouteGRPC()
|
||||
return nil
|
||||
@ -444,3 +459,29 @@ func shutdownServer(ctx context.Context, server *http.Server) error {
|
||||
logging.New().Info("server shutdown gracefully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func showBasicInformation(startConfig *Config) {
|
||||
fmt.Println(color.MagentaString(figure.NewFigure("Zitadel", "", true).String()))
|
||||
http := "http"
|
||||
if startConfig.TLS.Enabled || startConfig.ExternalSecure {
|
||||
http = "https"
|
||||
}
|
||||
|
||||
consoleURL := fmt.Sprintf("%s://%s:%v/ui/console\n", http, startConfig.ExternalDomain, startConfig.ExternalPort)
|
||||
healthCheckURL := fmt.Sprintf("%s://%s:%v/debug/healthz\n", http, startConfig.ExternalDomain, startConfig.ExternalPort)
|
||||
|
||||
insecure := !startConfig.TLS.Enabled && !startConfig.ExternalSecure
|
||||
|
||||
fmt.Printf(" ===============================================================\n\n")
|
||||
fmt.Printf(" Version : %s\n", build.Version())
|
||||
fmt.Printf(" TLS enabled : %v\n", startConfig.TLS.Enabled)
|
||||
fmt.Printf(" External Secure : %v\n", startConfig.ExternalSecure)
|
||||
fmt.Printf(" Console URL : %s", color.BlueString(consoleURL))
|
||||
fmt.Printf(" Health Check URL : %s", color.BlueString(healthCheckURL))
|
||||
if insecure {
|
||||
fmt.Printf("\n %s: you're using plain http without TLS. Be aware this is \n", color.RedString("Warning"))
|
||||
fmt.Printf(" not a secure setup and should only be used for test systems. \n")
|
||||
fmt.Printf(" Visit: %s \n", color.CyanString("https://zitadel.com/docs/self-hosting/manage/tls_modes"))
|
||||
}
|
||||
fmt.Printf("\n ===============================================================\n\n")
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/zitadel/zitadel/cmd/build"
|
||||
"github.com/zitadel/zitadel/cmd/initialise"
|
||||
"github.com/zitadel/zitadel/cmd/key"
|
||||
"github.com/zitadel/zitadel/cmd/ready"
|
||||
"github.com/zitadel/zitadel/cmd/setup"
|
||||
"github.com/zitadel/zitadel/cmd/start"
|
||||
)
|
||||
@ -55,6 +56,7 @@ func New(out io.Writer, in io.Reader, args []string, server chan<- *start.Server
|
||||
start.NewStartFromInit(server),
|
||||
start.NewStartFromSetup(server),
|
||||
key.New(),
|
||||
ready.New(),
|
||||
)
|
||||
|
||||
cmd.InitDefaultVersionFlag()
|
||||
|
@ -26,6 +26,9 @@
|
||||
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
|
||||
"styles": ["src/styles.scss"],
|
||||
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"],
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": ["node_modules"]
|
||||
},
|
||||
"allowedCommonJsDependencies": [
|
||||
"fast-sha256",
|
||||
"buffer",
|
||||
@ -46,7 +49,8 @@
|
||||
"inline": false
|
||||
},
|
||||
"styles": {
|
||||
"inlineCritical": false
|
||||
"inlineCritical": false,
|
||||
"minify": false
|
||||
}
|
||||
},
|
||||
"budgets": [
|
||||
|
16940
console/package-lock.json
generated
16940
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
import { CommonModule, registerLocaleData } from '@angular/common';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import localePt from '@angular/common/locales/pt';
|
||||
import localeBg from '@angular/common/locales/bg';
|
||||
import localeDe from '@angular/common/locales/de';
|
||||
import localeEn from '@angular/common/locales/en';
|
||||
@ -7,6 +8,7 @@ import localeEs from '@angular/common/locales/es';
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
import localeIt from '@angular/common/locales/it';
|
||||
import localeJa from '@angular/common/locales/ja';
|
||||
import localeMk from '@angular/common/locales/mk';
|
||||
import localePl from '@angular/common/locales/pl';
|
||||
import localeZh from '@angular/common/locales/zh';
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
@ -83,6 +85,10 @@ registerLocaleData(localeZh);
|
||||
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/zh.json'));
|
||||
registerLocaleData(localeBg);
|
||||
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/bg.json'));
|
||||
registerLocaleData(localePt);
|
||||
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/pt.json'));
|
||||
registerLocaleData(localeMk);
|
||||
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/mk.json'));
|
||||
|
||||
export class WebpackTranslateLoader implements TranslateLoader {
|
||||
getTranslation(lang: string): Observable<any> {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog';
|
||||
import { MatLegacyTableDataSource as MatTableDataSource } from '@angular/material/legacy-table';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import {
|
||||
ListProvidersRequest as AdminListProvidersRequest,
|
||||
ListProvidersResponse as AdminListProvidersResponse,
|
||||
@ -42,7 +42,7 @@ import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
|
||||
templateUrl: './idp-table.component.html',
|
||||
styleUrls: ['./idp-table.component.scss'],
|
||||
})
|
||||
export class IdpTableComponent implements OnInit {
|
||||
export class IdpTableComponent implements OnInit, OnDestroy {
|
||||
@Input() public serviceType!: PolicyComponentServiceType;
|
||||
@Input() service!: AdminService | ManagementService;
|
||||
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
|
||||
@ -62,6 +62,8 @@ export class IdpTableComponent implements OnInit {
|
||||
public IDPStylingType: any = IDPStylingType;
|
||||
public loginPolicy!: LoginPolicy.AsObject;
|
||||
|
||||
private reloadIDPs$: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private workflowService: OverlayWorkflowService,
|
||||
public translate: TranslateService,
|
||||
@ -72,6 +74,16 @@ export class IdpTableComponent implements OnInit {
|
||||
this.selection.changed.subscribe(() => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
|
||||
this.reloadIDPs$.subscribe(() => {
|
||||
this.getIdps()
|
||||
.then((resp) => {
|
||||
this.idps = resp;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -85,6 +97,10 @@ export class IdpTableComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.reloadIDPs$.complete();
|
||||
}
|
||||
|
||||
public isAllSelected(): boolean {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.data.length;
|
||||
@ -332,13 +348,7 @@ export class IdpTableComponent implements OnInit {
|
||||
this.toast.showInfo('IDP.TOAST.ADDED', true);
|
||||
|
||||
setTimeout(() => {
|
||||
this.getIdps()
|
||||
.then((resp) => {
|
||||
this.idps = resp;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
});
|
||||
})
|
||||
@ -351,13 +361,7 @@ export class IdpTableComponent implements OnInit {
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.ADDED', true);
|
||||
setTimeout(() => {
|
||||
this.getIdps()
|
||||
.then((resp) => {
|
||||
this.idps = resp;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -370,13 +374,7 @@ export class IdpTableComponent implements OnInit {
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.ADDED', true);
|
||||
setTimeout(() => {
|
||||
this.getIdps()
|
||||
.then((resp) => {
|
||||
this.idps = resp;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -385,72 +383,71 @@ export class IdpTableComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public removeIdp(idp: Provider.AsObject): Promise<any> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if (this.isDefault) {
|
||||
return this.addLoginPolicy()
|
||||
.then(() => {
|
||||
this.loginPolicy.isDefault = false;
|
||||
return (this.service as ManagementService)
|
||||
public removeIdp(idp: Provider.AsObject): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.CONTINUE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'IDP.REMOVE_WARN_TITLE',
|
||||
descriptionKey: 'IDP.REMOVE_WARN_DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if (this.isDefault) {
|
||||
this.addLoginPolicy()
|
||||
.then(() => {
|
||||
this.loginPolicy.isDefault = false;
|
||||
return (this.service as ManagementService)
|
||||
.removeIDPFromLoginPolicy(idp.id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.REMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
} else {
|
||||
(this.service as ManagementService)
|
||||
.removeIDPFromLoginPolicy(idp.id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.REMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.getIdps()
|
||||
.then((resp) => {
|
||||
this.idps = resp;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
return (this.service as ManagementService)
|
||||
.removeIDPFromLoginPolicy(idp.id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.REMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.getIdps()
|
||||
.then((resp) => {
|
||||
this.idps = resp;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
(this.service as AdminService)
|
||||
.removeIDPFromLoginPolicy(idp.id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.REMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.reloadIDPs$.next();
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService)
|
||||
.removeIDPFromLoginPolicy(idp.id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('IDP.TOAST.REMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.getIdps()
|
||||
.then((resp) => {
|
||||
this.idps = resp;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}, 2000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public isEnabled(idp: Provider.AsObject): boolean {
|
||||
|
@ -99,6 +99,28 @@
|
||||
{{ 'POLICY.DATA.FORCEMFA' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div *ngIf="loginData" class="login-policy-row">
|
||||
<mat-checkbox
|
||||
card-actions
|
||||
class="login-policy-toggle"
|
||||
color="primary"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="loginData.forceMfaLocalOnly"
|
||||
[disabled]="
|
||||
([
|
||||
serviceType === PolicyComponentServiceType.ADMIN
|
||||
? 'iam.policy.write'
|
||||
: serviceType === PolicyComponentServiceType.MGMT
|
||||
? 'policy.write'
|
||||
: ''
|
||||
]
|
||||
| hasRole
|
||||
| async) === false
|
||||
"
|
||||
>
|
||||
{{ 'POLICY.DATA.FORCEMFALOCALONLY' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<cnsl-card class="max-card-width" *ngIf="loginData">
|
||||
<cnsl-factor-table
|
||||
[service]="service"
|
||||
|
@ -152,6 +152,7 @@ export class LoginPolicyComponent implements OnInit {
|
||||
mgmtreq.setAllowRegister(this.loginData.allowRegister);
|
||||
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||
mgmtreq.setForceMfaLocalOnly(this.loginData.forceMfaLocalOnly);
|
||||
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||
mgmtreq.setMultiFactorsList(this.loginData.multiFactorsList);
|
||||
@ -185,6 +186,7 @@ export class LoginPolicyComponent implements OnInit {
|
||||
mgmtreq.setAllowRegister(this.loginData.allowRegister);
|
||||
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||
mgmtreq.setForceMfa(this.loginData.forceMfa);
|
||||
mgmtreq.setForceMfaLocalOnly(this.loginData.forceMfaLocalOnly);
|
||||
mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
mgmtreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||
mgmtreq.setDisableLoginWithEmail(this.loginData.disableLoginWithEmail);
|
||||
@ -217,6 +219,7 @@ export class LoginPolicyComponent implements OnInit {
|
||||
adminreq.setAllowRegister(this.loginData.allowRegister);
|
||||
adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
|
||||
adminreq.setForceMfa(this.loginData.forceMfa);
|
||||
adminreq.setForceMfaLocalOnly(this.loginData.forceMfaLocalOnly);
|
||||
adminreq.setPasswordlessType(this.loginData.passwordlessType);
|
||||
adminreq.setHidePasswordReset(this.loginData.hidePasswordReset);
|
||||
adminreq.setDisableLoginWithEmail(this.loginData.disableLoginWithEmail);
|
||||
|
@ -16,6 +16,7 @@
|
||||
color="primary"
|
||||
class="cnsl-action-button"
|
||||
mat-raised-button
|
||||
data-e2e="create-project-role-button"
|
||||
>
|
||||
<mat-icon data-e2e="add-new-role" class="icon">add</mat-icon>
|
||||
<span>{{ 'ACTIONS.NEW' | translate }}</span>
|
||||
|
@ -0,0 +1,32 @@
|
||||
<form>
|
||||
<cnsl-form-field class="full-width">
|
||||
<cnsl-label>{{ 'PROJECT.GRANT.CREATE.SEL_ORG_FORMFIELD' | translate }}</cnsl-label>
|
||||
<input
|
||||
cnslInput
|
||||
type="text"
|
||||
placeholder="Organization XY"
|
||||
#nameInput
|
||||
[formControl]="myControl"
|
||||
[matAutocomplete]="auto"
|
||||
data-e2e="add-org-input"
|
||||
/>
|
||||
|
||||
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)" [displayWith]="displayFn">
|
||||
<mat-option *ngIf="isLoading" class="is-loading">
|
||||
<mat-spinner diameter="30"></mat-spinner>
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let org of filteredOrgs" [value]="org">
|
||||
<div class="org-option" data-e2e="org-option">
|
||||
<div class="org-option-column">
|
||||
<span>{{ org.name }}</span>
|
||||
<span class="fill-space"></span>
|
||||
<span class="smaller cnsl-secondary-text">{{ org.primaryDomain }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
<span class="org-autocomplete-target-desc">
|
||||
{{ 'PROJECT.GRANT.CREATE.SEL_ORG_DESC' | translate }}
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
</form>
|
@ -0,0 +1,38 @@
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.org-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.org-option-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
|
||||
span {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.smaller {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.org-autocomplete-target-desc {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
margin-top: 0.5rem;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { SearchOrgAutocompleteComponent } from './search-org-autocomplete.component';
|
||||
|
||||
describe('SearchOrgComponent', () => {
|
||||
let component: SearchOrgAutocompleteComponent;
|
||||
let fixture: ComponentFixture<SearchOrgAutocompleteComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SearchOrgAutocompleteComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SearchOrgAutocompleteComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,83 @@
|
||||
import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
||||
import { MatLegacyAutocomplete as MatAutocomplete } from '@angular/material/legacy-autocomplete';
|
||||
import { debounceTime, from, map, Subject, switchMap, takeUntil, tap } from 'rxjs';
|
||||
import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
|
||||
import { Org, OrgNameQuery, OrgQuery, OrgState, OrgStateQuery } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { AuthenticationService } from 'src/app/services/authentication.service';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-search-org-autocomplete',
|
||||
templateUrl: './search-org-autocomplete.component.html',
|
||||
styleUrls: ['./search-org-autocomplete.component.scss'],
|
||||
})
|
||||
export class SearchOrgAutocompleteComponent implements OnInit, OnDestroy {
|
||||
public selectable: boolean = true;
|
||||
public myControl: UntypedFormControl = new UntypedFormControl();
|
||||
public filteredOrgs: Array<Org.AsObject> = [];
|
||||
public isLoading: boolean = false;
|
||||
@ViewChild('auto') public matAutocomplete!: MatAutocomplete;
|
||||
@Output() public selectionChanged: EventEmitter<Org.AsObject> = new EventEmitter();
|
||||
|
||||
private unsubscribed$: Subject<void> = new Subject();
|
||||
constructor(public authService: AuthenticationService, private auth: GrpcAuthService) {
|
||||
this.myControl.valueChanges
|
||||
.pipe(
|
||||
takeUntil(this.unsubscribed$),
|
||||
debounceTime(200),
|
||||
tap(() => (this.isLoading = true)),
|
||||
switchMap((value) => {
|
||||
const stateQuery = new OrgQuery();
|
||||
const orgStateQuery = new OrgStateQuery();
|
||||
orgStateQuery.setState(OrgState.ORG_STATE_ACTIVE);
|
||||
stateQuery.setStateQuery(orgStateQuery);
|
||||
|
||||
let queries: OrgQuery[] = [stateQuery];
|
||||
|
||||
if (value) {
|
||||
const nameQuery = new OrgQuery();
|
||||
const orgNameQuery = new OrgNameQuery();
|
||||
orgNameQuery.setName(value);
|
||||
orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
nameQuery.setNameQuery(orgNameQuery);
|
||||
queries = [stateQuery, nameQuery];
|
||||
}
|
||||
|
||||
return from(this.auth.listMyProjectOrgs(undefined, 0, queries)).pipe(
|
||||
map((resp) => {
|
||||
return resp.resultList.sort((left, right) => left.name.localeCompare(right.name));
|
||||
}),
|
||||
);
|
||||
}),
|
||||
)
|
||||
.subscribe((returnValue) => {
|
||||
this.isLoading = false;
|
||||
this.filteredOrgs = returnValue;
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
const query = new OrgQuery();
|
||||
const orgStateQuery = new OrgStateQuery();
|
||||
orgStateQuery.setState(OrgState.ORG_STATE_ACTIVE);
|
||||
query.setStateQuery(orgStateQuery);
|
||||
|
||||
this.auth.listMyProjectOrgs(undefined, 0, [query]).then((orgs) => {
|
||||
this.filteredOrgs = orgs.resultList;
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.unsubscribed$.next();
|
||||
}
|
||||
|
||||
public displayFn(org?: Org.AsObject): string {
|
||||
return org ? `${org.name}` : '';
|
||||
}
|
||||
|
||||
public selected(event: MatAutocompleteSelectedEvent): void {
|
||||
this.selectionChanged.emit(event.option.value);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatLegacyAutocompleteModule as MatAutocompleteModule } from '@angular/material/legacy-autocomplete';
|
||||
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
|
||||
import { MatLegacyChipsModule as MatChipsModule } from '@angular/material/legacy-chips';
|
||||
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
|
||||
import { MatLegacySelectModule as MatSelectModule } from '@angular/material/legacy-select';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
|
||||
import { SearchOrgAutocompleteComponent } from './search-org-autocomplete.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SearchOrgAutocompleteComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatAutocompleteModule,
|
||||
MatChipsModule,
|
||||
MatButtonModule,
|
||||
InputModule,
|
||||
MatIconModule,
|
||||
ReactiveFormsModule,
|
||||
MatProgressSpinnerModule,
|
||||
FormsModule,
|
||||
TranslateModule,
|
||||
MatSelectModule,
|
||||
],
|
||||
exports: [SearchOrgAutocompleteComponent],
|
||||
})
|
||||
export class SearchOrgAutocompleteModule {}
|
@ -44,7 +44,7 @@
|
||||
|
||||
<div *ngIf="http">
|
||||
<p>HTTP TOKEN</p>
|
||||
<p class="entry">{{ http.url }}.txt</p>
|
||||
<p class="entry">{{ http.url }}</p>
|
||||
|
||||
<div class="btn-container">
|
||||
<button mat-stroked-button (click)="saveFile()" color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate }}</button>
|
||||
|
@ -6,20 +6,8 @@
|
||||
>
|
||||
<ng-container *ngIf="currentCreateStep === 1">
|
||||
<h1>{{ 'PROJECT.GRANT.CREATE.SEL_ORG' | translate }}</h1>
|
||||
<p>{{ 'PROJECT.GRANT.CREATE.SEL_ORG_DESC' | translate }}</p>
|
||||
|
||||
<form (ngSubmit)="searchOrg(domain.value)">
|
||||
<cnsl-form-field class="org-domain">
|
||||
<cnsl-label>{{ 'PROJECT.GRANT.CREATE.SEL_ORG_FORMFIELD' | translate }}</cnsl-label>
|
||||
<input cnslInput #domain />
|
||||
</cnsl-form-field>
|
||||
|
||||
<button [disabled]="domain.value.length === 0" color="primary" type="submit" class="domain-button" mat-raised-button>
|
||||
{{ 'PROJECT.GRANT.CREATE.SEL_ORG_BUTTON' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<span *ngIf="org"> {{ 'PROJECT.GRANT.CREATE.FOR_ORG' | translate }} {{ org.name }} </span>
|
||||
<cnsl-search-org-autocomplete class="block" (selectionChanged)="selectOrg($event)"> </cnsl-search-org-autocomplete>
|
||||
<span *ngIf="org"> {{ 'PROJECT.GRANT.CREATE.FOR_ORG' | translate }} {{ org.name }} - {{ org.primaryDomain }} </span>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentCreateStep === 2">
|
||||
@ -37,7 +25,15 @@
|
||||
|
||||
<div class="btn-container">
|
||||
<ng-container *ngIf="currentCreateStep === 1">
|
||||
<button [disabled]="!org" (click)="next()" color="primary" mat-raised-button class="big-button" cdkFocusInitial>
|
||||
<button
|
||||
[disabled]="!org"
|
||||
(click)="next()"
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="big-button"
|
||||
cdkFocusInitial
|
||||
data-e2e="project-grant-continue"
|
||||
>
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</ng-container>
|
||||
@ -46,7 +42,15 @@
|
||||
<button (click)="previous()" mat-stroked-button class="small-button">
|
||||
{{ 'ACTIONS.BACK' | translate }}
|
||||
</button>
|
||||
<button color="primary" [disabled]="!org" (click)="addGrant()" mat-raised-button class="big-button" cdkFocusInitial>
|
||||
<button
|
||||
color="primary"
|
||||
[disabled]="!org"
|
||||
(click)="addGrant()"
|
||||
mat-raised-button
|
||||
class="big-button"
|
||||
cdkFocusInitial
|
||||
data-e2e="save-project-grant-button"
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</ng-container>
|
||||
|
@ -88,6 +88,10 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
public selectOrg(org: Org.AsObject): void {
|
||||
this.org = org;
|
||||
}
|
||||
|
||||
public selectRoles(roles: string[]): void {
|
||||
this.rolesKeyList = roles;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { ProjectRolesTableModule } from 'src/app/modules/project-roles-table/project-roles-table.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { SearchOrgAutocompleteModule } from 'src/app/modules/search-org-autocomplete/search-org-autocomplete.module';
|
||||
import { ProjectGrantCreateRoutingModule } from './project-grant-create-routing.module';
|
||||
import { ProjectGrantCreateComponent } from './project-grant-create.component';
|
||||
|
||||
@ -38,6 +39,7 @@ import { ProjectGrantCreateComponent } from './project-grant-create.component';
|
||||
MatProgressSpinnerModule,
|
||||
FormsModule,
|
||||
TranslateModule,
|
||||
SearchOrgAutocompleteModule,
|
||||
],
|
||||
})
|
||||
export default class ProjectGrantCreateModule {}
|
||||
|
@ -17,6 +17,7 @@
|
||||
class="cnsl-action-button"
|
||||
mat-raised-button
|
||||
[routerLink]="['/projects', projectId, 'projectgrants', 'create']"
|
||||
data-e2e="create-project-grant-button"
|
||||
>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
<span>{{ 'ACTIONS.NEW' | translate }}</span>
|
||||
|
@ -12,15 +12,15 @@
|
||||
<div class="newrole">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'PROJECT.ROLE.KEY' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="key" />
|
||||
<input cnslInput formControlName="key" data-e2e="role-key-input" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'PROJECT.ROLE.DISPLAY_NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="displayName" />
|
||||
<input cnslInput formControlName="displayName" data-e2e="role-display-name-input" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'PROJECT.ROLE.GROUP' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="group" />
|
||||
<input cnslInput formControlName="group" data-e2e="role-group-input" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<button
|
||||
@ -36,7 +36,14 @@
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<button class="add-line-btn" color="primary" type="button" mat-stroked-button (click)="addEntry()">
|
||||
<button
|
||||
class="add-line-btn"
|
||||
color="primary"
|
||||
type="button"
|
||||
data-e2e="new-project-role-button"
|
||||
mat-stroked-button
|
||||
(click)="addEntry()"
|
||||
>
|
||||
{{ 'PROJECT.ROLE.ADDNEWLINE' | translate }}
|
||||
</button>
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
export const supportedLanguages = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh', 'bg'];
|
||||
export const supportedLanguagesRegexp: RegExp = /de|en|es|fr|it|ja|pl|zh|bg/;
|
||||
export const supportedLanguages = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh', 'bg', 'pt', 'mk'];
|
||||
export const supportedLanguagesRegexp: RegExp = /de|en|es|fr|it|ja|pl|zh|bg|pt|mk/;
|
||||
export const fallbackLanguage: string = 'en';
|
||||
|
@ -1034,7 +1034,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Полски",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "SMTP настройки",
|
||||
@ -1234,7 +1236,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Полски",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "Проверката на имейл е извършена",
|
||||
@ -1333,6 +1337,8 @@
|
||||
"ALLOWREGISTER_DESC": "Ако опцията е избрана, в входа се появява допълнителна стъпка за регистрация на потребител.",
|
||||
"FORCEMFA": "Сила MFA",
|
||||
"FORCEMFA_DESC": "Ако опцията е избрана, потребителите трябва да конфигурират втори фактор за влизане.",
|
||||
"FORCEMFALOCALONLY": "Принудително MFA за локални потребители",
|
||||
"FORCEMFALOCALONLY_DESC": "Ако е избрана опцията, локалните удостоверени потребители трябва да конфигурират втори фактор за влизане.",
|
||||
"HIDEPASSWORDRESET": "Скриване на нулиране на парола",
|
||||
"HIDEPASSWORDRESET_DESC": "Ако опцията е избрана, потребителят не може да нулира паролата си в процеса на влизане.",
|
||||
"HIDELOGINNAMESUFFIX": "Скриване на суфикса на името за влизане",
|
||||
@ -1500,12 +1506,11 @@
|
||||
"SEL_PROJECT": "Търсене на проект",
|
||||
"SEL_ROLES": "Изберете ролите, които искате да бъдат добавени към субсидията",
|
||||
"SEL_USER": "Изберете потребители",
|
||||
"SEL_ORG": "Задайте домейна",
|
||||
"SEL_ORG_DESC": "Въведете пълния домейн, за да посочите организацията, която да предоставите.",
|
||||
"ORG_TITLE": "Организация",
|
||||
"SEL_ORG": "Търсете организация",
|
||||
"SEL_ORG_DESC": "Потърсете организацията за отпускане.",
|
||||
"ORG_DESCRIPTION": "На път сте да предоставите потребител за организацията {{name}}.",
|
||||
"ORG_DESCRIPTION_DESC": "Превключете контекста в заглавката по-горе, за да предоставите потребител за друга организация.",
|
||||
"SEL_ORG_FORMFIELD": "Пълен домейн",
|
||||
"SEL_ORG_FORMFIELD": "Организация",
|
||||
"SEL_ORG_BUTTON": "Организация на търсенето",
|
||||
"FOR_ORG": "Безвъзмездната помощ е създадена за:"
|
||||
},
|
||||
@ -1777,6 +1782,8 @@
|
||||
"DELETE": "Изтрий",
|
||||
"DELETE_TITLE": "Изтриване на IDP",
|
||||
"DELETE_DESCRIPTION": "На път сте да изтриете доставчик на самоличност. ",
|
||||
"REMOVE_WARN_TITLE": "Премахване на IDP",
|
||||
"REMOVE_WARN_DESCRIPTION": "На път сте да премахнете доставчик на самоличност. Това ще премахне избора на наличен IDP за вашите потребители и вече регистрираните потребители няма да могат да влязат отново. Сигурни ли сте, че ще продължите?",
|
||||
"DELETE_SELECTION_TITLE": "Изтриване на IDP",
|
||||
"DELETE_SELECTION_DESCRIPTION": "На път сте да изтриете доставчик на самоличност. ",
|
||||
"EMPTY": "Няма наличен IDP",
|
||||
@ -2099,7 +2106,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Полски",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "португалски",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "Добавяне на мениджър",
|
||||
|
@ -1040,7 +1040,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "SMTP Einstellungen",
|
||||
@ -1240,7 +1242,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "Email Verification erfolgreich",
|
||||
@ -1337,8 +1341,10 @@
|
||||
"ALLOWUSERNAMEPASSWORD_DESC": "Der konventionelle Login mit Benutzername und Passwort wird erlaubt.",
|
||||
"ALLOWEXTERNALIDP_DESC": "Der Login wird für die darunter liegenden Identitätsanbieter erlaubt.",
|
||||
"ALLOWREGISTER_DESC": "Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.",
|
||||
"FORCEMFA": "Mfa erzwingen",
|
||||
"FORCEMFA": "MFA erzwingen",
|
||||
"FORCEMFA_DESC": "Ist die Option gewählt, müssen Benutzer einen zweiten Faktor für den Login verwenden.",
|
||||
"FORCEMFALOCALONLY": "MFA für lokale Users erzwingen",
|
||||
"FORCEMFALOCALONLY_DESC": "Ist die Option gewählt, müssen lokal authentifizierte Benutzer einen zweiten Faktor für den Login verwenden.",
|
||||
"HIDEPASSWORDRESET": "Passwort vergessen ausblenden",
|
||||
"HIDEPASSWORDRESET_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link.",
|
||||
"HIDELOGINNAMESUFFIX": "Loginname Suffix ausblenden",
|
||||
@ -1506,13 +1512,11 @@
|
||||
"SEL_ROLES": "Selektiere die gewünschten Rollen für das Erstellen der Berechtigung.",
|
||||
"SEL_PROJECT": "Suchen nach Projekt",
|
||||
"SEL_USER": "Benutzer auswählen",
|
||||
"SEL_ORG": "Suchen nach Domain",
|
||||
"SEL_ORG_DESC": "Gebe die vollständige Domain ein, um Resultate zu erhalten.",
|
||||
"ORG_TITLE": "Organisation",
|
||||
"SEL_ORG": "Durchsuchen Sie eine Organisation",
|
||||
"SEL_ORG_DESC": "Suchen Sie nach der zu gewährenden Organisation.",
|
||||
"ORG_DESCRIPTION": "Du bist im Begriff, einen Benutzer für die Organisation {{name}} zu berechtigen.",
|
||||
"ORG_DESCRIPTION_DESC": "Wechsle den Kontext, um die Organisation zu wechseln.",
|
||||
"SEL_ORG_FORMFIELD": "Vollständige Domain",
|
||||
"SEL_ORG_BUTTON": "Suche Organisation",
|
||||
"SEL_ORG_FORMFIELD": "Organisation",
|
||||
"FOR_ORG": "Die Berechtigung wird erstellt für:"
|
||||
},
|
||||
"DETAIL": {
|
||||
@ -1784,6 +1788,8 @@
|
||||
"DELETE": "Löschen",
|
||||
"DELETE_TITLE": "IDP löschen",
|
||||
"DELETE_DESCRIPTION": "Sie sind im Begriff einen Identitätsanbieter zu löschen. Die dadurch hervorgerufenen Änderungen sind unwiderruflich. Wollen Sie dies wirklich tun?",
|
||||
"REMOVE_WARN_TITLE": "IDP entfernen?",
|
||||
"REMOVE_WARN_DESCRIPTION": "Sie sind dabei, einen Identitätsanbieter zu entfernen. Dadurch wird die Auswahl des verfügbaren IDP für Ihre Benutzer entfernt und bereits registrierte Benutzer können sich nicht erneut anmelden. Wollen Sie wirklich fortfahren?",
|
||||
"DELETE_SELECTION_TITLE": "Identitätsanbieter löschen",
|
||||
"DELETE_SELECTION_DESCRIPTION": "Sie sind im Begriff mehrere Identitätsanbieter zu löschen. Die dadurch hervorgerufenen Änderungen sind unwiderruflich. Wollen Sie dies wirklich tun?",
|
||||
"EMPTY": "Kein IDP vorhanden",
|
||||
@ -2109,7 +2115,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "Manager hinzufügen",
|
||||
|
@ -486,8 +486,8 @@
|
||||
"CHANGEUSERNAME": "modify",
|
||||
"CHANGEUSERNAME_TITLE": "Change username",
|
||||
"CHANGEUSERNAME_DESC": "Enter the new name in the field below.",
|
||||
"FIRSTNAME": "First Name",
|
||||
"LASTNAME": "Last Name",
|
||||
"FIRSTNAME": "Given Name",
|
||||
"LASTNAME": "Family Name",
|
||||
"NICKNAME": "Nickname",
|
||||
"DISPLAYNAME": "Display Name",
|
||||
"PREFERRED_LANGUAGE": "Language",
|
||||
@ -1041,7 +1041,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "SMTP Settings",
|
||||
@ -1241,7 +1243,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "Email verification done",
|
||||
@ -1294,8 +1298,8 @@
|
||||
"PC": "Password Change"
|
||||
},
|
||||
"CHIPS": {
|
||||
"firstname": "Firstname",
|
||||
"lastname": "Lastname",
|
||||
"firstname": "Given name",
|
||||
"lastname": "Family name",
|
||||
"code": "Code",
|
||||
"preferredLoginName": "Preferred Login Name",
|
||||
"displayName": "Displayname",
|
||||
@ -1340,6 +1344,8 @@
|
||||
"ALLOWREGISTER_DESC": "If the option is selected, an additional step for registering a user appears in the login.",
|
||||
"FORCEMFA": "Force MFA",
|
||||
"FORCEMFA_DESC": "If the option is selected, users have to configure a second factor for login.",
|
||||
"FORCEMFALOCALONLY": "Force MFA for local authenticated users",
|
||||
"FORCEMFALOCALONLY_DESC": "If the option is selected, local authenticated users have to configure a second factor for login.",
|
||||
"HIDEPASSWORDRESET": "Hide Password reset",
|
||||
"HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.",
|
||||
"HIDELOGINNAMESUFFIX": "Hide Loginname suffix",
|
||||
@ -1499,7 +1505,7 @@
|
||||
"GRANT": {
|
||||
"EMPTY": "No granted organization.",
|
||||
"TITLE": "Project Grants",
|
||||
"DESCRIPTION": "Allow an other organization to use your project.",
|
||||
"DESCRIPTION": "Allow another organization to use your project.",
|
||||
"EDITTITLE": "Edit roles",
|
||||
"CREATE": {
|
||||
"TITLE": "Create Organization Grant",
|
||||
@ -1507,13 +1513,11 @@
|
||||
"SEL_PROJECT": "Search for a project",
|
||||
"SEL_ROLES": "Select the roles you want to be added to the grant",
|
||||
"SEL_USER": "Select users",
|
||||
"SEL_ORG": "Set the domain",
|
||||
"SEL_ORG_DESC": "Enter the complete domain to specify the organization to grant.",
|
||||
"ORG_TITLE": "Organization",
|
||||
"SEL_ORG": "Search an organization",
|
||||
"SEL_ORG_DESC": "Search the organization to grant.",
|
||||
"ORG_DESCRIPTION": "You are about to grant a user for the organization {{name}}.",
|
||||
"ORG_DESCRIPTION_DESC": "Switch the context in the header above to grant a user for another organization.",
|
||||
"SEL_ORG_FORMFIELD": "Complete Domain",
|
||||
"SEL_ORG_BUTTON": "Search Organization",
|
||||
"SEL_ORG_FORMFIELD": "Organization",
|
||||
"FOR_ORG": "The grant is created for:"
|
||||
},
|
||||
"DETAIL": {
|
||||
@ -1766,8 +1770,8 @@
|
||||
"DISPLAYNAMEATTRIBUTE": "Displayname attribute",
|
||||
"EMAILATTRIBUTEATTRIBUTE": "Email attribute",
|
||||
"EMAILVERIFIEDATTRIBUTE": "Email verified attribute",
|
||||
"FIRSTNAMEATTRIBUTE": "Firstname attribute",
|
||||
"LASTNAMEATTRIBUTE": "Lastname attribute",
|
||||
"FIRSTNAMEATTRIBUTE": "Given name attribute",
|
||||
"LASTNAMEATTRIBUTE": "Family name attribute",
|
||||
"NICKNAMEATTRIBUTE": "Nickname attribute",
|
||||
"PHONEATTRIBUTE": "Phone attribute",
|
||||
"PHONEVERIFIEDATTRIBUTE": "Phone verified attribute",
|
||||
@ -1784,6 +1788,8 @@
|
||||
"DELETE": "Delete",
|
||||
"DELETE_TITLE": "Delete IDP",
|
||||
"DELETE_DESCRIPTION": "You are about to delete an identity provider. The resulting changes are irrevocable. Do you really want to do this?",
|
||||
"REMOVE_WARN_TITLE": "Remove IDP",
|
||||
"REMOVE_WARN_DESCRIPTION": "You are about to remove an identity provider. This will remove the selection of the available IDP for your users and already registered users won't be able to login again. Are you sure to continue?",
|
||||
"DELETE_SELECTION_TITLE": "Delete IDP",
|
||||
"DELETE_SELECTION_DESCRIPTION": "You are about to delete an identity provider. The resulting changes are irrevocable. Do you really want to do this?",
|
||||
"EMPTY": "No IDP available",
|
||||
@ -2106,7 +2112,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "Add a Manager",
|
||||
|
@ -1041,7 +1041,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "Ajustes SMTP",
|
||||
@ -1241,7 +1243,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "Verificación de email realizada",
|
||||
@ -1340,6 +1344,8 @@
|
||||
"ALLOWREGISTER_DESC": "Si esta opción es seleccionada, aparece un paso adicional durante el inicio de sesión para registrar un usuario.",
|
||||
"FORCEMFA": "Forzar MFA",
|
||||
"FORCEMFA_DESC": "Si esta opción es seleccionada, los usuarios tendrán que configurar un doble factor para iniciar sesión.",
|
||||
"FORCEMFALOCALONLY": "Forzar MFA para usuarios locales",
|
||||
"FORCEMFALOCALONLY_DESC": "Si esta opción es seleccionada, los usuarios autenticados localmente tendrán que configurar un doble factor para iniciar sesión",
|
||||
"HIDEPASSWORDRESET": "Ocultar restablecer contraseña",
|
||||
"HIDEPASSWORDRESET_DESC": "Si esta opción es seleccionada, el usuario no podrá restablecer su contraseña en el proceso de inicio de sesión.",
|
||||
"HIDELOGINNAMESUFFIX": "Ocultar sufijo del nombre de inicio de sesión",
|
||||
@ -1507,13 +1513,11 @@
|
||||
"SEL_PROJECT": "Buscar un proyecto",
|
||||
"SEL_ROLES": "Selecciona los roles que quieres que se añadan a la concesión",
|
||||
"SEL_USER": "Seleccionar usuarios",
|
||||
"SEL_ORG": "Establecer el dominio",
|
||||
"SEL_ORG_DESC": "Introduce el dominio completo para especificar la organización concesionaria.",
|
||||
"ORG_TITLE": "Organización",
|
||||
"SEL_ORG": "Buscar una organización",
|
||||
"SEL_ORG_DESC": "Busca la organización concesionaria.",
|
||||
"ORG_DESCRIPTION": "Estás a punto de conceder acceso a un usuario para la organización {{name}}.",
|
||||
"ORG_DESCRIPTION_DESC": "Cambia el contexto en la cabecera superior para conceder acceso a un usuario para otra organización.",
|
||||
"SEL_ORG_FORMFIELD": "Completar dominio",
|
||||
"SEL_ORG_BUTTON": "Buscar organización",
|
||||
"SEL_ORG_FORMFIELD": "Organización",
|
||||
"FOR_ORG": "La concesión se creó para:"
|
||||
},
|
||||
"DETAIL": {
|
||||
@ -1784,6 +1788,8 @@
|
||||
"DELETE": "Borrar",
|
||||
"DELETE_TITLE": "Borrar IDP",
|
||||
"DELETE_DESCRIPTION": "Estás a punto de borrar un proveedor de identidad. Los cambios son irrevocables. ¿Estás seguro de que quieres hacer esto?",
|
||||
"REMOVE_WARN_TITLE": "Quitar desplazado interno",
|
||||
"REMOVE_WARN_DESCRIPTION": "Está a punto de eliminar un proveedor de identidad. Esto eliminará la selección del IDP disponible para sus usuarios y los usuarios ya registrados no podrán volver a iniciar sesión. ¿Estás seguro de continuar?",
|
||||
"DELETE_SELECTION_TITLE": "Borrar IDP",
|
||||
"DELETE_SELECTION_DESCRIPTION": "Estás a punto de borrar un proveedor de identidad. Los cambios resultantes son irrevocables. ¿Estás seguro de que quieres hacer esto?",
|
||||
"EMPTY": "No hay IDP disponible",
|
||||
@ -2106,7 +2112,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "Añadir un Mánager",
|
||||
|
@ -1040,7 +1040,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "Paramètres SMTP",
|
||||
@ -1240,7 +1242,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "Vérification de l'email effectuée",
|
||||
@ -1339,6 +1343,8 @@
|
||||
"ALLOWREGISTER_DESC": "Si l'option est sélectionnée, une étape supplémentaire pour l'enregistrement d'un utilisateur apparaît dans la connexion.",
|
||||
"FORCEMFA": "Forcer MFA",
|
||||
"FORCEMFA_DESC": "Si l'option est sélectionnée, les utilisateurs doivent configurer un deuxième facteur pour la connexion.",
|
||||
"FORCEMFALOCALONLY": "Forcer MFA pour les utilisateurs locaux",
|
||||
"FORCEMFALOCALONLY_DESC": "Si l'option est sélectionnée, les utilisateurs locaux authentifiés doivent configurer un deuxième facteur pour la connexion.",
|
||||
"HIDEPASSWORDRESET": "Masquer la réinitialisation du mot de passe",
|
||||
"HIDEPASSWORDRESET_DESC": "Si l'option est sélectionnée, l'utilisateur ne peut pas réinitialiser son mot de passe lors du processus de connexion.",
|
||||
"HIDELOGINNAMESUFFIX": "Masquer le suffixe du nom de connexion",
|
||||
@ -1506,13 +1512,11 @@
|
||||
"SEL_PROJECT": "Rechercher un projet",
|
||||
"SEL_ROLES": "Sélectionnez les rôles que vous souhaitez ajouter à l'autorisation.",
|
||||
"SEL_USER": "Sélectionnez les utilisateurs",
|
||||
"SEL_ORG": "Définir le domaine",
|
||||
"SEL_ORG_DESC": "Entrez le domaine complet pour spécifier l'organisation à accorder.",
|
||||
"ORG_TITLE": "Organisation",
|
||||
"SEL_ORG": "Rechercher une organisation",
|
||||
"SEL_ORG_DESC": "Rechercher l'organisme à accorder",
|
||||
"ORG_DESCRIPTION": "Vous êtes sur le point d'accorder un utilisateur pour l'organisation{{name}}.",
|
||||
"ORG_DESCRIPTION_DESC": "Changez le contexte dans l'en-tête ci-dessus pour accorder un utilisateur pour une autre organisation.",
|
||||
"SEL_ORG_FORMFIELD": "Domaine complet",
|
||||
"SEL_ORG_BUTTON": "Rechercher une organisation",
|
||||
"SEL_ORG_FORMFIELD": "Organisation",
|
||||
"FOR_ORG": "L'autorisation est créée pour"
|
||||
},
|
||||
"DETAIL": {
|
||||
@ -1788,6 +1792,8 @@
|
||||
"DELETE": "Supprimer",
|
||||
"DELETE_TITLE": "Supprimer Idp",
|
||||
"DELETE_DESCRIPTION": "Vous êtes sur le point de supprimer un fournisseur d'identité. Les changements qui en résultent sont irrévocables. Voulez-vous vraiment le faire ?",
|
||||
"REMOVE_WARN_TITLE": "Supprimer le fournisseur d'identité",
|
||||
"REMOVE_WARN_DESCRIPTION": "Vous êtes sur le point de supprimer un fournisseur d'identité. Cela supprimera la sélection de l'IDP disponible pour vos utilisateurs et les utilisateurs déjà enregistrés ne pourront plus se reconnecter. Êtes-vous sûr de continuer ?",
|
||||
"DELETE_SELECTION_TITLE": "Supprimer Idp",
|
||||
"DELETE_SELECTION_DESCRIPTION": "Vous êtes sur le point de supprimer un fournisseur d'identité. Les changements qui en résultent sont irrévocables. Voulez-vous vraiment le faire ?",
|
||||
"EMPTY": "Aucun IDP disponible",
|
||||
@ -2098,7 +2104,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "Ajouter un manager",
|
||||
|
@ -1040,7 +1040,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "Impostazioni SMTP",
|
||||
@ -1240,7 +1242,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "Verifica dell'e-mail terminata con successo.",
|
||||
@ -1339,6 +1343,8 @@
|
||||
"ALLOWREGISTER_DESC": "Se l'opzione \u00e8 selezionata, nel login apparirà un passo aggiuntivo per la registrazione di un utente.",
|
||||
"FORCEMFA": "Forza MFA",
|
||||
"FORCEMFA_DESC": "Se l'opzione \u00e8 selezionata, gli utenti devono configurare un secondo fattore per il login.",
|
||||
"FORCEMFALOCALONLY": "Forza MFA per gli utenti locali",
|
||||
"FORCEMFALOCALONLY_DESC": "Se l'opzione è selezionata, gli utenti locali autenticati devono configurare un secondo fattore per l'accesso.",
|
||||
"HIDEPASSWORDRESET": "Nascondi ripristino della password",
|
||||
"HIDEPASSWORDRESET_DESC": "Se l'opzione \u00e8 selezionata, l'utente non pu\u00f2 resettare la sua password nel interfaccia login.",
|
||||
"HIDELOGINNAMESUFFIX": "Nascondi il suffisso del nome utente",
|
||||
@ -1506,13 +1512,11 @@
|
||||
"SEL_PROJECT": "Cerca un progetto",
|
||||
"SEL_ROLES": "Seleziona i ruoli che vuoi aggiungere",
|
||||
"SEL_USER": "Seleziona utenti",
|
||||
"SEL_ORG": "Impostare il dominio",
|
||||
"SEL_ORG_DESC": "Inserisci il dominio completo per specificare l'organizzazione da concedere.",
|
||||
"ORG_TITLE": "Organizzazione",
|
||||
"SEL_ORG": "Cerca un'organizzazione",
|
||||
"SEL_ORG_DESC": "Cerca l'organizzazione da concedere.",
|
||||
"ORG_DESCRIPTION": "Stai per concedere un utente per l'organizzazione {{name}}.",
|
||||
"ORG_DESCRIPTION_DESC": "Cambia il contesto nell'intestazione qui sopra per concedere un utente per un'altra organizzazione.",
|
||||
"SEL_ORG_FORMFIELD": "Dominio completo",
|
||||
"SEL_ORG_BUTTON": "Ricerca organizzazione",
|
||||
"SEL_ORG_FORMFIELD": "Organizzazione",
|
||||
"FOR_ORG": "Org grant \u00e8 creato per:"
|
||||
},
|
||||
"DETAIL": {
|
||||
@ -1788,6 +1792,8 @@
|
||||
"DELETE": "Rimuovi IDP",
|
||||
"DELETE_TITLE": "Rimuovi IDP",
|
||||
"DELETE_DESCRIPTION": "Stai per rimuovere un fornitore di identit\u00e0. I cambiamenti risultanti sono irrevocabili. Vuoi davvero farlo?",
|
||||
"REMOVE_WARN_TITLE": "Rimuovi IDP",
|
||||
"REMOVE_WARN_DESCRIPTION": "Stai per rimuovere un provider di identità. Questo rimuoverà la selezione dell'IDP disponibile per i tuoi utenti e gli utenti già registrati non potranno accedere nuovamente. Sei sicuro di continuare?",
|
||||
"DELETE_SELECTION_TITLE": "Rimuovere IDP",
|
||||
"DELETE_SELECTION_DESCRIPTION": "Stai per rimuovere un fornitore di identit\u00e0. I cambiamenti risultanti sono irrevocabili. Vuoi davvero farlo?",
|
||||
"EMPTY": "Nessun IDP disponible",
|
||||
@ -2110,7 +2116,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "Aggiungi un manager",
|
||||
|
@ -1041,7 +1041,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "SMTP設定",
|
||||
@ -1236,7 +1238,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "メール認証が完了しました",
|
||||
@ -1335,6 +1339,8 @@
|
||||
"ALLOWREGISTER_DESC": "このオプションが選択されている場合、ユーザーを登録するための追加のステップがログインに表示されます。",
|
||||
"FORCEMFA": "MFAを強制する",
|
||||
"FORCEMFA_DESC": "このオプションが選択されている場合、ユーザーはログイン用の二要素認証を構成する必要があります。",
|
||||
"FORCEMFALOCALONLY": "ローカル ユーザーに MFA を強制する",
|
||||
"FORCEMFALOCALONLY_DESC": "オプションが選択されている場合、ローカル認証されたユーザーはログインの 2 番目の要素を構成する必要があります。",
|
||||
"HIDEPASSWORDRESET": "パスワードリセットを非表示にする",
|
||||
"HIDEPASSWORDRESET_DESC": "このオプションが選択されている場合、ユーザーはログイン過程ででパスワードをリセットできません。",
|
||||
"HIDELOGINNAMESUFFIX": "ログイン名の接尾辞を非表示にする",
|
||||
@ -1502,13 +1508,11 @@
|
||||
"SEL_PROJECT": "プロジェクトを検索する",
|
||||
"SEL_ROLES": "許可するロールを選択する",
|
||||
"SEL_USER": "ユーザーを選択する",
|
||||
"SEL_ORG": "ドメインを設定する",
|
||||
"SEL_ORG_DESC": "完全なドメインを入力して、アクセスを許可する組織を指定する。",
|
||||
"ORG_TITLE": "組織",
|
||||
"SEL_ORG": "組織を検索する",
|
||||
"SEL_ORG_DESC": "付与する組織を検索する",
|
||||
"ORG_DESCRIPTION": "組織 {{name}} にユーザーをグラントします。",
|
||||
"ORG_DESCRIPTION_DESC": "上記のヘッダーのコンテキストを切り替えることで、別組織のユーザーにグラントできます。",
|
||||
"SEL_ORG_FORMFIELD": "完全なドメイン",
|
||||
"SEL_ORG_BUTTON": "組織を検索する",
|
||||
"SEL_ORG_FORMFIELD": "組織",
|
||||
"FOR_ORG": "グラントが以下に対して作成されます:"
|
||||
},
|
||||
"DETAIL": {
|
||||
@ -1779,6 +1783,8 @@
|
||||
"DELETE": "削除",
|
||||
"DELETE_TITLE": "IDPの削除",
|
||||
"DELETE_DESCRIPTION": "IDプロバイダーを削除しようとしています。変更は取消できません。本当によろしいですか?",
|
||||
"REMOVE_WARN_TITLE": "IDPを削除する",
|
||||
"REMOVE_WARN_DESCRIPTION": "ID プロバイダーを削除しようとしています。これにより、ユーザーが使用できる IDP の選択が削除され、すでに登録されているユーザーは再度ログインできなくなります。続けてもよろしいですか?",
|
||||
"DELETE_SELECTION_TITLE": "IDPの削除",
|
||||
"DELETE_SELECTION_DESCRIPTION": "IDプロバイダーを削除しようとしています。変更は取消できません。本当によろしいですか?",
|
||||
"EMPTY": "IDPは利用できません",
|
||||
@ -2101,7 +2107,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "マネージャーを追加する",
|
||||
|
2192
console/src/assets/i18n/mk.json
Normal file
2192
console/src/assets/i18n/mk.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1040,7 +1040,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "Ustawienia SMTP",
|
||||
@ -1240,7 +1242,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "Weryfikacja adresu e-mail zakończona",
|
||||
@ -1339,6 +1343,8 @@
|
||||
"ALLOWREGISTER_DESC": "Jeśli ta opcja jest zaznaczona, pojawi się dodatkowy krok rejestracji użytkownika w procesie logowania.",
|
||||
"FORCEMFA": "Wymuś MFA",
|
||||
"FORCEMFA_DESC": "Jeśli ta opcja jest zaznaczona, użytkownicy muszą skonfigurować drugi czynnik logowania.",
|
||||
"FORCEMFALOCALONLY": "Wymuś MFA dla lokalnych użytkowników",
|
||||
"FORCEMFALOCALONLY_DESC": "Jeśli ta opcja jest zaznaczona, lokalni uwierzytelnieni użytkownicy muszą skonfigurować drugi czynnik logowania.",
|
||||
"HIDEPASSWORDRESET": "Ukryj reset hasła",
|
||||
"HIDEPASSWORDRESET_DESC": "Jeśli ta opcja jest zaznaczona, użytkownik nie może zresetować swojego hasła w procesie logowania.",
|
||||
"HIDELOGINNAMESUFFIX": "Ukryj sufiks nazwy użytkownika",
|
||||
@ -1506,13 +1512,11 @@
|
||||
"SEL_PROJECT": "Wyszukaj projekt",
|
||||
"SEL_ROLES": "Wybierz role, które mają zostać dodane do udzielenia ",
|
||||
"SEL_USER": "Wybierz użytkowników",
|
||||
"SEL_ORG": "Ustaw domenę",
|
||||
"SEL_ORG_DESC": "Wprowadź pełną domenę, aby określić organizację, której chcesz udzielić dostępu.",
|
||||
"ORG_TITLE": "Organizacja",
|
||||
"SEL_ORG": "Wyszukaj organizację",
|
||||
"SEL_ORG_DESC": "Wyszukaj organizację, której chcesz przyznać.",
|
||||
"ORG_DESCRIPTION": "Masz zamiar udzielić użytkownikowi dostęp dla organizacji {{name}}.",
|
||||
"ORG_DESCRIPTION_DESC": "Przełącz kontekst w nagłówku powyżej, aby udzielić użytkownikowi dostępu dla innej organizacji.",
|
||||
"SEL_ORG_FORMFIELD": "Pełna domena",
|
||||
"SEL_ORG_BUTTON": "Wyszukaj organizację",
|
||||
"SEL_ORG_FORMFIELD": "Organizacja",
|
||||
"FOR_ORG": "Dostęp udzielany:"
|
||||
},
|
||||
"DETAIL": {
|
||||
@ -1788,6 +1792,8 @@
|
||||
"DELETE": "Usuń",
|
||||
"DELETE_TITLE": "Usuń dostawcę tożsamości",
|
||||
"DELETE_DESCRIPTION": "Zamierzasz usunąć dostawcę tożsamości. Spowoduje to nieodwracalne zmiany. Czy na pewno chcesz to zrobić?",
|
||||
"REMOVE_WARN_TITLE": "Usuń dostawcę tożsamości",
|
||||
"REMOVE_WARN_DESCRIPTION": "Zamierzasz usunąć dostawcę tożsamości. Spowoduje to usunięcie wyboru dostępnego dostawcy tożsamości dla Twoich użytkowników, a już zarejestrowani użytkownicy nie będą mogli zalogować się ponownie. Czy na pewno chcesz kontynuować?",
|
||||
"DELETE_SELECTION_TITLE": "Usuń dostawcę tożsamości",
|
||||
"DELETE_SELECTION_DESCRIPTION": "Zamierzasz usunąć dostawcę tożsamości. Spowoduje to nieodwracalne zmiany. Czy na pewno chcesz to zrobić?",
|
||||
"EMPTY": "Brak dostawcy tożsamości dostępnego",
|
||||
@ -2110,7 +2116,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "Dodaj managera",
|
||||
|
2190
console/src/assets/i18n/pt.json
Normal file
2190
console/src/assets/i18n/pt.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1040,7 +1040,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"SMTP": {
|
||||
"TITLE": "SMTP 设置",
|
||||
@ -1239,7 +1241,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"KEYS": {
|
||||
"emailVerificationDoneText": "电子邮件验证完成",
|
||||
@ -1338,6 +1342,8 @@
|
||||
"ALLOWREGISTER_DESC": "如果选择了该选项,登录中会出现一个用于注册用户的附加步骤。",
|
||||
"FORCEMFA": "强制使用 MFA",
|
||||
"FORCEMFA_DESC": "如果选择该选项,用户必须配置第二身份认证登录因素。",
|
||||
"FORCEMFALOCALONLY": "对本地用户强制执行 MFA",
|
||||
"FORCEMFALOCALONLY_DESC": "如果选择该选项,本地经过身份验证的用户必须配置第二个登录因素。",
|
||||
"HIDEPASSWORDRESET": "隐藏密码重置按钮",
|
||||
"HIDEPASSWORDRESET_DESC": "如果选择该选项,则用户无法在登录过程中重置其密码。",
|
||||
"HIDELOGINNAMESUFFIX": "隐藏登录名后缀",
|
||||
@ -1505,13 +1511,11 @@
|
||||
"SEL_PROJECT": "搜索项目",
|
||||
"SEL_ROLES": "选择要添加到授权中的角色",
|
||||
"SEL_USER": "选择一个或多个用户",
|
||||
"SEL_ORG": "选择一个组织",
|
||||
"SEL_ORG_DESC": "输入完整的域以指定要授予的组织。",
|
||||
"ORG_TITLE": "组织",
|
||||
"SEL_ORG": "搜索组织",
|
||||
"SEL_ORG_DESC": "搜索要授予的组织",
|
||||
"ORG_DESCRIPTION": "您即将授予组织 {{name}} 的用户。",
|
||||
"ORG_DESCRIPTION_DESC": "切换上面标题中的上下文以授予另一个组织的用户。",
|
||||
"SEL_ORG_FORMFIELD": "完整域名",
|
||||
"SEL_ORG_BUTTON": "搜索组织",
|
||||
"SEL_ORG_FORMFIELD": "组织",
|
||||
"FOR_ORG": "授予组织:"
|
||||
},
|
||||
"DETAIL": {
|
||||
@ -1787,6 +1791,8 @@
|
||||
"DELETE": "删除",
|
||||
"DELETE_TITLE": "删除 IDP",
|
||||
"DELETE_DESCRIPTION": "您即将删除身份提供者。由此产生的变化是不可撤销的。你真的想这样做吗?",
|
||||
"REMOVE_WARN_TITLE": "删除国内流离失所者",
|
||||
"REMOVE_WARN_DESCRIPTION": "您即将删除身份提供者。这将为您的用户删除可用 IDP 的选择,并且已经注册的用户将无法再次登录。您确定要继续吗?",
|
||||
"DELETE_SELECTION_TITLE": "删除 IDP",
|
||||
"DELETE_SELECTION_DESCRIPTION": "您即将删除身份提供者。由此产生的变化是不可撤销的。你真的想这样做吗?",
|
||||
"EMPTY": "没有可用的 IDP",
|
||||
@ -2097,7 +2103,9 @@
|
||||
"ja": "日本語",
|
||||
"pl": "Polski",
|
||||
"zh": "简体中文",
|
||||
"bg": "Български"
|
||||
"bg": "Български",
|
||||
"pt": "Portuguese",
|
||||
"mk": "Македонски"
|
||||
},
|
||||
"MEMBER": {
|
||||
"ADD": "添加管理者",
|
||||
|
9721
console/yarn.lock
Normal file
9721
console/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
259
docs/apis/assets/assets.md
Executable file
259
docs/apis/assets/assets.md
Executable file
@ -0,0 +1,259 @@
|
||||
---
|
||||
title: zitadel/assets
|
||||
---
|
||||
|
||||
## AssetsService
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### UploadDefaultLabelPolicyFont()
|
||||
|
||||
> UploadDefaultLabelPolicyFont()
|
||||
|
||||
POST: /instance/policy/label/font
|
||||
|
||||
|
||||
|
||||
### GetDefaultLabelPolicyFont()
|
||||
|
||||
> GetDefaultLabelPolicyFont()
|
||||
|
||||
GET: /instance/policy/label/font
|
||||
|
||||
|
||||
|
||||
### GetPreviewDefaultLabelPolicyFont()
|
||||
|
||||
> GetPreviewDefaultLabelPolicyFont()
|
||||
|
||||
GET: /instance/policy/label/font/_preview
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### UploadDefaultLabelPolicyIcon()
|
||||
|
||||
> UploadDefaultLabelPolicyIcon()
|
||||
|
||||
POST: /instance/policy/label/icon
|
||||
|
||||
### UploadDefaultLabelPolicyIcon()
|
||||
|
||||
> UploadDefaultLabelPolicyIconDark()
|
||||
|
||||
POST: /instance/policy/label/icon/dark
|
||||
|
||||
|
||||
|
||||
### GetDefaultLabelPolicyIcon()
|
||||
|
||||
> GetDefaultLabelPolicyIcon()
|
||||
|
||||
GET: /instance/policy/label/icon
|
||||
|
||||
### GetDefaultLabelPolicyIcon()
|
||||
|
||||
> GetDefaultLabelPolicyIconDark()
|
||||
|
||||
GET: /instance/policy/label/icon/dark
|
||||
|
||||
|
||||
|
||||
### GetPreviewDefaultLabelPolicyIcon()
|
||||
|
||||
> GetPreviewDefaultLabelPolicyIcon()
|
||||
|
||||
GET: /instance/policy/label/icon/_preview
|
||||
|
||||
### GetPreviewDefaultLabelPolicyIcon()
|
||||
|
||||
> GetPreviewDefaultLabelPolicyIconDark()
|
||||
|
||||
GET: /instance/policy/label/icon/dark/_preview
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### UploadDefaultLabelPolicyLogo()
|
||||
|
||||
> UploadDefaultLabelPolicyLogo()
|
||||
|
||||
POST: /instance/policy/label/logo
|
||||
|
||||
### UploadDefaultLabelPolicyLogo()
|
||||
|
||||
> UploadDefaultLabelPolicyLogoDark()
|
||||
|
||||
POST: /instance/policy/label/logo/dark
|
||||
|
||||
|
||||
|
||||
### GetDefaultLabelPolicyLogo()
|
||||
|
||||
> GetDefaultLabelPolicyLogo()
|
||||
|
||||
GET: /instance/policy/label/logo
|
||||
|
||||
### GetDefaultLabelPolicyLogo()
|
||||
|
||||
> GetDefaultLabelPolicyLogoDark()
|
||||
|
||||
GET: /instance/policy/label/logo/dark
|
||||
|
||||
|
||||
|
||||
### GetPreviewDefaultLabelPolicyLogo()
|
||||
|
||||
> GetPreviewDefaultLabelPolicyLogo()
|
||||
|
||||
GET: /instance/policy/label/logo/_preview
|
||||
|
||||
### GetPreviewDefaultLabelPolicyLogo()
|
||||
|
||||
> GetPreviewDefaultLabelPolicyLogoDark()
|
||||
|
||||
GET: /instance/policy/label/logo/dark/_preview
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### UploadOrgLabelPolicyFont()
|
||||
|
||||
> UploadOrgLabelPolicyFont()
|
||||
|
||||
POST: /org/policy/label/font
|
||||
|
||||
|
||||
|
||||
### GetOrgLabelPolicyFont()
|
||||
|
||||
> GetOrgLabelPolicyFont()
|
||||
|
||||
GET: /org/policy/label/font
|
||||
|
||||
|
||||
|
||||
### GetPreviewOrgLabelPolicyFont()
|
||||
|
||||
> GetPreviewOrgLabelPolicyFont()
|
||||
|
||||
GET: /org/policy/label/font/_preview
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### UploadOrgLabelPolicyIcon()
|
||||
|
||||
> UploadOrgLabelPolicyIcon()
|
||||
|
||||
POST: /org/policy/label/icon
|
||||
|
||||
### UploadOrgLabelPolicyIcon()
|
||||
|
||||
> UploadOrgLabelPolicyIconDark()
|
||||
|
||||
POST: /org/policy/label/icon/dark
|
||||
|
||||
|
||||
|
||||
### GetOrgLabelPolicyIcon()
|
||||
|
||||
> GetOrgLabelPolicyIcon()
|
||||
|
||||
GET: /org/policy/label/icon
|
||||
|
||||
### GetOrgLabelPolicyIcon()
|
||||
|
||||
> GetOrgLabelPolicyIconDark()
|
||||
|
||||
GET: /org/policy/label/icon/dark
|
||||
|
||||
|
||||
|
||||
### GetPreviewOrgLabelPolicyIcon()
|
||||
|
||||
> GetPreviewOrgLabelPolicyIcon()
|
||||
|
||||
GET: /org/policy/label/icon/_preview
|
||||
|
||||
### GetPreviewOrgLabelPolicyIcon()
|
||||
|
||||
> GetPreviewOrgLabelPolicyIconDark()
|
||||
|
||||
GET: /org/policy/label/icon/dark/_preview
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### UploadOrgLabelPolicyLogo()
|
||||
|
||||
> UploadOrgLabelPolicyLogo()
|
||||
|
||||
POST: /org/policy/label/logo
|
||||
|
||||
### UploadOrgLabelPolicyLogo()
|
||||
|
||||
> UploadOrgLabelPolicyLogoDark()
|
||||
|
||||
POST: /org/policy/label/logo/dark
|
||||
|
||||
|
||||
|
||||
### GetOrgLabelPolicyLogo()
|
||||
|
||||
> GetOrgLabelPolicyLogo()
|
||||
|
||||
GET: /org/policy/label/logo
|
||||
|
||||
### GetOrgLabelPolicyLogo()
|
||||
|
||||
> GetOrgLabelPolicyLogoDark()
|
||||
|
||||
GET: /org/policy/label/logo/dark
|
||||
|
||||
|
||||
|
||||
### GetPreviewOrgLabelPolicyLogo()
|
||||
|
||||
> GetPreviewOrgLabelPolicyLogo()
|
||||
|
||||
GET: /org/policy/label/logo/_preview
|
||||
|
||||
### GetPreviewOrgLabelPolicyLogo()
|
||||
|
||||
> GetPreviewOrgLabelPolicyLogoDark()
|
||||
|
||||
GET: /org/policy/label/logo/dark/_preview
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### UploadMyUserAvatar()
|
||||
|
||||
> UploadMyUserAvatar()
|
||||
|
||||
POST: /users/me/avatar
|
||||
|
||||
|
||||
|
||||
### GetMyUserAvatar()
|
||||
|
||||
> GetMyUserAvatar()
|
||||
|
||||
GET: /users/me/avatar
|
||||
|
||||
|
||||
|
||||
|
@ -36,9 +36,9 @@ The first parameter contains the following fields
|
||||
- `appendMetadata(string, Any)`
|
||||
The first parameter represents the key and the second a value which will be stored
|
||||
- `setFirstName(string)`
|
||||
Sets the first name
|
||||
Sets the given name
|
||||
- `setLastName(string)`
|
||||
Sets the last name
|
||||
Sets the family name
|
||||
- `setNickName(string)`
|
||||
Sets the nickname
|
||||
- `setDisplayName(string)`
|
||||
@ -75,9 +75,9 @@ A user selected **Register** on the overview page after external authentication.
|
||||
- `metadata`
|
||||
Array of [*metadata*](./objects#metadata-with-value-as-bytes). This function is deprecated, please use `api.v1.user.appendMetadata`
|
||||
- `setFirstName(string)`
|
||||
Sets the first name
|
||||
Sets the given name
|
||||
- `setLastName(string)`
|
||||
Sets the last name
|
||||
Sets the family name
|
||||
- `setNickName(string)`
|
||||
Sets the nick name
|
||||
- `setDisplayName(string)`
|
||||
|
@ -35,7 +35,7 @@ Open the Console (`https://{YourDomain}.zitadel.cloud/ui/console/projects`) and
|
||||
Then on the project detail page click on new application and enter a name for this app.
|
||||
Let's call this one `portal-web`.
|
||||
Select `Web`, continue, `CODE`, then enter `http://localhost:3000/api/auth/callback/zitadel` for the redirect, and `http://localhost:3000` for the post redirect. Then press on `create`.
|
||||
Because the requests from your NextJS application to ZITADEL are made on the server side, you can safely select `CODE`. With this you still get a secret which is then usable alongside PKCE. Your secret never gets exposed on the browser since it is kept in your NextJS server.
|
||||
Because the requests from your NextJS application to ZITADEL are made on the server side, you can safely select `CODE`. You will get a secret at the end of the stepper. With NextAuth your secret never gets exposed on the browser since it is kept in your NextJS server.
|
||||
|
||||
Copy the "Resource Id" of the project `Portal` as you will need this in your environment configuration file later.
|
||||
|
||||
|
@ -19,7 +19,7 @@ yarn install
|
||||
then to run the app:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
yarn dev
|
||||
```
|
||||
|
||||
then open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
@ -28,10 +28,11 @@ then open [http://localhost:3000](http://localhost:3000) with your browser to se
|
||||
|
||||
Before we can start building our application, we have to do a few configuration steps in ZITADEL Console.
|
||||
You will need to provide some information about your app.
|
||||
|
||||
Navigate to your Project, then add a new application at the top of the page.
|
||||
Select Web application type and continue.
|
||||
We recommend you use [Authorization Code](/apis/openidoauth/grant-types#authorization-code) in combination with [Proof Key for Code Exchange (PKCE)](/apis/openidoauth/grant-types#proof-key-for-code-exchange) for all web applications.
|
||||
As the requests from your application to ZITADEL are made on NextJS serverside, you can select `CODE` in the next step. This makes sure you still get a secret which is then used in combination with PKCE. Note that the secret never gets exposed on the browser and is therefore kept in a confidential environment.
|
||||
We use [Authorization Code](/apis/openidoauth/grant-types#authorization-code)for our NextJS application.
|
||||
Select `CODE` in the next step. This makes sure you still get a secret. Note that the secret never gets exposed on the browser and is therefore kept in a confidential environment.
|
||||
|
||||

|
||||
|
||||
@ -135,15 +136,11 @@ ZitadelProvider({
|
||||
...
|
||||
```
|
||||
|
||||
We recommend using the Authentication Code flow secured by PKCE for the Authentication flow.
|
||||
To be able to connect to ZITADEL, navigate to your Console Projects, create or select an existing project and add your app selecting WEB, then PKCE, and then add `http://localhost:3000/api/auth/callback/zitadel` as redirect url to your app.
|
||||
|
||||
To be able to connect to ZITADEL, make sure to add `http://localhost:3000/api/auth/callback/zitadel` as redirect url to your app.
|
||||
For simplicity reasons we set the default to the one that next-auth provides us. You'll be able to change the redirect later if you want to.
|
||||
|
||||
Hit Create, then in the detail view of your application make sure to enable dev mode. Dev mode ensures that you can start an auth flow from a non https endpoint for testing.
|
||||
|
||||
> Note that we get a clientId but no clientSecret because it is not needed for our authentication flow.
|
||||
|
||||
Now go to Token settings and check the checkbox for **User Info inside ID Token** to get your users name directly on authentication.
|
||||
|
||||
### Environment
|
||||
|
48
docs/docs/guides/integrate/identity-providers/migrate.mdx
Normal file
48
docs/docs/guides/integrate/identity-providers/migrate.mdx
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
title: Migrate from Generic Provider to specific Identity Provider
|
||||
sidebar_label: Migrate IDP
|
||||
---
|
||||
|
||||
## Migrate Generic OIDC Provider
|
||||
|
||||
You can migrate from a generic OIDC provider to the following supported templates:
|
||||
- AzureAD
|
||||
- Google
|
||||
|
||||
To migrate, you either use the [Migrate Generic OIDC Identity Provider (Instance)](/docs/apis/resources/admin/admin-service-migrate-generic-oidc-provider#migrate-generic-oidc-identity-provider) or [Migrate Generic OIDC Identity Provider (Organization)](/docs/apis/resources/mgmt/management-service-migrate-generic-oidc-provider#migrate-generic-oidc-identity-provider) API request.
|
||||
These calls change the type of the provider and don't delete any linked users.
|
||||
|
||||
:::note Linked users will not notice the change and be able to login as usual.
|
||||
:::
|
||||
|
||||
### Google Configuration
|
||||
|
||||
The available configuration is described in [Google Configuration](./google).
|
||||
|
||||
### AzureAD Configuration
|
||||
|
||||
The available configuration is described in [AzureAD Configuration](./azure-ad).
|
||||
|
||||
## Migrate with Terraform
|
||||
Please note that you only have to perform this migration if you already have an existing IDP with linked users, that should not loose the connection to the provider.
|
||||
If that isn't your case please just add a new provider from scratch.
|
||||
To migrate to a specific provider, you need to follow a few essential steps:
|
||||
1. Create a desired IDP as Terraform resource for example [Google](https://registry.terraform.io/providers/zitadel/zitadel/latest/docs/resources/idp_google).
|
||||
2. Make the corresponding API call to [migrate the IDP](./migrate#google-configuration), save the ID of the IDP for the import
|
||||
3. Before applying the Terraform resources again, import the new IDP resource.
|
||||
```bash
|
||||
#resource "zitadel_idp_google" "google" {
|
||||
# name = "Google"
|
||||
# client_id = "182902..."
|
||||
# client_secret = "GOCSPX-*****"
|
||||
# scopes = ["openid", "profile", "email"]
|
||||
# is_linking_allowed = false
|
||||
# is_creation_allowed = true
|
||||
# is_auto_creation = false
|
||||
# is_auto_update = true
|
||||
#}
|
||||
|
||||
# terraform import zitadel_idp_google.*resource_name* *id*:*client_secret*
|
||||
terraform import zitadel_idp_google.google 222302827723096428:GOCSPX-*****
|
||||
|
||||
You have now migrated your provider and you should be able to apply the resource again. There should be no changes and the IDP is maintained by Terraform again.
|
@ -0,0 +1,12 @@
|
||||
Now that you have started the registration within ZITADEL, you have to register the credentials in the browser.
|
||||
This requires a call to the browser api and looks something like the following.
|
||||
Make sure to send the public key credential creation options you got in the previous request from ZITADEL.
|
||||
|
||||
```bash
|
||||
const credential = await navigator.credentials.create({
|
||||
publicKey: publicKeyCredentialCreationOptions
|
||||
});
|
||||
```
|
||||
|
||||
For more information about WebAuthN and registering credential flow, read the following guide:
|
||||
[Registering a WebAuthN Credentials](https://webauthn.guide/#registration)
|
46
docs/docs/guides/integrate/login-ui/_list-mfa-options.mdx
Normal file
46
docs/docs/guides/integrate/login-ui/_list-mfa-options.mdx
Normal file
@ -0,0 +1,46 @@
|
||||
Your user has successfully authenticated, and now you ask him if he wants to setup MFA to have a more secure account.
|
||||
When he starts the configuration, first you want him to show the possible methods.
|
||||
You can either list it implicitly or call the settings service from ZITADEL to get what is configured on the login settings.
|
||||
|
||||
More detailed information about the API: [Get Login Settings Documentation](/apis/resources/settings_service/settings-service-get-login-settings)
|
||||
|
||||
Request Example:
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/settings/login \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''
|
||||
```
|
||||
|
||||
Response Example:
|
||||
The relevant part for the list is the second factor and multi factor list.
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "293",
|
||||
"changeDate": "2023-03-29T14:16:55.570482Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"settings": {
|
||||
"allowUsernamePassword": true,
|
||||
"allowRegister": true,
|
||||
"allowExternalIdp": true,
|
||||
"passkeysType": "PASSKEYS_TYPE_ALLOWED",
|
||||
"passwordCheckLifetime": "864000s",
|
||||
"externalLoginCheckLifetime": "864000s",
|
||||
"mfaInitSkipLifetime": "2592000s",
|
||||
"secondFactorCheckLifetime": "64800s",
|
||||
"multiFactorCheckLifetime": "43200s",
|
||||
"secondFactors": [
|
||||
"SECOND_FACTOR_TYPE_OTP",
|
||||
"SECOND_FACTOR_TYPE_U2F"
|
||||
],
|
||||
"multiFactors": [
|
||||
"MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION"
|
||||
],
|
||||
"resourceOwnerType": "RESOURCE_OWNER_TYPE_ORG"
|
||||
}
|
||||
}
|
||||
```
|
@ -96,13 +96,13 @@ curl --request POST \
|
||||
"idpId": "218528353504723201",
|
||||
"rawInformation": {
|
||||
"User": {
|
||||
"email": "fabienne@rootd.ch",
|
||||
"email": "minni@mouse.com",
|
||||
"email_verified": true,
|
||||
"family_name": "Bühler",
|
||||
"given_name": "Fabienne",
|
||||
"hd": "rootd.ch",
|
||||
"family_name": "Mouse",
|
||||
"given_name": "Minnie",
|
||||
"hd": "mouse.com",
|
||||
"locale": "de",
|
||||
"name": "Fabienne Bühler",
|
||||
"name": "Minnie Mouse",
|
||||
"picture": "https://lh3.googleusercontent.com/a/AAcKTtf973Q6NH8KzKTMEZELPU9lx45WpQ9FRBuxFdPb=s96-c",
|
||||
"sub": "111392805975715856637"
|
||||
}
|
||||
|
191
docs/docs/guides/integrate/login-ui/mfa.mdx
Normal file
191
docs/docs/guides/integrate/login-ui/mfa.mdx
Normal file
@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Multi-Factor (MFA)
|
||||
sidebar_label: Multi-Factor (MFA)
|
||||
---
|
||||
|
||||
import MfaOptions from './_list-mfa-options.mdx';
|
||||
import BrowserRegisterWebAuthN from './_browser_register_webauthn.mdx';
|
||||
|
||||
|
||||
Multi-factor authentication (MFA) is a multi-step account authentication which requires to user to enter more than only the password.
|
||||
It is highly recommended to use MFA or [Passkeys](./passkey) to make your user accounts more secure.
|
||||
|
||||
ZITADEL supports two different Methods:
|
||||
- Time-based one time password (TOTP), which are Authenticator apps like Google/Microsoft Authenticator, Authy, etc
|
||||
- Universal Second Factor (U2F), which is authentication with your device like Windows Hello, Apple FaceID, Fingerprint, FIDO2 keys, Yubikey, etc.
|
||||
|
||||
## TOTP Registration
|
||||
|
||||
### Flow
|
||||
|
||||

|
||||
|
||||
### List the Possible Methods
|
||||
|
||||
<MfaOptions/>
|
||||
|
||||
### Start TOTP Registration
|
||||
|
||||
The user has selected to setup Time-based One-Time-Password (TOTP).
|
||||
To show the user the QR to register TOTP with his Authenticator App like Google/Microsoft Authenticator or Authy you have to start the registration on the ZITADEL API.
|
||||
Generate the QR Code with the URI from the response.
|
||||
For users that do not have a QR Code reader make sure to also show the secret, to enable manual configuration.
|
||||
|
||||
More detailed information about the API: [Start TOTP Registration Documentation](/apis/resources/user_service/user-service-register-totp)
|
||||
|
||||
Request Example:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/totp \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Response Example:
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "2",
|
||||
"changeDate": "2023-06-28",
|
||||
"resourceOwner": "69629023906488334"
|
||||
},
|
||||
"uri": "otpauth://totp/ZITADEL:minni-mouse@mouse.com?algorithm=SHA1&digits=6&issuer=ZITADEL&period=30&secret=TJOPWSDYILLHXFV4MLKNNJOWFG7VSDCK",
|
||||
"secret": "TJOPWSDYILLHXFV4MLKNNJOWFG7VSDCK"
|
||||
}
|
||||
```
|
||||
|
||||
### Verify TOTP Registration
|
||||
|
||||
When the user has added the account to his authenticator app, he has to enter the code from the App to finish the registration.
|
||||
This code has to be sent to the verify endpoint in ZITADEL.
|
||||
|
||||
More detailed information about the API: [Verify TOTP Documentation](/apis/resources/user_service/user-service-verify-totp-registration)
|
||||
|
||||
Request Example:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/totp/_verify \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"code": "123456"
|
||||
}'
|
||||
```
|
||||
|
||||
## U2F Registration
|
||||
|
||||
### Flow
|
||||
|
||||

|
||||
|
||||
### List the Possible Methods
|
||||
|
||||
<MfaOptions/>
|
||||
|
||||
### Start U2F Registration
|
||||
|
||||
The user has selected to setup Universal Second Factor (U2F).
|
||||
To be able to authenticate in the browser you have to start the u2f registration within ZITADEL.
|
||||
|
||||
More detailed information about the API: [Start U2F Registration Documentation](/apis/resources/user_service/user-service-register-u-2-f)
|
||||
|
||||
Request Example:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/u2f \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"domain": "acme.com"
|
||||
}'
|
||||
```
|
||||
|
||||
Response Example:
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "2",
|
||||
"changeDate": "2023-07-03",
|
||||
"resourceOwner": "69629023906488334"
|
||||
},
|
||||
"u2fId": "163840776835432705",
|
||||
"publicKeyCredentialCreationOptions": {
|
||||
"publicKey": {
|
||||
"attestation": "none",
|
||||
"authenticatorSelection": {
|
||||
"userVerification": "required"
|
||||
},
|
||||
"challenge": "XaMYwWOZ5hj6pwtwJJlpcI-ExkO5TxevBMG4R8DoKQQ",
|
||||
"excludeCredentials": [
|
||||
{
|
||||
"id": "tVp1QfYhT8DkyEHVrv7blnpAo2YJzbZgZNBf7zPs6CI",
|
||||
"type": "public-key"
|
||||
}
|
||||
],
|
||||
"pubKeyCredParams": [
|
||||
{
|
||||
"alg": -7,
|
||||
"type": "public-key"
|
||||
}
|
||||
],
|
||||
"rp": {
|
||||
"id": "localhost",
|
||||
"name": "ZITADEL"
|
||||
},
|
||||
"timeout": 300000,
|
||||
"user": {
|
||||
"displayName": "Minie Mouse",
|
||||
"id": "MjE1NTk4MDAwNDY0OTk4OTQw",
|
||||
"name": "minie-mouse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Register new U2F on current device
|
||||
|
||||
<BrowserRegisterWebAuthN/>
|
||||
|
||||
### Verify U2F Registration
|
||||
|
||||
In the next request you have to verify the U2F within ZITADEL.
|
||||
Include the public key credential you got from the browser in your request.
|
||||
You can give the U2F a name, which makes it easier for the user to identify the registered authentication methods.
|
||||
Example: Google Pixel, iCloud Keychain, Yubikey, etc
|
||||
|
||||
More detailed information about the API: [Verify U2F Documentation](/apis/resources/user_service/user-service-verify-u-2-f-registration)
|
||||
|
||||
Example Request:
|
||||
|
||||
```bash
|
||||
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/u2f/$PASSKEY_ID \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"publicKeyCredential": {
|
||||
"type": "public-key",
|
||||
"id": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00",
|
||||
"rawId": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00",
|
||||
"response": {
|
||||
"attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgRKS3VpeE9tfExXRzkoUKnG4rQWPvtSSt4YtDGgTx32oCIQDPey-2YJ4uIg-QCM4jj6aE2U3tgMFM_RP7Efx6xRu3JGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAADju76085Yhmlt1CEOHkwLQAIKWsFWqxeMT8SxZnwp0ZMF1nk6yhs2m3AIvdixCNVgtNpQECAyYgASFYIMGUDSP2FAQn2MIfPMy7cyB_Y30VqixVgGULTBtFjfRiIlggjUGfQo3_-CrMmH3S-ZQkFKWKnNBQEAMkFtG-9A4zqW0",
|
||||
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQlhXdHh0WGxJeFZZa0pHT1dVaUVmM25zby02aXZKdWw2YmNmWHdMVlFIayIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAifQ"
|
||||
}
|
||||
},
|
||||
"tokenName": "Google Pixel"
|
||||
}'
|
||||
```
|
||||
|
||||
You have successfully registered a new U2F to the user.
|
313
docs/docs/guides/integrate/login-ui/passkey.mdx
Normal file
313
docs/docs/guides/integrate/login-ui/passkey.mdx
Normal file
@ -0,0 +1,313 @@
|
||||
---
|
||||
title: Passkeys
|
||||
sidebar_label: Passkeys
|
||||
---
|
||||
|
||||
Passkeys are a replacement for passwords that provide faster, easier, and more secure sign-ins to websites and apps even across multiple devices.
|
||||
Unlike passwords, passkeys are phishing-resistant and can improve the user experience and security at the same time.
|
||||
Passkeys and there underlying protocols are a standard defined by the [FIDO Standard](https://fidoalliance.org/) .
|
||||
|
||||
## Register Passkey
|
||||
|
||||
### Flow
|
||||
|
||||

|
||||
|
||||
There are two options to onboard users with passkeys:
|
||||
1. You send the user a link (email, sms, ...) with an embedded code, so the user is able to register passkey on any capable device
|
||||
2. You start the passkey registration directly on the current device used by a user
|
||||
|
||||
### Send Registration Link
|
||||
|
||||
When you want to send a link to your user, that enables him to register a new passkey device, you can choose if ZITADEL sends the code or ZITADEL can return the code in the API response. This way you can send a link through any channel of your choice (email, sms, phone, in-person, postal, ...).
|
||||
|
||||
If you asked ZITADEL to send the link to the user please make sure to populate the link with the needed values that point towards your registration UI.
|
||||
|
||||
More detailed information about the API: [Send Registration Link Documentation](/apis/resources/user_service/user-service-create-passkey-registration-link)
|
||||
|
||||
Request Example:
|
||||
Send either the sendLink or the returnCode (empty message) in the request body, depending on the use case you have.
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/passkeys/registration_link \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"sendLink": {
|
||||
"urlTemplate": "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}"
|
||||
},
|
||||
"returnCode": {}
|
||||
}'
|
||||
```
|
||||
|
||||
Response Example:
|
||||
The code is only filled if returnCode has been requested in the Request.Passkey
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "632",
|
||||
"changeDate": "2023-06-28T08:09:51.257699Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"code": {
|
||||
"id": "220526087715684609",
|
||||
"code": "2KEpIeQGSBBd"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Start Passkey Registration
|
||||
|
||||
When starting the passkey registration you can optionally send the registration code from the step above to ZITADEL to pair it with a specific user.
|
||||
|
||||
By specifying the authenticator type you can choose if the passkey should be cross platform or not. Per default all types are allowed:
|
||||
- PASSKEY_AUTHENTICATOR_UNSPECIFIED
|
||||
- PASSKEY_AUTHENTICATOR_PLATFORM
|
||||
- PASSKEY_AUTHENTICATOR_CROSS_PLATFORM
|
||||
|
||||
The API response will provide you the public key credential options, this will be used by the browser to obtain a signed challenge.
|
||||
|
||||
More detailed information about the API: [Start Passkey Registration Documentation](/apis/resources/user_service/user-service-register-passkey)
|
||||
|
||||
Request Example:
|
||||
The code only has to be filled if the user did get a registration code.
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/passkeys \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"code": {
|
||||
"id": "220526087715684609",
|
||||
"code": "2KEpIeQGSBBd"
|
||||
},
|
||||
"authenticator": "PASSKEY_AUTHENTICATOR_UNSPECIFIED"
|
||||
}'
|
||||
```
|
||||
|
||||
Response Example:
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "633",
|
||||
"changeDate": "2023-06-28T08:10:26.725981Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"passkeyId": "220526147258024193",
|
||||
"publicKeyCredentialCreationOptions": {
|
||||
"publicKey": {
|
||||
"attestation": "none",
|
||||
"authenticatorSelection": {
|
||||
"userVerification": "required"
|
||||
},
|
||||
"challenge": "JM1uLbVQR2xZJ210DA7E-3j0Cd9rHKUSmc8NyIJBtAY",
|
||||
"pubKeyCredParams": [
|
||||
{
|
||||
"alg": -7,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -35,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -36,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -257,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -258,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -259,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -37,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -38,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -39,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -8,
|
||||
"type": "public-key"
|
||||
}
|
||||
],
|
||||
"rp": {
|
||||
"id": "example.domain.com",
|
||||
"name": "ZITADEL"
|
||||
},
|
||||
"timeout": 300000,
|
||||
"user": {
|
||||
"displayName": "Minnie Mouse",
|
||||
"id": "MjE4NjYyNTk2OTE4NjQwODk3",
|
||||
"name": "minni-mouse@mouse.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Register new Passkey on current device
|
||||
|
||||
<BrowserRegisterWebAuthN/>
|
||||
|
||||
### Verify Passkey in ZITADEL
|
||||
|
||||
In the next request you have to verify the Passkey within ZITADEL.
|
||||
Include the public key credential you got from the browser in your request.
|
||||
You can give the Passkey a name, which makes it easier for the user to identify the registered authentication methods.
|
||||
Example: Google Pixel, iCloud Keychain, Yubikey, etc
|
||||
|
||||
More detailed information about the API: [Verify Passkey Registration Documentation](/apis/resources/user_service/user-service-verify-passkey-registration)
|
||||
|
||||
Example Request:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/passkeys/$PASSKEY_ID \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"publicKeyCredential": {
|
||||
"type": "public-key",
|
||||
"id": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00",
|
||||
"rawId": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00",
|
||||
"response": {
|
||||
"attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgRKS3VpeE9tfExXRzkoUKnG4rQWPvtSSt4YtDGgTx32oCIQDPey-2YJ4uIg-QCM4jj6aE2U3tgMFM_RP7Efx6xRu3JGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAADju76085Yhmlt1CEOHkwLQAIKWsFWqxeMT8SxZnwp0ZMF1nk6yhs2m3AIvdixCNVgtNpQECAyYgASFYIMGUDSP2FAQn2MIfPMy7cyB_Y30VqixVgGULTBtFjfRiIlggjUGfQo3_-CrMmH3S-ZQkFKWKnNBQEAMkFtG-9A4zqW0",
|
||||
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQlhXdHh0WGxJeFZZa0pHT1dVaUVmM25zby02aXZKdWw2YmNmWHdMVlFIayIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAifQ"
|
||||
}
|
||||
},
|
||||
"passkeyName": "Google Pixel"
|
||||
}'
|
||||
```
|
||||
|
||||
You have successfully registered a new passkey to the user.
|
||||
Next step is to authenticate the user with the new registered passkey.
|
||||
|
||||
## Login with Passkey
|
||||
|
||||
### Flow
|
||||
|
||||

|
||||
|
||||
|
||||
### Create Session
|
||||
|
||||
First step is to ask the user for his username and create a new session with the ZITADEL API.
|
||||
When creating the new session make sure to include the challenge for passkey.
|
||||
The response will include the public key credential request options for the passkey in the challenges.
|
||||
|
||||
More detailed information about the API: [Create Session Documentation](/apis/resources/session_service/session-service-create-session)
|
||||
|
||||
Example Request:
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/sessions \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"checks": {
|
||||
"user": {
|
||||
"loginName": "minni-mouse@mouse.com"
|
||||
}
|
||||
},
|
||||
"metadata": {},
|
||||
"challenges": [
|
||||
"CHALLENGE_KIND_PASSKEY"
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
Example Response:
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "2",
|
||||
"changeDate": "2023-06-27",
|
||||
"resourceOwner": "69629023906488334"
|
||||
},
|
||||
"sessionId": "d654e6ba-70a3-48ef-a95d-37c8d8a7901a",
|
||||
"sessionToken": "string",
|
||||
"challenges": {
|
||||
"passkey": {
|
||||
"publicKeyCredentialRequestOptions": {
|
||||
"publicKey": {
|
||||
"allowCredentials": [
|
||||
{
|
||||
"id": "ATmqBg-99qyOZk2zloPdJQyS2R7IkFT7v9Hoos_B_nM",
|
||||
"type": "public-key"
|
||||
}
|
||||
],
|
||||
"challenge": "GAOHYz2jE69kJMYo6Laij8yWw9-dKKgbViNhfuy0StA",
|
||||
"rpId": "example.domain.com",
|
||||
"timeout": 300000,
|
||||
"userVerification": "required"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Signing in Browser
|
||||
|
||||
After starting the passkey authentication on the side of ZITADEL you have to challenge the browser.
|
||||
To do this you need to call the browser API to get the credentials.
|
||||
Make sure to send the public key credential request options you got from ZITADEL.
|
||||
|
||||
```bash
|
||||
const credential = await navigator.credentials.get({
|
||||
publicKey: publicKeyCredentialRequestOptions
|
||||
});
|
||||
```
|
||||
|
||||
Read the [WebAuthN Guide](https://webauthn.guide/#authentication) for more information about "Authenticating with a WebAuthN Credential".
|
||||
|
||||
### Update Session with Passkey
|
||||
|
||||
Now that you have successfully authenticated in the browser, you can update the session of the user.
|
||||
Fill the passkey checks with the credential assertion data you get from the browser.
|
||||
|
||||
More detailed information about the API: [Update Session Documentation](/apis/resources/session_service/session-service-set-session)
|
||||
|
||||
Example Request:
|
||||
|
||||
```bash
|
||||
curl --request PATCH \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/sessions/218480890961985793 \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"sessionToken": "yMDi6uVPJAcphbbz0LaxC07ihWkNTe7m0Xqch8SzfM5Cz3HSIQIDZ65x1f5Qal0jxz0MEyo-_zYcUg",
|
||||
"checks": {
|
||||
"passkey": {
|
||||
"credentialAssertionData": {}
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
109
docs/docs/guides/integrate/login-ui/password-reset.mdx
Normal file
109
docs/docs/guides/integrate/login-ui/password-reset.mdx
Normal file
@ -0,0 +1,109 @@
|
||||
---
|
||||
title: Password Reset/Change
|
||||
---
|
||||
|
||||
When your user is on the password screen and has forgotten his password you will probably want him to be able to reset by himself.
|
||||
|
||||
## Flow
|
||||
|
||||

|
||||
|
||||
## Request Password Reset
|
||||
|
||||
First you will have to make a request, to ask for a password reset.
|
||||
The goal is to send the user a verification code, which he can use to verify the password reset request.
|
||||
|
||||
There are two possible ways: You can either let ZITADEL send the notification with the verification code, or you can ask ZITADEL for returning the code and send the email by yourself.
|
||||
|
||||
[Request Password Reset Documentation](/apis/resources/user_service/user-service-password-reset)
|
||||
|
||||
### ZITADEL sends the verification message
|
||||
|
||||
When you want ZITADEL to send the verification code you can define the notification channel.
|
||||
Per default the verification code will be sent to the email address of the user.
|
||||
|
||||
Make sure to also include the URL Template to customize the reset link in the email sent to the user.
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/password_reset \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"'' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"sendLink": {
|
||||
"notificationType": "NOTIFICATION_TYPE_Email",
|
||||
"urlTemplate": "https://example.com/password/changey?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### ZITADEL returns the code
|
||||
|
||||
Send the request with asking for the return Code in the body of the request.
|
||||
|
||||
#### Request
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/password_reset \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"'' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"returnCode": {}
|
||||
}'
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
You will get the verification code in the response:
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "625",
|
||||
"changeDate": "2023-06-27T15:02:10.321773Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"verificationCode": "IBJMUC"
|
||||
}
|
||||
```
|
||||
|
||||
## Send Verification Code
|
||||
|
||||
The verification code is generated and ZITADEL has sent it with the defined channel (email or sms) to your users.
|
||||
If you have chosen to get the code back in the response, you should now send the code to your user.
|
||||
|
||||
## Change Password
|
||||
|
||||
The next screen should allow the user to enter the verification code and a new password.
|
||||
From a user experience perspective it is nice to prefill the verification code, so the user doesn't have to do manually.
|
||||
|
||||
As soon as the user has typed the new password, you can send the change password request.
|
||||
The change password request allows you to set a new password for the user.
|
||||
|
||||
[Change Password Documentation](/apis/resources/user_service/user-service-set-password)
|
||||
|
||||
:::note
|
||||
This request can be used in the password reset flow as well as to let your user change the password manually.
|
||||
In this case it requires additionally the current password instead of the verification code.
|
||||
:::
|
||||
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/password \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"'' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"newPassword": {
|
||||
"password": "Secr3tP4ssw0rd!",
|
||||
"changeRequired": false
|
||||
},
|
||||
"verificationCode": "48CDAP"
|
||||
}'
|
||||
```
|
@ -10,7 +10,7 @@ sidebar_label: Username and Password
|
||||
## Register
|
||||
|
||||
First, we create a new user with a username and password. In the example below we add a user with profile data, a verified email address, and a password.
|
||||
[Create User Documentation](https://zitadel.com/docs/apis/resources/user_service/user-service-add-human-user)
|
||||
[Create User Documentation](/apis/resources/user_service/user-service-add-human-user)
|
||||
|
||||
### Request
|
||||
|
||||
|
@ -0,0 +1,21 @@
|
||||
Upon successful introspection, regardless of the token type or introspection method, a response with the boolean `active` is returned, indicating if the provided token is active and if the requesting client is part of the token audience. If `active` is true, further information will be provided:
|
||||
|
||||
| **Property** | **Description** |
|
||||
| --- | --- |
|
||||
| `aud` | The audience of the token |
|
||||
| `client_id` | The client_id of the application the token was issued to |
|
||||
| `exp` | Time the token expires (as unix time) |
|
||||
| `iat` | Time the token was issued at (as unix time) |
|
||||
| `iss` | Issuer of the token |
|
||||
| `jti` | Unique id of the token |
|
||||
| `nbf` | Time the token must not be used before (as unix time) |
|
||||
| `scope` | Space delimited list of scopes granted to the token |
|
||||
| `token_type` | Type of the inspected token. Value is always Bearer |
|
||||
| `username` | ZITADEL's login name of the user. Consists of username@primarydomain |
|
||||
|
||||
|
||||
Depending on the granted scopes, additional information about the authorized user is provided.
|
||||
|
||||
If the authorization fails, an HTTP 401 with invalid_client will be returned.
|
||||
|
||||
In summary, the introspection endpoint plays a crucial role in validating access tokens, either opaque or JWT, ensuring that they are not revoked.
|
103
docs/docs/guides/integrate/token-introspection/basic-auth.mdx
Normal file
103
docs/docs/guides/integrate/token-introspection/basic-auth.mdx
Normal file
@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Basic authentication
|
||||
---
|
||||
|
||||
import IntrospectionResponse from './_introspection-response.mdx';
|
||||
|
||||
This is a guide on how to secure your API using [Basic Authentication](https://zitadel.com/docs/apis/openidoauth/authn-methods#client-secret-basic).
|
||||
|
||||
## Register the API in ZITADEL
|
||||
|
||||
1. Go to your project and click on the **New** button as shown below.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-1.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
2. Give a name to your application (Test API 2 is the name given below) and select type **API**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-2.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
3. Select **Basic** as the authentication method and click **Continue**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-3.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
4. Now review your configuration and click **Create**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-4.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
5. You will now see the API’s **Client ID** and the **Client Secret**. Copy them and click **Close**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-5.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
6. When you click **URLs** on the left, you will see the relevant OIDC URLs. Note down the **issuer** URL, **token_endpoint** and **introspection_endpoint**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-6.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
7. Also note down the **Resource ID** of your project.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-7.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
## Token introspection
|
||||
|
||||
With Basic Authentication, you will receive a Client ID and Client Secret for your API. Send your client_id and client_secret as a Basic Auth Header in the following format:
|
||||
|
||||
```
|
||||
Authorization: "Basic " + base64( formUrlEncode(client_id) + ":" + formUrlEncode(client_secret) )
|
||||
|
||||
```
|
||||
|
||||
The request from the API to the introspection endpoint should be in the following format:
|
||||
|
||||
```
|
||||
curl --request POST \
|
||||
--url {your_domain}/oauth/v2/introspect \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--header 'Authorization: Basic {your_basic_auth_header}' \
|
||||
--data token=VjVxyCZmRmWYqd3_F5db9Pb9mHR5fqzhn...
|
||||
```
|
||||
|
||||
Here's an example of how this is done in Python code:
|
||||
|
||||
```
|
||||
def introspect_token(self, token_string):
|
||||
url = ZITADEL_INTROSPECTION_URL
|
||||
data = {'token': token_string, 'token_type_hint': 'access_token', 'scope': 'openid'}
|
||||
auth = HTTPBasicAuth(API_CLIENT_ID, API_CLIENT_SECRET)
|
||||
resp = requests.post(url, data=data, auth=auth)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
```
|
||||
|
||||
## Introspection response
|
||||
|
||||
<IntrospectionResponse/>
|
||||
|
||||
Follow this [tutorial](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-basic-authentication) to learn how to register an API application using Basic Auth with ZITADEL and test it.
|
||||
|
@ -0,0 +1,192 @@
|
||||
---
|
||||
title: JSON Web Token profile
|
||||
---
|
||||
|
||||
import IntrospectionResponse from './_introspection-response.mdx';
|
||||
|
||||
This is a guide on how to secure your API using [JSON Web Token (JWT) profile (recommended)](https://zitadel.com/docs/apis/openidoauth/authn-methods#client-secret-basic).
|
||||
|
||||
## Register the API in ZITADEL and generate private and public keys
|
||||
|
||||
1. Go to your project and click on the **New** button as shown below.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-1.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
2. Give a name to your application (Test API is the name given below) and select type **API**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-2.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
3. Select **JWT** as the authentication method and click **Continue**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-3.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
4. Now review your configuration and click **Create**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-4.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
5. You will now see the API’s **Client ID**. You will not see a client secret because we are using a private JWT key.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-5.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
6. Next, we must create the key pairs. Click on **New**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-6.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
7. Select **JSON** as the type of key. You can also set an expiration time for the key or leave it empty. Click on **Add**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-7.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
8. Download the created key by clicking the **Download** button and then click **Close**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-8.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
9. The key will be downloaded.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-9.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
10. When you click on URLs on the left, you will see the relevant OIDC URLs. Note down the **issuer** URL, **token_endpoint** and **introspection_endpoint**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-10.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
11. The key that you downloaded will be of the following format.
|
||||
```
|
||||
{
|
||||
"type":"application",
|
||||
"keyId":"<YOUR_KEY_ID>",
|
||||
"key":"-----BEGIN RSA PRIVATE KEY-----\<YOUR_PRIVATE_KEY>\n-----END RSA PRIVATE KEY-----\n",
|
||||
"appId":"<YOUR_APP_ID>",
|
||||
"clientId":"<YOUR_CLIENT_ID>"
|
||||
}
|
||||
```
|
||||
12. Also note down the **Resource ID** of your project.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-11.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
## Token introspection
|
||||
|
||||
You must send a client_assertion as a JWT signed with the API’s private key for ZITADEL to validate the signature against the registered public key.
|
||||
|
||||
Request parameters:
|
||||
|
||||
| **Parameter** | **Description** |
|
||||
---|---
|
||||
| `client_assertion` | When using JWT profile for token or introspection endpoints, you must provide a JWT as an assertion generated with the structure shown below and signed with the downloaded key. |
|
||||
| `client_assertion_type` | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
|
||||
|
||||
|
||||
You must create your client assertion JWT with the following format:
|
||||
|
||||
Header:
|
||||
```json
|
||||
{
|
||||
"alg": "RS256",
|
||||
"kid": "81693565968962154" (keyId from your key file)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Payload:
|
||||
```json
|
||||
{
|
||||
"iss": "78366401571920522@acme", (clientId from your key file)
|
||||
"sub": "78366401571920522@acme", (clientId from your key file)
|
||||
"aud": "https://{your_domain}", (your ZITADEL domain/issuer URL)
|
||||
"exp": 1605183582, (Unix timestamp of the expiry)
|
||||
"iat": 1605179982 (Unix timestamp of the creation signing time of the JWT, MUST NOT be older than 1h)
|
||||
}
|
||||
```
|
||||
|
||||
Create the JSON Web Token with the above header and payload, and sign it with the private key in your key file. You can do this programmatically or use tools like [https://github.com/zitadel/zitadel-tools](https://github.com/zitadel/zitadel-tools) and [https://dinochiesa.github.io/jwt/](https://dinochiesa.github.io/jwt/).
|
||||
|
||||
The request from the API to the introspection endpoint should be in the following format:
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url {your_domain}/oauth/v2/introspect \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
|
||||
--data client_assertion=eyJhbGciOiJSUzI1Ni... \
|
||||
--data token=VjVxyCZmRmWYqd3_F5db9Pb9mHR
|
||||
```
|
||||
|
||||
Here's an example of how this is done in Python code:
|
||||
|
||||
```python
|
||||
def introspect_token(self, token_string):
|
||||
#Create JWT for client assertion
|
||||
payload = {
|
||||
"iss": API_PRIVATE_KEY_FILE["client_id"],
|
||||
"sub": API_PRIVATE_KEY_FILE["client_id"],
|
||||
"aud": ZITADEL_DOMAIN,
|
||||
"exp": int(time.time()) + 60 * 60, # Expires in 1 hour
|
||||
"iat": int(time.time())
|
||||
}
|
||||
headers = {
|
||||
"alg": "RS256",
|
||||
"kid": API_PRIVATE_KEY_FILE["key_id"]
|
||||
}
|
||||
jwt_token = jwt.encode(payload, API_PRIVATE_KEY_FILE["private_key"], algorithm="RS256", headers=headers)
|
||||
|
||||
#Send introspection request
|
||||
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||
data = {
|
||||
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
||||
"client_assertion": jwt_token,
|
||||
"token": token_string
|
||||
}
|
||||
response = requests.post(ZITADEL_INTROSPECTION_URL, headers=headers, data=data)
|
||||
response.raise_for_status()
|
||||
token_data = response.json()
|
||||
print(f"Token data from introspection: {token_data}")
|
||||
return token_data
|
||||
|
||||
```
|
||||
## Introspection response
|
||||
|
||||
<IntrospectionResponse/>
|
||||
|
||||
Follow this [tutorial](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-jwt) to learn how to register an API application using JWT Profile with ZITADEL and test it.
|
||||
|
@ -125,6 +125,7 @@ Secondfactors (2FA):
|
||||
|
||||
Force a user to register and use a multifactor authentication, by checking the option "Force MFA".
|
||||
Ensure that you have added the MFA methods you want to allow.
|
||||
Or you can enable the "Force MFA for local authenticated users", which will enforce this rule only on local authentication, but not on users authenticated through an Identity Provider.
|
||||
|
||||
### Login Lifetimes
|
||||
|
||||
|
@ -19,7 +19,11 @@ You would have to create roles for administration and your clients in this very
|
||||
|
||||
To create a project, navigate to your organization, then projects or directly via <https://{your_domain}.zitadel.cloud/ui/console/projects>, and then click the button to create a new project.
|
||||
|
||||
<img alt="Empty Project" src="/docs/img/console_projects_empty.png" width="270px" />
|
||||
<img
|
||||
alt="Empty Project"
|
||||
src="/docs/img/console_projects_empty.png"
|
||||
width="270px"
|
||||
/>
|
||||
|
||||
then enter your project name and continue.
|
||||
|
||||
@ -51,7 +55,7 @@ Organizations can then create authorizations for their users on their own. The p
|
||||
|
||||
<img src="/docs/img/guides/console/grantsmenu.png" alt="Grants" width="170px" />
|
||||
|
||||
2. Enter the domain of the organization you want to grant (go to the organization detail page if you can't remember it), hit the search button and continue.
|
||||
2. Search the organization you want to grant using the auto complete input and continue.
|
||||
3. Select some roles you would like to grant to the organization and confirm.
|
||||
4. You should now see the granted organization in the section **grants**.
|
||||
|
||||
|
@ -43,6 +43,8 @@ ZITADEL is available in the following languages
|
||||
- Polish(pl)
|
||||
- 简体中文(zh)
|
||||
- Bulgarian (bg)
|
||||
- Portuguese (pt)
|
||||
- Macedonian (mk)
|
||||
|
||||
A language is displayed based on your agent's language header. The default language is English.
|
||||
|
||||
|
@ -0,0 +1,65 @@
|
||||
---
|
||||
title: Frontend and API communication
|
||||
---
|
||||
|
||||
This guide contains a use case and ZITADEL integration.
|
||||
|
||||
In a typical web application architecture, the front-end and back-end communicate to exchange data and provide functionality to users. Let's consider a use case where a front-end application needs to communicate with a back-end API using secure authentication and authorization. Let’s explore how ZITADEL can be used to add front-end login and facilitate this communication.
|
||||
|
||||
Single-Page Applications (SPAs) are web applications that run entirely in the browser, without a back-end server. In ZITADEL SPAs should use the Authorization Code Grant with PKCE or the Implicit Grant (if PKCE is not feasible) to obtain an access token.
|
||||
|
||||
While APIs are vital for communication between applications and services, they don't directly participate in user authentication. Instead, they often authorize client requests based on access tokens issued by an authorization server. APIs in ZITADEL use grant types like JWT Profile or Basic Authentication to access the authorization server's introspection endpoint for token validation.
|
||||
|
||||
## A real-world scenario
|
||||
|
||||
Suppose there is a news portal web app that allows users to browse through various news articles and personalize their news feed based on their preferences. The back-end API handles fetching and delivering news content from the database.
|
||||
|
||||
**Front-End Login**: A user visits the news portal and opts to log in for a personalized news feed. They are redirected to the Identity Provider’s (IdP) login page, where they authenticate with their credentials. Upon successful authentication, the IdP issues access and ID tokens to the front-end app.
|
||||
|
||||
** Back-End API Communication**: When the user browses through the news feed, the front-end app makes an API request to the back-end, including the access token in the Authorization header. The back-end API, upon receiving the request, uses the IdP’s introspection endpoint to validate the access token. Once validated, it fetches personalized news data based on the user's preferences from the news database.
|
||||
|
||||
While it is true that the back-end API typically needs to authenticate with the IdP, in this specific use case, the back-end API can work with a client credential / JWT since it's not a public client. This means that instead of relying on user-specific authentication, the back-end API can obtain a client credential (such as a client ID and client secret) from the IdP to authenticate itself and validate the access token received from the front-end app. This approach ensures secure communication between the front-end app, the back-end API, and the IdP, while still allowing the back-end API to access user-specific data and provide personalized news feeds.
|
||||
|
||||
## A simplified example with a React frontend and a Python Flask API
|
||||
|
||||
In this example, the application is a web-based quote generator that employs a secure user authentication system via ZITADEL. The functionality of the app is outlined below:
|
||||
|
||||
1. Upon starting, the application provides a login button
|
||||
2. The user is then redirected to ZITADEL's login page to enter their credentials.
|
||||
3. Once the login is successful, the application then greets the user by extracting the user's name, thus providing a personalized experience.
|
||||
4. The application presents an option for the user to generate a quote via a button. Upon pressing this button, the front-end application communicates with the back-end API using the user's access token received from ZITADEL.
|
||||
5. The back-end API introspects the access token for validity using ZITADEL's introspection endpoint. If the token is valid, the API generates a quote and sends it as a response to the front-end application, which is then displayed to the user in their browser.
|
||||
|
||||
## Setting up the applications and ZITADEL
|
||||
|
||||
|
||||
All code and instructions to run the sample application can be found at [https://github.com/zitadel/example-quote-generator-app/](https://github.com/zitadel/example-quote-generator-app/). You can also find the steps for the integration between the front-end, back-end API, and ZITADEL in the README.md.
|
||||
|
||||
You can create the front-end application (User Agent) and the API in the same project or in a different project. In this example, we have created both in one. Configure the applications with appropriate settings (as instructed).
|
||||
|
||||
<img src="/docs/img/guides/solution-scenarios/frontend-calling-backend-API_1.png" alt="User Agent and API applications in a single project"/>
|
||||
|
||||
|
||||
### Front-end login with ZITADEL
|
||||
|
||||
- You must create a User Agent application in your project to add login to your React application using the Authorization Code with PKCE flow. This allows the front-end application to integrate with ZITADEL to enable user authentication and authorization.
|
||||
- In the React front-end application, configure the ZITADEL OIDC client settings, including the client ID, ZITADEL URLs, redirect URIs, and required scopes.
|
||||
- Implement the login flow, authentication callbacks, and token handling logic in the front-end application.
|
||||
- When a user visits the front-end application, they are presented with a login option.
|
||||
- Upon clicking the login button, the frontend initiates the Authorization Code with PKCE authentication flow and redirects the user to the ZITADEL login page. The user enters their credentials and authenticates with ZITADEL. Authorization Code Flow returns an authorization code to the client application, which can then exchange it for an ID token and an access token directly. This provides the benefit of not exposing any tokens to the user agent and possibly other malicious applications with access to the user agent.
|
||||
- You must set up the required scopes and claims to ensure the front-end and back-end can exchange data securely. It’s important to note that when specifying the scope when calling the token API, the scope must contain the project ID of the ZITADEL project in which the API resides (to enable token validation by the back-end API):`scope:'openid profile email urn:zitadel:iam:org:project:id:<API_PROJECT_ID>:aud'`
|
||||
- Also, we want to include user info inside the token to avoid calling the user info endpoint, so go to Token Settings in the front-end app and select User Info inside ID Token.
|
||||
- After a successful authentication, ZITADEL generates an access token and an ID token.
|
||||
- The front-end application receives these tokens and stores them securely (e.g., in browser storage).
|
||||
|
||||
### Token exchange and user information
|
||||
|
||||
- Once the frontend obtains the tokens, it can extract certain information from the ID token itself (e.g., user ID, email, etc.) without making an additional request.
|
||||
- If more user information is required, the frontend can use the access token to call the ZITADEL User Info endpoint. This endpoint provides additional user details, such as name, profile picture, etc.
|
||||
|
||||
### Back-end API communication
|
||||
|
||||
- To communicate with the back-end API, the front-end includes the access token in the Authorization header of API requests.
|
||||
- The back-end API receives the request and needs to validate and authorize the token before processing the request.
|
||||
- The API performs token introspection using ZITADEL's introspection endpoint to validate the access token. This API uses Basic Authentication to invoke the [introspection endpoint](/docs/apis/openidoauth/endpoints#introspection_endpoint), which means it sends its client ID and client secret along with the access token received.
|
||||
- If the token is valid and active, the API proceeds to handle the requested action or fetch data from the underlying data sources.
|
@ -8,7 +8,7 @@
|
||||
<td><strong>Basic data</strong></td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Surname and first name</li>
|
||||
<li>Family and given name</li>
|
||||
<li>Email addresses</li>
|
||||
<li>User name</li>
|
||||
</ul>
|
||||
|
@ -21,7 +21,7 @@ Switzerland
|
||||
|
||||
Our representative in the EU is
|
||||
|
||||
**VGS Datenschutzpartner UG**
|
||||
**VGS Datenschutzpartner GmbH**
|
||||
Am Kaiserkai 69
|
||||
20457 Hamburg
|
||||
Germany
|
||||
|
@ -8,6 +8,8 @@ import DockerComposeSaSource from '!!raw-loader!./docker-compose-sa.yaml'
|
||||
import Disclaimer from './_disclaimer.mdx'
|
||||
import DefaultUser from './_defaultuser.mdx'
|
||||
import Next from './_next.mdx'
|
||||
import NoteInstanceNotFound from './troubleshooting/_note_instance_not_found.mdx';
|
||||
|
||||
|
||||
The setup is tested against Docker version 20.10.17 and Docker Compose version v2.2.3
|
||||
|
||||
@ -29,6 +31,8 @@ docker compose up --detach
|
||||
|
||||
<DefaultUser components={props.components} />
|
||||
|
||||
<NoteInstanceNotFound/>
|
||||
|
||||
## VideoGuide
|
||||
<iframe width="100%" height="315" src="https://www.youtube.com/embed/-02FaoN9Fko" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
|
@ -5,6 +5,8 @@ title: Knative
|
||||
import Disclaimer from './_disclaimer.mdx'
|
||||
import DefaultUser from './_defaultuser.mdx'
|
||||
import Next from './_next.mdx'
|
||||
import NoteInstanceNotFound from './troubleshooting/_note_instance_not_found.mdx';
|
||||
|
||||
|
||||
## Install Knative
|
||||
|
||||
@ -31,7 +33,7 @@ kn service create zitadel \
|
||||
--env ZITADEL_EXTERNALPORT=80 \
|
||||
--env ZITADEL_TLS_ENABLED=false \
|
||||
--env ZITADEL_EXTERNALDOMAIN=zitadel.default.127.0.0.1.sslip.io \
|
||||
--arg "start-from-init" --arg "--masterkey" --arg "MasterkeyNeedsToHave32Characters"
|
||||
--arg "start-from-init" --arg "--masterkey" --arg "MasterkeyNeedsToHave32Characters"
|
||||
```
|
||||
|
||||
### Knavite yaml
|
||||
@ -59,6 +61,8 @@ If you didn't configure something else, this is the default IAM admin users logi
|
||||
* username: zitadel-admin@<span></span>zitadel.zitadel.default.127.0.0.1.sslip.io
|
||||
* password: Password1!
|
||||
|
||||
<NoteInstanceNotFound/>
|
||||
|
||||
## VideoGuide
|
||||
<iframe width="100%" height="315" src="https://www.youtube.com/embed/m3TXmz3cK7E" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
<Next components={props.components} />
|
||||
|
@ -5,6 +5,8 @@ title: Kubernetes
|
||||
import Disclaimer from './_disclaimer.mdx'
|
||||
import DefaultUser from './_defaultuser.mdx'
|
||||
import Next from './_next.mdx'
|
||||
import NoteInstanceNotFound from './troubleshooting/_note_instance_not_found.mdx';
|
||||
|
||||
|
||||
Installation and configuration details are described in the [open source ZITADEL charts repo](https://github.com/zitadel/zitadel-charts).
|
||||
By default, the chart installs a secure and highly available ZITADEL instance.
|
||||
@ -46,6 +48,8 @@ kubectl port-forward svc/my-zitadel 8080
|
||||
|
||||
<DefaultUser components={props.components} />
|
||||
|
||||
<NoteInstanceNotFound/>
|
||||
|
||||
## Setup ZITADEL and a Service Account Admin
|
||||
|
||||
With this setup, you don't create a human user that has the IAM_OWNER role.
|
||||
|
@ -5,6 +5,8 @@ title: Linux
|
||||
import Disclaimer from './_disclaimer.mdx'
|
||||
import DefaultUser from './_defaultuser.mdx'
|
||||
import Next from './_next.mdx'
|
||||
import NoteInstanceNotFound from './troubleshooting/_note_instance_not_found.mdx';
|
||||
|
||||
|
||||
## Install CockroachDB
|
||||
|
||||
@ -38,6 +40,8 @@ ZITADEL_EXTERNALSECURE=false zitadel start-from-init --masterkey "MasterkeyNeeds
|
||||
|
||||
<DefaultUser components={props.components} />
|
||||
|
||||
<NoteInstanceNotFound/>
|
||||
|
||||
## VideoGuide
|
||||
<iframe width="100%" height="315" src="https://www.youtube.com/embed/YVLua-q7dbs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
@ -54,3 +58,5 @@ This key can be used to provision resources with for example [Terraform](/docs/g
|
||||
|
||||
<Next components={props.components} />
|
||||
<Disclaimer components={props.components} />
|
||||
|
||||
|
||||
|
@ -21,7 +21,6 @@ providers:
|
||||
filename: /etc/traefik/traefik.yaml
|
||||
|
||||
http:
|
||||
|
||||
middlewares:
|
||||
zitadel:
|
||||
headers:
|
||||
@ -29,7 +28,7 @@ http:
|
||||
allowedHosts:
|
||||
- 'my.domain'
|
||||
customRequestHeaders:
|
||||
:authority: 'my.domain'
|
||||
authority: 'my.domain'
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
|
@ -8,6 +8,7 @@ import ExampleTraefikSource from '!!raw-loader!./example-traefik.yaml'
|
||||
import ExampleZITADELConfigSource from '!!raw-loader!./example-zitadel-config.yaml'
|
||||
import ExampleZITADELSecretsSource from '!!raw-loader!./example-zitadel-secrets.yaml'
|
||||
import ExampleZITADELInitStepsSource from '!!raw-loader!./example-zitadel-init-steps.yaml'
|
||||
import NoteInstanceNotFound from '../troubleshooting/_note_instance_not_found.mdx';
|
||||
|
||||
With this example configuration, you create a near production environment for ZITADEL with [Docker Compose](https://docs.docker.com/compose/).
|
||||
|
||||
@ -61,7 +62,7 @@ export ZITADEL_MASTERKEY="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)"
|
||||
docker compose up --detach
|
||||
```
|
||||
|
||||
Make `127.0.0.1` available at `my.domain`. For example, this can be achived with an entry `127.0.1.1 my.domain` in the `/etc/hosts` file.
|
||||
Make `127.0.0.1` available at `my.domain`. For example, this can be achieved with an entry `127.0.0.1 my.domain` in the `/etc/hosts` file.
|
||||
|
||||
Open your favorite internet browser at [https://my.domain/ui/console/](https://my.domain/ui/console/).
|
||||
You can safely proceed, if your browser warns you about the insecure self-signed TLS certificate.
|
||||
@ -71,6 +72,8 @@ This is the IAM admin users login according to your configuration in the [exampl
|
||||
|
||||
Read more about [the login process](/guides/integrate/login-users).
|
||||
|
||||
<NoteInstanceNotFound/>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
You can connect to cockroach like this: `docker exec -it loadbalancing-example-my-cockroach-db-1 cockroach sql --host my-cockroach-db --certs-dir /cockroach/certs/`
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user