mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-04 02:32:35 +00:00
feat: add action v2 execution on requests and responses (#7637)
* feat: add execution of targets to grpc calls * feat: add execution of targets to grpc calls * feat: add execution of targets to grpc calls * feat: add execution of targets to grpc calls * feat: add execution of targets to grpc calls * feat: add execution of targets to grpc calls * feat: add execution of targets to grpc calls * feat: split request and response logic to handle the different context information * feat: split request and response logic to handle the different context information * fix: integration test * fix: import alias * fix: refactor execution package * fix: refactor execution interceptor integration and unit tests * fix: refactor execution interceptor integration and unit tests * fix: refactor execution interceptor integration and unit tests * fix: refactor execution interceptor integration and unit tests * fix: refactor execution interceptor integration and unit tests * docs: basic documentation for executions and targets * fix: change order for interceptors * fix: merge back origin/main * fix: change target definition command and query side (#7735) * fix: change target definition command and query side * fix: correct refactoring name changes * fix: correct refactoring name changes * fix: changing execution defintion with target list and type * fix: changing execution definition with target list and type * fix: add back search queries for target and include * fix: projections change for execution with targets suffix table * fix: projections change for execution with targets suffix table * fix: projections change for execution with targets suffix table * fix: projections change for execution with targets suffix table * fix: projections change for execution with targets suffix table * fix: projections change for execution with targets suffix table * fix: projections change for execution with targets suffix table * docs: add example to actions v2 * docs: add example to actions v2 * fix: correct integration tests on query for executions * fix: add separate event for execution v2 as content changed * fix: add separate event for execution v2 as content changed * fix: added review comment changes * fix: added review comment changes * fix: added review comment changes --------- Co-authored-by: adlerhurst <silvan.reusser@gmail.com> * fix: added review comment changes * fix: added review comment changes * Update internal/api/grpc/server/middleware/execution_interceptor.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * fix: added review comment changes * fix: added review comment changes * fix: added review comment changes * fix: added review comment changes * fix: added review comment changes * fix: added review comment changes --------- Co-authored-by: adlerhurst <silvan.reusser@gmail.com> Co-authored-by: Elio Bischof <elio@zitadel.com>
This commit is contained in:
139
docs/docs/apis/actionsv2/execution-local.md
Normal file
139
docs/docs/apis/actionsv2/execution-local.md
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: Actions v2 example execution locally
|
||||
---
|
||||
|
||||
In this guide, you will create a ZITADEL execution and target. After a user is created through the API, the target is called.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you start, make sure you have everything set up correctly.
|
||||
|
||||
- You need to be at least a ZITADEL [_IAM_OWNER_](/guides/manage/console/managers)
|
||||
- Your ZITADEL instance needs to have the actions feature enabled.
|
||||
|
||||
## Start example target
|
||||
|
||||
To start a simple HTTP server locally, which receives the webhook call, the following code example can be used:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// webhook HandleFunc to read the request body and then print out the contents
|
||||
func webhook(w http.ResponseWriter, req *http.Request) {
|
||||
// read the body content
|
||||
sentBody, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
// if there was an error while reading the body return an error
|
||||
http.Error(w, "error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// print out the read content
|
||||
fmt.Println(string(sentBody))
|
||||
}
|
||||
|
||||
func main() {
|
||||
// handle the HTTP call under "/webhook"
|
||||
http.HandleFunc("/webhook", webhook)
|
||||
|
||||
// start an HTTP server with the before defined function to handle the endpoint under "http://localhost:8090"
|
||||
http.ListenAndServe(":8090", nil)
|
||||
}
|
||||
```
|
||||
|
||||
What happens here is only a target which prints out the received request, which could also be handled with a different logic.
|
||||
|
||||
## Create target
|
||||
|
||||
As you see in the example above the target is created with HTTP and port '8090' and if we want to use it as webhook, the target can be created as follows:
|
||||
|
||||
[Create a target](/apis/resources/action_service_v3/action-service-create-target)
|
||||
|
||||
```shell
|
||||
curl -L -X POST 'https://$CUSTOM-DOMAIN/v3alpha/targets' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Authorization: Bearer <TOKEN>' \
|
||||
--data-raw '{
|
||||
"name": "local webhook",
|
||||
"restWebhook": {
|
||||
"interruptOnError": true
|
||||
},
|
||||
"endpoint": "http://localhost:8090/webhook",
|
||||
"timeout": "10s"
|
||||
}'
|
||||
```
|
||||
|
||||
Save the returned ID to set in the execution.
|
||||
|
||||
## Set execution
|
||||
|
||||
To call the target just created before, with the intention to print the request used for user creation by the user V2 API, we define an execution with a method condition.
|
||||
|
||||
[Set an execution](/apis/resources/action_service_v3/action-service-set-execution)
|
||||
|
||||
```shell
|
||||
curl -L -X PUT 'https://$CUSTOM-DOMAIN/v3alpha/executions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Authorization: Bearer <TOKEN>' \
|
||||
--data-raw '{
|
||||
"condition": {
|
||||
"request": {
|
||||
"method": "/zitadel.user.v2beta.UserService/AddHumanUser"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"target": "<TargetID returned>"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Example call
|
||||
|
||||
Now on every call on `/zitadel.user.v2beta.UserService/AddHumanUser` the local server prints out the received body of the request:
|
||||
|
||||
```shell
|
||||
curl -L -X PUT 'https://$CUSTOM-DOMAIN/v2beta/users/human' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Authorization: Bearer <TOKEN>' \
|
||||
--data-raw '{
|
||||
"profile": {
|
||||
"givenName": "Example_given",
|
||||
"familyName": "Example_family"
|
||||
},
|
||||
"email": {
|
||||
"email": "example@example.com"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Should print out something like, also described under [Sent information Request](./introduction#sent-information-request):
|
||||
```shell
|
||||
{
|
||||
"fullMethod": "/zitadel.user.v2beta.UserService/AddHumanUser",
|
||||
"instanceID": "262851882718855632",
|
||||
"orgID": "262851882718921168",
|
||||
"projectID": "262851882719052240",
|
||||
"userID": "262851882718986704",
|
||||
"request": {
|
||||
"profile": {
|
||||
"given_name": "Example_given",
|
||||
"family_name": "Example_family"
|
||||
},
|
||||
"email": {
|
||||
"email": "example@example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
167
docs/docs/apis/actionsv2/introduction.md
Normal file
167
docs/docs/apis/actionsv2/introduction.md
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
title: Actions V2
|
||||
---
|
||||
|
||||
This page describes the options you have when defining ZITADEL Actions V2.
|
||||
|
||||
## Endpoints
|
||||
|
||||
ZITADEL sends an HTTP Post request to the endpoint set as Target, the received request than can be edited and send back or custom processes can be handled.
|
||||
|
||||
### Sent information Request
|
||||
|
||||
The information sent to the Endpoint is structured as JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"fullMethod": "full method of the GRPC call",
|
||||
"instanceID": "instanceID of the called instance",
|
||||
"orgID": "ID of the organization related to the calling context",
|
||||
"projectID": "ID of the project related to the used application",
|
||||
"userID": "ID of the calling user",
|
||||
"request": "full request of the call"
|
||||
}
|
||||
```
|
||||
|
||||
### Sent information Response
|
||||
|
||||
The information sent to the Endpoint is structured as JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"fullMethod": "full method of the GRPC call",
|
||||
"instanceID": "instanceID of the called instance",
|
||||
"orgID": "ID of the organization related to the calling context",
|
||||
"projectID": "ID of the project related to the used application",
|
||||
"userID": "ID of the calling user",
|
||||
"request": "full request of the call",
|
||||
"response": "full response of the call"
|
||||
}
|
||||
```
|
||||
|
||||
## Target
|
||||
|
||||
The Target describes how ZITADEL interacts with the Endpoint.
|
||||
|
||||
There are different types of Targets:
|
||||
|
||||
- `Webhook`, the call handles the status code but response is irrelevant, can be InterruptOnError
|
||||
- `Call`, the call handles the status code and response, can be InterruptOnError
|
||||
- `Async`, the call handles neither status code nor response, but can be called in parallel with other Targets
|
||||
|
||||
`InterruptOnError` means that the Execution gets interrupted if any of the calls return with a status code >= 400, and the next Target will not be called anymore.
|
||||
|
||||
The API documentation to create a target can be found [here](/apis/resources/action_service_v3/action-service-create-target)
|
||||
|
||||
## Execution
|
||||
|
||||
ZITADEL decides on specific conditions if one or more Targets have to be called.
|
||||
The Execution resource contains 2 parts, the condition and the called targets.
|
||||
|
||||
The condition can be defined for 4 types of processes:
|
||||
|
||||
- `Requests`, before a request is processed by ZITADEL
|
||||
- `Responses`, before a response is sent back to the application
|
||||
- `Functions`, handling specific functionality in the logic of ZITADEL
|
||||
- `Events`, after a specific event happened and was stored in ZITADEL
|
||||
|
||||
The API documentation to set an Execution can be found [here](/apis/resources/action_service_v3/action-service-set-execution)
|
||||
|
||||
### Condition Best Match
|
||||
|
||||
As the conditions can be defined on different levels, ZITADEL tries to find out which Execution is the best match.
|
||||
This means that for example if you have an Execution defined on `all requests`, on the service `zitadel.user.v2beta.UserService` and on `/zitadel.user.v2beta.UserService/AddHumanUser`,
|
||||
ZITADEL would with a call on the `/zitadel.user.v2beta.UserService/AddHumanUser` use the Executions with the following priority:
|
||||
|
||||
1. `/zitadel.user.v2beta.UserService/AddHumanUser`
|
||||
2. `zitadel.user.v2beta.UserService`
|
||||
3. `all`
|
||||
|
||||
If you then have a call on `/zitadel.user.v2beta.UserService/UpdateHumanUser` the following priority would be found:
|
||||
|
||||
1. `zitadel.user.v2beta.UserService`
|
||||
2. `all`
|
||||
|
||||
And if you use a different service, for example `zitadel.session.v2.SessionService`, then the `all` Execution would still be used.
|
||||
|
||||
### Targets and Includes
|
||||
|
||||
An execution can not only contain a list of Targets, but also Includes.
|
||||
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
|
||||
{
|
||||
"condition": {
|
||||
"request": {
|
||||
"service": "zitadel.user.v2beta.UserService"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"target": "<TargetID1>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"condition": {
|
||||
"request": {
|
||||
"method": "/zitadel.user.v2beta.UserService/AddHumanUser"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"target": "<TargetID2>"
|
||||
},
|
||||
{
|
||||
"include": {
|
||||
"request": {
|
||||
"service": "zitadel.user.v2beta.UserService"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The called Targets on "/zitadel.user.v2beta.UserService/AddHumanUser" would be, in order:
|
||||
|
||||
1. `<TargetID2>`
|
||||
2. `<TargetID1>`
|
||||
|
||||
### Condition for Requests and Responses
|
||||
|
||||
For Request and Response there are 3 levels the condition can be defined:
|
||||
|
||||
- `Method`, handling a request or response of a specific GRPC full method, which includes the service name and method of the ZITADEL API
|
||||
- `Service`, handling any request or response under a service of the ZITADEL API
|
||||
- `All`, handling any request or response under the ZITADEL API
|
||||
|
||||
The available conditions can be found under:
|
||||
- [All available Methods](/apis/resources/action_service_v3/action-service-list-execution-methods), for example `/zitadel.user.v2beta.UserService/AddHumanUser`
|
||||
- [All available Services](/apis/resources/action_service_v3/action-service-list-execution-services), for example `zitadel.user.v2beta.UserService`
|
||||
|
||||
### Condition for Functions
|
||||
|
||||
Replace the current Actions with the following flows:
|
||||
|
||||
- [Internal Authentication](../actions/internal-authentication)
|
||||
- [External Authentication](../actions/external-authentication)
|
||||
- [Complement Token](../actions/complement-token)
|
||||
- [Customize SAML Response](../actions/customize-samlresponse)
|
||||
|
||||
The available conditions can be found under [all available Functions](/apis/resources/action_service_v3/action-service-list-execution-functions).
|
||||
|
||||
### Condition for Events
|
||||
|
||||
For event there are 3 levels the condition can be defined:
|
||||
|
||||
- Event, handling a specific event
|
||||
- Group, handling a specific group of events
|
||||
- All, handling any event in ZITADEL
|
||||
|
||||
The concept of events can be found under [Events](/concepts/architecture/software#events)
|
||||
@@ -7,7 +7,7 @@ By using ZITADEL actions, you can manipulate ZITADELs behavior on specific Event
|
||||
This is useful when you have special business requirements that ZITADEL doesn't support out-of-the-box.
|
||||
|
||||
:::info
|
||||
We're working on Actions continuously. In the [roadmap](https://zitadel.com/roadmap), you see how we are planning to expand and improve it. Please tell us about your needs and help us prioritize further fixes and features.
|
||||
We're working on Actions continuously. In the [roadmap](https://zitadel.com/roadmap), you see how we are planning to expand and improve it. Please tell us about your needs and help us prioritize further fixes and features.
|
||||
:::
|
||||
|
||||
## Why actions?
|
||||
|
||||
40
docs/docs/concepts/features/actions_v2.md
Normal file
40
docs/docs/concepts/features/actions_v2.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: ZITADEL Actions v2
|
||||
sidebar_label: Actions v2
|
||||
---
|
||||
|
||||
By using ZITADEL actions V2, you can manipulate ZITADELs behavior on specific API calls, events or functions.
|
||||
This is useful when you have special business requirements that ZITADEL doesn't support out-of-the-box.
|
||||
|
||||
:::info
|
||||
We're working on Actions continuously. In the [roadmap](https://zitadel.com/roadmap), you see how we are planning to expand and improve it. Please tell us about your needs and help us prioritize further fixes and features.
|
||||
:::
|
||||
|
||||
## Why actions?
|
||||
ZITADEL can't anticipate and solve every possible business rule and integration requirements from all ZITADEL users. Here are some examples:
|
||||
- A business requires domain specific data validation before a user can be created or authenticated.
|
||||
- A business needs to automate tasks. Roles should be assigned to users based on their ADFS 2016+ groups.
|
||||
- A business needs to store metadata on a user that is used for integrating applications.
|
||||
- A business needs to restrict the users who are allowed to register to a certain organization by their email domains.
|
||||
|
||||
With actions, ZITADEL provides a way to solve such problems.
|
||||
|
||||
## How it works
|
||||
There are 3 components necessary:
|
||||
- Endpoint, an external endpoint with the desired logic, can be whatever is necessary as long as it can receive an HTTP Post request.
|
||||
- Target, a resource in ZITADEL with all necessary information how to trigger an endpoint
|
||||
- Execution, a resource in ZITADEL with the information when to trigger which targets
|
||||
|
||||
The process is that ZITADEL decides at certain points that with a defined Execution a call to the defined Target(s) is triggered,
|
||||
so that everybody can implement their custom behaviour for as many processes as possible.
|
||||
|
||||
Possible conditions for the Execution:
|
||||
- Request, to react to or manipulate requests to ZITADEL, for example add information to newly created users
|
||||
- Response, to react to or manipulate responses to ZITADEL, for example to provision newly created users to other systems
|
||||
- Function, to react to different functionality in ZITADEL, replaces [Actions](/concepts/features/actions)
|
||||
- Event, to create to different events which get created in ZITADEL, for example to inform somebody if a user gets locked
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Actions v2 example execution locally](/apis/actionsv2/execution-local)
|
||||
- [Actions v2 reference](/apis/actionsv2/introduction#action)
|
||||
@@ -795,6 +795,15 @@ module.exports = {
|
||||
"apis/actions/objects",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Actions V2",
|
||||
collapsed: false,
|
||||
items: [
|
||||
"apis/actionsv2/introduction",
|
||||
"apis/actionsv2/execution-local",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "doc",
|
||||
label: "gRPC Status Codes",
|
||||
|
||||
Reference in New Issue
Block a user