mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 13:19:21 +00:00
Merge branch 'main' into move-go
This commit is contained in:
2
.devcontainer/base/Dockerfile.dockerignore
Normal file
2
.devcontainer/base/Dockerfile.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!commands
|
39
.devcontainer/base/commands/login-integration.post-attach.sh
Executable file
39
.devcontainer/base/commands/login-integration.post-attach.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "$FAIL_COMMANDS_ON_ERRORS" == "true" ]; then
|
||||||
|
set -e
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo -e "THANKS FOR CONTRIBUTING TO ZITADEL 🚀"
|
||||||
|
echo
|
||||||
|
echo "Your dev container is configured for fixing login integration tests."
|
||||||
|
echo "The login is running in a separate container with the same configuration."
|
||||||
|
echo "It calls the mock-zitadel container which provides a mocked Zitadel gRPC API."
|
||||||
|
echo
|
||||||
|
echo "Also the test suite is configured correctly."
|
||||||
|
echo "For example, run a single test file:"
|
||||||
|
echo "pnpm cypress run --spec integration/integration/login.cy.ts"
|
||||||
|
echo
|
||||||
|
echo "You can also run the test interactively."
|
||||||
|
echo "However, this is only possible from outside the dev container."
|
||||||
|
echo "On your host machine, run:"
|
||||||
|
echo "cd apps/login"
|
||||||
|
echo "pnpm cypress open"
|
||||||
|
echo
|
||||||
|
echo "If you want to change the login code, you can replace the login container by a hot reloading dev server."
|
||||||
|
echo "docker stop login-integration"
|
||||||
|
echo "pnpm turbo dev"
|
||||||
|
echo "Navigate to the page you want to fix, for example:"
|
||||||
|
echo "http://localhost:3001/ui/v2/login/verify?userId=221394658884845598&code=abc"
|
||||||
|
echo "Change some code and reload the page for instant feedback."
|
||||||
|
echo
|
||||||
|
echo "When you are done, make sure all integration tests pass:"
|
||||||
|
echo "pnpm cypress run"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ "$FAIL_COMMANDS_ON_ERRORS" != "true" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
18
.devcontainer/base/commands/login-integration.update-content.sh
Executable file
18
.devcontainer/base/commands/login-integration.update-content.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "$FAIL_COMMANDS_ON_ERRORS" == "true" ]; then
|
||||||
|
echo "Running in fail-on-errors mode"
|
||||||
|
set -e
|
||||||
|
fi
|
||||||
|
|
||||||
|
pnpm install --frozen-lockfile \
|
||||||
|
--filter @zitadel/login \
|
||||||
|
--filter @zitadel/client \
|
||||||
|
--filter @zitadel/proto \
|
||||||
|
--filter zitadel-monorepo
|
||||||
|
pnpm cypress install
|
||||||
|
pnpm test:integration:login
|
||||||
|
|
||||||
|
if [ "$FAIL_COMMANDS_ON_ERRORS" != "true" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
30
.devcontainer/base/commands/turbo-lint-unit.post-attach.sh
Executable file
30
.devcontainer/base/commands/turbo-lint-unit.post-attach.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "$FAIL_COMMANDS_ON_ERRORS" == "true" ]; then
|
||||||
|
set -e
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo -e "THANKS FOR CONTRIBUTING TO ZITADEL 🚀"
|
||||||
|
echo
|
||||||
|
echo "Your dev container is configured for fixing linting and unit tests."
|
||||||
|
echo "No other services are running alongside this container."
|
||||||
|
echo
|
||||||
|
echo "To fix all auto-fixable linting errors, run:"
|
||||||
|
echo "pnpm turbo lint:fix"
|
||||||
|
echo
|
||||||
|
echo "To watch console linting errors, run:"
|
||||||
|
echo "pnpm turbo watch lint --filter console"
|
||||||
|
echo
|
||||||
|
echo "To watch @zitadel/client unit test failures, run:"
|
||||||
|
echo "pnpm turbo watch test:unit --filter @zitadel/client"
|
||||||
|
echo
|
||||||
|
echo "To watch @zitadel/login relevant unit tests and linting failures, run:"
|
||||||
|
echo "pnpm turbo watch lint test:unit --filter @zitadel/login..."
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ "$FAIL_COMMANDS_ON_ERRORS" != "true" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
12
.devcontainer/base/commands/turbo-lint-unit.update-content.sh
Executable file
12
.devcontainer/base/commands/turbo-lint-unit.update-content.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "$FAIL_COMMANDS_ON_ERRORS" == "true" ]; then
|
||||||
|
set -e
|
||||||
|
fi
|
||||||
|
|
||||||
|
pnpm install --frozen-lockfile --recursive
|
||||||
|
pnpm turbo lint test:unit
|
||||||
|
|
||||||
|
if [ "$FAIL_COMMANDS_ON_ERRORS" != "true" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
35
.devcontainer/login-integration/docker-compose.yaml
Normal file
35
.devcontainer/login-integration/docker-compose.yaml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
services:
|
||||||
|
login-integration-dev:
|
||||||
|
extends:
|
||||||
|
file: ../base/docker-compose.yaml
|
||||||
|
service: devcontainer
|
||||||
|
container_name: login-integration-dev
|
||||||
|
env_file: ../../apps/login/.env.test
|
||||||
|
environment:
|
||||||
|
CORE_MOCK_STUBS_URL: http://localhost:22220/v1/stubs
|
||||||
|
LOGIN_BASE_URL: http://localhost:3001/ui/v2/login
|
||||||
|
CYPRESS_CACHE_FOLDER: /workspaces/.artifacts/cypress
|
||||||
|
network_mode: service:mock-zitadel
|
||||||
|
depends_on:
|
||||||
|
login-integration:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
login-integration:
|
||||||
|
container_name: login-integration
|
||||||
|
image: "${LOGIN_TAG:-ghcr.io/zitadel/zitadel-login:latest}"
|
||||||
|
build:
|
||||||
|
context: ../..
|
||||||
|
dockerfile: build/login/Dockerfile
|
||||||
|
env_file: ../../apps/login/.env.test
|
||||||
|
network_mode: service:mock-zitadel
|
||||||
|
|
||||||
|
mock-zitadel:
|
||||||
|
container_name: mock-zitadel
|
||||||
|
build:
|
||||||
|
context: ../../apps/login/integration/core-mock
|
||||||
|
additional_contexts:
|
||||||
|
- zitadel-protos=../../proto
|
||||||
|
ports:
|
||||||
|
- 22220:22220
|
||||||
|
- 22222:22222
|
||||||
|
- 3001:3001
|
22
.devcontainer/login-subtree/devcontainer.json
Normal file
22
.devcontainer/login-subtree/devcontainer.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/refs/heads/main/schemas/devContainer.base.schema.json",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/typescript-node:20-bookworm",
|
||||||
|
"name": "Login Subtree Container - Use the Login As If You Would Have Forked the Mirror Repo",
|
||||||
|
"workspaceFolder": "/login",
|
||||||
|
"workspaceMount": "source=${localWorkspaceFolder}/apps/login,target=/login,type=bind,consistency=cached",
|
||||||
|
"mounts": [],
|
||||||
|
"forwardPorts": [
|
||||||
|
22220,
|
||||||
|
22222,
|
||||||
|
3000,
|
||||||
|
3001
|
||||||
|
],
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/go:1": {
|
||||||
|
"version": "1.24"
|
||||||
|
},
|
||||||
|
"ghcr.io/guiyomh/features/golangci-lint:0": {},
|
||||||
|
"ghcr.io/jungaretti/features/make:1": {},
|
||||||
|
"ghcr.io/devcontainers/features/docker-outside-of-docker": {}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,28 +0,0 @@
|
|||||||
# .git
|
|
||||||
.codecov
|
|
||||||
.github
|
|
||||||
.gitignore
|
|
||||||
.dockerignore
|
|
||||||
**/Dockerfile
|
|
||||||
/k8s/
|
|
||||||
/node_modules/
|
|
||||||
/console/src/app/proto/generated/
|
|
||||||
/console/.angular
|
|
||||||
/console/tmp/
|
|
||||||
.releaserc.js
|
|
||||||
changelog.config.js
|
|
||||||
CONTRIBUTING.md
|
|
||||||
LICENSE
|
|
||||||
README.md
|
|
||||||
SECURITY.md
|
|
||||||
**/Dockerfile
|
|
||||||
**/*.Dockerfile
|
|
||||||
pkg/grpc/*/*.pb.*
|
|
||||||
pkg/grpc/*/*.swagger.json
|
|
||||||
**/.artifacts
|
|
||||||
console/.angular
|
|
||||||
console/node_modules
|
|
||||||
console/src/app/proto/generated/
|
|
||||||
console/tmp/
|
|
||||||
.vscode
|
|
||||||
build/*.Dockerfile
|
|
58
.github/workflows/login-integration-test.yml
vendored
Normal file
58
.github/workflows/login-integration-test.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
name: Integration test core
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
login_build_image:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
login-integration-test:
|
||||||
|
name: login-integration-test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Dev Container CLI
|
||||||
|
run: npm install -g @devcontainers/cli@0.80.0
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Pull Login Build Image
|
||||||
|
run: docker compose --file .devcontainer/login-integration/docker-compose.yaml pull
|
||||||
|
env:
|
||||||
|
LOGIN_TAG: ${{ inputs.login_build_image }}
|
||||||
|
- name: Run Integration Tests against the Login and a Mocked Zitadel API
|
||||||
|
run: npm run devcontainer:integration:login
|
||||||
|
env:
|
||||||
|
LOGIN_TAG: ${{ inputs.login_build_image }}
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
- name: Fix Failures
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
echo "Reproduce this check locally:"
|
||||||
|
echo "LOGIN_TAG=${{ inputs.login_build_image }} npm run devcontainer:integration:login"
|
||||||
|
echo "To fix the failures, open the dev container called \"Login Integration Tests\"."
|
||||||
|
echo "You will have the same environment as the pipeline check as well as some guidance on how to fix the errors."
|
||||||
|
- name: Show Compose Status
|
||||||
|
if: failure()
|
||||||
|
run: docker compose --file .devcontainer/base/docker-compose.yaml --file .devcontainer/login-integration-ci/docker-compose.yaml ps
|
||||||
|
- name: Print Config
|
||||||
|
if: failure()
|
||||||
|
run: COMPOSE_BAKE=1 docker compose --file .devcontainer/base/docker-compose.yaml --file .devcontainer/login-integration-ci/docker-compose.yaml config login-integration
|
||||||
|
env:
|
||||||
|
LOGIN_TAG: ${{ inputs.login_build_image }}
|
||||||
|
- name: Show Container Logs
|
||||||
|
if: failure()
|
||||||
|
run: docker compose --file .devcontainer/base/docker-compose.yaml --file .devcontainer/login-integration-ci/docker-compose.yaml logs --timestamps --no-color --tail 100 login-integration
|
||||||
|
- name: Inspect All Failed Containers
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
docker ps -a --filter "status=exited" --filter "status=created" --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Image}}"
|
||||||
|
for container in $(docker ps -a --filter "status=exited" --filter "status=created" -q); do
|
||||||
|
echo "Inspecting container $container"
|
||||||
|
docker inspect $container || true
|
||||||
|
done
|
102
CONTRIBUTING.md
102
CONTRIBUTING.md
@@ -1,6 +1,5 @@
|
|||||||
# Contributing to Zitadel
|
# Contributing to Zitadel
|
||||||
|
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Thank you for your interest about how to contribute! As you might know there is more than code to contribute. You can find all information needed to start contributing here.
|
Thank you for your interest about how to contribute! As you might know there is more than code to contribute. You can find all information needed to start contributing here.
|
||||||
@@ -150,51 +149,19 @@ The API is designed to be used by different clients, such as web applications, m
|
|||||||
Therefore, the API is designed to be easy to use, consistent, and reliable.
|
Therefore, the API is designed to be easy to use, consistent, and reliable.
|
||||||
Please check out the dedicated [API guidelines](./API_DESIGN.md) page when contributing to the API.
|
Please check out the dedicated [API guidelines](./API_DESIGN.md) page when contributing to the API.
|
||||||
|
|
||||||
|
|
||||||
#### <a name="dev-containers"></>Developing Zitadel with Dev Containers
|
|
||||||
|
|
||||||
You can use dev containers if you'd like to make sure you have the same development environment like the corresponding GitHub PR checks use.
|
|
||||||
The following dev containers are available:
|
|
||||||
|
|
||||||
- **.devcontainer/base/devcontainer.json**: Contains everything you need to run whatever you want.
|
|
||||||
- **.devcontainer/turbo-lint-unit/devcontainer.json**: Runs a dev container that executes frontent linting and unit tests and then exits. This is useful to reproduce the corresponding GitHub PR check.
|
|
||||||
- **.devcontainer/turbo-lint-unit-debug/devcontainer.json**: Runs a dev container that executes frontent linting and unit tests in watch mode. You can fix the errors right away and have immediate feedback.
|
|
||||||
- **.devcontainer/login-integration/devcontainer.json**: Runs a dev container that executes login integration tests and then exits. This is useful to reproduce the corresponding GitHub PR check.
|
|
||||||
- **.devcontainer/login-integration-debug/devcontainer.json**: Runs a dev container that spins up the login in a hot-reloading dev server and executes login integration tests interactively. You can fix the errors right away and have immediate feedback.
|
|
||||||
|
|
||||||
You can also run the GitHub PR checks locally in dev containers without having to connect to a dev container.
|
|
||||||
|
|
||||||
|
|
||||||
The following pnpm commands use the [devcontainer CLI](https://github.com/devcontainers/cli/) and exit when the checks are done.
|
|
||||||
The minimal system requirements are having Docker and the devcontainers CLI installed.
|
|
||||||
If you don't have the node_modules installed already, you need to install the devcontainers CLI manually. Run `npm i -g @devcontainers/cli`. Alternatively, the [official Microsoft VS Code extension for Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) offers a command `Dev Containers: Install devcontainer CLI`
|
|
||||||
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run devcontainer:lint-unit
|
|
||||||
npm run devcontainer:integration:login
|
|
||||||
```
|
|
||||||
|
|
||||||
If you don't have NPM installed, copy and execute the scripts from the package.json directly.
|
|
||||||
|
|
||||||
To connect to a dev container to have full IDE support, follow the instructions provided by your code editor/IDE to initiate the dev container.
|
|
||||||
This typically involves opening the "Command Palette" or similar functionality and searching for commands related to "Dev Containers" or "Remote Containers".
|
|
||||||
The quick start guide for VS Code can found [here](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-an-existing-folder-in-a-container)
|
|
||||||
|
|
||||||
For example, to build and run the Zitadel binary in a dev container, connect your IDE to the dev container described in .devcontainer/base/devcontainer.json.
|
|
||||||
Run the following commands inside the container to start Zitadel.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make compile && ./zitadel start-from-init --masterkey MasterkeyNeedsToHave32Characters --tlsMode disabled
|
|
||||||
```
|
|
||||||
|
|
||||||
Zitadel serves traffic as soon as you can see the following log line:
|
|
||||||
|
|
||||||
`INFO[0001] server is listening on [::]:8080`
|
|
||||||
|
|
||||||
## <a name="backend"></a>Contribute Backend Code
|
## <a name="backend"></a>Contribute Backend Code
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### <a name="backend-requirements"></a> Backend Requirements
|
||||||
|
|
||||||
By executing the commands from this section, you run everything you need to develop the Zitadel backend locally.
|
By executing the commands from this section, you run everything you need to develop the Zitadel backend locally.
|
||||||
|
|
||||||
|
> [!INFO]
|
||||||
|
> Some [dev containers are available](dev-containers) for remote development with docker and pipeline debugging in isolated environments.
|
||||||
|
> If you don't want to use one of the dev containers, you can develop the backend components directly on your local machine.
|
||||||
|
> To do so, proceed with installing the necessary dependencies.
|
||||||
|
|
||||||
Using [Docker Compose](https://docs.docker.com/compose/), you run a [PostgreSQL](https://www.postgresql.org/download/) on your local machine.
|
Using [Docker Compose](https://docs.docker.com/compose/), you run a [PostgreSQL](https://www.postgresql.org/download/) on your local machine.
|
||||||
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).
|
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.
|
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.
|
||||||
@@ -208,6 +175,8 @@ The commands in this section are tested against the following software versions:
|
|||||||
- [Go version 1.22](https://go.dev/doc/install)
|
- [Go version 1.22](https://go.dev/doc/install)
|
||||||
- [Delve 1.9.1](https://github.com/go-delve/delve/tree/v1.9.1/Documentation/installation)
|
- [Delve 1.9.1](https://github.com/go-delve/delve/tree/v1.9.1/Documentation/installation)
|
||||||
|
|
||||||
|
### <a name="build-and-run-zitadel"></a>Build and Run Zitadel
|
||||||
|
|
||||||
Make some changes to the source code, then run the database locally.
|
Make some changes to the source code, then run the database locally.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -588,6 +557,48 @@ For the turbo commands, check your options with `pnpm turbo --help`
|
|||||||
| `pnpm turbo down` | Remove containers and volumes | Shut down containers from the integration test setup `pnpm turbo down` |
|
| `pnpm turbo down` | Remove containers and volumes | Shut down containers from the integration test setup `pnpm turbo down` |
|
||||||
| `pnpm turbo clean` | Remove downloaded dependencies and other generated files | Remove generated docs `pnpm turbo clean --filter zitadel-docs` |
|
| `pnpm turbo clean` | Remove downloaded dependencies and other generated files | Remove generated docs `pnpm turbo clean --filter zitadel-docs` |
|
||||||
|
|
||||||
|
## <a name="dev-containers"></>Developing Zitadel with Dev Containers
|
||||||
|
|
||||||
|
You can use dev containers if you'd like to make sure you have the same development environment like the corresponding GitHub PR checks use.
|
||||||
|
The following dev containers are available:
|
||||||
|
|
||||||
|
- **.devcontainer/base/devcontainer.json**: Contains everything you need to run whatever you want.
|
||||||
|
- **.devcontainer/turbo-lint-unit/devcontainer.json**: Runs a dev container that executes frontent linting and unit tests and then exits. This is useful to reproduce the corresponding GitHub PR check.
|
||||||
|
- **.devcontainer/turbo-lint-unit-debug/devcontainer.json**: Runs a dev container that executes frontent linting and unit tests in watch mode. You can fix the errors right away and have immediate feedback.
|
||||||
|
- **.devcontainer/login-integration/devcontainer.json**: Runs a dev container that executes login integration tests and then exits. This is useful to reproduce the corresponding GitHub PR check.
|
||||||
|
- **.devcontainer/login-integration-debug/devcontainer.json**: Runs a dev container that spins up the login in a hot-reloading dev server and executes login integration tests interactively. You can fix the errors right away and have immediate feedback.
|
||||||
|
|
||||||
|
You can also run the GitHub PR checks locally in dev containers without having to connect to a dev container.
|
||||||
|
|
||||||
|
|
||||||
|
The following pnpm commands use the [devcontainer CLI](https://github.com/devcontainers/cli/) and exit when the checks are done.
|
||||||
|
The minimal system requirements are having Docker and the devcontainers CLI installed.
|
||||||
|
If you don't have the node_modules installed already, you need to install the devcontainers CLI manually. Run `npm i -g @devcontainers/cli@0.80.0`. Alternatively, the [official Microsoft VS Code extension for Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) offers a command `Dev Containers: Install devcontainer CLI`
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run devcontainer:lint-unit
|
||||||
|
npm run devcontainer:integration:login
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't have NPM installed, copy and execute the scripts from the package.json directly.
|
||||||
|
|
||||||
|
To connect to a dev container to have full IDE support, follow the instructions provided by your code editor/IDE to initiate the dev container.
|
||||||
|
This typically involves opening the "Command Palette" or similar functionality and searching for commands related to "Dev Containers" or "Remote Containers".
|
||||||
|
The quick start guide for VS Code can found [here](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-an-existing-folder-in-a-container)
|
||||||
|
|
||||||
|
For example, to build and run the Zitadel binary in a dev container, connect your IDE to the dev container described in .devcontainer/base/devcontainer.json.
|
||||||
|
Run the following commands inside the container to start Zitadel.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make compile && ./zitadel start-from-init --masterkey MasterkeyNeedsToHave32Characters --tlsMode disabled
|
||||||
|
```
|
||||||
|
|
||||||
|
Zitadel serves traffic as soon as you can see the following log line:
|
||||||
|
|
||||||
|
`INFO[0001] server is listening on [::]:8080`
|
||||||
|
|
||||||
|
|
||||||
## <a name="contribute-translations"></a>Contribute Translations
|
## <a name="contribute-translations"></a>Contribute Translations
|
||||||
|
|
||||||
Zitadel loads translations from four files:
|
Zitadel loads translations from four files:
|
||||||
@@ -608,11 +619,6 @@ You also have to add some changes to the following files:
|
|||||||
- [Customized Text Docs](./docs/docs/guides/manage/customize/texts.md)
|
- [Customized Text Docs](./docs/docs/guides/manage/customize/texts.md)
|
||||||
- [Add language option](./internal/api/ui/login/static/templates/external_not_found_option.html)
|
- [Add language option](./internal/api/ui/login/static/templates/external_not_found_option.html)
|
||||||
|
|
||||||
## Want to start Zitadel?
|
|
||||||
|
|
||||||
You can find an installation guide for all the different environments here:
|
|
||||||
[https://zitadel.com/docs/self-hosting/deploy/overview](https://zitadel.com/docs/self-hosting/deploy/overview)
|
|
||||||
|
|
||||||
## **Did you find a security flaw?**
|
## **Did you find a security flaw?**
|
||||||
|
|
||||||
- Please read [Security Policy](./SECURITY.md).
|
- Please read [Security Policy](./SECURITY.md).
|
||||||
|
@@ -100,9 +100,7 @@ linters:
|
|||||||
- .keys
|
- .keys
|
||||||
- .vscode
|
- .vscode
|
||||||
- build
|
- build
|
||||||
- console
|
|
||||||
- deploy
|
- deploy
|
||||||
- docs
|
|
||||||
- guides
|
- guides
|
||||||
- internal/api/ui/login/static
|
- internal/api/ui/login/static
|
||||||
- openapi
|
- openapi
|
||||||
@@ -111,6 +109,12 @@ linters:
|
|||||||
- third_party$
|
- third_party$
|
||||||
- builtin$
|
- builtin$
|
||||||
- examples$
|
- examples$
|
||||||
|
- apps
|
||||||
|
- packages
|
||||||
|
- console
|
||||||
|
- docs
|
||||||
|
- load-test
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
max-issues-per-linter: 0
|
max-issues-per-linter: 0
|
||||||
max-same-issues: 0
|
max-same-issues: 0
|
||||||
@@ -135,9 +139,7 @@ formatters:
|
|||||||
- .keys
|
- .keys
|
||||||
- .vscode
|
- .vscode
|
||||||
- build
|
- build
|
||||||
- console
|
|
||||||
- deploy
|
- deploy
|
||||||
- docs
|
|
||||||
- guides
|
- guides
|
||||||
- internal/api/ui/login/static
|
- internal/api/ui/login/static
|
||||||
- openapi
|
- openapi
|
||||||
@@ -146,3 +148,8 @@ formatters:
|
|||||||
- third_party$
|
- third_party$
|
||||||
- builtin$
|
- builtin$
|
||||||
- examples$
|
- examples$
|
||||||
|
- apps
|
||||||
|
- packages
|
||||||
|
- console
|
||||||
|
- docs
|
||||||
|
- load-test
|
||||||
|
@@ -524,7 +524,7 @@ OIDC:
|
|||||||
PollInterval: 5s # ZITADEL_OIDC_DEVICEAUTH_POLLINTERVAL
|
PollInterval: 5s # ZITADEL_OIDC_DEVICEAUTH_POLLINTERVAL
|
||||||
UserCode:
|
UserCode:
|
||||||
CharSet: "BCDFGHJKLMNPQRSTVWXZ" # ZITADEL_OIDC_DEVICEAUTH_USERCODE_CHARSET
|
CharSet: "BCDFGHJKLMNPQRSTVWXZ" # ZITADEL_OIDC_DEVICEAUTH_USERCODE_CHARSET
|
||||||
CharAmount: 8 # ZITADEL_OIDC_DEVICEAUTH_USERCODE_CHARARMOUNT
|
CharAmount: 8 # ZITADEL_OIDC_DEVICEAUTH_USERCODE_CHARAMOUNT
|
||||||
DashInterval: 4 # ZITADEL_OIDC_DEVICEAUTH_USERCODE_DASHINTERVAL
|
DashInterval: 4 # ZITADEL_OIDC_DEVICEAUTH_USERCODE_DASHINTERVAL
|
||||||
DefaultLoginURLV2: "/ui/v2/login/login?authRequest=" # ZITADEL_OIDC_DEFAULTLOGINURLV2
|
DefaultLoginURLV2: "/ui/v2/login/login?authRequest=" # ZITADEL_OIDC_DEFAULTLOGINURLV2
|
||||||
DefaultLogoutURLV2: "/ui/v2/login/logout?post_logout_redirect=" # ZITADEL_OIDC_DEFAULTLOGOUTURLV2
|
DefaultLogoutURLV2: "/ui/v2/login/logout?post_logout_redirect=" # ZITADEL_OIDC_DEFAULTLOGOUTURLV2
|
||||||
|
@@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/api"
|
"github.com/zitadel/zitadel/internal/api"
|
||||||
"github.com/zitadel/zitadel/internal/api/assets"
|
"github.com/zitadel/zitadel/internal/api/assets"
|
||||||
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
|
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
|
||||||
|
action_v2 "github.com/zitadel/zitadel/internal/api/grpc/action/v2"
|
||||||
action_v2_beta "github.com/zitadel/zitadel/internal/api/grpc/action/v2beta"
|
action_v2_beta "github.com/zitadel/zitadel/internal/api/grpc/action/v2beta"
|
||||||
"github.com/zitadel/zitadel/internal/api/grpc/admin"
|
"github.com/zitadel/zitadel/internal/api/grpc/admin"
|
||||||
app "github.com/zitadel/zitadel/internal/api/grpc/app/v2beta"
|
app "github.com/zitadel/zitadel/internal/api/grpc/app/v2beta"
|
||||||
@@ -509,6 +510,9 @@ func startAPIs(
|
|||||||
if err := apis.RegisterService(ctx, action_v2_beta.CreateServer(config.SystemDefaults, commands, queries, domain.AllActionFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil {
|
if err := apis.RegisterService(ctx, action_v2_beta.CreateServer(config.SystemDefaults, commands, queries, domain.AllActionFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := apis.RegisterService(ctx, action_v2.CreateServer(config.SystemDefaults, commands, queries, domain.AllActionFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if err := apis.RegisterService(ctx, project_v2beta.CreateServer(config.SystemDefaults, commands, queries, permissionCheck)); err != nil {
|
if err := apis.RegisterService(ctx, project_v2beta.CreateServer(config.SystemDefaults, commands, queries, permissionCheck)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/repository/execution"
|
"github.com/zitadel/zitadel/internal/repository/execution"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) SetExecution(ctx context.Context, req *connect.Request[action.SetExecutionRequest]) (*connect.Response[action.SetExecutionResponse], error) {
|
func (s *Server) SetExecution(ctx context.Context, req *connect.Request[action.SetExecutionRequest]) (*connect.Response[action.SetExecutionResponse], error) {
|
@@ -28,7 +28,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/integration"
|
"github.com/zitadel/zitadel/internal/integration"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/app"
|
"github.com/zitadel/zitadel/pkg/grpc/app"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/management"
|
"github.com/zitadel/zitadel/pkg/grpc/management"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/metadata"
|
"github.com/zitadel/zitadel/pkg/grpc/metadata"
|
||||||
@@ -48,7 +48,7 @@ var (
|
|||||||
|
|
||||||
func TestServer_ExecutionTarget(t *testing.T) {
|
func TestServer_ExecutionTarget(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
fullMethod := action.ActionService_GetTarget_FullMethodName
|
fullMethod := action.ActionService_GetTarget_FullMethodName
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -272,7 +272,7 @@ func TestServer_ExecutionTarget(t *testing.T) {
|
|||||||
|
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.ctx, time.Minute)
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.ctx, time.Minute)
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
got, err := instance.Client.ActionV2beta.GetTarget(tt.ctx, tt.req)
|
got, err := instance.Client.ActionV2.GetTarget(tt.ctx, tt.req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(ttt, err)
|
require.Error(ttt, err)
|
||||||
return
|
return
|
||||||
@@ -292,7 +292,7 @@ func TestServer_ExecutionTarget(t *testing.T) {
|
|||||||
|
|
||||||
func TestServer_ExecutionTarget_Event(t *testing.T) {
|
func TestServer_ExecutionTarget_Event(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
|
|
||||||
event := "session.added"
|
event := "session.added"
|
||||||
urlRequest, closeF, calledF, resetF := integration.TestServerCall(nil, 0, http.StatusOK, nil)
|
urlRequest, closeF, calledF, resetF := integration.TestServerCall(nil, 0, http.StatusOK, nil)
|
||||||
@@ -349,7 +349,7 @@ func TestServer_ExecutionTarget_Event(t *testing.T) {
|
|||||||
|
|
||||||
func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
|
func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
|
|
||||||
event := "session.added"
|
event := "session.added"
|
||||||
// call takes longer than timeout of target
|
// call takes longer than timeout of target
|
||||||
@@ -401,7 +401,7 @@ func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
|
|||||||
|
|
||||||
func TestServer_ExecutionTarget_Event_LongerThanTransactionTimeout(t *testing.T) {
|
func TestServer_ExecutionTarget_Event_LongerThanTransactionTimeout(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
|
|
||||||
event := "session.added"
|
event := "session.added"
|
||||||
urlRequest, closeF, calledF, resetF := integration.TestServerCall(nil, 1*time.Second, http.StatusOK, nil)
|
urlRequest, closeF, calledF, resetF := integration.TestServerCall(nil, 1*time.Second, http.StatusOK, nil)
|
||||||
@@ -467,7 +467,7 @@ func waitForExecutionOnCondition(ctx context.Context, t *testing.T, instance *in
|
|||||||
|
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
got, err := instance.Client.ActionV2beta.ListExecutions(ctx, &action.ListExecutionsRequest{
|
got, err := instance.Client.ActionV2.ListExecutions(ctx, &action.ListExecutionsRequest{
|
||||||
Filters: []*action.ExecutionSearchFilter{
|
Filters: []*action.ExecutionSearchFilter{
|
||||||
{Filter: &action.ExecutionSearchFilter_InConditionsFilter{
|
{Filter: &action.ExecutionSearchFilter_InConditionsFilter{
|
||||||
InConditionsFilter: &action.InConditionsFilter{Conditions: []*action.Condition{condition}},
|
InConditionsFilter: &action.InConditionsFilter{Conditions: []*action.Condition{condition}},
|
||||||
@@ -488,7 +488,6 @@ func waitForExecutionOnCondition(ctx context.Context, t *testing.T, instance *in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, retryDuration, tick, "timeout waiting for expected execution result")
|
}, retryDuration, tick, "timeout waiting for expected execution result")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForTarget(ctx context.Context, t *testing.T, instance *integration.Instance, endpoint string, ty domain.TargetType, interrupt bool) *action.CreateTargetResponse {
|
func waitForTarget(ctx context.Context, t *testing.T, instance *integration.Instance, endpoint string, ty domain.TargetType, interrupt bool) *action.CreateTargetResponse {
|
||||||
@@ -496,7 +495,7 @@ func waitForTarget(ctx context.Context, t *testing.T, instance *integration.Inst
|
|||||||
|
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
got, err := instance.Client.ActionV2beta.ListTargets(ctx, &action.ListTargetsRequest{
|
got, err := instance.Client.ActionV2.ListTargets(ctx, &action.ListTargetsRequest{
|
||||||
Filters: []*action.TargetSearchFilter{
|
Filters: []*action.TargetSearchFilter{
|
||||||
{Filter: &action.TargetSearchFilter_InTargetIdsFilter{
|
{Filter: &action.TargetSearchFilter_InTargetIdsFilter{
|
||||||
InTargetIdsFilter: &action.InTargetIDsFilter{TargetIds: []string{resp.GetId()}},
|
InTargetIdsFilter: &action.InTargetIDsFilter{TargetIds: []string{resp.GetId()}},
|
||||||
@@ -577,8 +576,8 @@ func conditionFunction(function string) *action.Condition {
|
|||||||
|
|
||||||
func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMCtx := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
|
ctxLoginClient := instance.WithAuthorizationToken(CTX, integration.UserTypeLogin)
|
||||||
|
|
||||||
client, err := instance.CreateOIDCImplicitFlowClient(isolatedIAMCtx, t, redirectURIImplicit, loginV2)
|
client, err := instance.CreateOIDCImplicitFlowClient(isolatedIAMCtx, t, redirectURIImplicit, loginV2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -893,8 +892,8 @@ func contextInfoForUserOIDC(instance *integration.Instance, function string, cli
|
|||||||
|
|
||||||
func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMCtx := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
|
ctxLoginClient := instance.WithAuthorizationToken(CTX, integration.UserTypeLogin)
|
||||||
|
|
||||||
client, err := instance.CreateOIDCImplicitFlowClient(isolatedIAMCtx, t, redirectURIImplicit, loginV2)
|
client, err := instance.CreateOIDCImplicitFlowClient(isolatedIAMCtx, t, redirectURIImplicit, loginV2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -1086,8 +1085,8 @@ func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *
|
|||||||
|
|
||||||
func TestServer_ExecutionTargetPreSAMLResponse(t *testing.T) {
|
func TestServer_ExecutionTargetPreSAMLResponse(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMCtx := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
|
ctxLoginClient := instance.WithAuthorizationToken(CTX, integration.UserTypeLogin)
|
||||||
|
|
||||||
idpMetadata, err := instance.GetSAMLIDPMetadata()
|
idpMetadata, err := instance.GetSAMLIDPMetadata()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
@@ -12,12 +12,12 @@ import (
|
|||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/integration"
|
"github.com/zitadel/zitadel/internal/integration"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_SetExecution_Request(t *testing.T) {
|
func TestServer_SetExecution_Request(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
|
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -29,7 +29,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.SetExecutionRequest{
|
req: &action.SetExecutionRequest{
|
||||||
Condition: &action.Condition{
|
Condition: &action.Condition{
|
||||||
ConditionType: &action.Condition_Request{
|
ConditionType: &action.Condition_Request{
|
||||||
@@ -62,7 +62,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
|
|||||||
ConditionType: &action.Condition_Request{
|
ConditionType: &action.Condition_Request{
|
||||||
Request: &action.RequestExecution{
|
Request: &action.RequestExecution{
|
||||||
Condition: &action.RequestExecution_Method{
|
Condition: &action.RequestExecution_Method{
|
||||||
Method: "/zitadel.session.v2beta.NotExistingService/List",
|
Method: "/zitadel.session.v2.NotExistingService/List",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -79,7 +79,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
|
|||||||
ConditionType: &action.Condition_Request{
|
ConditionType: &action.Condition_Request{
|
||||||
Request: &action.RequestExecution{
|
Request: &action.RequestExecution{
|
||||||
Condition: &action.RequestExecution_Method{
|
Condition: &action.RequestExecution_Method{
|
||||||
Method: "/zitadel.session.v2beta.SessionService/ListSessions",
|
Method: "/zitadel.session.v2.SessionService/ListSessions",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -113,7 +113,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
|
|||||||
ConditionType: &action.Condition_Request{
|
ConditionType: &action.Condition_Request{
|
||||||
Request: &action.RequestExecution{
|
Request: &action.RequestExecution{
|
||||||
Condition: &action.RequestExecution_Service{
|
Condition: &action.RequestExecution_Service{
|
||||||
Service: "zitadel.session.v2beta.SessionService",
|
Service: "zitadel.session.v2.SessionService",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -144,7 +144,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// We want to have the same response no matter how often we call the function
|
// We want to have the same response no matter how often we call the function
|
||||||
creationDate := time.Now().UTC()
|
creationDate := time.Now().UTC()
|
||||||
got, err := instance.Client.ActionV2beta.SetExecution(tt.ctx, tt.req)
|
got, err := instance.Client.ActionV2.SetExecution(tt.ctx, tt.req)
|
||||||
setDate := time.Now().UTC()
|
setDate := time.Now().UTC()
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
@@ -174,7 +174,7 @@ func assertSetExecutionResponse(t *testing.T, creationDate, setDate time.Time, e
|
|||||||
|
|
||||||
func TestServer_SetExecution_Response(t *testing.T) {
|
func TestServer_SetExecution_Response(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
|
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -186,7 +186,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.SetExecutionRequest{
|
req: &action.SetExecutionRequest{
|
||||||
Condition: &action.Condition{
|
Condition: &action.Condition{
|
||||||
ConditionType: &action.Condition_Response{
|
ConditionType: &action.Condition_Response{
|
||||||
@@ -219,7 +219,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
|
|||||||
ConditionType: &action.Condition_Response{
|
ConditionType: &action.Condition_Response{
|
||||||
Response: &action.ResponseExecution{
|
Response: &action.ResponseExecution{
|
||||||
Condition: &action.ResponseExecution_Method{
|
Condition: &action.ResponseExecution_Method{
|
||||||
Method: "/zitadel.session.v2beta.NotExistingService/List",
|
Method: "/zitadel.session.v2.NotExistingService/List",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -236,7 +236,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
|
|||||||
ConditionType: &action.Condition_Response{
|
ConditionType: &action.Condition_Response{
|
||||||
Response: &action.ResponseExecution{
|
Response: &action.ResponseExecution{
|
||||||
Condition: &action.ResponseExecution_Method{
|
Condition: &action.ResponseExecution_Method{
|
||||||
Method: "/zitadel.session.v2beta.SessionService/ListSessions",
|
Method: "/zitadel.session.v2.SessionService/ListSessions",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -270,7 +270,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
|
|||||||
ConditionType: &action.Condition_Response{
|
ConditionType: &action.Condition_Response{
|
||||||
Response: &action.ResponseExecution{
|
Response: &action.ResponseExecution{
|
||||||
Condition: &action.ResponseExecution_Service{
|
Condition: &action.ResponseExecution_Service{
|
||||||
Service: "zitadel.session.v2beta.SessionService",
|
Service: "zitadel.session.v2.SessionService",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -300,7 +300,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
creationDate := time.Now().UTC()
|
creationDate := time.Now().UTC()
|
||||||
got, err := instance.Client.ActionV2beta.SetExecution(tt.ctx, tt.req)
|
got, err := instance.Client.ActionV2.SetExecution(tt.ctx, tt.req)
|
||||||
setDate := time.Now().UTC()
|
setDate := time.Now().UTC()
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@@ -318,7 +318,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
|
|||||||
|
|
||||||
func TestServer_SetExecution_Event(t *testing.T) {
|
func TestServer_SetExecution_Event(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
|
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -330,7 +330,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.SetExecutionRequest{
|
req: &action.SetExecutionRequest{
|
||||||
Condition: &action.Condition{
|
Condition: &action.Condition{
|
||||||
ConditionType: &action.Condition_Event{
|
ConditionType: &action.Condition_Event{
|
||||||
@@ -463,7 +463,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
creationDate := time.Now().UTC()
|
creationDate := time.Now().UTC()
|
||||||
got, err := instance.Client.ActionV2beta.SetExecution(tt.ctx, tt.req)
|
got, err := instance.Client.ActionV2.SetExecution(tt.ctx, tt.req)
|
||||||
setDate := time.Now().UTC()
|
setDate := time.Now().UTC()
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@@ -481,7 +481,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
|
|||||||
|
|
||||||
func TestServer_SetExecution_Function(t *testing.T) {
|
func TestServer_SetExecution_Function(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
|
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -493,7 +493,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.SetExecutionRequest{
|
req: &action.SetExecutionRequest{
|
||||||
Condition: &action.Condition{
|
Condition: &action.Condition{
|
||||||
ConditionType: &action.Condition_Response{
|
ConditionType: &action.Condition_Response{
|
||||||
@@ -548,7 +548,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
creationDate := time.Now().UTC()
|
creationDate := time.Now().UTC()
|
||||||
got, err := instance.Client.ActionV2beta.SetExecution(tt.ctx, tt.req)
|
got, err := instance.Client.ActionV2.SetExecution(tt.ctx, tt.req)
|
||||||
setDate := time.Now().UTC()
|
setDate := time.Now().UTC()
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
@@ -15,13 +15,13 @@ import (
|
|||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/integration"
|
"github.com/zitadel/zitadel/internal/integration"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
filter "github.com/zitadel/zitadel/pkg/grpc/filter/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/filter/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_GetTarget(t *testing.T) {
|
func TestServer_GetTarget(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
dep func(context.Context, *action.GetTargetRequest, *action.GetTargetResponse) error
|
dep func(context.Context, *action.GetTargetRequest, *action.GetTargetResponse) error
|
||||||
@@ -36,7 +36,7 @@ func TestServer_GetTarget(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
args: args{
|
args: args{
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.GetTargetRequest{},
|
req: &action.GetTargetRequest{},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@@ -199,7 +199,7 @@ func TestServer_GetTarget(t *testing.T) {
|
|||||||
}
|
}
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, 2*time.Minute)
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, 2*time.Minute)
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
got, err := instance.Client.ActionV2beta.GetTarget(tt.args.ctx, tt.args.req)
|
got, err := instance.Client.ActionV2.GetTarget(tt.args.ctx, tt.args.req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
assert.Error(ttt, err, "Error: "+err.Error())
|
assert.Error(ttt, err, "Error: "+err.Error())
|
||||||
return
|
return
|
||||||
@@ -213,7 +213,7 @@ func TestServer_GetTarget(t *testing.T) {
|
|||||||
|
|
||||||
func TestServer_ListTargets(t *testing.T) {
|
func TestServer_ListTargets(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
dep func(context.Context, *action.ListTargetsRequest, *action.ListTargetsResponse)
|
dep func(context.Context, *action.ListTargetsRequest, *action.ListTargetsResponse)
|
||||||
@@ -228,7 +228,7 @@ func TestServer_ListTargets(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
args: args{
|
args: args{
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.ListTargetsRequest{},
|
req: &action.ListTargetsRequest{},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@@ -419,7 +419,7 @@ func TestServer_ListTargets(t *testing.T) {
|
|||||||
|
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, time.Minute)
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, time.Minute)
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
got, listErr := instance.Client.ActionV2beta.ListTargets(tt.args.ctx, tt.args.req)
|
got, listErr := instance.Client.ActionV2.ListTargets(tt.args.ctx, tt.args.req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(ttt, listErr, "Error: "+listErr.Error())
|
require.Error(ttt, listErr, "Error: "+listErr.Error())
|
||||||
return
|
return
|
||||||
@@ -445,7 +445,7 @@ func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationRes
|
|||||||
|
|
||||||
func TestServer_ListExecutions(t *testing.T) {
|
func TestServer_ListExecutions(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
|
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
@@ -462,7 +462,7 @@ func TestServer_ListExecutions(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
args: args{
|
args: args{
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.ListExecutionsRequest{},
|
req: &action.ListExecutionsRequest{},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@@ -767,7 +767,7 @@ func TestServer_ListExecutions(t *testing.T) {
|
|||||||
|
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, time.Minute)
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, time.Minute)
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
got, listErr := instance.Client.ActionV2beta.ListExecutions(tt.args.ctx, tt.args.req)
|
got, listErr := instance.Client.ActionV2.ListExecutions(tt.args.ctx, tt.args.req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(ttt, listErr, "Error: "+listErr.Error())
|
require.Error(ttt, listErr, "Error: "+listErr.Error())
|
||||||
return
|
return
|
@@ -14,12 +14,12 @@ import (
|
|||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/integration"
|
"github.com/zitadel/zitadel/internal/integration"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_CreateTarget(t *testing.T) {
|
func TestServer_CreateTarget(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
type want struct {
|
type want struct {
|
||||||
id bool
|
id bool
|
||||||
creationDate bool
|
creationDate bool
|
||||||
@@ -36,7 +36,7 @@ func TestServer_CreateTarget(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.CreateTargetRequest{
|
req: &action.CreateTargetRequest{
|
||||||
Name: gofakeit.Name(),
|
Name: gofakeit.Name(),
|
||||||
},
|
},
|
||||||
@@ -205,7 +205,7 @@ func TestServer_CreateTarget(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
creationDate := time.Now().UTC()
|
creationDate := time.Now().UTC()
|
||||||
got, err := instance.Client.ActionV2beta.CreateTarget(tt.ctx, tt.req)
|
got, err := instance.Client.ActionV2.CreateTarget(tt.ctx, tt.req)
|
||||||
changeDate := time.Now().UTC()
|
changeDate := time.Now().UTC()
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
@@ -243,7 +243,7 @@ func assertCreateTargetResponse(t *testing.T, creationDate, changeDate time.Time
|
|||||||
|
|
||||||
func TestServer_UpdateTarget(t *testing.T) {
|
func TestServer_UpdateTarget(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
isolatedIAMOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
req *action.UpdateTargetRequest
|
req *action.UpdateTargetRequest
|
||||||
@@ -267,7 +267,7 @@ func TestServer_UpdateTarget(t *testing.T) {
|
|||||||
request.Id = targetID
|
request.Id = targetID
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.UpdateTargetRequest{
|
req: &action.UpdateTargetRequest{
|
||||||
Name: gu.Ptr(gofakeit.Name()),
|
Name: gu.Ptr(gofakeit.Name()),
|
||||||
},
|
},
|
||||||
@@ -278,7 +278,6 @@ func TestServer_UpdateTarget(t *testing.T) {
|
|||||||
name: "not existing",
|
name: "not existing",
|
||||||
prepare: func(request *action.UpdateTargetRequest) {
|
prepare: func(request *action.UpdateTargetRequest) {
|
||||||
request.Id = "notexisting"
|
request.Id = "notexisting"
|
||||||
return
|
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: isolatedIAMOwnerCTX,
|
ctx: isolatedIAMOwnerCTX,
|
||||||
@@ -426,7 +425,7 @@ func TestServer_UpdateTarget(t *testing.T) {
|
|||||||
creationDate := time.Now().UTC()
|
creationDate := time.Now().UTC()
|
||||||
tt.prepare(tt.args.req)
|
tt.prepare(tt.args.req)
|
||||||
|
|
||||||
got, err := instance.Client.ActionV2beta.UpdateTarget(tt.args.ctx, tt.args.req)
|
got, err := instance.Client.ActionV2.UpdateTarget(tt.args.ctx, tt.args.req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
return
|
return
|
||||||
@@ -461,7 +460,7 @@ func assertUpdateTargetResponse(t *testing.T, creationDate, changeDate time.Time
|
|||||||
|
|
||||||
func TestServer_DeleteTarget(t *testing.T) {
|
func TestServer_DeleteTarget(t *testing.T) {
|
||||||
instance := integration.NewInstance(CTX)
|
instance := integration.NewInstance(CTX)
|
||||||
iamOwnerCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
iamOwnerCtx := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -472,7 +471,7 @@ func TestServer_DeleteTarget(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "missing permission",
|
name: "missing permission",
|
||||||
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
|
ctx: instance.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner),
|
||||||
req: &action.DeleteTargetRequest{
|
req: &action.DeleteTargetRequest{
|
||||||
Id: "notexisting",
|
Id: "notexisting",
|
||||||
},
|
},
|
||||||
@@ -526,7 +525,7 @@ func TestServer_DeleteTarget(t *testing.T) {
|
|||||||
if tt.prepare != nil {
|
if tt.prepare != nil {
|
||||||
creationDate, deletionDate = tt.prepare(tt.req)
|
creationDate, deletionDate = tt.prepare(tt.req)
|
||||||
}
|
}
|
||||||
got, err := instance.Client.ActionV2beta.DeleteTarget(tt.ctx, tt.req)
|
got, err := instance.Client.ActionV2.DeleteTarget(tt.ctx, tt.req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
return
|
return
|
@@ -8,12 +8,12 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/durationpb"
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
filter "github.com/zitadel/zitadel/internal/api/grpc/filter/v2beta"
|
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
|
||||||
"github.com/zitadel/zitadel/internal/command"
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -82,7 +82,7 @@ func targetsToPb(targets []*query.Target) []*action.Target {
|
|||||||
|
|
||||||
func targetToPb(t *query.Target) *action.Target {
|
func targetToPb(t *query.Target) *action.Target {
|
||||||
target := &action.Target{
|
target := &action.Target{
|
||||||
Id: t.ObjectDetails.ID,
|
Id: t.ID,
|
||||||
Name: t.Name,
|
Name: t.Name,
|
||||||
Timeout: durationpb.New(t.Timeout),
|
Timeout: durationpb.New(t.Timeout),
|
||||||
Endpoint: t.Endpoint,
|
Endpoint: t.Endpoint,
|
||||||
@@ -99,11 +99,11 @@ func targetToPb(t *query.Target) *action.Target {
|
|||||||
target.TargetType = nil
|
target.TargetType = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !t.ObjectDetails.EventDate.IsZero() {
|
if !t.EventDate.IsZero() {
|
||||||
target.ChangeDate = timestamppb.New(t.ObjectDetails.EventDate)
|
target.ChangeDate = timestamppb.New(t.EventDate)
|
||||||
}
|
}
|
||||||
if !t.ObjectDetails.CreationDate.IsZero() {
|
if !t.CreationDate.IsZero() {
|
||||||
target.CreationDate = timestamppb.New(t.ObjectDetails.CreationDate)
|
target.CreationDate = timestamppb.New(t.CreationDate)
|
||||||
}
|
}
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
@@ -334,11 +334,11 @@ func executionToPb(e *query.Execution) *action.Execution {
|
|||||||
Condition: executionIDToCondition(e.ID),
|
Condition: executionIDToCondition(e.ID),
|
||||||
Targets: targets,
|
Targets: targets,
|
||||||
}
|
}
|
||||||
if !e.ObjectDetails.EventDate.IsZero() {
|
if !e.EventDate.IsZero() {
|
||||||
exec.ChangeDate = timestamppb.New(e.ObjectDetails.EventDate)
|
exec.ChangeDate = timestamppb.New(e.EventDate)
|
||||||
}
|
}
|
||||||
if !e.ObjectDetails.CreationDate.IsZero() {
|
if !e.CreationDate.IsZero() {
|
||||||
exec.CreationDate = timestamppb.New(e.ObjectDetails.CreationDate)
|
exec.CreationDate = timestamppb.New(e.CreationDate)
|
||||||
}
|
}
|
||||||
return exec
|
return exec
|
||||||
}
|
}
|
@@ -11,8 +11,8 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/command"
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/action/v2beta/actionconnect"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2/actionconnect"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ actionconnect.ActionServiceHandler = (*Server)(nil)
|
var _ actionconnect.ActionServiceHandler = (*Server)(nil)
|
||||||
@@ -51,7 +51,7 @@ func (s *Server) RegisterConnectServer(interceptors ...connect.Interceptor) (str
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) FileDescriptor() protoreflect.FileDescriptor {
|
func (s *Server) FileDescriptor() protoreflect.FileDescriptor {
|
||||||
return action.File_zitadel_action_v2beta_action_service_proto
|
return action.File_zitadel_action_v2_action_service_proto
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) AppName() string {
|
func (s *Server) AppName() string {
|
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/command"
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) CreateTarget(ctx context.Context, req *connect.Request[action.CreateTargetRequest]) (*connect.Response[action.CreateTargetResponse], error) {
|
func (s *Server) CreateTarget(ctx context.Context, req *connect.Request[action.CreateTargetRequest]) (*connect.Response[action.CreateTargetResponse], error) {
|
||||||
@@ -89,11 +89,8 @@ func createTargetToCommand(req *action.CreateTargetRequest) *command.AddTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateTargetToCommand(req *action.UpdateTargetRequest) *command.ChangeTarget {
|
func updateTargetToCommand(req *action.UpdateTargetRequest) *command.ChangeTarget {
|
||||||
expirationSigningKey := false
|
|
||||||
// TODO handle expiration, currently only immediate expiration is supported
|
// TODO handle expiration, currently only immediate expiration is supported
|
||||||
if req.GetExpirationSigningKey() != nil {
|
expirationSigningKey := req.GetExpirationSigningKey() != nil
|
||||||
expirationSigningKey = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if req == nil {
|
if req == nil {
|
||||||
return nil
|
return nil
|
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/command"
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_createTargetToCommand(t *testing.T) {
|
func Test_createTargetToCommand(t *testing.T) {
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
@@ -75,12 +76,20 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
|
|||||||
return nil, zerrors.ThrowInvalidArgument(err, "SAML-nuo0vphhh9", "Errors.Intent.ResponseInvalid")
|
return nil, zerrors.ThrowInvalidArgument(err, "SAML-nuo0vphhh9", "Errors.Intent.ResponseInvalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userMapper := NewUser()
|
||||||
// nameID is required, but at least in ADFS it will not be sent unless explicitly configured
|
// nameID is required, but at least in ADFS it will not be sent unless explicitly configured
|
||||||
if s.Assertion.Subject == nil || s.Assertion.Subject.NameID == nil {
|
if s.Assertion.Subject == nil || s.Assertion.Subject.NameID == nil {
|
||||||
return nil, zerrors.ThrowInvalidArgument(err, "SAML-EFG32", "Errors.Intent.ResponseInvalid")
|
if strings.TrimSpace(s.TransientMappingAttributeName) == "" {
|
||||||
|
return nil, zerrors.ThrowInvalidArgument(err, "SAML-EFG32", "Errors.Intent.MissingTransientMappingAttributeName")
|
||||||
}
|
}
|
||||||
|
// workaround to use the transient mapping attribute when the subject / nameID are missing (e.g. in ADFS, Shibboleth)
|
||||||
|
mappingID, err := s.transientMappingID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userMapper.SetID(mappingID)
|
||||||
|
} else {
|
||||||
nameID := s.Assertion.Subject.NameID
|
nameID := s.Assertion.Subject.NameID
|
||||||
userMapper := NewUser()
|
|
||||||
// use the nameID as default mapping id
|
// use the nameID as default mapping id
|
||||||
userMapper.SetID(nameID.Value)
|
userMapper.SetID(nameID.Value)
|
||||||
if nameID.Format == string(saml.TransientNameIDFormat) {
|
if nameID.Format == string(saml.TransientNameIDFormat) {
|
||||||
@@ -90,6 +99,8 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
|
|||||||
}
|
}
|
||||||
userMapper.SetID(mappingID)
|
userMapper.SetID(mappingID)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, statement := range s.Assertion.AttributeStatements {
|
for _, statement := range s.Assertion.AttributeStatements {
|
||||||
for _, attribute := range statement.Attributes {
|
for _, attribute := range statement.Attributes {
|
||||||
values := make([]string, len(attribute.Values))
|
values := make([]string, len(attribute.Values))
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -21,7 +21,8 @@ import (
|
|||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/integration/scim"
|
"github.com/zitadel/zitadel/internal/integration/scim"
|
||||||
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
"github.com/zitadel/zitadel/pkg/grpc/action/v2"
|
||||||
|
action_v2beta "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||||
app "github.com/zitadel/zitadel/pkg/grpc/app/v2beta"
|
app "github.com/zitadel/zitadel/pkg/grpc/app/v2beta"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/auth"
|
"github.com/zitadel/zitadel/pkg/grpc/auth"
|
||||||
@@ -69,7 +70,8 @@ type Client struct {
|
|||||||
OIDCv2 oidc_pb.OIDCServiceClient
|
OIDCv2 oidc_pb.OIDCServiceClient
|
||||||
OrgV2beta org_v2beta.OrganizationServiceClient
|
OrgV2beta org_v2beta.OrganizationServiceClient
|
||||||
OrgV2 org.OrganizationServiceClient
|
OrgV2 org.OrganizationServiceClient
|
||||||
ActionV2beta action.ActionServiceClient
|
ActionV2beta action_v2beta.ActionServiceClient
|
||||||
|
ActionV2 action.ActionServiceClient
|
||||||
FeatureV2beta feature_v2beta.FeatureServiceClient
|
FeatureV2beta feature_v2beta.FeatureServiceClient
|
||||||
FeatureV2 feature.FeatureServiceClient
|
FeatureV2 feature.FeatureServiceClient
|
||||||
UserSchemaV3 userschema_v3alpha.ZITADELUserSchemasClient
|
UserSchemaV3 userschema_v3alpha.ZITADELUserSchemasClient
|
||||||
@@ -112,7 +114,8 @@ func newClient(ctx context.Context, target string) (*Client, error) {
|
|||||||
OIDCv2: oidc_pb.NewOIDCServiceClient(cc),
|
OIDCv2: oidc_pb.NewOIDCServiceClient(cc),
|
||||||
OrgV2beta: org_v2beta.NewOrganizationServiceClient(cc),
|
OrgV2beta: org_v2beta.NewOrganizationServiceClient(cc),
|
||||||
OrgV2: org.NewOrganizationServiceClient(cc),
|
OrgV2: org.NewOrganizationServiceClient(cc),
|
||||||
ActionV2beta: action.NewActionServiceClient(cc),
|
ActionV2beta: action_v2beta.NewActionServiceClient(cc),
|
||||||
|
ActionV2: action.NewActionServiceClient(cc),
|
||||||
FeatureV2beta: feature_v2beta.NewFeatureServiceClient(cc),
|
FeatureV2beta: feature_v2beta.NewFeatureServiceClient(cc),
|
||||||
FeatureV2: feature.NewFeatureServiceClient(cc),
|
FeatureV2: feature.NewFeatureServiceClient(cc),
|
||||||
UserSchemaV3: userschema_v3alpha.NewZITADELUserSchemasClient(cc),
|
UserSchemaV3: userschema_v3alpha.NewZITADELUserSchemasClient(cc),
|
||||||
@@ -1057,27 +1060,27 @@ func (i *Instance) CreateTarget(ctx context.Context, t *testing.T, name, endpoin
|
|||||||
RestAsync: &action.RESTAsync{},
|
RestAsync: &action.RESTAsync{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target, err := i.Client.ActionV2beta.CreateTarget(ctx, req)
|
target, err := i.Client.ActionV2.CreateTarget(ctx, req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Instance) DeleteTarget(ctx context.Context, t *testing.T, id string) {
|
func (i *Instance) DeleteTarget(ctx context.Context, t *testing.T, id string) {
|
||||||
_, err := i.Client.ActionV2beta.DeleteTarget(ctx, &action.DeleteTargetRequest{
|
_, err := i.Client.ActionV2.DeleteTarget(ctx, &action.DeleteTargetRequest{
|
||||||
Id: id,
|
Id: id,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Instance) DeleteExecution(ctx context.Context, t *testing.T, cond *action.Condition) {
|
func (i *Instance) DeleteExecution(ctx context.Context, t *testing.T, cond *action.Condition) {
|
||||||
_, err := i.Client.ActionV2beta.SetExecution(ctx, &action.SetExecutionRequest{
|
_, err := i.Client.ActionV2.SetExecution(ctx, &action.SetExecutionRequest{
|
||||||
Condition: cond,
|
Condition: cond,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Instance) SetExecution(ctx context.Context, t *testing.T, cond *action.Condition, targets []string) *action.SetExecutionResponse {
|
func (i *Instance) SetExecution(ctx context.Context, t *testing.T, cond *action.Condition, targets []string) *action.SetExecutionResponse {
|
||||||
target, err := i.Client.ActionV2beta.SetExecution(ctx, &action.SetExecutionRequest{
|
target, err := i.Client.ActionV2.SetExecution(ctx, &action.SetExecutionRequest{
|
||||||
Condition: cond,
|
Condition: cond,
|
||||||
Targets: targets,
|
Targets: targets,
|
||||||
})
|
})
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||||
"github.com/zitadel/zitadel/internal/api/ui/console"
|
"github.com/zitadel/zitadel/internal/api/ui/console"
|
||||||
"github.com/zitadel/zitadel/internal/api/ui/login"
|
"github.com/zitadel/zitadel/internal/api/ui/login"
|
||||||
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||||
@@ -417,12 +418,14 @@ func (u *userNotifier) reduceSessionOTPSMSChallenged(event eventstore.Event) (*h
|
|||||||
if alreadyHandled {
|
if alreadyHandled {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s, err := u.queries.SessionByID(ctx, true, e.Aggregate().ID, "", nil)
|
|
||||||
|
ctx, err = u.queries.Origin(ctx, e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, err = u.queries.Origin(ctx, e)
|
sessionWriteModel := command.NewSessionWriteModel(e.Aggregate().ID, e.Aggregate().InstanceID)
|
||||||
|
err = u.queries.es.FilterToQueryReducer(ctx, sessionWriteModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -432,8 +435,8 @@ func (u *userNotifier) reduceSessionOTPSMSChallenged(event eventstore.Event) (*h
|
|||||||
return u.queue.Insert(ctx,
|
return u.queue.Insert(ctx,
|
||||||
¬ification.Request{
|
¬ification.Request{
|
||||||
Aggregate: e.Aggregate(),
|
Aggregate: e.Aggregate(),
|
||||||
UserID: s.UserFactor.UserID,
|
UserID: sessionWriteModel.UserID,
|
||||||
UserResourceOwner: s.UserFactor.ResourceOwner,
|
UserResourceOwner: sessionWriteModel.UserResourceOwner,
|
||||||
TriggeredAtOrigin: http_util.DomainContext(ctx).Origin(),
|
TriggeredAtOrigin: http_util.DomainContext(ctx).Origin(),
|
||||||
EventType: e.EventType,
|
EventType: e.EventType,
|
||||||
NotificationType: domain.NotificationTypeSms,
|
NotificationType: domain.NotificationTypeSms,
|
||||||
|
@@ -1349,19 +1349,12 @@ func Test_userNotifier_reduceOTPSMSChallenged(t *testing.T) {
|
|||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, queue *mock.MockQueue) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, queue *mock.MockQueue) (f fields, a args, w want) {
|
||||||
testCode := "testcode"
|
testCode := "testcode"
|
||||||
_, code := cryptoValue(t, ctrl, testCode)
|
_, code := cryptoValue(t, ctrl, testCode)
|
||||||
queries.EXPECT().SessionByID(gomock.Any(), gomock.Any(), sessionID, gomock.Any(), nil).Return(&query.Session{
|
|
||||||
ID: sessionID,
|
|
||||||
ResourceOwner: instanceID,
|
|
||||||
UserFactor: query.SessionUserFactor{
|
|
||||||
UserID: userID,
|
|
||||||
ResourceOwner: orgID,
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
queue.EXPECT().Insert(
|
queue.EXPECT().Insert(
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
¬ification.Request{
|
¬ification.Request{
|
||||||
UserID: userID,
|
UserID: "", // Empty since no session events are provided
|
||||||
UserResourceOwner: orgID,
|
UserResourceOwner: "", // Empty since no session events are provided
|
||||||
TriggeredAtOrigin: eventOrigin,
|
TriggeredAtOrigin: eventOrigin,
|
||||||
URLTemplate: "",
|
URLTemplate: "",
|
||||||
Code: code,
|
Code: code,
|
||||||
@@ -1387,11 +1380,15 @@ func Test_userNotifier_reduceOTPSMSChallenged(t *testing.T) {
|
|||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
).Return(nil)
|
).Return(nil)
|
||||||
|
|
||||||
|
mockQuerier := es_repo_mock.NewMockQuerier(ctrl)
|
||||||
|
mockQuerier.EXPECT().FilterToReducer(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
||||||
|
|
||||||
return fields{
|
return fields{
|
||||||
queries: queries,
|
queries: queries,
|
||||||
queue: queue,
|
queue: queue,
|
||||||
es: eventstore.NewEventstore(&eventstore.Config{
|
es: eventstore.NewEventstore(&eventstore.Config{
|
||||||
Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier,
|
Querier: mockQuerier,
|
||||||
}),
|
}),
|
||||||
}, args{
|
}, args{
|
||||||
event: &session.OTPSMSChallengedEvent{
|
event: &session.OTPSMSChallengedEvent{
|
||||||
@@ -1421,19 +1418,12 @@ func Test_userNotifier_reduceOTPSMSChallenged(t *testing.T) {
|
|||||||
IsPrimary: true,
|
IsPrimary: true,
|
||||||
}},
|
}},
|
||||||
}, nil)
|
}, nil)
|
||||||
queries.EXPECT().SessionByID(gomock.Any(), gomock.Any(), sessionID, gomock.Any(), nil).Return(&query.Session{
|
|
||||||
ID: sessionID,
|
|
||||||
ResourceOwner: instanceID,
|
|
||||||
UserFactor: query.SessionUserFactor{
|
|
||||||
UserID: userID,
|
|
||||||
ResourceOwner: orgID,
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
queue.EXPECT().Insert(
|
queue.EXPECT().Insert(
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
¬ification.Request{
|
¬ification.Request{
|
||||||
UserID: userID,
|
UserID: "", // Empty since no session events are provided
|
||||||
UserResourceOwner: orgID,
|
UserResourceOwner: "", // Empty since no session events are provided
|
||||||
TriggeredAtOrigin: fmt.Sprintf("%s://%s:%d", externalProtocol, instancePrimaryDomain, externalPort),
|
TriggeredAtOrigin: fmt.Sprintf("%s://%s:%d", externalProtocol, instancePrimaryDomain, externalPort),
|
||||||
URLTemplate: "",
|
URLTemplate: "",
|
||||||
Code: code,
|
Code: code,
|
||||||
@@ -1459,11 +1449,15 @@ func Test_userNotifier_reduceOTPSMSChallenged(t *testing.T) {
|
|||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
).Return(nil)
|
).Return(nil)
|
||||||
|
|
||||||
|
mockQuerier := es_repo_mock.NewMockQuerier(ctrl)
|
||||||
|
mockQuerier.EXPECT().FilterToReducer(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
||||||
|
|
||||||
return fields{
|
return fields{
|
||||||
queries: queries,
|
queries: queries,
|
||||||
queue: queue,
|
queue: queue,
|
||||||
es: eventstore.NewEventstore(&eventstore.Config{
|
es: eventstore.NewEventstore(&eventstore.Config{
|
||||||
Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier,
|
Querier: mockQuerier,
|
||||||
}),
|
}),
|
||||||
}, args{
|
}, args{
|
||||||
event: &session.OTPSMSChallengedEvent{
|
event: &session.OTPSMSChallengedEvent{
|
||||||
@@ -1484,19 +1478,11 @@ func Test_userNotifier_reduceOTPSMSChallenged(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "external code",
|
name: "external code",
|
||||||
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, queue *mock.MockQueue) (f fields, a args, w want) {
|
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, queue *mock.MockQueue) (f fields, a args, w want) {
|
||||||
queries.EXPECT().SessionByID(gomock.Any(), gomock.Any(), sessionID, gomock.Any(), nil).Return(&query.Session{
|
|
||||||
ID: sessionID,
|
|
||||||
ResourceOwner: instanceID,
|
|
||||||
UserFactor: query.SessionUserFactor{
|
|
||||||
UserID: userID,
|
|
||||||
ResourceOwner: orgID,
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
queue.EXPECT().Insert(
|
queue.EXPECT().Insert(
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
¬ification.Request{
|
¬ification.Request{
|
||||||
UserID: userID,
|
UserID: "", // Empty since no session events are provided
|
||||||
UserResourceOwner: orgID,
|
UserResourceOwner: "", // Empty since no session events are provided
|
||||||
TriggeredAtOrigin: eventOrigin,
|
TriggeredAtOrigin: eventOrigin,
|
||||||
URLTemplate: "",
|
URLTemplate: "",
|
||||||
Code: nil,
|
Code: nil,
|
||||||
@@ -1522,11 +1508,15 @@ func Test_userNotifier_reduceOTPSMSChallenged(t *testing.T) {
|
|||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
).Return(nil)
|
).Return(nil)
|
||||||
|
|
||||||
|
mockQuerier := es_repo_mock.NewMockQuerier(ctrl)
|
||||||
|
mockQuerier.EXPECT().FilterToReducer(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
||||||
|
|
||||||
return fields{
|
return fields{
|
||||||
queries: queries,
|
queries: queries,
|
||||||
queue: queue,
|
queue: queue,
|
||||||
es: eventstore.NewEventstore(&eventstore.Config{
|
es: eventstore.NewEventstore(&eventstore.Config{
|
||||||
Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier,
|
Querier: mockQuerier,
|
||||||
}),
|
}),
|
||||||
}, args{
|
}, args{
|
||||||
event: &session.OTPSMSChallengedEvent{
|
event: &session.OTPSMSChallengedEvent{
|
||||||
|
@@ -66,18 +66,24 @@
|
|||||||
|
|
||||||
.type-icon {
|
.type-icon {
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
}
|
|
||||||
|
|
||||||
.type-button-icon,
|
|
||||||
.type-icon,
|
|
||||||
span {
|
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-icon,
|
|
||||||
.type-button-icon {
|
.type-button-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[mat-icon-button] {
|
||||||
|
margin-right: 0;
|
||||||
|
|
||||||
|
.type-button-icon {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.trigger-wrapper {
|
.trigger-wrapper {
|
||||||
|
@@ -423,6 +423,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
if (allowed) {
|
if (allowed) {
|
||||||
this.oidcForm.enable();
|
this.oidcForm.enable();
|
||||||
|
this.oidcForm.controls['clientId'].disable();
|
||||||
this.oidcTokenForm.enable();
|
this.oidcTokenForm.enable();
|
||||||
this.apiForm.enable();
|
this.apiForm.enable();
|
||||||
this.samlForm.enable();
|
this.samlForm.enable();
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
NEXT_PUBLIC_BASE_PATH="/ui/v2/login"
|
ZITADEL_API_URL=http://localhost:22222
|
||||||
ZITADEL_API_URL=http://mock-zitadel:22222
|
|
||||||
ZITADEL_SERVICE_USER_TOKEN="yolo"
|
ZITADEL_SERVICE_USER_TOKEN="yolo"
|
||||||
EMAIL_VERIFICATION=true
|
EMAIL_VERIFICATION=true
|
||||||
DEBUG=true
|
DEBUG=true
|
||||||
|
PORT=3001
|
||||||
|
NEXT_PUBLIC_BASE_PATH=/ui/v2/login
|
||||||
|
2
apps/login/.gitignore
vendored
2
apps/login/.gitignore
vendored
@@ -2,6 +2,7 @@ custom-config.js
|
|||||||
.env*.local
|
.env*.local
|
||||||
standalone
|
standalone
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
cypress
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
@@ -11,6 +12,5 @@ node_modules
|
|||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
.env
|
|
||||||
.vscode
|
.vscode
|
||||||
/blob-report/
|
/blob-report/
|
||||||
|
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "login-test-acceptance",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"test:acceptance": "dotenv -e ../login/.env.test.local playwright",
|
|
||||||
"test:acceptance:setup": "cd ../.. && make login_test_acceptance_setup_env && NODE_ENV=test turbo run test:acceptance:setup:dev",
|
|
||||||
"test:acceptance:setup:dev": "cd ../.. && make login_test_acceptance_setup_dev",
|
|
||||||
"clean": "rm -rf .turbo node_modules"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@faker-js/faker": "^9.7.0",
|
|
||||||
"@otplib/core": "^12.0.0",
|
|
||||||
"@otplib/plugin-crypto": "^12.0.0",
|
|
||||||
"@otplib/plugin-thirty-two": "^12.0.0",
|
|
||||||
"@playwright/test": "^1.52.0",
|
|
||||||
"dotenv-cli": "^8.0.0",
|
|
||||||
"gaxios": "^7.1.0",
|
|
||||||
"typescript": "^5.8.3"
|
|
||||||
}
|
|
||||||
}
|
|
2
apps/login/next-env-vars.d.ts
vendored
2
apps/login/next-env-vars.d.ts
vendored
@@ -27,6 +27,8 @@ declare namespace NodeJS {
|
|||||||
/**
|
/**
|
||||||
* Optional: custom request headers to be added to every request
|
* Optional: custom request headers to be added to every request
|
||||||
* Split by comma, key value pairs separated by colon
|
* Split by comma, key value pairs separated by colon
|
||||||
|
* For example: to call the Zitadel API at an internal address, you can set:
|
||||||
|
* `CUSTOM_REQUEST_HEADERS=Host:http://zitadel-internal:8080`
|
||||||
*/
|
*/
|
||||||
CUSTOM_REQUEST_HEADERS?: string;
|
CUSTOM_REQUEST_HEADERS?: string;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"packageManager": "pnpm@9.1.2+sha256.19c17528f9ca20bd442e4ca42f00f1b9808a9cb419383cd04ba32ef19322aba7",
|
"packageManager": "pnpm@10.13.1",
|
||||||
"name": "@zitadel/login",
|
"name": "@zitadel/login",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -14,8 +14,7 @@
|
|||||||
"test:unit": "vitest --run",
|
"test:unit": "vitest --run",
|
||||||
"lint-staged": "lint-staged",
|
"lint-staged": "lint-staged",
|
||||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
|
||||||
"test:integration:login": "cypress run",
|
"test:integration:login": "wait-on --simultaneous 1 http://localhost:3001/ui/v2/login/verify?userId=221394658884845598&code=abc && cypress run",
|
||||||
"test:integration:login:debug": "cypress open",
|
|
||||||
"test:acceptance": "dotenv -e ../login/.env.test.local playwright",
|
"test:acceptance": "dotenv -e ../login/.env.test.local playwright",
|
||||||
"test:acceptance:setup": "cd ../.. && make login_test_acceptance_setup_env && NODE_ENV=test turbo run test:acceptance:setup:dev",
|
"test:acceptance:setup": "cd ../.. && make login_test_acceptance_setup_env && NODE_ENV=test turbo run test:acceptance:setup:dev",
|
||||||
"test:acceptance:setup:dev": "cd ../.. && make login_test_acceptance_setup_dev"
|
"test:acceptance:setup:dev": "cd ../.. && make login_test_acceptance_setup_dev"
|
||||||
@@ -53,6 +52,11 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "^7.23.0",
|
"@babel/eslint-parser": "^7.23.0",
|
||||||
"@bufbuild/buf": "^1.53.0",
|
"@bufbuild/buf": "^1.53.0",
|
||||||
|
"@faker-js/faker": "^9.7.0",
|
||||||
|
"@otplib/core": "^12.0.0",
|
||||||
|
"@otplib/plugin-crypto": "^12.0.0",
|
||||||
|
"@otplib/plugin-thirty-two": "^12.0.0",
|
||||||
|
"@playwright/test": "^1.52.0",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@types/ms": "2.1.0",
|
"@types/ms": "2.1.0",
|
||||||
@@ -66,34 +70,30 @@
|
|||||||
"@vercel/git-hooks": "1.0.0",
|
"@vercel/git-hooks": "1.0.0",
|
||||||
"@vitejs/plugin-react": "^4.4.1",
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
"autoprefixer": "10.4.21",
|
"autoprefixer": "10.4.21",
|
||||||
|
"concurrently": "^9.1.2",
|
||||||
|
"cypress": "^14.5.2",
|
||||||
|
"dotenv-cli": "^8.0.0",
|
||||||
|
"env-cmd": "^10.0.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-next": "15.4.0-canary.86",
|
"eslint-config-next": "15.4.0-canary.86",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"gaxios": "^7.1.0",
|
||||||
"grpc-tools": "1.13.0",
|
"grpc-tools": "1.13.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"lint-staged": "15.5.1",
|
"lint-staged": "15.5.1",
|
||||||
"make-dir-cli": "4.0.0",
|
"make-dir-cli": "4.0.0",
|
||||||
|
"nodemon": "^3.1.9",
|
||||||
"postcss": "8.5.3",
|
"postcss": "8.5.3",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^3.2.0",
|
"prettier-plugin-organize-imports": "^3.2.0",
|
||||||
"prettier-plugin-tailwindcss": "0.6.11",
|
"prettier-plugin-tailwindcss": "0.6.11",
|
||||||
"sass": "^1.87.0",
|
"sass": "^1.87.0",
|
||||||
|
"start-server-and-test": "^2.0.11",
|
||||||
"tailwindcss": "3.4.14",
|
"tailwindcss": "3.4.14",
|
||||||
"ts-proto": "^2.7.0",
|
"ts-proto": "^2.7.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "^2.0.0",
|
"vitest": "^2.0.0",
|
||||||
"concurrently": "^9.1.2",
|
"wait-on": "^7.2.0"
|
||||||
"cypress": "^14.5.2",
|
|
||||||
"dotenv-cli": "^8.0.0",
|
|
||||||
"env-cmd": "^10.0.0",
|
|
||||||
"nodemon": "^3.1.9",
|
|
||||||
"start-server-and-test": "^2.0.11",
|
|
||||||
"@faker-js/faker": "^9.7.0",
|
|
||||||
"@otplib/core": "^12.0.0",
|
|
||||||
"@otplib/plugin-crypto": "^12.0.0",
|
|
||||||
"@otplib/plugin-thirty-two": "^12.0.0",
|
|
||||||
"@playwright/test": "^1.52.0",
|
|
||||||
"gaxios": "^7.1.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
9151
apps/login/pnpm-lock.yaml
generated
Normal file
9151
apps/login/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,4 @@ if [ -n "${ZITADEL_SERVICE_USER_TOKEN_FILE}" ] && [ -f "${ZITADEL_SERVICE_USER_T
|
|||||||
export ZITADEL_SERVICE_USER_TOKEN=$(cat "${ZITADEL_SERVICE_USER_TOKEN_FILE}")
|
export ZITADEL_SERVICE_USER_TOKEN=$(cat "${ZITADEL_SERVICE_USER_TOKEN_FILE}")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
exec node /runtime/apps/login/server.js
|
||||||
|
|
||||||
exec node /runtime/apps/login/apps/login/server.js
|
|
||||||
|
@@ -184,7 +184,7 @@ https://github.com/zitadel/actions/blob/main/examples/add_metadata.js
|
|||||||
|
|
||||||
## Use provided fields of identity providers
|
## Use provided fields of identity providers
|
||||||
|
|
||||||
If you want to ensure that the data of a user are always update you can automatically update user fields during authentication and safe time of your customers and your team. 🤯
|
If you want to ensure that the data of a user are always up-to-date, you can automatically update user fields during authentication and save time of your customers and your team.
|
||||||
|
|
||||||
### Trigger
|
### Trigger
|
||||||
|
|
||||||
|
@@ -145,7 +145,7 @@ This object contains context information about the request to the [authorization
|
|||||||
- `requestedOrgDomain` *bool*
|
- `requestedOrgDomain` *bool*
|
||||||
- `applicationResourceOwner` *string*
|
- `applicationResourceOwner` *string*
|
||||||
- `privateLabelingSetting` *Number*
|
- `privateLabelingSetting` *Number*
|
||||||
<ul><li>0: Unspecified</li><li>1: Enforce project resource owner policy</li><li>2: Allow login user resource owner policy</li></ul>
|
<ul><li>0: Unspecified</li><li>1: Enforce project's policy</li><li>2: Allow user's organization login policy</li></ul>
|
||||||
- `selectedIdpConfigId` *string*
|
- `selectedIdpConfigId` *string*
|
||||||
- `linkingUsers` Array of [*ExternalUser*](#external-user)
|
- `linkingUsers` Array of [*ExternalUser*](#external-user)
|
||||||
- `passwordVerified` *bool*
|
- `passwordVerified` *bool*
|
||||||
|
@@ -111,6 +111,6 @@ ZITADEL reserves some claims to assert certain data. Please check out the [reser
|
|||||||
| urn:zitadel:iam:org:project:roles | `{"urn:zitadel:iam:org:project:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on the current project (where your client belongs to). |
|
| urn:zitadel:iam:org:project:roles | `{"urn:zitadel:iam:org:project:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on the current project (where your client belongs to). |
|
||||||
| urn:zitadel:iam:org:project:\{projectid}:roles | `{"urn:zitadel:iam:org:project:id3:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on a specific project. |
|
| urn:zitadel:iam:org:project:\{projectid}:roles | `{"urn:zitadel:iam:org:project:id3:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on a specific project. |
|
||||||
| urn:zitadel:iam:user:metadata | `{"urn:zitadel:iam:user:metadata": [ {"key": "VmFsdWU=" } ] }` | The metadata claim will include all metadata of a user. The values are base64 encoded. |
|
| urn:zitadel:iam:user:metadata | `{"urn:zitadel:iam:user:metadata": [ {"key": "VmFsdWU=" } ] }` | The metadata claim will include all metadata of a user. The values are base64 encoded. |
|
||||||
| urn:zitadel:iam:user:resourceowner:id | `{"urn:zitadel:iam:user:resourceowner:id": "orgid"}` | This claim represents the id of the resource owner organisation of the user. |
|
| urn:zitadel:iam:user:resourceowner:id | `{"urn:zitadel:iam:user:resourceowner:id": "orgid"}` | This claim represents the user's organization ID. |
|
||||||
| urn:zitadel:iam:user:resourceowner:name | `{"urn:zitadel:iam:user:resourceowner:name": "ACME"}` | This claim represents the name of the resource owner organisation of the user. |
|
| urn:zitadel:iam:user:resourceowner:name | `{"urn:zitadel:iam:user:resourceowner:name": "ACME"}` | This claim represents the user's organization's name. |
|
||||||
| urn:zitadel:iam:user:resourceowner:primary_domain | `{"urn:zitadel:iam:user:resourceowner:primary_domain": "acme.ch"}` | This claim represents the primary domain of the resource owner organisation of the user. |
|
| urn:zitadel:iam:user:resourceowner:primary_domain | `{"urn:zitadel:iam:user:resourceowner:primary_domain": "acme.ch"}` | This claim represents the user's organization's primary domain. |
|
||||||
|
@@ -32,7 +32,7 @@ In addition to the standard compliant scopes we utilize the following scopes.
|
|||||||
| `urn:zitadel:iam:org:domain:primary:{domainname}` | `urn:zitadel:iam:org:domain:primary:acme.ch` | When requesting this scope **ZITADEL** will enforce that the user is a member of the selected organization and the username is suffixed by the provided domain. If the organization does not exist a failure is displayed |
|
| `urn:zitadel:iam:org:domain:primary:{domainname}` | `urn:zitadel:iam:org:domain:primary:acme.ch` | When requesting this scope **ZITADEL** will enforce that the user is a member of the selected organization and the username is suffixed by the provided domain. If the organization does not exist a failure is displayed |
|
||||||
| `urn:zitadel:iam:org:roles:id:{orgID}` | `urn:zitadel:iam:org:roles:id:178204173316174381` | This scope can be used one or more times to limit the granted organization IDs in the returned roles. Unknown organization IDs are ignored. When this scope is not used, all granted organizations are returned inside the roles. |
|
| `urn:zitadel:iam:org:roles:id:{orgID}` | `urn:zitadel:iam:org:roles:id:178204173316174381` | This scope can be used one or more times to limit the granted organization IDs in the returned roles. Unknown organization IDs are ignored. When this scope is not used, all granted organizations are returned inside the roles. |
|
||||||
| `urn:zitadel:iam:org:project:id:{projectid}:aud` | `urn:zitadel:iam:org:project:id:69234237810729019:aud` | By adding this scope, the requested project id will be added to the audience of the access token |
|
| `urn:zitadel:iam:org:project:id:{projectid}:aud` | `urn:zitadel:iam:org:project:id:69234237810729019:aud` | By adding this scope, the requested project id will be added to the audience of the access token |
|
||||||
| `urn:zitadel:iam:org:project:id:zitadel:aud` | `urn:zitadel:iam:org:project:id:zitadel:aud` | By adding this scope, the ZITADEL project ID will be added to the audience of the access token |
|
| `urn:zitadel:iam:org:project:id:zitadel:aud` | `urn:zitadel:iam:org:project:id:zitadel:aud` | By adding this scope, the ZITADEL project id will be added to the audience of the access token |
|
||||||
| `urn:zitadel:iam:user:metadata` | `urn:zitadel:iam:user:metadata` | By adding this scope, the metadata of the user will be included in the token. The values are base64 encoded. |
|
| `urn:zitadel:iam:user:metadata` | `urn:zitadel:iam:user:metadata` | By adding this scope, the metadata of the user will be included in the token. The values are base64 encoded. |
|
||||||
| `urn:zitadel:iam:user:resourceowner` | `urn:zitadel:iam:user:resourceowner` | By adding this scope: id, name and primary_domain of the resource owner (the users organization) will be included in the token. |
|
| `urn:zitadel:iam:user:resourceowner` | `urn:zitadel:iam:user:resourceowner` | By adding this scope: id, name and primary_domain of the user's organization will be included in the token. |
|
||||||
| `urn:zitadel:iam:org:idp:id:{idp_id}` | `urn:zitadel:iam:org:idp:id:76625965177954913` | By adding this scope the user will directly be redirected to the identity provider to authenticate. Make sure you also send the primary domain scope if a custom login policy is configured. Otherwise the system will not be able to identify the identity provider. |
|
| `urn:zitadel:iam:org:idp:id:{idp_id}` | `urn:zitadel:iam:org:idp:id:76625965177954913` | By adding this scope the user will directly be redirected to the identity provider to authenticate. Make sure you also send the primary domain scope if a custom login policy is configured. Otherwise the system will not be able to identify the identity provider. |
|
||||||
|
@@ -103,9 +103,7 @@ curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/actions/executions' \
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID returned>"
|
||||||
"target": "<TargetID returned>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
@@ -129,9 +129,7 @@ curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/actions/executions' \
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID returned>"
|
||||||
"target": "<TargetID returned>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
@@ -107,9 +107,7 @@ curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/actions/executions' \
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID returned>"
|
||||||
"target": "<TargetID returned>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
@@ -154,9 +154,7 @@ curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/actions/executions' \
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID returned>"
|
||||||
"target": "<TargetID returned>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
@@ -114,9 +114,7 @@ curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/actions/executions' \
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID returned>"
|
||||||
"target": "<TargetID returned>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
@@ -107,9 +107,7 @@ curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/actions/executions' \
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID returned>"
|
||||||
"target": "<TargetID returned>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
@@ -173,9 +173,7 @@ curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/actions/executions' \
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID returned>"
|
||||||
"target": "<TargetID returned>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
@@ -107,9 +107,7 @@ curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/actions/executions' \
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID returned>"
|
||||||
"target": "<TargetID returned>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
@@ -406,17 +406,11 @@ If you then have a call on `/zitadel.user.v2.UserService/UpdateHumanUser` the fo
|
|||||||
|
|
||||||
And if you use a different service, for example `zitadel.session.v2.SessionService`, then the `all` Execution would still be used.
|
And if you use a different service, for example `zitadel.session.v2.SessionService`, then the `all` Execution would still be used.
|
||||||
|
|
||||||
### Targets and Includes
|
### Targets
|
||||||
|
|
||||||
:::info
|
An execution can contain only a list of Targets, and Targets are comma separated string values.
|
||||||
Includes are limited to 3 levels, which mean that include1->include2->include3 is the maximum for now.
|
|
||||||
If you have feedback to the include logic, or a reason why 3 levels are not enough, please open [an issue on github](https://github.com/zitadel/zitadel/issues) or [start a discussion on github](https://github.com/zitadel/zitadel/discussions)/[start a topic on discord](https://zitadel.com/chat)
|
|
||||||
:::
|
|
||||||
|
|
||||||
An execution can not only contain a list of Targets, but also Includes.
|
Here's an example of a Target defined on a service (e.g. `zitadel.user.v2.UserService`)
|
||||||
The Includes can be defined in the Execution directly, which means you include all defined Targets by a before set Execution.
|
|
||||||
|
|
||||||
If you define 2 Executions as follows:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -426,13 +420,12 @@ If you define 2 Executions as follows:
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID1>"
|
||||||
"target": "<TargetID1>"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Here's an example of a Target defined on a method (e.g. `/zitadel.user.v2.UserService/AddHumanUser`)
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"condition": {
|
"condition": {
|
||||||
@@ -441,21 +434,13 @@ If you define 2 Executions as follows:
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
"<TargetID2>",
|
||||||
"target": "<TargetID2>"
|
"<TargetID1>"
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": {
|
|
||||||
"request": {
|
|
||||||
"service": "zitadel.user.v2.UserService"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The called Targets on "/zitadel.user.v2.UserService/AddHumanUser" would be, in order:
|
The called Targets on `/zitadel.user.v2.UserService/AddHumanUser` would be, in order:
|
||||||
|
|
||||||
1. `<TargetID2>`
|
1. `<TargetID2>`
|
||||||
2. `<TargetID1>`
|
2. `<TargetID1>`
|
||||||
|
@@ -77,8 +77,8 @@ You can choose from
|
|||||||
| Setting | Description |
|
| Setting | Description |
|
||||||
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| Unspecified | If nothing is specified the default will trigger. (System settings) |
|
| Unspecified | If nothing is specified the default will trigger. (System settings) |
|
||||||
| Enforce project resource owner policy | This setting will enforce the private labeling of the organization (resource owner) of the project through the whole login process. |
|
| Enforce project's policy | This setting will enforce the private labeling of the organization of the project through the whole login process. |
|
||||||
| Allow Login User resource owner policy | With this setting first the private labeling of the organization (resource owner) of the project will trigger. As soon as the user and its organization (resource owner) is identified by ZITADEL, the settings will change to the organization of the user. |
|
| Allow login user policy | With this setting first the private labeling of the organization of the project will trigger. As soon as the user and its organization is identified by ZITADEL, the settings will change to the organization of the user. |
|
||||||
|
|
||||||
In a B2B use case, you would typically use the organization setting. If you want to omit organization detection, you can preselect an organization with the [primary domain scope](/apis/openidoauth/scopes#reserved-scopes) (ex. `urn:zitadel:iam:org:domain:primary:{domainname}`).
|
In a B2B use case, you would typically use the organization setting. If you want to omit organization detection, you can preselect an organization with the [primary domain scope](/apis/openidoauth/scopes#reserved-scopes) (ex. `urn:zitadel:iam:org:domain:primary:{domainname}`).
|
||||||
|
|
||||||
|
@@ -80,7 +80,7 @@ curl --request POST \
|
|||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
| ---------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| ---------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| org_ids | list of strings | provide a list of organizationIDs to select which organizations should be exported (eg, `[ "70669144072186707", "70671105999825752" ]`); leave empty to export all |
|
| org_ids | list of strings | provide a list of Organization IDs to select which organizations should be exported (eg, `[ "70669144072186707", "70671105999825752" ]`); leave empty to export all |
|
||||||
| excluded_org_ids | list of strings | to exclude several organization, if for example no organizations are selected |
|
| excluded_org_ids | list of strings | to exclude several organization, if for example no organizations are selected |
|
||||||
| with_passwords | bool | to include the hashed_passwords of the users in the export |
|
| with_passwords | bool | to include the hashed_passwords of the users in the export |
|
||||||
| with_otp | bool | to include the OTP-code of the users in the export |
|
| with_otp | bool | to include the OTP-code of the users in the export |
|
||||||
@@ -143,7 +143,7 @@ curl --request POST \
|
|||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
| ---------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| ---------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| org_ids | list of strings | provide a list of organizationIDs to select which organizations should be exported (eg, `[ "70669144072186707", "70671105999825752" ]`); leave empty to export all |
|
| org_ids | list of strings | provide a list of Organization IDs to select which organizations should be exported (eg, `[ "70669144072186707", "70671105999825752" ]`); leave empty to export all |
|
||||||
| excluded_org_ids | list of strings | to exclude several organization, if for example no organizations are selected |
|
| excluded_org_ids | list of strings | to exclude several organization, if for example no organizations are selected |
|
||||||
| with_passwords | bool | to include the hashed_passwords of the users in the export |
|
| with_passwords | bool | to include the hashed_passwords of the users in the export |
|
||||||
| with_otp | bool | to include the OTP-code of the users in the export |
|
| with_otp | bool | to include the OTP-code of the users in the export |
|
||||||
|
@@ -43,7 +43,7 @@ In order to define the need of the **Portal Application** some planning consider
|
|||||||
|
|
||||||
You can decide whether a organization is preselected for the login or if the user is redirected to the default login screen. Using OpenID Connect, you can send the user to a specific organization by defining the organization in a [reserved scope](/docs/apis/openidoauth/scopes#reserved-scopes) (primary domain).
|
You can decide whether a organization is preselected for the login or if the user is redirected to the default login screen. Using OpenID Connect, you can send the user to a specific organization by defining the organization in a [reserved scope](/docs/apis/openidoauth/scopes#reserved-scopes) (primary domain).
|
||||||
Settings to the branding or the login options of the organization can be made from the organization section in [Console](/docs/concepts/features/console).
|
Settings to the branding or the login options of the organization can be made from the organization section in [Console](/docs/concepts/features/console).
|
||||||
The behavior of the login branding can be set in your projects detail page. You can choose the branding of the selected organization, the user resource owner, or the projects resource owner.
|
The behavior of the login branding can be set in your projects detail page. You can choose the branding of the selected organization, the user's organization, or the project's organization.
|
||||||
|
|
||||||
### Organizations
|
### Organizations
|
||||||
|
|
||||||
|
@@ -293,7 +293,7 @@ Excitingly, v3 introduces the foundational elements for Actions V2, opening up a
|
|||||||
|
|
||||||
### v4.x
|
### v4.x
|
||||||
|
|
||||||
**Current State**: Implementation
|
**Current State**: General Availability / Stable
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -311,9 +311,13 @@ Excitingly, v3 introduces the foundational elements for Actions V2, opening up a
|
|||||||
This change, along with standardized naming and improved documentation, will simplify integration, accelerate development, and create a more intuitive experience for our customers and community.
|
This change, along with standardized naming and improved documentation, will simplify integration, accelerate development, and create a more intuitive experience for our customers and community.
|
||||||
|
|
||||||
Resources integrated in this release:
|
Resources integrated in this release:
|
||||||
- Instances
|
- Applications (in beta)
|
||||||
|
- Authorizations (in beta)
|
||||||
|
- Instances (in beta)
|
||||||
- Organizations
|
- Organizations
|
||||||
- Projects
|
- Permissions (in beta)
|
||||||
|
- Projects (in beta)
|
||||||
|
- Settings (beta) now includes 3 new endpoints: `ListOrganizationSettings()`, `SetOrganizationSettings()` and `DeleteOrganizationSettings()`
|
||||||
- Users
|
- Users
|
||||||
|
|
||||||
For more details read the [Github Issue](https://github.com/zitadel/zitadel/issues/6305)
|
For more details read the [Github Issue](https://github.com/zitadel/zitadel/issues/6305)
|
||||||
@@ -371,38 +375,121 @@ Excitingly, v3 introduces the foundational elements for Actions V2, opening up a
|
|||||||
Starting now, it will be the default login experience for all new customers.
|
Starting now, it will be the default login experience for all new customers.
|
||||||
With this release, 8.0 we are also focused on implementing previously missing features, such as device authorization and LDAP IDP support, to make the new UI fully feature-complete.
|
With this release, 8.0 we are also focused on implementing previously missing features, such as device authorization and LDAP IDP support, to make the new UI fully feature-complete.
|
||||||
|
|
||||||
- [Hosted Login V2](http://localhost:3000/docs/guides/integrate/login/hosted-login#hosted-login-version-2-beta)
|
- [Hosted Login V2](../guides/integrate/login/hosted-login#hosted-login-version-2-beta)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Web Keys</summary>
|
<summary>Actions v2</summary>
|
||||||
|
|
||||||
Web Keys in ZITADEL are used to sign and verify JSON Web Tokens (JWT).
|
This API enables you to manage custom executions and targets—formerly known as actions—across your entire ZITADEL instance.
|
||||||
ID tokens are created, signed and returned by ZITADEL when a OpenID connect (OIDC) or OAuth2 authorization flow completes and a user is authenticated.
|
With Actions V2, you gain significantly more flexibility to tailor ZITADEL’s behavior compared to previous versions.
|
||||||
Based on customer and community feedback, we've updated our key management system. You now have full manual control over key generation and rotation, instead of the previous automatic process.
|
Actions are now available instance-wide, eliminating the need to configure them for each organization individually.
|
||||||
|
ZITADEL no longer restricts the implementation language, tooling, or runtime for action executions.
|
||||||
|
Instead, you define external endpoints that are called by ZITADEL and maintained by you.
|
||||||
|
|
||||||
Read the full description about Web Keys in our [Documentation](https://zitadel.com/docs/guides/integrate/login/oidc/webkeys).
|
- [Actions V2](../apis/resources/action_service_v2)
|
||||||
|
</details>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>SCIM 2.0 Server - User Resource</summary>
|
<summary>Deprecated endpoints</summary>
|
||||||
|
|
||||||
The Zitadel SCIM v2 service provider interface enables seamless integration of identity and access management (IAM) systems with Zitadel, following the System for Cross-domain Identity Management (SCIM) v2.0 specification.
|
<Deprecated/>
|
||||||
This interface allows standardized management of IAM resources, making it easier to automate user provisioning and deprovisioning.
|
|
||||||
|
|
||||||
- [SCIM 2.0 API](https://zitadel.com/docs/apis/scim2)
|
<details>
|
||||||
- [Manage Users Guide](https://zitadel.com/docs/guides/manage/user/scim2)
|
<summary>Organization Objects V1 > Users V1</summary>
|
||||||
|
|
||||||
|
- `AddMachineKey()`
|
||||||
|
- `AddMachineUser()`
|
||||||
|
- `AddPersonalAccessToken()`
|
||||||
|
- `BulkRemoveUserMetadata()`
|
||||||
|
- `BulkSetUserMetadata()`
|
||||||
|
- `GenerateMachineSecret()`
|
||||||
|
- `GetMachineKeyByIDs()`
|
||||||
|
- `GetOrgByDomainGlobal()`
|
||||||
|
- `GetPersonalAccessTokenByIDs()`
|
||||||
|
- `GetUserMetadata()`
|
||||||
|
- `ListAppKeys()`
|
||||||
|
- `ListMachineKeys()`
|
||||||
|
- `ListPersonalAccessTokens()`
|
||||||
|
- `ListUserMetadata()`
|
||||||
|
- `RemoveMachineKey()`
|
||||||
|
- `RemoveMachineSecret()`
|
||||||
|
- `RemovePersonalAccessToken()`
|
||||||
|
- `RemoveUserMetadata()`
|
||||||
|
- `SetUserMetadata()`
|
||||||
|
- `UpdateHumanPhone()`
|
||||||
|
- `UpdateMachine()`
|
||||||
|
- `UpdateUserName()`
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Caches</summary>
|
<summary>Projects V1</summary>
|
||||||
|
|
||||||
ZITADEL supports the use of a caches to speed up the lookup of frequently needed objects.
|
- `AddProject()`
|
||||||
As opposed to HTTP caches which might reside between ZITADEL and end-user applications, the cache build into ZITADEL uses active invalidation when an object gets updated.
|
- `AddProjectGrant()`
|
||||||
Another difference is that HTTP caches only cache the result of a complete request and the built-in cache stores objects needed for the internal business logic.
|
- `AddProjectRole()`
|
||||||
For example, each request made to ZITADEL needs to retrieve and set instance information in middleware.
|
- `BulkAddProjectRoles()`
|
||||||
|
- `DeactivateProject()`
|
||||||
|
- `DeactivateProjectGrant()`
|
||||||
|
- `GetGrantedProjectByID()`
|
||||||
|
- `GetProjectByID()`
|
||||||
|
- `GetProjectGrantByID()`
|
||||||
|
- `ListAllProjectGrants()`
|
||||||
|
- `ListGrantedProjectRoles()`
|
||||||
|
- `ListGrantedProjects()`
|
||||||
|
- `ListProjectGrants()`
|
||||||
|
- `ListProjectRoles()`
|
||||||
|
- `ListProjects()`
|
||||||
|
- `ReactivateProject()`
|
||||||
|
- `ReactivateProjectGrant()`
|
||||||
|
- `RemoveProject()`
|
||||||
|
- `RemoveProjectGrant()`
|
||||||
|
- `RemoveProjectRole()`
|
||||||
|
- `UpdateProject()`
|
||||||
|
- `UpdateProjectGrant()`
|
||||||
|
- `UpdateProjectRole()`
|
||||||
|
</details>
|
||||||
|
|
||||||
Read more about Zitadel Caches [here](https://zitadel.com/docs/self-hosting/manage/cache)
|
<details>
|
||||||
|
<summary>Members V1</summary>
|
||||||
|
|
||||||
|
- `AddIAMMember()`
|
||||||
|
- `AddOrgMember()`
|
||||||
|
- `AddProjectGrantMember()`
|
||||||
|
- `AddProjectMember()`
|
||||||
|
- `ListIAMMembers()`
|
||||||
|
- `ListOrgMembers()`
|
||||||
|
- `ListProjectGrantMembers()`
|
||||||
|
- `ListProjectMembers()`
|
||||||
|
- `ListUserMemberships()`
|
||||||
|
- `RemoveIAMMember()`
|
||||||
|
- `RemoveOrgMember()`
|
||||||
|
- `RemoveProjectGrantMember()`
|
||||||
|
- `RemoveProjectMember()`
|
||||||
|
- `UpdateIAMMember()`
|
||||||
|
- `UpdateOrgMember()`
|
||||||
|
- `UpdateProjectGrantMember()`
|
||||||
|
- `UpdateProjectMember()`
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Instance Lifecycle V1 > System Service V1</summary>
|
||||||
|
|
||||||
|
- `AddInstanceTrustedDomain()`
|
||||||
|
- `GetMyInstance()`
|
||||||
|
- `ListInstanceDomains()`
|
||||||
|
- `ListInstanceTrustedDomains()`
|
||||||
|
- `RemoveInstanceTrustedDomain()`
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Instance Objects V1 > Organizations V1 </summary>
|
||||||
|
|
||||||
|
- `GetDefaultOrg()`
|
||||||
|
- `GetOrgByID()`
|
||||||
|
- `IsOrgUnique()`
|
||||||
</details>
|
</details>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
1
docs/docs/self-hosting/deploy/.gitignore
vendored
Normal file
1
docs/docs/self-hosting/deploy/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
login-client-pat
|
@@ -1,67 +1,65 @@
|
|||||||
---
|
---
|
||||||
title: Set up ZITADEL with Docker Compose
|
title: Set up Zitadel with Docker Compose
|
||||||
sidebar_label: Docker Compose
|
sidebar_label: Docker Compose
|
||||||
---
|
---
|
||||||
|
|
||||||
import CodeBlock from '@theme/CodeBlock';
|
import CodeBlock from '@theme/CodeBlock';
|
||||||
import DockerComposeSource from '!!raw-loader!./docker-compose.yaml'
|
import DockerComposeSource from '!!raw-loader!./docker-compose.yaml'
|
||||||
import DockerComposeSaSource from '!!raw-loader!./docker-compose-sa.yaml'
|
import ExampleZitadelConfigSource from '!!raw-loader!./example-zitadel-config.yaml'
|
||||||
import Disclaimer from './_disclaimer.mdx'
|
import ExampleZitadelSecretsSource from '!!raw-loader!./example-zitadel-secrets.yaml'
|
||||||
import DefaultUser from './_defaultuser.mdx'
|
import ExampleZitadelInitStepsSource from '!!raw-loader!./example-zitadel-init-steps.yaml'
|
||||||
import Next from './_next.mdx'
|
|
||||||
import NoteInstanceNotFound from './troubleshooting/_note_instance_not_found.mdx';
|
|
||||||
|
|
||||||
|
The stack consists of four long-running containers and a couple of short-lived containers:
|
||||||
|
- A [Traefik](https://doc.traefik.io/traefik/) reverse proxy container with upstream HTTP/2 enabled, issuing a self-signed TLS certificate.
|
||||||
|
- A Login container that is accessible via Traefik at `/ui/v2/login`
|
||||||
|
- A Zitadel container that is accessible via Traefik at all other paths than `/ui/v2/login`.
|
||||||
|
- An insecure [PostgreSQL](https://www.postgresql.org/docs/current/index.html).
|
||||||
|
|
||||||
The setup is tested against Docker version 20.10.17 and Docker Compose version v2.2.3
|
The Traefik container and the login container call the Zitadel container via the internal Docker network at `h2c://zitadel:8080`
|
||||||
|
|
||||||
## Docker compose
|
The setup is tested against Docker version 28.3.2 and Docker Compose version v2.38.2
|
||||||
|
|
||||||
By executing the commands below, you will download the following file:
|
By executing the commands below, you will download the following files:
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>docker-compose.yaml</summary>
|
<summary>docker-compose.yaml</summary>
|
||||||
<CodeBlock language="yaml">{DockerComposeSource}</CodeBlock>
|
<CodeBlock language="yaml">{DockerComposeSource}</CodeBlock>
|
||||||
</details>
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>example-zitadel-config.yaml</summary>
|
||||||
|
<CodeBlock language="yaml">{ExampleZitadelConfigSource}</CodeBlock>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>example-zitadel-secrets.yaml</summary>
|
||||||
|
<CodeBlock language="yaml">{ExampleZitadelSecretsSource}</CodeBlock>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>example-zitadel-init-steps.yaml</summary>
|
||||||
|
<CodeBlock language="yaml">{ExampleZitadelInitStepsSource}</CodeBlock>
|
||||||
|
</details>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Download the docker compose example configuration.
|
# Download the docker compose example configuration.
|
||||||
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/docker-compose.yaml
|
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/docker-compose.yaml
|
||||||
|
|
||||||
# Run the database and application containers.
|
# Download and adjust the example configuration file containing standard configuration.
|
||||||
docker compose up --detach
|
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/example-zitadel-config.yaml
|
||||||
|
|
||||||
|
# Download and adjust the example configuration file containing secret configuration.
|
||||||
|
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/example-zitadel-secrets.yaml
|
||||||
|
|
||||||
|
# Download and adjust the example configuration file containing database initialization configuration.
|
||||||
|
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/example-zitadel-init-steps.yaml
|
||||||
|
|
||||||
|
# Make sure you have the latest version of the images
|
||||||
|
docker compose pull
|
||||||
|
|
||||||
|
# Run the containers
|
||||||
|
docker compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
<DefaultUser components={props.components} />
|
Open your favorite internet browser at https://localhost/ui/console?login_hint=zitadel-admin@zitadel.localhost.
|
||||||
|
Your browser warns you about the insecure self-signed TLS certificate. As localhost resolves to your local machine, you can safely proceed.
|
||||||
|
Use the password *Password1!* to log in.
|
||||||
|
|
||||||
<NoteInstanceNotFound/>
|
Read more about [the login process](/guides/integrate/login/oidc/login-users).
|
||||||
|
|
||||||
## 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>
|
|
||||||
|
|
||||||
## Docker compose with service account
|
|
||||||
|
|
||||||
By executing the commands below, you will download the following file:
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>docker-compose-sa.yaml</summary>
|
|
||||||
<CodeBlock language="yaml">{DockerComposeSaSource}</CodeBlock>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Download the docker compose example configuration.
|
|
||||||
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/docker-compose-sa.yaml -O docker-compose.yaml
|
|
||||||
|
|
||||||
# create the machine key directory
|
|
||||||
mkdir machinekey
|
|
||||||
|
|
||||||
# Run the database and application containers.
|
|
||||||
docker compose up --detach
|
|
||||||
|
|
||||||
# then you can move your machine key
|
|
||||||
mv ./machinekey/zitadel-admin-sa.json $HOME/zitadel-admin-sa.json
|
|
||||||
```
|
|
||||||
|
|
||||||
This key can be used to provision resources with for example [Terraform](/docs/guides/manage/terraform-provider).
|
|
||||||
|
|
||||||
<Next components={props.components} />
|
|
||||||
<Disclaimer components={props.components} />
|
|
@@ -1,49 +0,0 @@
|
|||||||
services:
|
|
||||||
zitadel:
|
|
||||||
# The user should have the permission to write to ./machinekey
|
|
||||||
user: "${UID:-1000}"
|
|
||||||
restart: 'always'
|
|
||||||
networks:
|
|
||||||
- 'zitadel'
|
|
||||||
image: 'ghcr.io/zitadel/zitadel:latest'
|
|
||||||
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled'
|
|
||||||
environment:
|
|
||||||
ZITADEL_DATABASE_POSTGRES_HOST: db
|
|
||||||
ZITADEL_DATABASE_POSTGRES_PORT: 5432
|
|
||||||
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
|
|
||||||
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
|
|
||||||
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel
|
|
||||||
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
|
|
||||||
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: postgres
|
|
||||||
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: postgres
|
|
||||||
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
|
|
||||||
ZITADEL_EXTERNALSECURE: false
|
|
||||||
ZITADEL_FIRSTINSTANCE_MACHINEKEYPATH: /machinekey/zitadel-admin-sa.json
|
|
||||||
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_USERNAME: zitadel-admin-sa
|
|
||||||
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME: Admin
|
|
||||||
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINEKEY_TYPE: 1
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: 'service_healthy'
|
|
||||||
ports:
|
|
||||||
- '8080:8080'
|
|
||||||
volumes:
|
|
||||||
- ./machinekey:/machinekey
|
|
||||||
|
|
||||||
db:
|
|
||||||
restart: 'always'
|
|
||||||
image: postgres:17-alpine
|
|
||||||
environment:
|
|
||||||
PGUSER: postgres
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
networks:
|
|
||||||
- 'zitadel'
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready", "-d", "zitadel", "-U", "postgres"]
|
|
||||||
interval: '10s'
|
|
||||||
timeout: '30s'
|
|
||||||
retries: 5
|
|
||||||
start_period: '20s'
|
|
||||||
|
|
||||||
networks:
|
|
||||||
zitadel:
|
|
@@ -1,41 +1,117 @@
|
|||||||
services:
|
services:
|
||||||
zitadel:
|
|
||||||
restart: 'always'
|
db:
|
||||||
networks:
|
image: postgres:17-alpine
|
||||||
- 'zitadel'
|
restart: unless-stopped
|
||||||
image: 'ghcr.io/zitadel/zitadel:latest'
|
|
||||||
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled'
|
|
||||||
environment:
|
environment:
|
||||||
ZITADEL_DATABASE_POSTGRES_HOST: db
|
- POSTGRES_USER=root
|
||||||
ZITADEL_DATABASE_POSTGRES_PORT: 5432
|
- POSTGRES_PASSWORD=postgres
|
||||||
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
|
networks:
|
||||||
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
|
- 'storage'
|
||||||
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel
|
healthcheck:
|
||||||
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
|
test: [ "CMD-SHELL", "pg_isready", "-d", "db_prod" ]
|
||||||
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: postgres
|
interval: 10s
|
||||||
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: postgres
|
timeout: 60s
|
||||||
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
|
retries: 5
|
||||||
ZITADEL_EXTERNALSECURE: false
|
start_period: 10s
|
||||||
|
volumes:
|
||||||
|
- 'data:/var/lib/postgresql/data:rw'
|
||||||
|
|
||||||
|
zitadel-init:
|
||||||
|
restart: 'no'
|
||||||
|
networks:
|
||||||
|
- 'storage'
|
||||||
|
image: 'ghcr.io/zitadel/zitadel:v4.0.0-rc.2'
|
||||||
|
command: [ init, --config, /example-zitadel-config.yaml, --config, /example-zitadel-secrets.yaml ]
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: 'service_healthy'
|
condition: 'service_healthy'
|
||||||
ports:
|
volumes:
|
||||||
- '8080:8080'
|
- './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
|
||||||
|
- './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
|
||||||
|
|
||||||
db:
|
zitadel-setup:
|
||||||
restart: 'always'
|
restart: 'no'
|
||||||
image: postgres:17-alpine
|
|
||||||
environment:
|
|
||||||
PGUSER: postgres
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
networks:
|
networks:
|
||||||
- 'zitadel'
|
- 'storage'
|
||||||
healthcheck:
|
image: 'ghcr.io/zitadel/zitadel:v4.0.0-rc.2'
|
||||||
test: ["CMD-SHELL", "pg_isready", "-d", "zitadel", "-U", "postgres"]
|
command: [ setup, --config, /current-dir/example-zitadel-config.yaml, --config, /current-dir/example-zitadel-secrets.yaml, --steps, /current-dir/example-zitadel-init-steps.yaml, --masterkey, MasterkeyNeedsToHave32Characters ]
|
||||||
interval: '10s'
|
depends_on:
|
||||||
timeout: '30s'
|
zitadel-init:
|
||||||
retries: 5
|
condition: 'service_completed_successfully'
|
||||||
start_period: '20s'
|
restart: false
|
||||||
|
volumes:
|
||||||
|
- '.:/current-dir:rw'
|
||||||
|
|
||||||
networks:
|
|
||||||
zitadel:
|
zitadel:
|
||||||
|
restart: 'unless-stopped'
|
||||||
|
networks:
|
||||||
|
- 'backend'
|
||||||
|
- 'storage'
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.zitadel.rule=!PathPrefix(`/ui/v2/login`)"
|
||||||
|
- "traefik.http.routers.zitadel.tls=true" # Traefik uses a self-signed certificate
|
||||||
|
- "traefik.http.services.zitadel.loadbalancer.passhostheader=true"
|
||||||
|
- "traefik.http.services.zitadel.loadbalancer.server.scheme=h2c"
|
||||||
|
- "traefik.http.services.zitadel.loadbalancer.server.port=8080"
|
||||||
|
image: 'ghcr.io/zitadel/zitadel:v4.0.0-rc.2'
|
||||||
|
command: [ start, --config, /example-zitadel-config.yaml, --config, /example-zitadel-secrets.yaml, --masterkey, MasterkeyNeedsToHave32Characters ]
|
||||||
|
depends_on:
|
||||||
|
zitadel-setup:
|
||||||
|
condition: 'service_completed_successfully'
|
||||||
|
restart: true
|
||||||
|
volumes:
|
||||||
|
- './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
|
||||||
|
- './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "/app/zitadel", "ready", "--config", "/example-zitadel-config.yaml", "--config", "/example-zitadel-secrets.yaml" ]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 60s
|
||||||
|
retries: 5
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
|
login:
|
||||||
|
restart: 'unless-stopped'
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.login.rule=PathPrefix(`/ui/v2/login`)"
|
||||||
|
- "traefik.http.routers.login.tls=true" # Traefik uses a self-signed certificate
|
||||||
|
- "traefik.http.services.login.loadbalancer.passhostheader=true"
|
||||||
|
- "traefik.http.services.login.loadbalancer.server.port=3000"
|
||||||
|
image: 'ghcr.io/zitadel/zitadel-login:v4.0.0-rc.2'
|
||||||
|
# If you can't use the network_mode service:zitadel, you can pass the environment variable CUSTOM_REQUEST_HEADERS=Host:localhost instead.
|
||||||
|
network_mode: service:zitadel
|
||||||
|
environment:
|
||||||
|
- ZITADEL_API_URL=http://localhost:8080
|
||||||
|
- NEXT_PUBLIC_BASE_PATH=/ui/v2/login
|
||||||
|
- ZITADEL_SERVICE_USER_TOKEN_FILE=/current-dir/login-client-pat
|
||||||
|
user: "${UID:-1000}"
|
||||||
|
volumes:
|
||||||
|
- '.:/current-dir:ro'
|
||||||
|
depends_on:
|
||||||
|
zitadel-setup:
|
||||||
|
condition: 'service_completed_successfully'
|
||||||
|
restart: false
|
||||||
|
|
||||||
|
traefik:
|
||||||
|
image: traefik:latest
|
||||||
|
command: --providers.docker --api.insecure=true --entrypoints.websecure.address=:443 --log.level=DEBUG --accesslog
|
||||||
|
networks:
|
||||||
|
- 'backend'
|
||||||
|
ports:
|
||||||
|
- "443:443"
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
depends_on:
|
||||||
|
zitadel:
|
||||||
|
condition: 'service_healthy'
|
||||||
|
login:
|
||||||
|
condition: 'service_started'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
storage:
|
||||||
|
backend:
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/defaults.yaml
|
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/defaults.yaml
|
||||||
|
|
||||||
ExternalSecure: true
|
ExternalSecure: true
|
||||||
ExternalDomain: 127.0.0.1.sslip.io
|
|
||||||
ExternalPort: 443
|
ExternalPort: 443
|
||||||
|
|
||||||
# Traefik terminates TLS. Inside the Docker network, we use plain text.
|
# Traefik terminates TLS. Inside the Docker network, we use plain text.
|
||||||
@@ -16,12 +15,6 @@ Database:
|
|||||||
User.SSL.Mode: 'disable'
|
User.SSL.Mode: 'disable'
|
||||||
Admin.SSL.Mode: 'disable'
|
Admin.SSL.Mode: 'disable'
|
||||||
|
|
||||||
# By default, ZITADEL should redirect to /ui/v2/login
|
|
||||||
OIDC:
|
|
||||||
DefaultLoginURLV2: "/ui/v2/login/login?authRequest=" # ZITADEL_OIDC_DEFAULTLOGINURLV2
|
|
||||||
DefaultLogoutURLV2: "/ui/v2/login/logout?post_logout_redirect=" # ZITADEL_OIDC_DEFAULTLOGOUTURLV2
|
|
||||||
SAML.DefaultLoginURLV2: "/ui/v2/login/login?authRequest=" # ZITADEL_SAML_DEFAULTLOGINURLV2
|
|
||||||
|
|
||||||
# Access logs allow us to debug Network issues
|
# Access logs allow us to debug Network issues
|
||||||
LogStore.Access.Stdout.Enabled: true
|
LogStore.Access.Stdout.Enabled: true
|
||||||
|
|
@@ -1,12 +1,11 @@
|
|||||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/setup/steps.yaml
|
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/setup/steps.yaml
|
||||||
FirstInstance:
|
FirstInstance:
|
||||||
PatPath: '/pat'
|
LoginClientPatPath: '/current-dir/login-client-pat'
|
||||||
Org:
|
Org:
|
||||||
# We want to authenticate immediately at the console without changing the password
|
# We want to authenticate immediately at the console without changing the password
|
||||||
Human:
|
Human.PasswordChangeRequired: false
|
||||||
PasswordChangeRequired: false
|
LoginClient:
|
||||||
Machine:
|
Machine:
|
||||||
Machine:
|
Username: 'login-client'
|
||||||
Username: 'login-container'
|
Name: 'Automatically Initialized IAM Login Client'
|
||||||
Name: 'Login Container'
|
|
||||||
Pat.ExpirationDate: '2029-01-01T00:00:00Z'
|
Pat.ExpirationDate: '2029-01-01T00:00:00Z'
|
@@ -1 +0,0 @@
|
|||||||
.env-file
|
|
@@ -1,157 +0,0 @@
|
|||||||
services:
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: postgres:17-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=root
|
|
||||||
- POSTGRES_PASSWORD=postgres
|
|
||||||
networks:
|
|
||||||
- 'storage'
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 60s
|
|
||||||
retries: 5
|
|
||||||
start_period: 10s
|
|
||||||
volumes:
|
|
||||||
- 'data:/var/lib/postgresql/data:rw'
|
|
||||||
|
|
||||||
zitadel-init:
|
|
||||||
restart: 'no'
|
|
||||||
networks:
|
|
||||||
- 'storage'
|
|
||||||
image: 'ghcr.io/zitadel/zitadel:latest'
|
|
||||||
command: 'init --config /example-zitadel-config.yaml --config /example-zitadel-secrets.yaml'
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: 'service_healthy'
|
|
||||||
volumes:
|
|
||||||
- './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
|
|
||||||
- './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
|
|
||||||
|
|
||||||
zitadel-setup:
|
|
||||||
restart: 'no'
|
|
||||||
networks:
|
|
||||||
- 'storage'
|
|
||||||
# We use the debug image so we have the environment to
|
|
||||||
# - create the .env file for the login to authenticate at Zitadel
|
|
||||||
# - set the correct permissions for the .env-file folder
|
|
||||||
image: 'ghcr.io/zitadel/zitadel:latest-debug'
|
|
||||||
user: root
|
|
||||||
entrypoint: '/bin/sh'
|
|
||||||
command:
|
|
||||||
- -c
|
|
||||||
- >
|
|
||||||
/app/zitadel setup
|
|
||||||
--config /example-zitadel-config.yaml
|
|
||||||
--config /example-zitadel-secrets.yaml
|
|
||||||
--steps /example-zitadel-init-steps.yaml
|
|
||||||
--masterkey ${ZITADEL_MASTERKEY} &&
|
|
||||||
mv /pat /.env-file/pat || exit 0 &&
|
|
||||||
echo ZITADEL_SERVICE_USER_TOKEN=$(cat /.env-file/pat) > /.env-file/.env &&
|
|
||||||
chown -R 1001:${GID} /.env-file &&
|
|
||||||
chmod -R 770 /.env-file
|
|
||||||
environment:
|
|
||||||
- GID
|
|
||||||
depends_on:
|
|
||||||
zitadel-init:
|
|
||||||
condition: 'service_completed_successfully'
|
|
||||||
restart: false
|
|
||||||
volumes:
|
|
||||||
- './.env-file:/.env-file:rw'
|
|
||||||
- './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
|
|
||||||
- './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
|
|
||||||
- './example-zitadel-init-steps.yaml:/example-zitadel-init-steps.yaml:ro'
|
|
||||||
|
|
||||||
zitadel:
|
|
||||||
restart: 'unless-stopped'
|
|
||||||
networks:
|
|
||||||
- 'backend'
|
|
||||||
- 'storage'
|
|
||||||
image: 'ghcr.io/zitadel/zitadel:latest'
|
|
||||||
command: >
|
|
||||||
start --config /example-zitadel-config.yaml
|
|
||||||
--config /example-zitadel-secrets.yaml
|
|
||||||
--masterkey ${ZITADEL_MASTERKEY}
|
|
||||||
depends_on:
|
|
||||||
zitadel-setup:
|
|
||||||
condition: 'service_completed_successfully'
|
|
||||||
restart: true
|
|
||||||
volumes:
|
|
||||||
- './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
|
|
||||||
- './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
healthcheck:
|
|
||||||
test: [
|
|
||||||
"CMD", "/app/zitadel", "ready",
|
|
||||||
"--config", "/example-zitadel-config.yaml",
|
|
||||||
"--config", "/example-zitadel-secrets.yaml"
|
|
||||||
]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 60s
|
|
||||||
retries: 5
|
|
||||||
start_period: 10s
|
|
||||||
|
|
||||||
# The use-new-login service configures Zitadel to use the new login v2 for all applications.
|
|
||||||
# It also gives the setupped machine user the necessary IAM_LOGIN_CLIENT role.
|
|
||||||
use-new-login:
|
|
||||||
restart: 'on-failure'
|
|
||||||
user: "1001"
|
|
||||||
networks:
|
|
||||||
- 'backend'
|
|
||||||
image: 'badouralix/curl-jq:alpine'
|
|
||||||
entrypoint: '/bin/sh'
|
|
||||||
command:
|
|
||||||
- -c
|
|
||||||
- >
|
|
||||||
curl -X PUT -H "Host: 127.0.0.1.sslip.io" -H "Authorization: Bearer $(cat ./.env-file/pat)" --insecure http://zitadel:8080/v2/features/instance -d '{"loginV2": {"required": true}}' &&
|
|
||||||
LOGIN_USER=$(curl --fail-with-body -H "Host: 127.0.0.1.sslip.io" -H "Authorization: Bearer $(cat ./.env-file/pat)" --insecure http://zitadel:8080/auth/v1/users/me | jq -r '.user.id') &&
|
|
||||||
curl -X PUT -H "Host: 127.0.0.1.sslip.io" -H "Authorization: Bearer $(cat ./.env-file/pat)" --insecure http://zitadel:8080/admin/v1/members/$${LOGIN_USER} -d '{"roles": ["IAM_OWNER", "IAM_LOGIN_CLIENT"]}'
|
|
||||||
volumes:
|
|
||||||
- './.env-file:/.env-file:ro'
|
|
||||||
depends_on:
|
|
||||||
zitadel:
|
|
||||||
condition: 'service_healthy'
|
|
||||||
restart: false
|
|
||||||
|
|
||||||
login:
|
|
||||||
restart: 'unless-stopped'
|
|
||||||
networks:
|
|
||||||
- 'backend'
|
|
||||||
image: 'ghcr.io/zitadel/login:main'
|
|
||||||
environment:
|
|
||||||
- ZITADEL_API_URL=http://zitadel:8080
|
|
||||||
- CUSTOM_REQUEST_HEADERS=Host:127.0.0.1.sslip.io
|
|
||||||
- NEXT_PUBLIC_BASE_PATH="/ui/v2/login"
|
|
||||||
user: "${UID:-1000}"
|
|
||||||
volumes:
|
|
||||||
- './.env-file:/.env-file:ro'
|
|
||||||
depends_on:
|
|
||||||
zitadel:
|
|
||||||
condition: 'service_healthy'
|
|
||||||
restart: false
|
|
||||||
|
|
||||||
traefik:
|
|
||||||
restart: 'unless-stopped'
|
|
||||||
networks:
|
|
||||||
- 'backend'
|
|
||||||
image: "traefik:latest"
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
volumes:
|
|
||||||
- "./example-traefik.yaml:/etc/traefik/traefik.yaml"
|
|
||||||
depends_on:
|
|
||||||
zitadel:
|
|
||||||
condition: 'service_healthy'
|
|
||||||
login:
|
|
||||||
condition: 'service_started'
|
|
||||||
|
|
||||||
networks:
|
|
||||||
storage:
|
|
||||||
backend:
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
data:
|
|
@@ -1,40 +0,0 @@
|
|||||||
log:
|
|
||||||
level: DEBUG
|
|
||||||
|
|
||||||
accessLog: {}
|
|
||||||
|
|
||||||
entrypoints:
|
|
||||||
websecure:
|
|
||||||
address: ":443"
|
|
||||||
|
|
||||||
providers:
|
|
||||||
file:
|
|
||||||
filename: /etc/traefik/traefik.yaml
|
|
||||||
|
|
||||||
http:
|
|
||||||
routers:
|
|
||||||
login:
|
|
||||||
entryPoints:
|
|
||||||
- websecure
|
|
||||||
service: login
|
|
||||||
rule: 'Host(`127.0.0.1.sslip.io`) && PathPrefix(`/ui/v2/login`)'
|
|
||||||
tls: {}
|
|
||||||
zitadel:
|
|
||||||
entryPoints:
|
|
||||||
- websecure
|
|
||||||
service: zitadel
|
|
||||||
rule: 'Host(`127.0.0.1.sslip.io`) && !PathPrefix(`/ui/v2/login`)'
|
|
||||||
tls: {}
|
|
||||||
|
|
||||||
services:
|
|
||||||
login:
|
|
||||||
loadBalancer:
|
|
||||||
servers:
|
|
||||||
- url: http://login:3000
|
|
||||||
passHostHeader: true
|
|
||||||
zitadel:
|
|
||||||
loadBalancer:
|
|
||||||
servers:
|
|
||||||
- url: h2c://zitadel:8080
|
|
||||||
passHostHeader: true
|
|
||||||
|
|
@@ -1,74 +0,0 @@
|
|||||||
---
|
|
||||||
title: A Zitadel Load Balancing Example
|
|
||||||
---
|
|
||||||
|
|
||||||
import CodeBlock from '@theme/CodeBlock';
|
|
||||||
import DockerComposeSource from '!!raw-loader!./docker-compose.yaml'
|
|
||||||
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'
|
|
||||||
|
|
||||||
The stack consists of four long-running containers and a couple of short-lived containers:
|
|
||||||
- A [Traefik](https://doc.traefik.io/traefik/) reverse proxy container with upstream HTTP/2 enabled, issuing a self-signed TLS certificate.
|
|
||||||
- A Login container that is accessible via Traefik at `/ui/v2/login`
|
|
||||||
- A Zitadel container that is accessible via Traefik at all other paths than `/ui/v2/login`.
|
|
||||||
- An insecure [PostgreSQL](https://www.postgresql.org/docs/current/index.html).
|
|
||||||
|
|
||||||
The Traefik container and the login container call the Zitadel container via the internal Docker network at `h2c://zitadel:8080`
|
|
||||||
|
|
||||||
The setup is tested against Docker version 28.0.4 and Docker Compose version v2.34.0
|
|
||||||
|
|
||||||
By executing the commands below, you will download the following files:
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>docker-compose.yaml</summary>
|
|
||||||
<CodeBlock language="yaml">{DockerComposeSource}</CodeBlock>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>example-traefik.yaml</summary>
|
|
||||||
<CodeBlock language="yaml">{ExampleTraefikSource}</CodeBlock>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>example-zitadel-config.yaml</summary>
|
|
||||||
<CodeBlock language="yaml">{ExampleZITADELConfigSource}</CodeBlock>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>example-zitadel-secrets.yaml</summary>
|
|
||||||
<CodeBlock language="yaml">{ExampleZITADELSecretsSource}</CodeBlock>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>example-zitadel-init-steps.yaml</summary>
|
|
||||||
<CodeBlock language="yaml">{ExampleZITADELInitStepsSource}</CodeBlock>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Download the docker compose example configuration.
|
|
||||||
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/loadbalancing-example/docker-compose.yaml
|
|
||||||
|
|
||||||
# Download the Traefik example configuration.
|
|
||||||
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/loadbalancing-example/example-traefik.yaml
|
|
||||||
|
|
||||||
# Download and adjust the example configuration file containing standard configuration.
|
|
||||||
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-config.yaml
|
|
||||||
|
|
||||||
# Download and adjust the example configuration file containing secret configuration.
|
|
||||||
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-secrets.yaml
|
|
||||||
|
|
||||||
# Download and adjust the example configuration file containing database initialization configuration.
|
|
||||||
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/loadbalancing-example/example-zitadel-init-steps.yaml
|
|
||||||
|
|
||||||
# A single ZITADEL instance always needs the same 32 bytes long masterkey
|
|
||||||
# Generate one to a file if you haven't done so already and pass it as environment variable
|
|
||||||
LC_ALL=C tr -dc '[:graph:]' </dev/urandom | head -c 32 > ./zitadel-masterkey
|
|
||||||
export ZITADEL_MASTERKEY="$(cat ./zitadel-masterkey)"
|
|
||||||
|
|
||||||
# Run the database and application containers
|
|
||||||
docker compose up --detach --wait
|
|
||||||
```
|
|
||||||
|
|
||||||
Open your favorite internet browser at https://127.0.0.1.sslip.io/ui/console?login_hint=zitadel-admin@zitadel.127.0.0.1.sslip.io.
|
|
||||||
Your browser warns you about the insecure self-signed TLS certificate. As 127.0.0.1.sslip.io resolves to your localhost, you can safely proceed.
|
|
||||||
Use the password *Password1!* to log in.
|
|
||||||
|
|
||||||
Read more about [the login process](/guides/integrate/login/oidc/login-users).
|
|
83
docs/docs/self-hosting/manage/service_ping.md
Normal file
83
docs/docs/self-hosting/manage/service_ping.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
title: Service Ping
|
||||||
|
sidebar_label: Service Ping
|
||||||
|
---
|
||||||
|
|
||||||
|
Service Ping is a feature that periodically sends anonymized analytics and usage data from your ZITADEL system to a central endpoint.
|
||||||
|
This data helps improve ZITADEL by providing insights into its usage patterns.
|
||||||
|
|
||||||
|
The feature is enabled by default, but can be disabled either completely or for specific reports.
|
||||||
|
Checkout the configuration options below.
|
||||||
|
|
||||||
|
## Data Sent by Service Ping
|
||||||
|
|
||||||
|
### Base Information
|
||||||
|
|
||||||
|
If the feature is enabled, the base information will always be sent. To prevent that, you can opt out by disabling the entire Service Ping:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ServicePing:
|
||||||
|
Enabled: false # ZITADEL_SERVICEPING_ENABLED
|
||||||
|
```
|
||||||
|
|
||||||
|
The base information sent back includes the following:
|
||||||
|
- your systemID
|
||||||
|
- the currently run version of ZITADEL
|
||||||
|
- information on all instances
|
||||||
|
- id
|
||||||
|
- creation date
|
||||||
|
- domains
|
||||||
|
|
||||||
|
### Resource Counts
|
||||||
|
|
||||||
|
Resource counts is a report that provides us with information about the number of resources in your ZITADEL instances.
|
||||||
|
|
||||||
|
The following resources are counted:
|
||||||
|
- Instances
|
||||||
|
- Organizations
|
||||||
|
- Projects per organization
|
||||||
|
- Users per organization
|
||||||
|
- Instance Administrators
|
||||||
|
- Identity Providers
|
||||||
|
- LDAP Identity Providers
|
||||||
|
- Actions (V1)
|
||||||
|
- Targets and set up executions
|
||||||
|
- Login Policies
|
||||||
|
- Password Complexity Policies
|
||||||
|
- Password Expiry Policies
|
||||||
|
- Lockout Policies
|
||||||
|
|
||||||
|
The list might be extended in the future to include more resources.
|
||||||
|
|
||||||
|
To disable this report, set the following in your configuration file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ServicePing:
|
||||||
|
Telemetry:
|
||||||
|
ResourceCounts:
|
||||||
|
Enabled: false # ZITADEL_SERVICEPING_TELEMETRY_RESOURCECOUNT_ENABLED
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The Service Ping feature can be configured through the runtime configuration. Please check out the configuration file
|
||||||
|
for all available options. Below is a list of the most important options:
|
||||||
|
|
||||||
|
### Interval
|
||||||
|
|
||||||
|
This defines at which interval the Service Ping feature sends data to the central endpoint. It supports the extended cron syntax
|
||||||
|
and by default is set to `@daily`, which means it will send data every day. The time is randomized on startup to prevent
|
||||||
|
all systems from sending data at the same time.
|
||||||
|
|
||||||
|
You can adjust it to your needs to make sure there is no performance impact on your system.
|
||||||
|
For example, if you already have some scheduled job syncing data in and out of ZITADEL around a specific time or have regularly a
|
||||||
|
lot of traffic during the day, you might want to change it to a different time, e.g. `15 4 * * *` to send it every day at 4:15 AM.
|
||||||
|
|
||||||
|
The interval must be at least 30 minutes to prevent too frequent requests to the central endpoint.
|
||||||
|
|
||||||
|
### MaxAttempts
|
||||||
|
|
||||||
|
This defines how many attempts the Service Ping feature will make to send data to the central endpoint before giving up
|
||||||
|
for a specific interval and report. If one report fails, it will be retried up to this number of times.
|
||||||
|
Other reports will still be handled in parallel and have their own retry count. This means if the base information
|
||||||
|
only succeeded after three attempts, the resource count still has five attempts to be sent.
|
@@ -64,6 +64,5 @@
|
|||||||
"@docusaurus/module-type-aliases": "^3.8.1",
|
"@docusaurus/module-type-aliases": "^3.8.1",
|
||||||
"@docusaurus/types": "^3.8.1",
|
"@docusaurus/types": "^3.8.1",
|
||||||
"tailwindcss": "^3.2.4"
|
"tailwindcss": "^3.2.4"
|
||||||
},
|
}
|
||||||
"packageManager": "pnpm@9.1.2+sha256.19c17528f9ca20bd442e4ca42f00f1b9808a9cb419383cd04ba32ef19322aba7"
|
|
||||||
}
|
}
|
@@ -1084,7 +1084,6 @@ module.exports = {
|
|||||||
"self-hosting/deploy/devcontainer",
|
"self-hosting/deploy/devcontainer",
|
||||||
"self-hosting/deploy/knative",
|
"self-hosting/deploy/knative",
|
||||||
"self-hosting/deploy/kubernetes",
|
"self-hosting/deploy/kubernetes",
|
||||||
"self-hosting/deploy/loadbalancing-example/loadbalancing-example",
|
|
||||||
"self-hosting/deploy/troubleshooting/troubleshooting",
|
"self-hosting/deploy/troubleshooting/troubleshooting",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -1118,6 +1117,7 @@ module.exports = {
|
|||||||
"self-hosting/manage/tls_modes",
|
"self-hosting/manage/tls_modes",
|
||||||
"self-hosting/manage/database/database",
|
"self-hosting/manage/database/database",
|
||||||
"self-hosting/manage/cache",
|
"self-hosting/manage/cache",
|
||||||
|
"self-hosting/manage/service_ping",
|
||||||
"self-hosting/manage/updating_scaling",
|
"self-hosting/manage/updating_scaling",
|
||||||
"self-hosting/manage/usage_control",
|
"self-hosting/manage/usage_control",
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React, { Fragment, useContext, useEffect, useState } from "react";
|
import { Fragment, useContext, useEffect, useState } from "react";
|
||||||
import { AuthRequestContext } from "../utils/authrequest";
|
import { AuthRequestContext } from "../utils/authrequest";
|
||||||
import { Listbox } from "@headlessui/react";
|
import { Listbox } from "@headlessui/react";
|
||||||
import { Transition } from "@headlessui/react";
|
import { Transition } from "@headlessui/react";
|
||||||
@@ -111,10 +111,18 @@ export function SetAuthRequest() {
|
|||||||
"urn:zitadel:iam:org:project:id:zitadel:aud",
|
"urn:zitadel:iam:org:project:id:zitadel:aud",
|
||||||
"urn:zitadel:iam:user:metadata",
|
"urn:zitadel:iam:user:metadata",
|
||||||
`urn:zitadel:iam:org:id:${
|
`urn:zitadel:iam:org:id:${
|
||||||
organizationId ? organizationId : "[organizationId]"
|
organizationId ? organizationId : "[Organization ID]"
|
||||||
}`,
|
}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const scopeExplanations = new Map([
|
||||||
|
['urn:zitadel:iam:org:project:id:zitadel:aud', 'Requested projectid will be added to the audience of the access token.'],
|
||||||
|
['urn:zitadel:iam:user:metadata', 'Metadata of the user will be included in the token. The values are base64 encoded.'],
|
||||||
|
[`urn:zitadel:iam:org:id:${
|
||||||
|
organizationId ? organizationId : "[organizationId]"
|
||||||
|
}`, 'Enforce that the user is a member of the selected organization.']
|
||||||
|
]);
|
||||||
|
|
||||||
const [scopeState, setScopeState] = useState(
|
const [scopeState, setScopeState] = useState(
|
||||||
[true, true, true, false, false, false, false, false]
|
[true, true, true, false, false, false, false, false]
|
||||||
// new Array(allScopes.length).fill(false)
|
// new Array(allScopes.length).fill(false)
|
||||||
@@ -161,8 +169,13 @@ export function SetAuthRequest() {
|
|||||||
return input;
|
return input;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(() => {
|
||||||
setCodeChallenge(await encodeCodeChallenge(codeVerifier));
|
const updateCodeChallange = async () => {
|
||||||
|
const newCodeChallange = await encodeCodeChallenge(codeVerifier)
|
||||||
|
setCodeChallenge(newCodeChallange);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCodeChallange();
|
||||||
}, [codeVerifier]);
|
}, [codeVerifier]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -525,7 +538,7 @@ export function SetAuthRequest() {
|
|||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
setOrganizationId(value);
|
setOrganizationId(value);
|
||||||
allScopes[7] = `urn:zitadel:iam:org:id:${
|
allScopes[7] = `urn:zitadel:iam:org:id:${
|
||||||
value ? value : "[organizationId]"
|
value ? value : "[Organization ID]"
|
||||||
}`;
|
}`;
|
||||||
toggleScope(8, true);
|
toggleScope(8, true);
|
||||||
setScope(
|
setScope(
|
||||||
@@ -559,6 +572,7 @@ export function SetAuthRequest() {
|
|||||||
name="scopes"
|
name="scopes"
|
||||||
value={`${scope}`}
|
value={`${scope}`}
|
||||||
checked={scopeState[scopeIndex]}
|
checked={scopeState[scopeIndex]}
|
||||||
|
disabled={scope === 'openid'}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
toggleScope(scopeIndex);
|
toggleScope(scopeIndex);
|
||||||
}}
|
}}
|
||||||
@@ -571,6 +585,11 @@ export function SetAuthRequest() {
|
|||||||
</strong>
|
</strong>
|
||||||
) : null}
|
) : null}
|
||||||
</label>
|
</label>
|
||||||
|
{scopeExplanations.has(scope) && (
|
||||||
|
<span className={clsx(hintClasses, 'ml-1')}>
|
||||||
|
{scopeExplanations.get(scope)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@@ -4,7 +4,6 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
|
|||||||
import Layout from "@theme/Layout";
|
import Layout from "@theme/Layout";
|
||||||
import ThemedImage from "@theme/ThemedImage";
|
import ThemedImage from "@theme/ThemedImage";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
import Column from "../components/column";
|
import Column from "../components/column";
|
||||||
import {
|
import {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
export const AuthRequestContext = React.createContext(null);
|
export const AuthRequestContext = React.createContext(null);
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ export default ({ children }) => {
|
|||||||
const id_token_hint = params.get("id_token_hint");
|
const id_token_hint = params.get("id_token_hint");
|
||||||
const organization_id = params.get("organization_id");
|
const organization_id = params.get("organization_id");
|
||||||
|
|
||||||
setInstance(instance_param ?? "https://mydomain-xyza.zitadel.cloud/");
|
setInstance(instance_param ?? "http://localhost:8080/");
|
||||||
setClientId(client_id ?? "170086824411201793@yourapp");
|
setClientId(client_id ?? "170086824411201793@yourapp");
|
||||||
setRedirectUri(
|
setRedirectUri(
|
||||||
redirect_uri ?? "http://localhost:8080/api/auth/callback/zitadel"
|
redirect_uri ?? "http://localhost:8080/api/auth/callback/zitadel"
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
"clean": "rm -rf .turbo node_modules"
|
"clean": "rm -rf .turbo node_modules"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
"@types/pg": "^8.11.6",
|
"@types/pg": "^8.11.6",
|
||||||
"cypress-wait-until": "^3.0.2",
|
"cypress-wait-until": "^3.0.2",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
@@ -25,10 +25,8 @@
|
|||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
"wait-on": "^7.2.0"
|
"wait-on": "^7.2.0",
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^22.3.0",
|
"@types/node": "^22.3.0",
|
||||||
"cypress": "^13.13.3"
|
"cypress": "^14.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"packageManager": "pnpm@9.1.2+sha256.19c17528f9ca20bd442e4ca42f00f1b9808a9cb419383cd04ba32ef19322aba7",
|
"packageManager": "pnpm@10.13.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "zitadel",
|
"name": "zitadel",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"packageManager": "pnpm@10.13.1",
|
||||||
"name": "@zitadel/client",
|
"name": "@zitadel/client",
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"packageManager": "pnpm@10.13.1",
|
||||||
"name": "@zitadel/proto",
|
"name": "@zitadel/proto",
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -77,7 +78,7 @@
|
|||||||
},
|
},
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"generate": "pnpm exec buf generate ../../proto",
|
"generate": "buf generate ../../proto",
|
||||||
"clean": "rm -rf zitadel .turbo node_modules google protoc-gen-openapiv2 validate cjs types es"
|
"clean": "rm -rf zitadel .turbo node_modules google protoc-gen-openapiv2 validate cjs types es"
|
||||||
},
|
},
|
||||||
"nx": {
|
"nx": {
|
||||||
|
2077
pnpm-lock.yaml
generated
2077
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
728
proto/zitadel/action/v2/action_service.proto
Normal file
728
proto/zitadel/action/v2/action_service.proto
Normal file
@@ -0,0 +1,728 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package zitadel.action.v2;
|
||||||
|
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
import "google/api/field_behavior.proto";
|
||||||
|
import "google/protobuf/duration.proto";
|
||||||
|
import "google/protobuf/struct.proto";
|
||||||
|
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||||
|
import "validate/validate.proto";
|
||||||
|
|
||||||
|
import "zitadel/protoc_gen_zitadel/v2/options.proto";
|
||||||
|
|
||||||
|
import "zitadel/action/v2/target.proto";
|
||||||
|
import "zitadel/action/v2/execution.proto";
|
||||||
|
import "zitadel/action/v2/query.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "zitadel/filter/v2/filter.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/zitadel/zitadel/pkg/grpc/action/v2;action";
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||||
|
info: {
|
||||||
|
title: "Action Service";
|
||||||
|
version: "2.0";
|
||||||
|
description: "This API is intended to manage custom executions (previously known as actions) in a ZITADEL instance.";
|
||||||
|
contact:{
|
||||||
|
name: "ZITADEL"
|
||||||
|
url: "https://zitadel.com"
|
||||||
|
email: "hi@zitadel.com"
|
||||||
|
}
|
||||||
|
license: {
|
||||||
|
name: "Apache 2.0",
|
||||||
|
url: "https://github.com/zitadel/zitadel/blob/main/LICENSING.md";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
schemes: HTTPS;
|
||||||
|
schemes: HTTP;
|
||||||
|
|
||||||
|
consumes: "application/json";
|
||||||
|
consumes: "application/grpc";
|
||||||
|
|
||||||
|
produces: "application/json";
|
||||||
|
produces: "application/grpc";
|
||||||
|
|
||||||
|
consumes: "application/grpc-web+proto";
|
||||||
|
produces: "application/grpc-web+proto";
|
||||||
|
|
||||||
|
host: "$CUSTOM-DOMAIN";
|
||||||
|
base_path: "/";
|
||||||
|
|
||||||
|
external_docs: {
|
||||||
|
description: "Detailed information about ZITADEL",
|
||||||
|
url: "https://zitadel.com/docs"
|
||||||
|
}
|
||||||
|
security_definitions: {
|
||||||
|
security: {
|
||||||
|
key: "OAuth2";
|
||||||
|
value: {
|
||||||
|
type: TYPE_OAUTH2;
|
||||||
|
flow: FLOW_ACCESS_CODE;
|
||||||
|
authorization_url: "$CUSTOM-DOMAIN/oauth/v2/authorize";
|
||||||
|
token_url: "$CUSTOM-DOMAIN/oauth/v2/token";
|
||||||
|
scopes: {
|
||||||
|
scope: {
|
||||||
|
key: "openid";
|
||||||
|
value: "openid";
|
||||||
|
}
|
||||||
|
scope: {
|
||||||
|
key: "urn:zitadel:iam:org:project:id:zitadel:aud";
|
||||||
|
value: "urn:zitadel:iam:org:project:id:zitadel:aud";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
security: {
|
||||||
|
security_requirement: {
|
||||||
|
key: "OAuth2";
|
||||||
|
value: {
|
||||||
|
scope: "openid";
|
||||||
|
scope: "urn:zitadel:iam:org:project:id:zitadel:aud";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responses: {
|
||||||
|
key: "403";
|
||||||
|
value: {
|
||||||
|
description: "Returned when the user does not have permission to access the resource.";
|
||||||
|
schema: {
|
||||||
|
json_schema: {
|
||||||
|
ref: "#/definitions/rpcStatus";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responses: {
|
||||||
|
key: "404";
|
||||||
|
value: {
|
||||||
|
description: "Returned when the resource does not exist.";
|
||||||
|
schema: {
|
||||||
|
json_schema: {
|
||||||
|
ref: "#/definitions/rpcStatus";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Service to manage custom executions.
|
||||||
|
// The service provides methods to create, update, delete and list targets and executions.
|
||||||
|
service ActionService {
|
||||||
|
|
||||||
|
// Create Target
|
||||||
|
//
|
||||||
|
// Create a new target to your endpoint, which can be used in executions.
|
||||||
|
//
|
||||||
|
// Required permission:
|
||||||
|
// - `action.target.write`
|
||||||
|
//
|
||||||
|
// Required feature flag:
|
||||||
|
// - `actions`
|
||||||
|
rpc CreateTarget (CreateTargetRequest) returns (CreateTargetResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v2/actions/targets"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "action.target.write"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "Target created successfully";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "409"
|
||||||
|
value: {
|
||||||
|
description: "The target to create already exists.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "400"
|
||||||
|
value: {
|
||||||
|
description: "The feature flag `actions` is not enabled.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Target
|
||||||
|
//
|
||||||
|
// Update an existing target.
|
||||||
|
// To generate a new signing key set the optional expirationSigningKey.
|
||||||
|
//
|
||||||
|
// Required permission:
|
||||||
|
// - `action.target.write`
|
||||||
|
//
|
||||||
|
// Required feature flag:
|
||||||
|
// - `actions`
|
||||||
|
rpc UpdateTarget (UpdateTargetRequest) returns (UpdateTargetResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v2/actions/targets/{id}"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "action.target.write"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "Target successfully updated or left unchanged";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "404"
|
||||||
|
value: {
|
||||||
|
description: "The target to update does not exist.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "400"
|
||||||
|
value: {
|
||||||
|
description: "The feature flag `actions` is not enabled.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Target
|
||||||
|
//
|
||||||
|
// Delete an existing target. This will remove it from any configured execution as well.
|
||||||
|
// In case the target is not found, the request will return a successful response as
|
||||||
|
// the desired state is already achieved.
|
||||||
|
//
|
||||||
|
// Required permission:
|
||||||
|
// - `action.target.delete`
|
||||||
|
//
|
||||||
|
// Required feature flag:
|
||||||
|
// - `actions`
|
||||||
|
rpc DeleteTarget (DeleteTargetRequest) returns (DeleteTargetResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
delete: "/v2/actions/targets/{id}"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "action.target.delete"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "Target deleted successfully";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "400"
|
||||||
|
value: {
|
||||||
|
description: "The feature flag `actions` is not enabled.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Target
|
||||||
|
//
|
||||||
|
// Returns the target identified by the requested ID.
|
||||||
|
//
|
||||||
|
// Required permission:
|
||||||
|
// - `action.target.read`
|
||||||
|
//
|
||||||
|
// Required feature flag:
|
||||||
|
// - `actions`
|
||||||
|
rpc GetTarget (GetTargetRequest) returns (GetTargetResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/v2/actions/targets/{id}"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "action.target.read"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200"
|
||||||
|
value: {
|
||||||
|
description: "Target retrieved successfully";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "404"
|
||||||
|
value: {
|
||||||
|
description: "The target to update does not exist.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "400"
|
||||||
|
value: {
|
||||||
|
description: "The feature flag `actions` is not enabled.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// List targets
|
||||||
|
//
|
||||||
|
// List all matching targets. By default all targets of the instance are returned.
|
||||||
|
// Make sure to include a limit and sorting for pagination.
|
||||||
|
//
|
||||||
|
// Required permission:
|
||||||
|
// - `action.target.read`
|
||||||
|
//
|
||||||
|
// Required feature flag:
|
||||||
|
// - `actions`
|
||||||
|
rpc ListTargets (ListTargetsRequest) returns (ListTargetsResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v2/actions/targets/search",
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "action.target.read"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "A list of all targets matching the query";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "400";
|
||||||
|
value: {
|
||||||
|
description: "invalid list query";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "400"
|
||||||
|
value: {
|
||||||
|
description: "The feature flag `actions` is not enabled.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Execution
|
||||||
|
//
|
||||||
|
// Sets an execution to call a target or include the targets of another execution.
|
||||||
|
// Setting an empty list of targets will remove all targets from the execution, making it a noop.
|
||||||
|
//
|
||||||
|
// Required permission:
|
||||||
|
// - `action.execution.write`
|
||||||
|
//
|
||||||
|
// Required feature flag:
|
||||||
|
// - `actions`
|
||||||
|
rpc SetExecution (SetExecutionRequest) returns (SetExecutionResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
put: "/v2/actions/executions"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "action.execution.write"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "Execution successfully updated or left unchanged";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "400"
|
||||||
|
value: {
|
||||||
|
description: "Condition to set execution does not exist or the feature flag `actions` is not enabled.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Executions
|
||||||
|
//
|
||||||
|
// List all matching executions. By default all executions of the instance are returned that have at least one execution target.
|
||||||
|
// Make sure to include a limit and sorting for pagination.
|
||||||
|
//
|
||||||
|
// Required permission:
|
||||||
|
// - `action.execution.read`
|
||||||
|
//
|
||||||
|
// Required feature flag:
|
||||||
|
// - `actions`
|
||||||
|
rpc ListExecutions (ListExecutionsRequest) returns (ListExecutionsResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v2/actions/executions/search"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "action.execution.read"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "A list of all non noop executions matching the query";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
key: "400";
|
||||||
|
value: {
|
||||||
|
description: "Invalid list query or the feature flag `actions` is not enabled.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Execution Functions
|
||||||
|
//
|
||||||
|
// List all available functions which can be used as condition for executions.
|
||||||
|
rpc ListExecutionFunctions (ListExecutionFunctionsRequest) returns (ListExecutionFunctionsResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/v2/actions/executions/functions"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "authenticated"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "List all functions successfully";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Execution Methods
|
||||||
|
//
|
||||||
|
// List all available methods which can be used as condition for executions.
|
||||||
|
rpc ListExecutionMethods (ListExecutionMethodsRequest) returns (ListExecutionMethodsResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/v2/actions/executions/methods"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "authenticated"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "List all methods successfully";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Execution Services
|
||||||
|
//
|
||||||
|
// List all available services which can be used as condition for executions.
|
||||||
|
rpc ListExecutionServices (ListExecutionServicesRequest) returns (ListExecutionServicesResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/v2/actions/executions/services"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||||
|
auth_option: {
|
||||||
|
permission: "authenticated"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
responses: {
|
||||||
|
key: "200";
|
||||||
|
value: {
|
||||||
|
description: "List all services successfully";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateTargetRequest {
|
||||||
|
string name = 1 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"ip_allow_list\"";
|
||||||
|
min_length: 1
|
||||||
|
max_length: 1000
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Defines the target type and how the response of the target is treated.
|
||||||
|
oneof target_type {
|
||||||
|
option (validate.required) = true;
|
||||||
|
// Wait for response but response body is ignored, status is checked, call is sent as post.
|
||||||
|
RESTWebhook rest_webhook = 2;
|
||||||
|
// Wait for response and response body is used, status is checked, call is sent as post.
|
||||||
|
RESTCall rest_call = 3;
|
||||||
|
// Call is executed in parallel to others, ZITADEL does not wait until the call is finished. The state is ignored, call is sent as post.
|
||||||
|
RESTAsync rest_async = 4;
|
||||||
|
}
|
||||||
|
// Timeout defines the duration until ZITADEL cancels the execution.
|
||||||
|
// If the target doesn't respond before this timeout expires, then the connection is closed and the action fails. Depending on the target type and possible setting on `interrupt_on_error` following targets will not be called. In case of a `rest_async` target only this specific target will fail, without any influence on other targets of the same execution.
|
||||||
|
google.protobuf.Duration timeout = 5 [
|
||||||
|
(validate.rules).duration = {gte: {}, lte: {seconds: 270}},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"10s\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
string endpoint = 6 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"https://example.com/hooks/ip_check\""
|
||||||
|
min_length: 1
|
||||||
|
max_length: 1000
|
||||||
|
}
|
||||||
|
];
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
||||||
|
example: "{\"name\": \"ip_allow_list\",\"restWebhook\":{\"interruptOnError\":true},\"timeout\":\"10s\",\"endpoint\":\"https://example.com/hooks/ip_check\"}";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateTargetResponse {
|
||||||
|
// The unique identifier of the newly created target.
|
||||||
|
string id = 1 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"69629012906488334\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// The timestamp of the target creation.
|
||||||
|
google.protobuf.Timestamp creation_date = 2 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"2024-12-18T07:50:47.492Z\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Key used to sign and check payload sent to the target.
|
||||||
|
string signing_key = 3 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"98KmsU67\""
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateTargetRequest {
|
||||||
|
string id = 1 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 200,
|
||||||
|
example: "\"69629026806489455\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
optional string name = 2 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"ip_allow_list\""
|
||||||
|
min_length: 1
|
||||||
|
max_length: 1000
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Defines the target type and how the response of the target is treated.
|
||||||
|
oneof target_type {
|
||||||
|
// Wait for response but response body is ignored, status is checked, call is sent as post.
|
||||||
|
RESTWebhook rest_webhook = 3;
|
||||||
|
// Wait for response and response body is used, status is checked, call is sent as post.
|
||||||
|
RESTCall rest_call = 4;
|
||||||
|
// Call is executed in parallel to others, ZITADEL does not wait until the call is finished. The state is ignored, call is sent as post.
|
||||||
|
RESTAsync rest_async = 5;
|
||||||
|
}
|
||||||
|
// Timeout defines the duration until ZITADEL cancels the execution.
|
||||||
|
// If the target doesn't respond before this timeout expires, then the connection is closed and the action fails. Depending on the target type and possible setting on `interrupt_on_error` following targets will not be called. In case of a `rest_async` target only this specific target will fail, without any influence on other targets of the same execution.
|
||||||
|
optional google.protobuf.Duration timeout = 6 [
|
||||||
|
(validate.rules).duration = {gte: {}, lte: {seconds: 270}},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"10s\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
optional string endpoint = 7 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"https://example.com/hooks/ip_check\""
|
||||||
|
min_length: 1
|
||||||
|
max_length: 1000
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Regenerate the key used for signing and checking the payload sent to the target.
|
||||||
|
// Set the graceful period for the existing key. During that time, the previous
|
||||||
|
// signing key and the new one will be used to sign the request to allow you a smooth
|
||||||
|
// transition onf your API.
|
||||||
|
//
|
||||||
|
// Note that we currently only allow an immediate rotation ("0s") and will support
|
||||||
|
// longer expirations in the future.
|
||||||
|
optional google.protobuf.Duration expiration_signing_key = 8 [
|
||||||
|
(validate.rules).duration = {const: {seconds: 0, nanos: 0}},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"0s\""
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
||||||
|
example: "{\"name\": \"ip_allow_list\",\"restCall\":{\"interruptOnError\":true},\"timeout\":\"10s\",\"endpoint\":\"https://example.com/hooks/ip_check\",\"expirationSigningKey\":\"0s\"}";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateTargetResponse {
|
||||||
|
// The timestamp of the change of the target.
|
||||||
|
google.protobuf.Timestamp change_date = 1 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"2025-01-23T10:34:18.051Z\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Key used to sign and check payload sent to the target.
|
||||||
|
optional string signing_key = 2 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"98KmsU67\""
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteTargetRequest {
|
||||||
|
string id = 1 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||||
|
(google.api.field_behavior) = REQUIRED,
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 200,
|
||||||
|
example: "\"69629026806489455\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteTargetResponse {
|
||||||
|
// The timestamp of the deletion of the target.
|
||||||
|
// Note that the deletion date is only guaranteed to be set if the deletion was successful during the request.
|
||||||
|
// In case the deletion occurred in a previous request, the deletion date might be empty.
|
||||||
|
google.protobuf.Timestamp deletion_date = 3 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"2025-01-23T10:34:18.051Z\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTargetRequest {
|
||||||
|
string id = 1 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 200,
|
||||||
|
example: "\"69629026806489455\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTargetResponse {
|
||||||
|
Target target = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListTargetsRequest {
|
||||||
|
// List limitations and ordering.
|
||||||
|
optional zitadel.filter.v2.PaginationRequest pagination = 1;
|
||||||
|
// The field the result is sorted by. The default is the creation date. Beware that if you change this, your result pagination might be inconsistent.
|
||||||
|
optional TargetFieldName sorting_column = 2 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
default: "\"TARGET_FIELD_NAME_CREATION_DATE\""
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Define the criteria to query for.
|
||||||
|
repeated TargetSearchFilter filters = 3;
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
||||||
|
example: "{\"pagination\":{\"offset\":0,\"limit\":0,\"asc\":true},\"sortingColumn\":\"TARGET_FIELD_NAME_CREATION_DATE\",\"filters\":[{\"targetNameFilter\":{\"targetName\":\"ip_allow_list\",\"method\":\"TEXT_FILTER_METHOD_EQUALS\"}},{\"inTargetIdsFilter\":{\"targetIds\":[\"69629023906488334\",\"69622366012355662\"]}}]}";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListTargetsResponse {
|
||||||
|
reserved 'result';
|
||||||
|
zitadel.filter.v2.PaginationResponse pagination = 1;
|
||||||
|
repeated Target targets = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetExecutionRequest {
|
||||||
|
// Condition defining when the execution should be used.
|
||||||
|
Condition condition = 1;
|
||||||
|
// Ordered list of targets called during the execution.
|
||||||
|
repeated string targets = 2;
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
||||||
|
example: "{\"condition\":{\"request\":{\"method\":\"zitadel.session.v2.SessionService/ListSessions\"}},\"targets\":[{\"target\":\"69629026806489455\"}]}";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetExecutionResponse {
|
||||||
|
// The timestamp of the execution set.
|
||||||
|
google.protobuf.Timestamp set_date = 1 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"2024-12-18T07:50:47.492Z\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListExecutionsRequest {
|
||||||
|
// List limitations and ordering.
|
||||||
|
optional zitadel.filter.v2.PaginationRequest pagination = 1;
|
||||||
|
// The field the result is sorted by. The default is the creation date. Beware that if you change this, your result pagination might be inconsistent.
|
||||||
|
optional ExecutionFieldName sorting_column = 2 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
default: "\"EXECUTION_FIELD_NAME_CREATION_DATE\""
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Define the criteria to query for.
|
||||||
|
repeated ExecutionSearchFilter filters = 3;
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
||||||
|
example: "{\"pagination\":{\"offset\":0,\"limit\":0,\"asc\":true},\"sortingColumn\":\"EXECUTION_FIELD_NAME_ID\",\"filters\":[{\"targetFilter\":{\"targetId\":\"69629023906488334\"}}]}";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListExecutionsResponse {
|
||||||
|
reserved 'result';
|
||||||
|
zitadel.filter.v2.PaginationResponse pagination = 1;
|
||||||
|
repeated Execution executions = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListExecutionFunctionsRequest{}
|
||||||
|
message ListExecutionFunctionsResponse{
|
||||||
|
// All available methods
|
||||||
|
repeated string functions = 1;
|
||||||
|
}
|
||||||
|
message ListExecutionMethodsRequest{}
|
||||||
|
message ListExecutionMethodsResponse{
|
||||||
|
// All available methods
|
||||||
|
repeated string methods = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListExecutionServicesRequest{}
|
||||||
|
message ListExecutionServicesResponse{
|
||||||
|
// All available methods
|
||||||
|
repeated string services = 1;
|
||||||
|
}
|
135
proto/zitadel/action/v2/execution.proto
Normal file
135
proto/zitadel/action/v2/execution.proto
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package zitadel.action.v2;
|
||||||
|
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
import "google/api/field_behavior.proto";
|
||||||
|
import "google/protobuf/duration.proto";
|
||||||
|
import "google/protobuf/struct.proto";
|
||||||
|
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||||
|
import "validate/validate.proto";
|
||||||
|
import "zitadel/protoc_gen_zitadel/v2/options.proto";
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/zitadel/zitadel/pkg/grpc/action/v2;action";
|
||||||
|
|
||||||
|
message Execution {
|
||||||
|
Condition condition = 1;
|
||||||
|
// The timestamp of the execution creation.
|
||||||
|
google.protobuf.Timestamp creation_date = 2 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"2024-12-18T07:50:47.492Z\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// The timestamp of the last change to the execution.
|
||||||
|
google.protobuf.Timestamp change_date = 3 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"2025-01-23T10:34:18.051Z\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Ordered list of targets called during the execution.
|
||||||
|
repeated string targets = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Condition {
|
||||||
|
// Condition-types under which conditions the execution should happen, only one possible.
|
||||||
|
oneof condition_type {
|
||||||
|
option (validate.required) = true;
|
||||||
|
|
||||||
|
// Condition-type to execute if a request on the defined API point happens.
|
||||||
|
RequestExecution request = 1;
|
||||||
|
// Condition-type to execute on response if a request on the defined API point happens.
|
||||||
|
ResponseExecution response = 2;
|
||||||
|
// Condition-type to execute if function is used, replaces actions v1.
|
||||||
|
FunctionExecution function = 3;
|
||||||
|
// Condition-type to execute if an event is created in the system.
|
||||||
|
EventExecution event = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequestExecution {
|
||||||
|
// Condition for the request execution. Only one is possible.
|
||||||
|
oneof condition{
|
||||||
|
option (validate.required) = true;
|
||||||
|
// GRPC-method as condition.
|
||||||
|
string method = 1 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 1000,
|
||||||
|
example: "\"/zitadel.session.v2.SessionService/ListSessions\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// GRPC-service as condition.
|
||||||
|
string service = 2 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 1000,
|
||||||
|
example: "\"zitadel.session.v2.SessionService\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// All calls to any available services and methods as condition.
|
||||||
|
bool all = 3 [(validate.rules).bool = {const: true}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResponseExecution {
|
||||||
|
// Condition for the response execution. Only one is possible.
|
||||||
|
oneof condition{
|
||||||
|
option (validate.required) = true;
|
||||||
|
// GRPC-method as condition.
|
||||||
|
string method = 1 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 1000,
|
||||||
|
example: "\"/zitadel.session.v2.SessionService/ListSessions\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// GRPC-service as condition.
|
||||||
|
string service = 2 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 1000,
|
||||||
|
example: "\"zitadel.session.v2.SessionService\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// All calls to any available services and methods as condition.
|
||||||
|
bool all = 3 [(validate.rules).bool = {const: true}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executed on the specified function
|
||||||
|
message FunctionExecution {
|
||||||
|
string name = 1 [(validate.rules).string = {min_len: 1, max_len: 1000}];
|
||||||
|
}
|
||||||
|
|
||||||
|
message EventExecution {
|
||||||
|
// Condition for the event execution. Only one is possible.
|
||||||
|
oneof condition{
|
||||||
|
option (validate.required) = true;
|
||||||
|
// Event name as condition.
|
||||||
|
string event = 1 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 1000,
|
||||||
|
example: "\"user.human.added\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Event group as condition, all events under this group.
|
||||||
|
string group = 2 [
|
||||||
|
(validate.rules).string = {min_len: 1, max_len: 1000},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
min_length: 1,
|
||||||
|
max_length: 1000,
|
||||||
|
example: "\"user.human\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// all events as condition.
|
||||||
|
bool all = 3 [(validate.rules).bool = {const: true}];
|
||||||
|
}
|
||||||
|
}
|
108
proto/zitadel/action/v2/query.proto
Normal file
108
proto/zitadel/action/v2/query.proto
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package zitadel.action.v2;
|
||||||
|
|
||||||
|
option go_package = "github.com/zitadel/zitadel/pkg/grpc/action/v2;action";
|
||||||
|
|
||||||
|
import "google/api/field_behavior.proto";
|
||||||
|
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||||
|
import "validate/validate.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
import "zitadel/action/v2/execution.proto";
|
||||||
|
import "zitadel/filter/v2/filter.proto";
|
||||||
|
|
||||||
|
message ExecutionSearchFilter {
|
||||||
|
oneof filter {
|
||||||
|
option (validate.required) = true;
|
||||||
|
|
||||||
|
InConditionsFilter in_conditions_filter = 1;
|
||||||
|
ExecutionTypeFilter execution_type_filter = 2;
|
||||||
|
TargetFilter target_filter = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message InConditionsFilter {
|
||||||
|
// Defines the conditions to query for.
|
||||||
|
repeated Condition conditions = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecutionTypeFilter {
|
||||||
|
// Defines the type to query for.
|
||||||
|
ExecutionType execution_type = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TargetFilter {
|
||||||
|
// Defines the id to query for.
|
||||||
|
string target_id = 1 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
description: "the id of the targets to include"
|
||||||
|
example: "\"69629023906488334\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TargetFieldName {
|
||||||
|
TARGET_FIELD_NAME_UNSPECIFIED = 0;
|
||||||
|
TARGET_FIELD_NAME_ID = 1;
|
||||||
|
TARGET_FIELD_NAME_CREATED_DATE = 2;
|
||||||
|
TARGET_FIELD_NAME_CHANGED_DATE = 3;
|
||||||
|
TARGET_FIELD_NAME_NAME = 4;
|
||||||
|
TARGET_FIELD_NAME_TARGET_TYPE = 5;
|
||||||
|
TARGET_FIELD_NAME_URL = 6;
|
||||||
|
TARGET_FIELD_NAME_TIMEOUT = 7;
|
||||||
|
TARGET_FIELD_NAME_INTERRUPT_ON_ERROR = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TargetSearchFilter {
|
||||||
|
oneof filter {
|
||||||
|
option (validate.required) = true;
|
||||||
|
|
||||||
|
TargetNameFilter target_name_filter = 1;
|
||||||
|
InTargetIDsFilter in_target_ids_filter = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message TargetNameFilter {
|
||||||
|
// Defines the name of the target to query for.
|
||||||
|
string target_name = 1 [
|
||||||
|
(validate.rules).string = {max_len: 200},
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
max_length: 200;
|
||||||
|
example: "\"ip_allow_list\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Defines which text comparison method used for the name query.
|
||||||
|
zitadel.filter.v2.TextFilterMethod method = 2 [
|
||||||
|
(validate.rules).enum.defined_only = true,
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
description: "defines which text equality method is used";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
message InTargetIDsFilter {
|
||||||
|
// Defines the ids to query for.
|
||||||
|
repeated string target_ids = 1 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
description: "the ids of the targets to include"
|
||||||
|
example: "[\"69629023906488334\",\"69622366012355662\"]";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExecutionType {
|
||||||
|
EXECUTION_TYPE_UNSPECIFIED = 0;
|
||||||
|
EXECUTION_TYPE_REQUEST = 1;
|
||||||
|
EXECUTION_TYPE_RESPONSE = 2;
|
||||||
|
EXECUTION_TYPE_EVENT = 3;
|
||||||
|
EXECUTION_TYPE_FUNCTION = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum ExecutionFieldName {
|
||||||
|
EXECUTION_FIELD_NAME_UNSPECIFIED = 0;
|
||||||
|
EXECUTION_FIELD_NAME_ID = 1;
|
||||||
|
EXECUTION_FIELD_NAME_CREATED_DATE = 2;
|
||||||
|
EXECUTION_FIELD_NAME_CHANGED_DATE = 3;
|
||||||
|
}
|
75
proto/zitadel/action/v2/target.proto
Normal file
75
proto/zitadel/action/v2/target.proto
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package zitadel.action.v2;
|
||||||
|
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
import "google/api/field_behavior.proto";
|
||||||
|
import "google/protobuf/duration.proto";
|
||||||
|
import "google/protobuf/struct.proto";
|
||||||
|
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||||
|
import "validate/validate.proto";
|
||||||
|
import "zitadel/protoc_gen_zitadel/v2/options.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/zitadel/zitadel/pkg/grpc/action/v2;action";
|
||||||
|
|
||||||
|
message Target {
|
||||||
|
// The unique identifier of the target.
|
||||||
|
string id = 1 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"69629012906488334\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// The timestamp of the target creation.
|
||||||
|
google.protobuf.Timestamp creation_date = 2 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"2024-12-18T07:50:47.492Z\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// The timestamp of the last change to the target (e.g. creation, activation, deactivation).
|
||||||
|
google.protobuf.Timestamp change_date = 3 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"2025-01-23T10:34:18.051Z\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
string name = 4 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"ip_allow_list\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
// Defines the target type and how the response of the target is treated.
|
||||||
|
oneof target_type {
|
||||||
|
RESTWebhook rest_webhook = 5;
|
||||||
|
RESTCall rest_call = 6;
|
||||||
|
RESTAsync rest_async = 7;
|
||||||
|
}
|
||||||
|
// Timeout defines the duration until ZITADEL cancels the execution.
|
||||||
|
// If the target doesn't respond before this timeout expires, the the connection is closed and the action fails. Depending on the target type and possible setting on `interrupt_on_error` following targets will not be called. In case of a `rest_async` target only this specific target will fail, without any influence on other targets of the same execution.
|
||||||
|
google.protobuf.Duration timeout = 8 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"10s\"";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
string endpoint = 9 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"https://example.com/hooks/ip_check\""
|
||||||
|
}
|
||||||
|
];
|
||||||
|
string signing_key = 10 [
|
||||||
|
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||||
|
example: "\"98KmsU67\""
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
message RESTWebhook {
|
||||||
|
// Define if any error stops the whole execution. By default the process continues as normal.
|
||||||
|
bool interrupt_on_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RESTCall {
|
||||||
|
// Define if any error stops the whole execution. By default the process continues as normal.
|
||||||
|
bool interrupt_on_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RESTAsync {}
|
@@ -675,7 +675,7 @@ message SetExecutionRequest {
|
|||||||
// Ordered list of targets called during the execution.
|
// Ordered list of targets called during the execution.
|
||||||
repeated string targets = 2;
|
repeated string targets = 2;
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
||||||
example: "{\"condition\":{\"request\":{\"method\":\"zitadel.session.v2.SessionService/ListSessions\"}},\"targets\":[{\"target\":\"69629026806489455\"}]}";
|
example: "{\"condition\":{\"request\":{\"method\":\"zitadel.session.v2.SessionService/ListSessions\"}},\"targets\":[\"69629026806489455\"]}";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user