mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 10:47:37 +00:00
update main
This commit is contained in:
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -54,8 +54,6 @@ jobs:
|
||||
console_cache_path: ${{ needs.console.outputs.cache_path }}
|
||||
version: ${{ needs.version.outputs.version }}
|
||||
node_version: "20"
|
||||
secrets:
|
||||
DEPOT_TOKEN: ${{ secrets.DEPOT_TOKEN }}
|
||||
|
||||
core-unit-test:
|
||||
needs: core
|
||||
@@ -81,7 +79,7 @@ jobs:
|
||||
with:
|
||||
node_version: "18"
|
||||
buf_version: "latest"
|
||||
go_lint_version: "v1.64.8"
|
||||
go_lint_version: "latest"
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
|
||||
@@ -103,8 +101,6 @@ jobs:
|
||||
with:
|
||||
login_build_image_name: "ghcr.io/zitadel/zitadel-login-build"
|
||||
node_version: "20"
|
||||
secrets:
|
||||
DEPOT_TOKEN: ${{ secrets.DEPOT_TOKEN }}
|
||||
|
||||
login-integration-test:
|
||||
uses: ./.github/workflows/login-integration-test.yml
|
||||
|
3
.github/workflows/compile.yml
vendored
3
.github/workflows/compile.yml
vendored
@@ -21,9 +21,6 @@ on:
|
||||
node_version:
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
DEPOT_TOKEN:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
executable:
|
||||
|
9
.github/workflows/lint.yml
vendored
9
.github/workflows/lint.yml
vendored
@@ -53,13 +53,10 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: depot/setup-action@v1
|
||||
- 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: Lint and Unit Test All JavaScript Code
|
||||
run: npm run devcontainer:lint-unit
|
||||
env:
|
||||
DEPOT_TOKEN: ${{ secrets.DEPOT_TOKEN }}
|
||||
|
||||
core:
|
||||
name: core
|
||||
@@ -78,7 +75,7 @@ jobs:
|
||||
path: ${{ inputs.core_cache_path }}
|
||||
key: ${{ inputs.core_cache_key }}
|
||||
fail-on-cache-miss: true
|
||||
- uses: golangci/golangci-lint-action@v6
|
||||
- uses: golangci/golangci-lint-action@v8
|
||||
with:
|
||||
version: ${{ inputs.go_lint_version }}
|
||||
github-token: ${{ github.token }}
|
||||
|
9
.github/workflows/login-container.yml
vendored
9
.github/workflows/login-container.yml
vendored
@@ -14,9 +14,6 @@ on:
|
||||
login_build_image:
|
||||
description: 'The full image tag of the standalone login image'
|
||||
value: '${{ inputs.login_build_image_name }}:${{ github.sha }}'
|
||||
secrets:
|
||||
DEPOT_TOKEN:
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
@@ -35,7 +32,6 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: depot/setup-action@v1
|
||||
- name: Login meta
|
||||
id: login-meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -52,8 +48,10 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Bake login multi-arch
|
||||
uses: depot/bake-action@v1
|
||||
uses: docker/bake-action@v6
|
||||
env:
|
||||
NODE_VERSION: ${{ inputs.node_version }}
|
||||
DEPOT_TOKEN: ${{ secrets.DEPOT_TOKEN }}
|
||||
@@ -62,7 +60,6 @@ jobs:
|
||||
provenance: true
|
||||
sbom: true
|
||||
targets: login-standalone
|
||||
project: w47wkxzdtw
|
||||
files: |
|
||||
./apps/login/docker-bake.hcl
|
||||
./apps/login/docker-bake-release.hcl
|
||||
|
377
.golangci.yaml
377
.golangci.yaml
@@ -1,309 +1,148 @@
|
||||
issues:
|
||||
new-from-rev: main
|
||||
# Set to 0 to disable.
|
||||
max-issues-per-linter: 0
|
||||
# Set to 0 to disable.
|
||||
max-same-issues: 0
|
||||
exclude-dirs:
|
||||
- .artifacts
|
||||
- .backups
|
||||
- .codecov
|
||||
- .github
|
||||
- .keys
|
||||
- .vscode
|
||||
- build
|
||||
- console
|
||||
- deploy
|
||||
- docs
|
||||
- guides
|
||||
- internal/api/ui/login/static
|
||||
- openapi
|
||||
- proto
|
||||
- tools
|
||||
- login
|
||||
|
||||
version: "2"
|
||||
run:
|
||||
concurrency: 4
|
||||
timeout: 10m
|
||||
go: '1.22'
|
||||
go: "1.24"
|
||||
linters:
|
||||
enable:
|
||||
# Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false]
|
||||
- asciicheck
|
||||
# checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
|
||||
- bodyclose
|
||||
# check the function whether use a non-inherited context [fast: false, auto-fix: false]
|
||||
- contextcheck
|
||||
# Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
|
||||
- gocognit
|
||||
# Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
|
||||
- unused
|
||||
# Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
|
||||
- errcheck
|
||||
# Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false]
|
||||
- errname
|
||||
# errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false]
|
||||
- errorlint
|
||||
# check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
|
||||
- exhaustive
|
||||
# Gci controls golang package import order and makes it always deterministic. [fast: true, auto-fix: false]
|
||||
- gci
|
||||
# Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: false]
|
||||
- gocognit
|
||||
- gocritic
|
||||
# Linter for Go source code that specializes in simplifying a code [fast: false, auto-fix: false]
|
||||
- gosimple
|
||||
# Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: false, auto-fix: false]
|
||||
- govet
|
||||
# Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
|
||||
- ineffassign
|
||||
# Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
|
||||
- misspell
|
||||
# Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false]
|
||||
- nakedret
|
||||
# Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: false, auto-fix: false]
|
||||
- staticcheck
|
||||
# Like the front-end of a Go compiler, parses and type-checks Go code [fast: false, auto-fix: false]
|
||||
- typecheck
|
||||
# Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false]
|
||||
- nolintlint
|
||||
# Checks for misuse of Sprintf to construct a host with port in a URL.
|
||||
- nosprintfhostport
|
||||
# checks whether Err of rows is checked successfully in `sql.Rows` [fast: false, auto-fix: false]
|
||||
- rowserrcheck
|
||||
# Checks that sql.Rows and sql.Stmt are closed. [fast: false, auto-fix: false]
|
||||
- sqlclosecheck
|
||||
# Remove unnecessary type conversions [fast: false, auto-fix: false]
|
||||
- unconvert
|
||||
disable:
|
||||
# Checks for dangerous unicode character sequences [fast: true, auto-fix: false]
|
||||
# not needed because github does that out of the box
|
||||
- bidichk
|
||||
# containedctx is a linter that detects struct contained context.Context field [fast: true, auto-fix: false]
|
||||
# using contextcheck which looks more active
|
||||
- containedctx
|
||||
# checks function and package cyclomatic complexity [fast: false, auto-fix: false]
|
||||
# not use because gocognit is used
|
||||
- cyclop
|
||||
# The owner seems to have abandoned the linter. Replaced by unused.
|
||||
# deprecated, replaced by unused
|
||||
- deadcode
|
||||
# check declaration order and count of types, constants, variables and functions [fast: true, auto-fix: false]
|
||||
# FUTURE: IMO it sometimes makes sense to declare consts or types after a func
|
||||
- decorder
|
||||
# Go linter that checks if package imports are in a list of acceptable packages [fast: false, auto-fix: false]
|
||||
# not required because of dependabot
|
||||
- depguard
|
||||
# Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
|
||||
# FUTURE: old code is not compatible
|
||||
- dogsled
|
||||
# Tool for code clone detection [fast: true, auto-fix: false]
|
||||
# FUTURE: old code is not compatible
|
||||
- dupl
|
||||
# checks for duplicate words in the source code
|
||||
# not sure if it makes sense
|
||||
- dupword
|
||||
# check for two durations multiplied together [fast: false, auto-fix: false]
|
||||
# FUTURE: checks for accident `1 * time.Second * time.Second`
|
||||
- durationcheck
|
||||
# Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. [fast: false, auto-fix: false]
|
||||
# FUTURE: use asap, because we use json alot. nice feature is possiblity to check if err check is required
|
||||
- errchkjson
|
||||
# execinquery is a linter about query string checker in Query function which reads your Go src files and warning it finds
|
||||
# FUTURE: might find some errors in sql queries
|
||||
- execinquery
|
||||
# Checks if all struct's fields are initialized [fast: false, auto-fix: false]
|
||||
# deprecated
|
||||
- exhaustivestruct
|
||||
# Checks if all structure fields are initialized
|
||||
# Not all fields have to be initialized
|
||||
- exhaustruct
|
||||
# checks for pointers to enclosing loop variables [fast: false, auto-fix: false]
|
||||
# FUTURE: finds bugs hard to find, could occur much later
|
||||
- exportloopref
|
||||
# Forbids identifiers [fast: true, auto-fix: false]
|
||||
# see no reason. allows to define regexp which are not allowed to use
|
||||
- forbidigo
|
||||
# finds forced type assertions [fast: true, auto-fix: false]
|
||||
# not used because we mostly use `_, _ = a.(int)`
|
||||
- forcetypeassert
|
||||
# Tool for detection of long functions [fast: true, auto-fix: false]
|
||||
# not used because it ignores complexity
|
||||
- funlen
|
||||
# check that no global variables exist [fast: true, auto-fix: false]
|
||||
# We use some global variables which is ok IMO
|
||||
- gochecknoglobals
|
||||
# Checks that no init functions are present in Go code [fast: true, auto-fix: false]
|
||||
# we use inits for the database abstraction
|
||||
- gochecknoinits
|
||||
# Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
|
||||
# FUTURE: might be cool to check
|
||||
- goconst
|
||||
# Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
|
||||
# not used because cyclop also checks complexity of package
|
||||
- gocyclo
|
||||
# Check if comments end in a period [fast: true, auto-fix: true]
|
||||
# FUTURE: checks if comments are written as specified
|
||||
- godot
|
||||
# Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
|
||||
# FUTURE: maybe makes sense later. IMO some view todos are ok for later tasks.
|
||||
- godox
|
||||
# Golang linter to check the errors handling expressions [fast: false, auto-fix: false]
|
||||
# Not used in favore of errorlint
|
||||
- goerr113
|
||||
# Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
|
||||
# ignored in favor of goimports
|
||||
- gofmt
|
||||
# Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
|
||||
# ignored in favor of goimports
|
||||
- gofumpt
|
||||
# Checks is file header matches to pattern [fast: true, auto-fix: false]
|
||||
# ignored because we don't write licenses as headers
|
||||
- goheader
|
||||
# In addition to fixing imports, goimports also formats your code in the same style as gofmt. [fast: true, auto-fix: true]
|
||||
# ignored in favor of gci
|
||||
- goimports
|
||||
#deprecated]: Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: false, auto-fix: false]
|
||||
# ignored in favor of goimports
|
||||
- golint
|
||||
# An analyzer to detect magic numbers. [fast: true, auto-fix: false]
|
||||
# FUTURE: not that critical at the moment
|
||||
- gomnd
|
||||
# Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
|
||||
# FUTURE: not a problem at the moment
|
||||
- gomoddirectives
|
||||
# Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
|
||||
# FUTURE: maybe interesting because of licenses
|
||||
- gomodguard
|
||||
# Checks that printf-like functions are named with `f` at the end [fast: true, auto-fix: false]
|
||||
# FUTURE: not a problem at the moment
|
||||
- goprintffuncname
|
||||
# Inspects source code for security problems [fast: false, auto-fix: false]
|
||||
# TODO: I think it would be more interesting to integrate into gh code scanning: https://github.com/securego/gosec#integrating-with-code-scanning
|
||||
- gosec
|
||||
# An analyzer to analyze expression groups. [fast: true, auto-fix: false]
|
||||
# I think the groups (vars, consts, imports, ...) we have atm are ok
|
||||
- grouper
|
||||
# Checks that your code uses short syntax for if-statements whenever possible [fast: true, auto-fix: false]
|
||||
# Dont't use its deprecated
|
||||
- ifshort
|
||||
# Enforces consistent import aliases [fast: false, auto-fix: false]
|
||||
# FUTURE: aliasing of imports is more or less consistent
|
||||
- importas
|
||||
# A linter that checks the number of methods inside an interface.
|
||||
# No need at the moment, repository abstraction was removed
|
||||
- interfacebloat
|
||||
# A linter that suggests interface types
|
||||
# Don't use it's archived
|
||||
- interfacer
|
||||
# Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false]
|
||||
# FUTURE: check if no interface is returned
|
||||
- ireturn
|
||||
# Reports long lines [fast: true, auto-fix: false]
|
||||
# FUTURE: would make code more readable
|
||||
- lll
|
||||
# Checks key valur pairs for common logger libraries (kitlog,klog,logr,zap).
|
||||
# FUTURE: useable as soon as we switch logger library
|
||||
- loggercheck
|
||||
# maintidx measures the maintainability index of each function. [fast: true, auto-fix: false]
|
||||
# not used because volume of halstead complexity feels strange as measurement https://en.wikipedia.org/wiki/Halstead_complexity_measures
|
||||
- maintidx
|
||||
# Finds slice declarations with non-zero initial length [fast: false, auto-fix: false]
|
||||
# I would prefer to use https://github.com/alexkohler/prealloc
|
||||
- makezero
|
||||
# Reports deeply nested if statements [fast: true, auto-fix: false]
|
||||
# focus only on if's
|
||||
- nestif
|
||||
# Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
|
||||
# FUTURE: check if it is allowed to return nil partially in error catch
|
||||
- nilerr
|
||||
# Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false]
|
||||
# FUTURE: would reduce checks and panics
|
||||
- errcheck
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
- nilnil
|
||||
disable:
|
||||
- bidichk
|
||||
- containedctx
|
||||
- cyclop
|
||||
- decorder
|
||||
- depguard
|
||||
- dogsled
|
||||
- dupl
|
||||
- dupword
|
||||
- durationcheck
|
||||
- err113
|
||||
- errchkjson
|
||||
- exhaustruct
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- goconst
|
||||
- gocyclo
|
||||
- godot
|
||||
- godox
|
||||
- goheader
|
||||
- gomoddirectives
|
||||
- gomodguard
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- grouper
|
||||
- importas
|
||||
- interfacebloat
|
||||
- ireturn
|
||||
- lll
|
||||
- loggercheck
|
||||
- maintidx
|
||||
- makezero
|
||||
- mnd
|
||||
- nestif
|
||||
- nilerr
|
||||
- nilnil
|
||||
# nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false]
|
||||
# DISCUSS: IMO the readability of does not always increase using more empty lines
|
||||
- nlreturn
|
||||
# noctx finds sending http request without context.Context [fast: false, auto-fix: false]
|
||||
# only interesting if using http
|
||||
- noctx
|
||||
# Reports all names returns
|
||||
# Named returns are not allowed which IMO reduces readability of code
|
||||
- nonamedreturns
|
||||
# detects snake case of variable naming and function name.
|
||||
# has not been a problem in our code and deprecated
|
||||
- nosnakecase
|
||||
# paralleltest detects missing usage of t.Parallel() method in your Go test [fast: true, auto-fix: false]
|
||||
# FUTURE: will break all of our tests
|
||||
- paralleltest
|
||||
# Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false]
|
||||
# FUTURE: would improve performance
|
||||
- prealloc
|
||||
# find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false]
|
||||
# FUTURE: checks for overwrites
|
||||
- predeclared
|
||||
# Check Prometheus metrics naming via promlint [fast: true, auto-fix: false]
|
||||
# Not interesting at the moment
|
||||
- promlinter
|
||||
# Checks that package variables are not reassigned
|
||||
# FUTURE: checks if vars like Err's are reassigned which might break code
|
||||
- reassign
|
||||
# Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
|
||||
# Linter aggregator, would allow to use less other linters
|
||||
- revive
|
||||
# checks for unpinned variables in go programs
|
||||
# deprecated
|
||||
- scopelint
|
||||
# Finds unused struct fields [fast: false, auto-fix: false]
|
||||
# deprecated, replaced by unused
|
||||
- structcheck
|
||||
# Stylecheck is a replacement for golint [fast: false, auto-fix: false]
|
||||
# we use goimports
|
||||
- stylecheck
|
||||
# Checks the struct tags. [fast: true, auto-fix: false]
|
||||
# FUTURE: would help for new structs
|
||||
- tagliatelle
|
||||
# tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 [fast: false, auto-fix: false]
|
||||
# FUTURE: currently are no env vars set
|
||||
- tenv
|
||||
# linter checks if examples are testable (have an expected output)
|
||||
# FUTURE: as soon as examples are added
|
||||
- testableexamples
|
||||
# linter that makes you use a separate _test package [fast: true, auto-fix: false]
|
||||
# don't use because we test some unexported functions
|
||||
- testpackage
|
||||
# thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false]
|
||||
# FUTURE: nice to improve test quality
|
||||
- thelper
|
||||
# tparallel detects inappropriate usage of t.Parallel() method in your Go test codes [fast: false, auto-fix: false]
|
||||
# FUTURE: nice to improve test quality
|
||||
- tparallel
|
||||
# Reports unused function parameters [fast: false, auto-fix: false]
|
||||
# DISCUSS: nice idea and would improve code quality, but how to handle false positives?
|
||||
- unparam
|
||||
# A linter that detect the possibility to use variables/constants from the Go standard library.
|
||||
# FUTURE: improves code quality
|
||||
- usestdlibvars
|
||||
# Finds unused global variables and constants [fast: false, auto-fix: false]
|
||||
# deprecated, replaced by unused
|
||||
- varcheck
|
||||
# checks that the length of a variable's name matches its scope [fast: false, auto-fix: false]
|
||||
# I would not use it because it more or less checks if var lenght matches
|
||||
- varnamelen
|
||||
# wastedassign finds wasted assignment statements. [fast: false, auto-fix: false]
|
||||
# FUTURE: would improve code quality (maybe already checked by vet?)
|
||||
- wastedassign
|
||||
# Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true]
|
||||
# Not sure if it improves code readability
|
||||
- whitespace
|
||||
# Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
|
||||
# FUTURE: improves UX because all the errors will be ZITADEL errors
|
||||
- wrapcheck
|
||||
# Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false]
|
||||
# FUTURE: improves code quality by allowing and blocking line breaks
|
||||
- wsl
|
||||
linters-settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard # Standard section: captures all standard packages.
|
||||
- default # Default section: contains all imports that could not be matched to another section type.
|
||||
- prefix(github.com/zitadel/zitadel) # Custom section: groups all imports with the specified Prefix.
|
||||
custom-order: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- .artifacts
|
||||
- .backups
|
||||
- .codecov
|
||||
- .github
|
||||
- .keys
|
||||
- .vscode
|
||||
- build
|
||||
- console
|
||||
- deploy
|
||||
- docs
|
||||
- guides
|
||||
- internal/api/ui/login/static
|
||||
- openapi
|
||||
- proto
|
||||
- tools
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
new-from-rev: main
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
- default
|
||||
- prefix(github.com/zitadel/zitadel)
|
||||
custom-order: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- .artifacts
|
||||
- .backups
|
||||
- .codecov
|
||||
- .github
|
||||
- .keys
|
||||
- .vscode
|
||||
- build
|
||||
- console
|
||||
- deploy
|
||||
- docs
|
||||
- guides
|
||||
- internal/api/ui/login/static
|
||||
- openapi
|
||||
- proto
|
||||
- tools
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
@@ -167,7 +167,7 @@ With [make](https://www.gnu.org/software/make/), you build a debuggable Zitadel
|
||||
Then, you test your changes via the console your binary is serving at http://<span because="breaks the link"></span>localhost:8080 and by verifying the database.
|
||||
Once you are happy with your changes, you run end-to-end tests and tear everything down.
|
||||
|
||||
Zitadel uses [golangci-lint](https://golangci-lint.run) for code quality checks. Please use [this configuration](.golangci.yaml) when running `golangci-lint`. We recommend to set golangci-lint as linter in your IDE.
|
||||
Zitadel uses [golangci-lint v2](https://golangci-lint.run) for code quality checks. Please use [this configuration](.golangci.yaml) when running `golangci-lint`. We recommend to set golangci-lint as linter in your IDE.
|
||||
|
||||
The commands in this section are tested against the following software versions:
|
||||
|
||||
|
@@ -10,7 +10,8 @@
|
||||
"lint:check:ng": "ng lint",
|
||||
"lint:check:prettier": "prettier --check src",
|
||||
"lint:fix": "prettier --write src",
|
||||
"generate": "pnpm exec buf generate ../proto --include-imports --include-wkt"
|
||||
"generate": "pnpm exec buf generate ../proto --include-imports --include-wkt",
|
||||
"clean": "rm -rf dist .angular .turbo node_modules src/app/proto/generated"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
@@ -656,12 +656,14 @@ The endpoint has to be opened in the user agent (browser) to terminate the user
|
||||
|
||||
No parameters are needed apart from the user agent cookie, but you can provide the following to customize the behavior:
|
||||
|
||||
| Parameter | Description |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| id_token_hint | the id_token that was previously issued to the client |
|
||||
| client_id | client_id of the application |
|
||||
| post_logout_redirect_uri | Callback uri of the logout where the user (agent) will be redirected to. Must match exactly one of the preregistered in Console. |
|
||||
| state | Opaque value used to maintain state between the request and the callback |
|
||||
| Parameter | Description |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| id_token_hint | the id_token that was previously issued to the client |
|
||||
| client_id | client_id of the application |
|
||||
| post_logout_redirect_uri | Callback uri of the logout where the user (agent) will be redirected to. Must match exactly one of the preregistered in Console. |
|
||||
| state | Opaque value used to maintain state between the request and the callback |
|
||||
| logout_hint | A valid login name of a user. Will be used to select the user to logout. Only supported when using the login UI V2. |
|
||||
| ui_locales | Spaces delimited list of preferred locales for the login UI, e.g. `de-CH de en`. If none is provided or matches the possible locales provided by the login UI, the `accept-language` header of the browser will be taken into account. |
|
||||
|
||||
The `post_logout_redirect_uri` will be checked against the previously registered uris of the client provided by the `azp` claim of the `id_token_hint` or the `client_id` parameter.
|
||||
If both parameters are provided, they must be equal.
|
||||
|
@@ -13,7 +13,8 @@
|
||||
"test:open:angular": "CYPRESS_BASE_URL=http://localhost:4200 CYPRESS_BACKEND_URL=http://localhost:8080 CYPRESS_WEBHOOK_HANDLER_HOST=host.docker.internal pnpm run open --",
|
||||
"test:e2e:angular": "CYPRESS_BASE_URL=http://localhost:4200 CYPRESS_BACKEND_URL=http://localhost:8080 CYPRESS_WEBHOOK_HANDLER_HOST=host.docker.internal pnpm run e2e --",
|
||||
"lint": "prettier --check cypress",
|
||||
"lint:fix": "prettier --write cypress"
|
||||
"lint:fix": "prettier --write cypress",
|
||||
"clean": "rm -rf .turbo node_modules"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
8
go.mod
8
go.mod
@@ -82,7 +82,7 @@ require (
|
||||
github.com/twilio/twilio-go v1.26.1
|
||||
github.com/zitadel/exifremove v0.1.0
|
||||
github.com/zitadel/logging v0.6.2
|
||||
github.com/zitadel/oidc/v3 v3.39.1
|
||||
github.com/zitadel/oidc/v3 v3.42.0
|
||||
github.com/zitadel/passwap v0.9.0
|
||||
github.com/zitadel/saml v0.3.5
|
||||
github.com/zitadel/schema v1.3.1
|
||||
@@ -101,8 +101,8 @@ require (
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.15.0
|
||||
golang.org/x/text v0.26.0
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/text v0.27.0
|
||||
google.golang.org/api v0.233.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9
|
||||
google.golang.org/grpc v1.72.1
|
||||
@@ -119,7 +119,7 @@ require (
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
|
||||
github.com/crewjam/httperr v0.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
|
18
go.sum
18
go.sum
@@ -104,8 +104,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.0 h1:DBvuZxjdKkRP/dr4GVV4w2fnmrk5Hxc90T51LZjv0JA=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
|
||||
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
@@ -810,8 +810,10 @@ github.com/zitadel/exifremove v0.1.0 h1:qD50ezWsfeeqfcvs79QyyjVfK+snN12v0U0deaU8
|
||||
github.com/zitadel/exifremove v0.1.0/go.mod h1:rzKJ3woL/Rz2KthVBiSBKIBptNTvgmk9PLaeUKTm+ek=
|
||||
github.com/zitadel/logging v0.6.2 h1:MW2kDDR0ieQynPZ0KIZPrh9ote2WkxfBif5QoARDQcU=
|
||||
github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4=
|
||||
github.com/zitadel/oidc/v3 v3.39.1 h1:6QwGwI3yxh4somT7fwRCeT1KOn/HOGv0PA0dFciwJjE=
|
||||
github.com/zitadel/oidc/v3 v3.39.1/go.mod h1:aH8brOrzoliAybVdfq2xIdGvbtl0j/VsKRNa7WE72gI=
|
||||
github.com/zitadel/oidc/v3 v3.41.1-0.20250718152526-16ebef905b40 h1:MmUhfhwIcPStWqsTW+Pw+kYa5SNY7TxwzktUDohwO78=
|
||||
github.com/zitadel/oidc/v3 v3.41.1-0.20250718152526-16ebef905b40/go.mod h1:Y/rY7mHTzMGrZgf7REgQZFWxySlaSVqqFdBmNZq+9wA=
|
||||
github.com/zitadel/oidc/v3 v3.42.0 h1:cqlCYIEapmDprp5a5hUl9ivkUOu1SQxOqbrKdalHqGk=
|
||||
github.com/zitadel/oidc/v3 v3.42.0/go.mod h1:Y/rY7mHTzMGrZgf7REgQZFWxySlaSVqqFdBmNZq+9wA=
|
||||
github.com/zitadel/passwap v0.9.0 h1:QvDK8OHKdb73C0m+mwXvu87UJSBqix3oFwTVENHdv80=
|
||||
github.com/zitadel/passwap v0.9.0/go.mod h1:6QzwFjDkIr3FfudzSogTOx5Ydhq4046dRJtDM/kX+G8=
|
||||
github.com/zitadel/saml v0.3.5 h1:L1RKWS5y66cGepVxUGjx/WSBOtrtSpRA/J3nn5BJLOY=
|
||||
@@ -948,8 +950,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -992,8 +994,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
|
@@ -185,7 +185,7 @@ func (s *Styling) Reducers() []handler.AggregateReducer {
|
||||
}
|
||||
|
||||
func (m *Styling) processLabelPolicy(event eventstore.Event) (_ *handler.Statement, err error) {
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
policy := new(iam_model.LabelPolicyView)
|
||||
switch event.Type() {
|
||||
case instance.LabelPolicyAddedEventType,
|
||||
|
@@ -605,7 +605,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
||||
{Key: "added", Value: "value"},
|
||||
},
|
||||
}
|
||||
return expectPreUserinfoExecution(ctx, t, instance, req, response)
|
||||
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||
},
|
||||
req: &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: func() string {
|
||||
@@ -630,7 +630,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
||||
"addedLog",
|
||||
},
|
||||
}
|
||||
return expectPreUserinfoExecution(ctx, t, instance, req, response)
|
||||
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||
},
|
||||
req: &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: func() string {
|
||||
@@ -655,7 +655,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
||||
{Key: "key", Value: []byte("value")},
|
||||
},
|
||||
}
|
||||
return expectPreUserinfoExecution(ctx, t, instance, req, response)
|
||||
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||
},
|
||||
req: &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: func() string {
|
||||
@@ -692,7 +692,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
||||
{Key: "added3", Value: "value3"},
|
||||
},
|
||||
}
|
||||
return expectPreUserinfoExecution(ctx, t, instance, req, response)
|
||||
return expectPreUserinfoExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||
},
|
||||
req: &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: func() string {
|
||||
@@ -755,7 +755,7 @@ func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func expectPreUserinfoExecution(ctx context.Context, t *testing.T, instance *integration.Instance, req *oidc_pb.CreateCallbackRequest, response *oidc_api.ContextInfoResponse) (string, func()) {
|
||||
func expectPreUserinfoExecution(ctx context.Context, t *testing.T, instance *integration.Instance, clientID string, req *oidc_pb.CreateCallbackRequest, response *oidc_api.ContextInfoResponse) (string, func()) {
|
||||
userEmail := gofakeit.Email()
|
||||
userPhone := "+41" + gofakeit.Phone()
|
||||
userResp := instance.CreateHumanUserVerified(ctx, instance.DefaultOrg.Id, userEmail, userPhone)
|
||||
@@ -767,7 +767,7 @@ func expectPreUserinfoExecution(ctx context.Context, t *testing.T, instance *int
|
||||
SessionToken: sessionResp.GetSessionToken(),
|
||||
},
|
||||
}
|
||||
expectedContextInfo := contextInfoForUserOIDC(instance, "function/preuserinfo", userResp, userEmail, userPhone)
|
||||
expectedContextInfo := contextInfoForUserOIDC(instance, "function/preuserinfo", clientID, userResp, userEmail, userPhone)
|
||||
|
||||
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
|
||||
|
||||
@@ -845,7 +845,7 @@ func getAccessTokenClaims(ctx context.Context, t *testing.T, instance *integrati
|
||||
return claims
|
||||
}
|
||||
|
||||
func contextInfoForUserOIDC(instance *integration.Instance, function string, userResp *user.AddHumanUserResponse, email, phone string) *oidc_api.ContextInfo {
|
||||
func contextInfoForUserOIDC(instance *integration.Instance, function string, clientID string, userResp *user.AddHumanUserResponse, email, phone string) *oidc_api.ContextInfo {
|
||||
return &oidc_api.ContextInfo{
|
||||
Function: function,
|
||||
UserInfo: &oidc.UserInfo{
|
||||
@@ -878,6 +878,9 @@ func contextInfoForUserOIDC(instance *integration.Instance, function string, use
|
||||
},
|
||||
},
|
||||
UserMetadata: nil,
|
||||
Application: &oidc_api.ContextInfoApplication{
|
||||
ClientID: clientID,
|
||||
},
|
||||
Org: &query.UserInfoOrg{
|
||||
ID: instance.DefaultOrg.GetId(),
|
||||
Name: instance.DefaultOrg.GetName(),
|
||||
@@ -918,7 +921,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
||||
{Key: "added1", Value: "value"},
|
||||
},
|
||||
}
|
||||
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
|
||||
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||
},
|
||||
req: &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: func() string {
|
||||
@@ -943,7 +946,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
||||
"addedLog",
|
||||
},
|
||||
}
|
||||
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
|
||||
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||
},
|
||||
req: &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: func() string {
|
||||
@@ -968,7 +971,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
||||
{Key: "key", Value: []byte("value")},
|
||||
},
|
||||
}
|
||||
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
|
||||
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||
},
|
||||
req: &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: func() string {
|
||||
@@ -1005,7 +1008,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
||||
{Key: "added3", Value: "value3"},
|
||||
},
|
||||
}
|
||||
return expectPreAccessTokenExecution(ctx, t, instance, req, response)
|
||||
return expectPreAccessTokenExecution(ctx, t, instance, client.GetClientId(), req, response)
|
||||
},
|
||||
req: &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: func() string {
|
||||
@@ -1060,7 +1063,7 @@ func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *integration.Instance, req *oidc_pb.CreateCallbackRequest, response *oidc_api.ContextInfoResponse) (string, func()) {
|
||||
func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *integration.Instance, clientID string, req *oidc_pb.CreateCallbackRequest, response *oidc_api.ContextInfoResponse) (string, func()) {
|
||||
userEmail := gofakeit.Email()
|
||||
userPhone := "+41" + gofakeit.Phone()
|
||||
userResp := instance.CreateHumanUserVerified(ctx, instance.DefaultOrg.Id, userEmail, userPhone)
|
||||
@@ -1072,7 +1075,7 @@ func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *
|
||||
SessionToken: sessionResp.GetSessionToken(),
|
||||
},
|
||||
}
|
||||
expectedContextInfo := contextInfoForUserOIDC(instance, "function/preaccesstoken", userResp, userEmail, userPhone)
|
||||
expectedContextInfo := contextInfoForUserOIDC(instance, "function/preaccesstoken", clientID, userResp, userEmail, userPhone)
|
||||
|
||||
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
|
||||
|
||||
|
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/zitadel/logging"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
http_utils "github.com/zitadel/zitadel/internal/api/http"
|
||||
@@ -30,6 +31,8 @@ import (
|
||||
const (
|
||||
LoginClientHeader = "x-zitadel-login-client"
|
||||
LoginPostLogoutRedirectParam = "post_logout_redirect"
|
||||
LoginLogoutHintParam = "logout_hint"
|
||||
LoginUILocalesParam = "ui_locales"
|
||||
LoginPath = "/login"
|
||||
LogoutPath = "/logout"
|
||||
LogoutDonePath = "/logout/done"
|
||||
@@ -283,14 +286,19 @@ func (o *OPStorage) TerminateSessionFromRequest(ctx context.Context, endSessionR
|
||||
// we'll redirect to the UI (V2) and let it decide which session to terminate
|
||||
//
|
||||
// If there's no id_token_hint and for v1 logins, we handle them separately
|
||||
if endSessionRequest.IDTokenHintClaims == nil &&
|
||||
(authz.GetFeatures(ctx).LoginV2.Required || headers.Get(LoginClientHeader) != "") {
|
||||
if endSessionRequest.IDTokenHintClaims == nil && (authz.GetFeatures(ctx).LoginV2.Required || headers.Get(LoginClientHeader) != "") {
|
||||
redirectURI := v2PostLogoutRedirectURI(endSessionRequest.RedirectURI)
|
||||
// if no base uri is set, fallback to the default configured in the runtime config
|
||||
if authz.GetFeatures(ctx).LoginV2.BaseURI == nil || authz.GetFeatures(ctx).LoginV2.BaseURI.String() == "" {
|
||||
return o.defaultLogoutURLV2 + redirectURI, nil
|
||||
logoutURI := authz.GetFeatures(ctx).LoginV2.BaseURI
|
||||
// if no logout uri is set, fallback to the default configured in the runtime config
|
||||
if logoutURI == nil || logoutURI.String() == "" {
|
||||
logoutURI, err = url.Parse(o.defaultLogoutURLV2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
logoutURI = logoutURI.JoinPath(LogoutPath)
|
||||
}
|
||||
return buildLoginV2LogoutURL(authz.GetFeatures(ctx).LoginV2.BaseURI, redirectURI), nil
|
||||
return buildLoginV2LogoutURL(logoutURI, redirectURI, endSessionRequest.LogoutHint, endSessionRequest.UILocales), nil
|
||||
}
|
||||
|
||||
// V1:
|
||||
@@ -367,12 +375,25 @@ func (o *OPStorage) federatedLogout(ctx context.Context, sessionID string, postL
|
||||
return login.ExternalLogoutPath(sessionID)
|
||||
}
|
||||
|
||||
func buildLoginV2LogoutURL(baseURI *url.URL, redirectURI string) string {
|
||||
baseURI.JoinPath(LogoutPath)
|
||||
q := baseURI.Query()
|
||||
func buildLoginV2LogoutURL(logoutURI *url.URL, redirectURI, logoutHint string, uiLocales []language.Tag) string {
|
||||
if strings.HasSuffix(logoutURI.Path, "/") && len(logoutURI.Path) > 1 {
|
||||
logoutURI.Path = strings.TrimSuffix(logoutURI.Path, "/")
|
||||
}
|
||||
|
||||
q := logoutURI.Query()
|
||||
q.Set(LoginPostLogoutRedirectParam, redirectURI)
|
||||
baseURI.RawQuery = q.Encode()
|
||||
return baseURI.String()
|
||||
if logoutHint != "" {
|
||||
q.Set(LoginLogoutHintParam, logoutHint)
|
||||
}
|
||||
if len(uiLocales) > 0 {
|
||||
locales := make([]string, len(uiLocales))
|
||||
for i, locale := range uiLocales {
|
||||
locales[i] = locale.String()
|
||||
}
|
||||
q.Set(LoginUILocalesParam, strings.Join(locales, " "))
|
||||
}
|
||||
logoutURI.RawQuery = q.Encode()
|
||||
return logoutURI.String()
|
||||
}
|
||||
|
||||
// v2PostLogoutRedirectURI will take care that the post_logout_redirect_uri is correctly set for v2 logins.
|
||||
|
98
internal/api/oidc/auth_request_test.go
Normal file
98
internal/api/oidc/auth_request_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TestBuildLoginV2LogoutURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
testName string
|
||||
logoutURIStr string
|
||||
redirectURI string
|
||||
logoutHint string
|
||||
uiLocales []language.Tag
|
||||
expectedParams map[string]string
|
||||
}{
|
||||
{
|
||||
testName: "basic with only redirectURI",
|
||||
logoutURIStr: "https://example.com/logout",
|
||||
redirectURI: "https://client/cb",
|
||||
expectedParams: map[string]string{
|
||||
"post_logout_redirect": "https://client/cb",
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "with logout hint",
|
||||
logoutURIStr: "https://example.com/logout",
|
||||
redirectURI: "https://client/cb",
|
||||
logoutHint: "user@example.com",
|
||||
expectedParams: map[string]string{
|
||||
"post_logout_redirect": "https://client/cb",
|
||||
"logout_hint": "user@example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "with ui_locales",
|
||||
logoutURIStr: "https://example.com/logout",
|
||||
redirectURI: "https://client/cb",
|
||||
uiLocales: []language.Tag{language.English, language.Italian},
|
||||
expectedParams: map[string]string{
|
||||
"post_logout_redirect": "https://client/cb",
|
||||
"ui_locales": "en it",
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "with all params",
|
||||
logoutURIStr: "https://example.com/logout",
|
||||
redirectURI: "https://client/cb",
|
||||
logoutHint: "logoutme",
|
||||
uiLocales: []language.Tag{language.Make("de-CH"), language.Make("fr")},
|
||||
expectedParams: map[string]string{
|
||||
"post_logout_redirect": "https://client/cb",
|
||||
"logout_hint": "logoutme",
|
||||
"ui_locales": "de-CH fr",
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "base with trailing slash",
|
||||
logoutURIStr: "https://example.com/logout/",
|
||||
redirectURI: "https://client/cb",
|
||||
expectedParams: map[string]string{
|
||||
"post_logout_redirect": "https://client/cb",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
// t.Parallel()
|
||||
|
||||
// Given
|
||||
logoutURI, err := url.Parse(tc.logoutURIStr)
|
||||
require.NoError(t, err)
|
||||
|
||||
// When
|
||||
got := buildLoginV2LogoutURL(logoutURI, tc.redirectURI, tc.logoutHint, tc.uiLocales)
|
||||
|
||||
// Then
|
||||
gotURL, err := url.Parse(got)
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, gotURL.String(), "/logout/")
|
||||
|
||||
q := gotURL.Query()
|
||||
// Ensure no unexpected params
|
||||
require.Len(t, q, len(tc.expectedParams))
|
||||
|
||||
for k, v := range tc.expectedParams {
|
||||
assert.Equal(t, v, q.Get(k))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -498,7 +498,7 @@ func TestOPStorage_TerminateSession(t *testing.T) {
|
||||
_, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider)
|
||||
require.NoError(t, err)
|
||||
|
||||
postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state")
|
||||
postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state", "", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String())
|
||||
|
||||
@@ -535,7 +535,7 @@ func TestOPStorage_TerminateSession_refresh_grant(t *testing.T) {
|
||||
_, err = rp.Userinfo[*oidc.UserInfo](CTX, tokens.AccessToken, tokens.TokenType, tokens.IDTokenClaims.Subject, provider)
|
||||
require.NoError(t, err)
|
||||
|
||||
postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state")
|
||||
postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state", "", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String())
|
||||
|
||||
@@ -551,6 +551,17 @@ func TestOPStorage_TerminateSession_refresh_grant(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func buildLogoutURL(origin, logoutURLV2 string, redirectURI string, extraParams map[string]string) string {
|
||||
u, _ := url.Parse(origin + logoutURLV2 + redirectURI)
|
||||
q := u.Query()
|
||||
for k, v := range extraParams {
|
||||
q.Set(k, v)
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
// Append the redirect URI as a URL-escaped string
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -565,7 +576,7 @@ func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) {
|
||||
return clientID
|
||||
}(),
|
||||
authRequestID: createAuthRequest,
|
||||
logoutURL: http_utils.BuildOrigin(Instance.Host(), Instance.Config.Secure) + Instance.Config.LogoutURLV2 + logoutRedirectURI + "?state=state",
|
||||
logoutURL: buildLogoutURL(http_utils.BuildOrigin(Instance.Host(), Instance.Config.Secure), Instance.Config.LogoutURLV2, logoutRedirectURI+"?state=state", map[string]string{"logout_hint": "hint", "ui_locales": "it-IT en-US"}),
|
||||
},
|
||||
{
|
||||
name: "login v2 config",
|
||||
@@ -574,7 +585,7 @@ func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) {
|
||||
return clientID
|
||||
}(),
|
||||
authRequestID: createAuthRequestNoLoginClientHeader,
|
||||
logoutURL: http_utils.BuildOrigin(Instance.Host(), Instance.Config.Secure) + Instance.Config.LogoutURLV2 + logoutRedirectURI + "?state=state",
|
||||
logoutURL: buildLogoutURL(http_utils.BuildOrigin(Instance.Host(), Instance.Config.Secure), Instance.Config.LogoutURLV2, logoutRedirectURI+"?state=state", map[string]string{"logout_hint": "hint", "ui_locales": "it-IT en-US"}),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -601,7 +612,7 @@ func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) {
|
||||
assertTokens(t, tokens, false)
|
||||
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime, sessionID)
|
||||
|
||||
postLogoutRedirect, err := rp.EndSession(CTX, provider, "", logoutRedirectURI, "state")
|
||||
postLogoutRedirect, err := rp.EndSession(CTX, provider, "", logoutRedirectURI, "state", "hint", oidc.ParseLocales([]string{"it-IT", "en-US"}))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.logoutURL, postLogoutRedirect.String())
|
||||
|
||||
|
@@ -311,7 +311,7 @@ func Test_ZITADEL_API_terminated_session(t *testing.T) {
|
||||
require.Equal(t, User.GetUserId(), myUserResp.GetUser().GetId())
|
||||
|
||||
// end session
|
||||
postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state")
|
||||
postLogoutRedirect, err := rp.EndSession(CTX, provider, tokens.IDToken, logoutRedirectURI, "state", "", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, logoutRedirectURI+"?state=state", postLogoutRedirect.String())
|
||||
|
||||
|
@@ -100,6 +100,7 @@ func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionR
|
||||
token.userID,
|
||||
token.scope,
|
||||
client.projectID,
|
||||
client.clientID,
|
||||
client.projectRoleAssertion,
|
||||
true,
|
||||
true,
|
||||
|
@@ -31,7 +31,7 @@ for example the v2 code exchange and refresh token.
|
||||
*/
|
||||
|
||||
func (s *Server) accessTokenResponseFromSession(ctx context.Context, client op.Client, session *command.OIDCSession, state, projectID string, projectRoleAssertion, accessTokenRoleAssertion, idTokenRoleAssertion, userInfoAssertion bool) (_ *oidc.AccessTokenResponse, err error) {
|
||||
getUserInfo := s.getUserInfo(session.UserID, projectID, projectRoleAssertion, userInfoAssertion, session.Scope)
|
||||
getUserInfo := s.getUserInfo(session.UserID, projectID, client.GetID(), projectRoleAssertion, userInfoAssertion, session.Scope)
|
||||
getSigner := s.getSignerOnce()
|
||||
|
||||
resp := &oidc.AccessTokenResponse{
|
||||
@@ -113,8 +113,8 @@ type userInfoFunc func(ctx context.Context, roleAssertion bool, triggerType doma
|
||||
|
||||
// getUserInfo returns a function which retrieves userinfo from the database once.
|
||||
// However, each time, role claims are asserted and also action flows will trigger.
|
||||
func (s *Server) getUserInfo(userID, projectID string, projectRoleAssertion, userInfoAssertion bool, scope []string) userInfoFunc {
|
||||
userInfo := s.userInfo(userID, scope, projectID, projectRoleAssertion, userInfoAssertion, false)
|
||||
func (s *Server) getUserInfo(userID, projectID, clientID string, projectRoleAssertion, userInfoAssertion bool, scope []string) userInfoFunc {
|
||||
userInfo := s.userInfo(userID, scope, projectID, clientID, projectRoleAssertion, userInfoAssertion, false)
|
||||
return func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (*oidc.UserInfo, error) {
|
||||
return userInfo(ctx, roleAssertion, triggerType)
|
||||
}
|
||||
|
@@ -218,7 +218,7 @@ func validateTokenExchangeAudience(requestedAudience, subjectAudience, actorAudi
|
||||
// Both tokens may point to the same object (subjectToken) in case of a regular Token Exchange.
|
||||
// When the subject and actor Tokens point to different objects, the new tokens will be for impersonation / delegation.
|
||||
func (s *Server) createExchangeTokens(ctx context.Context, tokenType oidc.TokenType, client *Client, subjectToken, actorToken *exchangeToken, audience, scopes []string) (_ *oidc.TokenExchangeResponse, err error) {
|
||||
getUserInfo := s.getUserInfo(subjectToken.userID, client.client.ProjectID, client.client.ProjectRoleAssertion, client.IDTokenUserinfoClaimsAssertion(), scopes)
|
||||
getUserInfo := s.getUserInfo(subjectToken.userID, client.client.ProjectID, client.GetID(), client.client.ProjectRoleAssertion, client.IDTokenUserinfoClaimsAssertion(), scopes)
|
||||
getSigner := s.getSignerOnce()
|
||||
|
||||
resp := &oidc.TokenExchangeResponse{
|
||||
|
@@ -54,6 +54,7 @@ func (s *Server) UserInfo(ctx context.Context, r *op.Request[oidc.UserInfoReques
|
||||
token.userID,
|
||||
token.scope,
|
||||
projectID,
|
||||
token.clientID,
|
||||
assertion,
|
||||
true,
|
||||
false,
|
||||
@@ -86,6 +87,7 @@ func (s *Server) userInfo(
|
||||
userID string,
|
||||
scope []string,
|
||||
projectID string,
|
||||
clientID string,
|
||||
projectRoleAssertion, userInfoAssertion, currentProjectOnly bool,
|
||||
) func(ctx context.Context, roleAssertion bool, triggerType domain.TriggerType) (_ *oidc.UserInfo, err error) {
|
||||
var (
|
||||
@@ -120,7 +122,7 @@ func (s *Server) userInfo(
|
||||
Claims: maps.Clone(rawUserInfo.Claims),
|
||||
}
|
||||
assertRoles(projectID, qu, roleAudience, requestedRoles, roleAssertion, userInfo)
|
||||
return userInfo, s.userinfoFlows(ctx, qu, userInfo, triggerType)
|
||||
return userInfo, s.userinfoFlows(ctx, qu, userInfo, triggerType, clientID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +287,8 @@ func setUserInfoRoleClaims(userInfo *oidc.UserInfo, roles *projectsRoles) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, userInfo *oidc.UserInfo, triggerType domain.TriggerType) (err error) {
|
||||
//nolint:gocognit
|
||||
func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, userInfo *oidc.UserInfo, triggerType domain.TriggerType, clientID string) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@@ -319,6 +322,13 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
|
||||
}
|
||||
}),
|
||||
),
|
||||
actions.SetFields("application",
|
||||
actions.SetFields("getClientId", func(c *actions.FieldConfig) interface{} {
|
||||
return func(goja.FunctionCall) goja.Value {
|
||||
return c.Runtime.ToValue(clientID)
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -427,6 +437,7 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
|
||||
User: qu.User,
|
||||
UserMetadata: qu.Metadata,
|
||||
Org: qu.Org,
|
||||
Application: &ContextInfoApplication{ClientID: clientID},
|
||||
UserGrants: qu.UserGrants,
|
||||
}
|
||||
|
||||
@@ -463,13 +474,17 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
|
||||
}
|
||||
|
||||
type ContextInfo struct {
|
||||
Function string `json:"function,omitempty"`
|
||||
UserInfo *oidc.UserInfo `json:"userinfo,omitempty"`
|
||||
User *query.User `json:"user,omitempty"`
|
||||
UserMetadata []query.UserMetadata `json:"user_metadata,omitempty"`
|
||||
Org *query.UserInfoOrg `json:"org,omitempty"`
|
||||
UserGrants []query.UserGrant `json:"user_grants,omitempty"`
|
||||
Response *ContextInfoResponse `json:"response,omitempty"`
|
||||
Function string `json:"function,omitempty"`
|
||||
UserInfo *oidc.UserInfo `json:"userinfo,omitempty"`
|
||||
User *query.User `json:"user,omitempty"`
|
||||
UserMetadata []query.UserMetadata `json:"user_metadata,omitempty"`
|
||||
Org *query.UserInfoOrg `json:"org,omitempty"`
|
||||
UserGrants []query.UserGrant `json:"user_grants,omitempty"`
|
||||
Application *ContextInfoApplication `json:"application,omitempty"`
|
||||
Response *ContextInfoResponse `json:"response,omitempty"`
|
||||
}
|
||||
type ContextInfoApplication struct {
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
}
|
||||
|
||||
type ContextInfoResponse struct {
|
||||
|
@@ -6,7 +6,7 @@ import "context"
|
||||
type Init func(context.Context, *Check) error
|
||||
|
||||
type Check struct {
|
||||
Executes []func(ex Executer, projectionName string) (bool, error)
|
||||
Executes []func(ctx context.Context, executer Executer, projectionName string) (bool, error)
|
||||
}
|
||||
|
||||
func (c *Check) IsNoop() bool {
|
||||
|
@@ -646,7 +646,7 @@ func (h *Handler) executeStatements(ctx context.Context, tx *sql.Tx, statements
|
||||
for i, statement := range statements {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
return lastProcessedIndex, ctx.Err()
|
||||
default:
|
||||
err := h.executeStatement(ctx, tx, statement)
|
||||
if err != nil {
|
||||
@@ -669,7 +669,7 @@ func (h *Handler) executeStatement(ctx context.Context, tx *sql.Tx, statement *S
|
||||
return err
|
||||
}
|
||||
|
||||
if err = statement.Execute(tx, h.projection.Name()); err != nil {
|
||||
if err = statement.Execute(ctx, tx, h.projection.Name()); err != nil {
|
||||
h.log().WithError(err).Error("statement execution failed")
|
||||
|
||||
_, rollbackErr := tx.ExecContext(ctx, "ROLLBACK TO SAVEPOINT exec_stmt")
|
||||
|
@@ -200,7 +200,7 @@ func (h *Handler) Init(ctx context.Context) error {
|
||||
}
|
||||
for i, execute := range check.Init().Executes {
|
||||
logging.WithFields("projection", h.projection.Name(), "execute", i).Debug("executing check")
|
||||
next, err := execute(tx, h.projection.Name())
|
||||
next, err := execute(ctx, tx, h.projection.Name())
|
||||
if err != nil {
|
||||
logging.OnError(tx.Rollback()).Debug("unable to rollback")
|
||||
return err
|
||||
@@ -218,7 +218,7 @@ func NewTableCheck(table *Table, opts ...execOption) *handler.Check {
|
||||
create := func(config execConfig) string {
|
||||
return createTableStatement(table, config.tableName, "")
|
||||
}
|
||||
executes := make([]func(handler.Executer, string) (bool, error), len(table.indices)+1)
|
||||
executes := make([]func(context.Context, handler.Executer, string) (bool, error), len(table.indices)+1)
|
||||
executes[0] = execNextIfExists(config, create, opts, true)
|
||||
for i, index := range table.indices {
|
||||
executes[i+1] = execNextIfExists(config, createIndexCheck(index), opts, true)
|
||||
@@ -239,7 +239,7 @@ func NewMultiTableCheck(primaryTable *Table, secondaryTables ...*SuffixedTable)
|
||||
}
|
||||
|
||||
return &handler.Check{
|
||||
Executes: []func(handler.Executer, string) (bool, error){
|
||||
Executes: []func(context.Context, handler.Executer, string) (bool, error){
|
||||
execNextIfExists(config, create, nil, true),
|
||||
},
|
||||
}
|
||||
@@ -257,14 +257,14 @@ func NewViewCheck(selectStmt string, secondaryTables ...*SuffixedTable) *handler
|
||||
}
|
||||
|
||||
return &handler.Check{
|
||||
Executes: []func(handler.Executer, string) (bool, error){
|
||||
Executes: []func(context.Context, handler.Executer, string) (bool, error){
|
||||
execNextIfExists(config, create, nil, false),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func execNextIfExists(config execConfig, q query, opts []execOption, executeNext bool) func(handler.Executer, string) (bool, error) {
|
||||
return func(handler handler.Executer, name string) (shouldExecuteNext bool, err error) {
|
||||
func execNextIfExists(config execConfig, q query, opts []execOption, executeNext bool) func(ctx context.Context, handler handler.Executer, name string) (bool, error) {
|
||||
return func(ctx context.Context, handler handler.Executer, name string) (shouldExecuteNext bool, err error) {
|
||||
_, err = handler.Exec("SAVEPOINT exec_stmt")
|
||||
if err != nil {
|
||||
return false, zerrors.ThrowInternal(err, "V2-U1wlz", "create savepoint failed")
|
||||
@@ -280,7 +280,7 @@ func execNextIfExists(config execConfig, q query, opts []execOption, executeNext
|
||||
return
|
||||
}
|
||||
}()
|
||||
err = exec(config, q, opts)(handler, name)
|
||||
err = exec(config, q, opts)(ctx, handler, name)
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -91,7 +92,7 @@ type Statement struct {
|
||||
Execute Exec
|
||||
}
|
||||
|
||||
type Exec func(ex Executer, projectionName string) error
|
||||
type Exec func(ctx context.Context, ex Executer, projectionName string) error
|
||||
|
||||
func WithTableSuffix(name string) func(*execConfig) {
|
||||
return func(o *execConfig) {
|
||||
@@ -670,7 +671,7 @@ type execConfig struct {
|
||||
type query func(config execConfig) string
|
||||
|
||||
func exec(config execConfig, q query, opts []execOption) Exec {
|
||||
return func(ex Executer, projectionName string) (err error) {
|
||||
return func(ctx context.Context, ex Executer, projectionName string) (err error) {
|
||||
if projectionName == "" {
|
||||
return ErrNoProjection
|
||||
}
|
||||
@@ -694,12 +695,12 @@ func exec(config execConfig, q query, opts []execOption) Exec {
|
||||
}
|
||||
|
||||
func multiExec(execList []Exec) Exec {
|
||||
return func(ex Executer, projectionName string) error {
|
||||
return func(ctx context.Context, ex Executer, projectionName string) error {
|
||||
for _, exec := range execList {
|
||||
if exec == nil {
|
||||
continue
|
||||
}
|
||||
if err := exec(ex, projectionName); err != nil {
|
||||
if err := exec(ctx, ex, projectionName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"reflect"
|
||||
@@ -197,7 +198,7 @@ func TestNewCreateStatement(t *testing.T) {
|
||||
tt.want.executer.t = t
|
||||
stmt := NewCreateStatement(tt.args.event, tt.args.values)
|
||||
|
||||
err := stmt.Execute(tt.want.executer, tt.args.table)
|
||||
err := stmt.Execute(t.Context(), tt.want.executer, tt.args.table)
|
||||
if !tt.want.isErr(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -506,7 +507,7 @@ func TestNewUpsertStatement(t *testing.T) {
|
||||
tt.want.executer.t = t
|
||||
stmt := NewUpsertStatement(tt.args.event, tt.args.conflictCols, tt.args.values)
|
||||
|
||||
err := stmt.Execute(tt.want.executer, tt.args.table)
|
||||
err := stmt.Execute(t.Context(), tt.want.executer, tt.args.table)
|
||||
if !tt.want.isErr(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -710,7 +711,7 @@ func TestNewUpdateStatement(t *testing.T) {
|
||||
tt.want.executer.t = t
|
||||
stmt := NewUpdateStatement(tt.args.event, tt.args.values, tt.args.conditions)
|
||||
|
||||
err := stmt.Execute(tt.want.executer, tt.args.table)
|
||||
err := stmt.Execute(t.Context(), tt.want.executer, tt.args.table)
|
||||
if !tt.want.isErr(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -827,7 +828,7 @@ func TestNewDeleteStatement(t *testing.T) {
|
||||
tt.want.executer.t = t
|
||||
stmt := NewDeleteStatement(tt.args.event, tt.args.conditions)
|
||||
|
||||
err := stmt.Execute(tt.want.executer, tt.args.table)
|
||||
err := stmt.Execute(t.Context(), tt.want.executer, tt.args.table)
|
||||
if !tt.want.isErr(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -878,7 +879,7 @@ func TestNewNoOpStatement(t *testing.T) {
|
||||
return
|
||||
}
|
||||
tt.want.executer.t = t
|
||||
err := stmt.Execute(tt.want.executer, tt.args.table)
|
||||
err := stmt.Execute(t.Context(), tt.want.executer, tt.args.table)
|
||||
if !tt.want.isErr(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -1054,7 +1055,7 @@ func TestNewMultiStatement(t *testing.T) {
|
||||
return
|
||||
}
|
||||
tt.want.executer.t = t
|
||||
err := stmt.Execute(tt.want.executer, tt.args.table)
|
||||
err := stmt.Execute(t.Context(), tt.want.executer, tt.args.table)
|
||||
if !tt.want.isErr(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -1338,7 +1339,7 @@ func TestNewCopyStatement(t *testing.T) {
|
||||
tt.want.executer.t = t
|
||||
stmt := NewCopyStatement(tt.args.event, tt.args.conflictingCols, tt.args.from, tt.args.to, tt.args.conds)
|
||||
|
||||
err := stmt.Execute(tt.want.executer, tt.args.table)
|
||||
err := stmt.Execute(t.Context(), tt.want.executer, tt.args.table)
|
||||
if !tt.want.isErr(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -1349,7 +1350,7 @@ func TestNewCopyStatement(t *testing.T) {
|
||||
|
||||
func TestStatement_Execute(t *testing.T) {
|
||||
type fields struct {
|
||||
execute func(ex Executer, projectionName string) error
|
||||
execute func(ctx context.Context, ex Executer, projectionName string) error
|
||||
}
|
||||
type want struct {
|
||||
isErr func(error) bool
|
||||
@@ -1366,7 +1367,7 @@ func TestStatement_Execute(t *testing.T) {
|
||||
{
|
||||
name: "execute returns no error",
|
||||
fields: fields{
|
||||
execute: func(ex Executer, projectionName string) error { return nil },
|
||||
execute: func(ctx context.Context, ex Executer, projectionName string) error { return nil },
|
||||
},
|
||||
args: args{
|
||||
projectionName: "my_projection",
|
||||
@@ -1383,7 +1384,7 @@ func TestStatement_Execute(t *testing.T) {
|
||||
projectionName: "my_projection",
|
||||
},
|
||||
fields: fields{
|
||||
execute: func(ex Executer, projectionName string) error { return errTest },
|
||||
execute: func(ctx context.Context, ex Executer, projectionName string) error { return errTest },
|
||||
},
|
||||
want: want{
|
||||
isErr: func(err error) bool {
|
||||
@@ -1397,7 +1398,7 @@ func TestStatement_Execute(t *testing.T) {
|
||||
stmt := &Statement{
|
||||
Execute: tt.fields.execute,
|
||||
}
|
||||
if err := stmt.Execute(nil, tt.args.projectionName); !tt.want.isErr(err) {
|
||||
if err := stmt.Execute(t.Context(), nil, tt.args.projectionName); !tt.want.isErr(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
|
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
const ExecutionUserID = "EXECUTION"
|
||||
|
||||
func HandlerContext(event *eventstore.Aggregate) context.Context {
|
||||
ctx := authz.WithInstanceID(context.Background(), event.InstanceID)
|
||||
func HandlerContext(parent context.Context, event *eventstore.Aggregate) context.Context {
|
||||
ctx := authz.WithInstanceID(parent, event.InstanceID)
|
||||
return authz.SetCtxData(ctx, authz.CtxData{UserID: ExecutionUserID, OrgID: event.ResourceOwner})
|
||||
}
|
||||
|
||||
|
@@ -113,7 +113,7 @@ func idsForEventType(eventType string) []string {
|
||||
}
|
||||
|
||||
func (u *eventHandler) reduce(e eventstore.Event) (*handler.Statement, error) {
|
||||
ctx := HandlerContext(e.Aggregate())
|
||||
ctx := HandlerContext(context.Background(), e.Aggregate())
|
||||
|
||||
targets, err := u.query.TargetsByExecutionID(ctx, idsForEventType(string(e.Type())))
|
||||
if err != nil {
|
||||
@@ -125,8 +125,8 @@ func (u *eventHandler) reduce(e eventstore.Event) (*handler.Statement, error) {
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(e, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(e.Aggregate())
|
||||
return handler.NewStatement(e, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, e.Aggregate())
|
||||
req, err := NewRequest(e, targets)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -440,7 +440,7 @@ func TestActionProjection_reduces(t *testing.T) {
|
||||
assert.Nil(t, stmt.Execute)
|
||||
return
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.stmtErr != nil {
|
||||
w.stmtErr(t, err)
|
||||
return
|
||||
|
@@ -95,8 +95,8 @@ func (u *backChannelLogoutNotifier) reduceUserSignedOut(event eventstore.Event)
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Gr63h", "reduce.wrong.event.type %s", user.HumanSignedOutType)
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx, err := u.queries.HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx, err := u.queries.HandlerContext(ctx, event.Aggregate())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -116,8 +116,8 @@ func (u *backChannelLogoutNotifier) reduceSessionTerminated(event eventstore.Eve
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-D6H2h", "reduce.wrong.event.type %s", session.TerminateType)
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx, err := u.queries.HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx, err := u.queries.HandlerContext(ctx, event.Aggregate())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
const NotifyUserID = "NOTIFICATION" //TODO: system?
|
||||
|
||||
func HandlerContext(event *eventstore.Aggregate) context.Context {
|
||||
ctx := authz.WithInstanceID(context.Background(), event.InstanceID)
|
||||
func HandlerContext(parent context.Context, event *eventstore.Aggregate) context.Context {
|
||||
ctx := authz.WithInstanceID(parent, event.InstanceID)
|
||||
return authz.SetCtxData(ctx, authz.CtxData{UserID: NotifyUserID, OrgID: event.ResourceOwner})
|
||||
}
|
||||
|
||||
@@ -18,12 +18,11 @@ func ContextWithNotifier(ctx context.Context, aggregate *eventstore.Aggregate) c
|
||||
return authz.WithInstanceID(authz.SetCtxData(ctx, authz.CtxData{UserID: NotifyUserID, OrgID: aggregate.ResourceOwner}), aggregate.InstanceID)
|
||||
}
|
||||
|
||||
func (n *NotificationQueries) HandlerContext(event *eventstore.Aggregate) (context.Context, error) {
|
||||
ctx := context.Background()
|
||||
instance, err := n.InstanceByID(ctx, event.InstanceID)
|
||||
func (n *NotificationQueries) HandlerContext(parent context.Context, event *eventstore.Aggregate) (context.Context, error) {
|
||||
instance, err := n.InstanceByID(parent, event.InstanceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = authz.WithInstance(ctx, instance)
|
||||
ctx := authz.WithInstance(parent, instance)
|
||||
return authz.SetCtxData(ctx, authz.CtxData{UserID: NotifyUserID, OrgID: event.ResourceOwner}), nil
|
||||
}
|
||||
|
@@ -62,8 +62,8 @@ func (u *quotaNotifier) reduceNotificationDue(event eventstore.Event) (*handler.
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-DLxdE", "reduce.wrong.event.type %s", quota.NotificationDueEventType)
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, map[string]interface{}{"dueEventID": e.ID}, quota.NotifiedEventType)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/call"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
@@ -69,12 +68,11 @@ func (t *telemetryPusher) Reducers() []handler.AggregateReducer {
|
||||
}
|
||||
|
||||
func (t *telemetryPusher) pushMilestones(event eventstore.Event) (*handler.Statement, error) {
|
||||
ctx := call.WithTimestamp(context.Background())
|
||||
e, ok := event.(*milestone.ReachedEvent)
|
||||
if !ok {
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-lDTs5", "reduce.wrong.event.type %s", event.Type())
|
||||
}
|
||||
return handler.NewStatement(event, func(handler.Executer, string) error {
|
||||
return handler.NewStatement(event, func(ctx context.Context, _ handler.Executer, _ string) error {
|
||||
// Do not push the milestone again if this was a migration event.
|
||||
if e.ReachedDate != nil {
|
||||
return nil
|
||||
|
@@ -203,8 +203,8 @@ func (u *userNotifier) reduceInitCodeAdded(event eventstore.Event) (*handler.Sta
|
||||
if !ok {
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-EFe2f", "reduce.wrong.event.type %s", user.HumanInitialCodeAddedType)
|
||||
}
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.UserV1InitialCodeAddedType, user.UserV1InitialCodeSentType,
|
||||
user.HumanInitialCodeAddedType, user.HumanInitialCodeSentType)
|
||||
@@ -253,8 +253,8 @@ func (u *userNotifier) reduceEmailCodeAdded(event eventstore.Event) (*handler.St
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.UserV1EmailCodeAddedType, user.UserV1EmailCodeSentType,
|
||||
user.HumanEmailCodeAddedType, user.HumanEmailCodeSentType)
|
||||
@@ -309,8 +309,8 @@ func (u *userNotifier) reducePasswordCodeAdded(event eventstore.Event) (*handler
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.UserV1PasswordCodeAddedType, user.UserV1PasswordCodeSentType,
|
||||
user.HumanPasswordCodeAddedType, user.HumanPasswordCodeSentType)
|
||||
@@ -362,8 +362,8 @@ func (u *userNotifier) reduceOTPSMSCodeAdded(event eventstore.Event) (*handler.S
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-ASF3g", "reduce.wrong.event.type %s", user.HumanOTPSMSCodeAddedType)
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.HumanOTPSMSCodeAddedType,
|
||||
user.HumanOTPSMSCodeSentType)
|
||||
@@ -406,8 +406,8 @@ func (u *userNotifier) reduceSessionOTPSMSChallenged(event eventstore.Event) (*h
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
session.OTPSMSChallengedType,
|
||||
session.OTPSMSSentType)
|
||||
@@ -455,8 +455,8 @@ func (u *userNotifier) reduceOTPEmailCodeAdded(event eventstore.Event) (*handler
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-JL3hw", "reduce.wrong.event.type %s", user.HumanOTPEmailCodeAddedType)
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.HumanOTPEmailCodeAddedType,
|
||||
user.HumanOTPEmailCodeSentType)
|
||||
@@ -507,8 +507,8 @@ func (u *userNotifier) reduceSessionOTPEmailChallenged(event eventstore.Event) (
|
||||
if e.ReturnCode {
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
session.OTPEmailChallengedType,
|
||||
session.OTPEmailSentType)
|
||||
@@ -573,8 +573,8 @@ func (u *userNotifier) reduceDomainClaimed(event eventstore.Event) (*handler.Sta
|
||||
if !ok {
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Drh5w", "reduce.wrong.event.type %s", user.UserDomainClaimedType)
|
||||
}
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil,
|
||||
user.UserDomainClaimedType, user.UserDomainClaimedSentType)
|
||||
if err != nil {
|
||||
@@ -619,8 +619,8 @@ func (u *userNotifier) reducePasswordlessCodeRequested(event eventstore.Event) (
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, map[string]interface{}{"id": e.ID}, user.HumanPasswordlessInitCodeSentType)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -668,8 +668,8 @@ func (u *userNotifier) reducePasswordChanged(event eventstore.Event) (*handler.S
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Yko2z8", "reduce.wrong.event.type %s", user.HumanPasswordChangedType)
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.HumanPasswordChangeSentType)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -720,8 +720,8 @@ func (u *userNotifier) reducePhoneCodeAdded(event eventstore.Event) (*handler.St
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.UserV1PhoneCodeAddedType, user.UserV1PhoneCodeSentType,
|
||||
user.HumanPhoneCodeAddedType, user.HumanPhoneCodeSentType)
|
||||
@@ -768,8 +768,8 @@ func (u *userNotifier) reduceInviteCodeAdded(event eventstore.Event) (*handler.S
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.HumanInviteCodeAddedType, user.HumanInviteCodeSentType)
|
||||
if err != nil {
|
||||
|
@@ -133,8 +133,8 @@ func (u *userNotifierLegacy) reduceInitCodeAdded(event eventstore.Event) (*handl
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-EFe2f", "reduce.wrong.event.type %s", user.HumanInitialCodeAddedType)
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.UserV1InitialCodeAddedType, user.UserV1InitialCodeSentType,
|
||||
user.HumanInitialCodeAddedType, user.HumanInitialCodeSentType)
|
||||
@@ -194,8 +194,8 @@ func (u *userNotifierLegacy) reduceEmailCodeAdded(event eventstore.Event) (*hand
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.UserV1EmailCodeAddedType, user.UserV1EmailCodeSentType,
|
||||
user.HumanEmailCodeAddedType, user.HumanEmailCodeSentType)
|
||||
@@ -254,8 +254,8 @@ func (u *userNotifierLegacy) reducePasswordCodeAdded(event eventstore.Event) (*h
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.UserV1PasswordCodeAddedType, user.UserV1PasswordCodeSentType,
|
||||
user.HumanPasswordCodeAddedType, user.HumanPasswordCodeSentType)
|
||||
@@ -337,7 +337,7 @@ func (u *userNotifierLegacy) reduceSessionOTPSMSChallenged(event eventstore.Even
|
||||
if e.CodeReturned {
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
ctx := HandlerContext(context.Background(), event.Aggregate())
|
||||
s, err := u.queries.SessionByID(ctx, true, e.Aggregate().ID, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -363,7 +363,7 @@ func (u *userNotifierLegacy) reduceOTPSMS(
|
||||
sentCommand func(ctx context.Context, userID, resourceOwner string, generatorInfo *senders.CodeGeneratorInfo) (err error),
|
||||
eventTypes ...eventstore.EventType,
|
||||
) (*handler.Statement, error) {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
ctx := HandlerContext(context.Background(), event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, expiry, nil, eventTypes...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -445,7 +445,7 @@ func (u *userNotifierLegacy) reduceSessionOTPEmailChallenged(event eventstore.Ev
|
||||
if e.ReturnCode {
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
ctx := HandlerContext(context.Background(), event.Aggregate())
|
||||
s, err := u.queries.SessionByID(ctx, true, e.Aggregate().ID, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -484,7 +484,7 @@ func (u *userNotifierLegacy) reduceOTPEmail(
|
||||
sentCommand func(ctx context.Context, userID string, resourceOwner string) (err error),
|
||||
eventTypes ...eventstore.EventType,
|
||||
) (*handler.Statement, error) {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
ctx := HandlerContext(context.Background(), event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, expiry, nil, eventTypes...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -543,8 +543,8 @@ func (u *userNotifierLegacy) reduceDomainClaimed(event eventstore.Event) (*handl
|
||||
if !ok {
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Drh5w", "reduce.wrong.event.type %s", user.UserDomainClaimedType)
|
||||
}
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil,
|
||||
user.UserDomainClaimedType, user.UserDomainClaimedSentType)
|
||||
if err != nil {
|
||||
@@ -598,8 +598,8 @@ func (u *userNotifierLegacy) reducePasswordlessCodeRequested(event eventstore.Ev
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, map[string]interface{}{"id": e.ID}, user.HumanPasswordlessInitCodeSentType)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -653,8 +653,8 @@ func (u *userNotifierLegacy) reducePasswordChanged(event eventstore.Event) (*han
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Yko2z8", "reduce.wrong.event.type %s", user.HumanPasswordChangedType)
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.queries.IsAlreadyHandled(ctx, event, nil, user.HumanPasswordChangeSentType)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -719,8 +719,8 @@ func (u *userNotifierLegacy) reducePhoneCodeAdded(event eventstore.Event) (*hand
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.UserV1PhoneCodeAddedType, user.UserV1PhoneCodeSentType,
|
||||
user.HumanPhoneCodeAddedType, user.HumanPhoneCodeSentType)
|
||||
@@ -777,8 +777,8 @@ func (u *userNotifierLegacy) reduceInviteCodeAdded(event eventstore.Event) (*han
|
||||
return handler.NewNoOpStatement(e), nil
|
||||
}
|
||||
|
||||
return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error {
|
||||
ctx := HandlerContext(event.Aggregate())
|
||||
return handler.NewStatement(event, func(ctx context.Context, ex handler.Executer, projectionName string) error {
|
||||
ctx = HandlerContext(ctx, event.Aggregate())
|
||||
alreadyHandled, err := u.checkIfCodeAlreadyHandledOrExpired(ctx, event, e.Expiry, nil,
|
||||
user.HumanInviteCodeAddedType, user.HumanInviteCodeSentType)
|
||||
if err != nil {
|
||||
|
@@ -283,7 +283,7 @@ func Test_userNotifierLegacy_reduceInitCodeAdded(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -596,7 +596,7 @@ func Test_userNotifierLegacy_reduceEmailCodeAdded(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -949,7 +949,7 @@ func Test_userNotifierLegacy_reducePasswordCodeAdded(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1080,7 +1080,7 @@ func Test_userNotifierLegacy_reduceDomainClaimed(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1355,7 +1355,7 @@ func Test_userNotifierLegacy_reducePasswordlessCodeRequested(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1495,7 +1495,7 @@ func Test_userNotifierLegacy_reducePasswordChanged(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
|
@@ -188,7 +188,7 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -366,7 +366,7 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
|
||||
assert.Nil(t, stmt.Execute)
|
||||
return
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -601,7 +601,7 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) {
|
||||
assert.Nil(t, stmt.Execute)
|
||||
return
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -731,7 +731,7 @@ func Test_userNotifier_reduceDomainClaimed(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -906,7 +906,7 @@ func Test_userNotifier_reducePasswordlessCodeRequested(t *testing.T) {
|
||||
assert.Nil(t, stmt.Execute)
|
||||
return
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1066,7 +1066,7 @@ func Test_userNotifier_reducePasswordChanged(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1329,7 +1329,7 @@ func Test_userNotifier_reduceOTPEmailChallenged(t *testing.T) {
|
||||
assert.Nil(t, stmt.Execute)
|
||||
return
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1590,7 +1590,7 @@ func Test_userNotifier_reduceOTPSMSChallenged(t *testing.T) {
|
||||
assert.Nil(t, stmt.Execute)
|
||||
return
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1886,7 +1886,7 @@ func Test_userNotifier_reduceInviteCodeAdded(t *testing.T) {
|
||||
assert.Nil(t, stmt.Execute)
|
||||
return
|
||||
}
|
||||
err = stmt.Execute(nil, "")
|
||||
err = stmt.Execute(t.Context(), nil, "")
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
|
@@ -99,7 +99,7 @@ func assertReduce(t *testing.T, stmt *handler.Statement, err error, projection s
|
||||
want.executer.Validate(t)
|
||||
return
|
||||
}
|
||||
err = stmt.Execute(want.executer, projection)
|
||||
err = stmt.Execute(t.Context(), want.executer, projection)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
@@ -1,45 +1,6 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database/dialect"
|
||||
)
|
||||
|
||||
const (
|
||||
schema = "queue"
|
||||
applicationName = "zitadel_queue"
|
||||
)
|
||||
|
||||
var conns = &sync.Map{}
|
||||
|
||||
type queueKey struct{}
|
||||
|
||||
func WithQueue(parent context.Context) context.Context {
|
||||
return context.WithValue(parent, queueKey{}, struct{}{})
|
||||
}
|
||||
|
||||
func init() {
|
||||
dialect.RegisterBeforeAcquire(func(ctx context.Context, c *pgx.Conn) error {
|
||||
if _, ok := ctx.Value(queueKey{}).(struct{}); !ok {
|
||||
return nil
|
||||
}
|
||||
_, err := c.Exec(ctx, "SET search_path TO "+schema+"; SET application_name TO "+applicationName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conns.Store(c, struct{}{})
|
||||
return nil
|
||||
})
|
||||
dialect.RegisterAfterRelease(func(c *pgx.Conn) error {
|
||||
_, ok := conns.LoadAndDelete(c)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
_, err := c.Exec(context.Background(), "SET search_path TO DEFAULT; SET application_name TO "+dialect.DefaultAppName)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
@@ -27,11 +27,10 @@ func (m *Migrator) Execute(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
migrator, err := rivermigrate.New(m.driver, nil)
|
||||
migrator, err := rivermigrate.New(m.driver, &rivermigrate.Config{Schema: schema})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = WithQueue(ctx)
|
||||
_, err = migrator.Migrate(ctx, rivermigrate.DirectionUp, nil)
|
||||
return err
|
||||
|
||||
|
@@ -41,6 +41,7 @@ func NewQueue(config *Config) (_ *Queue, err error) {
|
||||
Queues: make(map[string]river.QueueConfig),
|
||||
JobTimeout: -1,
|
||||
Middleware: middleware,
|
||||
Schema: schema,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -56,7 +57,6 @@ func (q *Queue) Start(ctx context.Context) (err error) {
|
||||
if q == nil || !q.shouldStart {
|
||||
return nil
|
||||
}
|
||||
ctx = WithQueue(ctx)
|
||||
|
||||
q.client, err = river.NewClient(q.driver, q.config)
|
||||
if err != nil {
|
||||
@@ -112,7 +112,6 @@ func WithQueueName(name string) InsertOpt {
|
||||
|
||||
func (q *Queue) Insert(ctx context.Context, args river.JobArgs, opts ...InsertOpt) error {
|
||||
options := new(river.InsertOpts)
|
||||
ctx = WithQueue(ctx)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@
|
||||
"scripts": {
|
||||
"bundle": "webpack",
|
||||
"lint": "prettier --check src/**",
|
||||
"lint:fix": "prettier --write src"
|
||||
"lint:fix": "prettier --write src",
|
||||
"clean": "rm -rf dist .turbo node_modules"
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,9 @@
|
||||
"devcontainer:integration:login:build": "npm run devcontainer:integration:login:build:env && npm run devcontainer:integration:login:build:services",
|
||||
"devcontainer:integration:login:build:env": "devcontainer build --config .devcontainer/login-integration/devcontainer.json --workspace-folder .",
|
||||
"devcontainer:integration:login:build:services": "COMPOSE_BAKE=1 docker compose --file .devcontainer/base/docker-compose.yaml --file .devcontainer/login-integration/docker-compose.yaml build login-integration",
|
||||
"devcontainer:integration:login:up": "devcontainer up --prebuild --config .devcontainer/login-integration/devcontainer.json --workspace-folder . ----remove-existing-container"
|
||||
"devcontainer:integration:login:up": "devcontainer up --prebuild --config .devcontainer/login-integration/devcontainer.json --workspace-folder . --remove-existing-container",
|
||||
"clean": "turbo run clean",
|
||||
"clean:all": "pnpm run clean && rm -rf .turbo node_modules"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"packageManager": "pnpm@10.13.1",
|
||||
"name": "@zitadel/client",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.1",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -78,7 +78,7 @@
|
||||
"@connectrpc/connect": "^2.0.0",
|
||||
"@connectrpc/connect-node": "^2.0.0",
|
||||
"@connectrpc/connect-web": "^2.0.0",
|
||||
"@zitadel/proto": "latest",
|
||||
"@zitadel/proto": "workspace:*",
|
||||
"jose": "^5.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
6
packages/zitadel-proto/.npmignore
Normal file
6
packages/zitadel-proto/.npmignore
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
.turbo
|
||||
*.log
|
||||
.DS_Store
|
||||
buf.gen.yaml
|
||||
turbo.json
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"packageManager": "pnpm@10.13.1",
|
||||
"name": "@zitadel/proto",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.1",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -9,6 +9,15 @@
|
||||
"type": "module",
|
||||
"main": "./cjs/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
"cjs/**",
|
||||
"es/**",
|
||||
"types/**",
|
||||
"zitadel/**",
|
||||
"google/**",
|
||||
"validate/**",
|
||||
"protoc-gen-openapiv2/**"
|
||||
],
|
||||
"exports": {
|
||||
"./zitadel/*": {
|
||||
"types": "./types/zitadel/*.d.ts",
|
||||
|
Reference in New Issue
Block a user