mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-03 23:05:14 +00:00
Merge branch 'refs/heads/main' into next-rc
This commit is contained in:
commit
dfd7dde86c
@ -25,6 +25,18 @@ Tracing:
|
||||
# The endpoint of the otel collector endpoint
|
||||
Endpoint: "" #ZITADEL_TRACING_ENDPOINT
|
||||
|
||||
# Profiler enables capturing profiling data (CPU, Memory, ...) for performance analysis
|
||||
Profiler:
|
||||
# Choose one of "google" and "none"
|
||||
# Depending on the type there are different configuration options
|
||||
# for type 'google'
|
||||
# ProjectID: google-project-id
|
||||
#
|
||||
# type 'none' or '' disables profiling
|
||||
Type: none # ZITADEL_PROFILER_TYPE
|
||||
# projectID for google
|
||||
ProjectID: '' # ZITADEL_PROFILER_PROJECTID
|
||||
|
||||
Telemetry:
|
||||
# As long as Enabled is true, ZITADEL tries to send usage data to the configured Telemetry.Endpoints.
|
||||
# Data is projected by ZITADEL even if Enabled is false.
|
||||
|
@ -31,6 +31,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
static_config "github.com/zitadel/zitadel/internal/static/config"
|
||||
metrics "github.com/zitadel/zitadel/internal/telemetry/metrics/config"
|
||||
profiler "github.com/zitadel/zitadel/internal/telemetry/profiler/config"
|
||||
tracing "github.com/zitadel/zitadel/internal/telemetry/tracing/config"
|
||||
)
|
||||
|
||||
@ -49,6 +50,7 @@ type Config struct {
|
||||
Database database.Config
|
||||
Tracing tracing.Config
|
||||
Metrics metrics.Config
|
||||
Profiler profiler.Config
|
||||
Projections projection.Config
|
||||
Auth auth_es.Config
|
||||
Admin admin_es.Config
|
||||
@ -114,6 +116,9 @@ func MustNewConfig(v *viper.Viper) *Config {
|
||||
err = config.Metrics.NewMeter()
|
||||
logging.OnError(err).Fatal("unable to set meter")
|
||||
|
||||
err = config.Profiler.NewProfiler()
|
||||
logging.OnError(err).Fatal("unable to set profiler")
|
||||
|
||||
id.Configure(config.Machine)
|
||||
actions.SetHTTPConfig(&config.Actions.HTTP)
|
||||
|
||||
|
@ -57,11 +57,14 @@
|
||||
</cnsl-action-keys>
|
||||
</div>
|
||||
</button>
|
||||
<cnsl-filter-user
|
||||
*ngIf="!selection.hasValue()"
|
||||
(filterChanged)="applySearchQuery($any($event))"
|
||||
(filterOpen)="filterOpen = $event"
|
||||
></cnsl-filter-user>
|
||||
</ng-template>
|
||||
<cnsl-filter-user
|
||||
actions
|
||||
*ngIf="!selection.hasValue()"
|
||||
(filterChanged)="applySearchQuery($any($event))"
|
||||
(filterOpen)="filterOpen = $event"
|
||||
></cnsl-filter-user>
|
||||
<ng-template cnslHasRole [hasRole]="['user.write']" actions>
|
||||
<button
|
||||
(click)="gotoRouterLink(['/users', type === Type.TYPE_HUMAN ? 'create' : 'create-machine'])"
|
||||
color="primary"
|
||||
|
@ -169,4 +169,19 @@ For event there are 3 levels the condition can be defined:
|
||||
- Group, handling a specific group of events
|
||||
- All, handling any event in ZITADEL
|
||||
|
||||
The concept of events can be found under [Events](/concepts/architecture/software#events)
|
||||
The concept of events can be found under [Events](/concepts/architecture/software#events)
|
||||
|
||||
### Error forwarding
|
||||
|
||||
If you want to forward a specific error from the Target through ZITADEL, you can provide a response from the Target with status code 200 and a JSON in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"forwardedStatusCode": 403,
|
||||
"forwardedErrorMessage": "Call is forbidden through the IP AllowList definition"
|
||||
}
|
||||
```
|
||||
|
||||
Only values from 400 to 499 will be forwarded through ZITADEL, other StatusCodes will end in a PreconditionFailed error.
|
||||
|
||||
If the Target returns any other status code than >= 200 and < 299, the execution is looked at as failed, and a PreconditionFailed error is logged.
|
||||
|
@ -761,8 +761,8 @@ module.exports = {
|
||||
label: "Web key Lifecycle (Preview)",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
title: "Action Service API (Preview)",
|
||||
slug: "/apis/resources/action_service_v3",
|
||||
title: "Web Key Service API (Preview)",
|
||||
slug: "/apis/resources/webkey_service_v3",
|
||||
description:
|
||||
"This API is intended to manage web keys for a ZITADEL instance, used to sign and validate OIDC tokens.\n" +
|
||||
"\n" +
|
||||
|
3
go.mod
3
go.mod
@ -3,6 +3,7 @@ module github.com/zitadel/zitadel
|
||||
go 1.22.2
|
||||
|
||||
require (
|
||||
cloud.google.com/go/profiler v0.4.1
|
||||
cloud.google.com/go/storage v1.43.0
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
@ -103,7 +104,7 @@ require (
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/go-tpm v0.9.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
|
||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
|
6
go.sum
6
go.sum
@ -16,6 +16,8 @@ cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuA
|
||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
||||
cloud.google.com/go/monitoring v1.19.0 h1:NCXf8hfQi+Kmr56QJezXRZ6GPb80ZI7El1XztyUuLQI=
|
||||
cloud.google.com/go/monitoring v1.19.0/go.mod h1:25IeMR5cQ5BoZ8j1eogHE5VPJLlReQ7zFp5OiLgiGZw=
|
||||
cloud.google.com/go/profiler v0.4.1 h1:Q7+lOvikTGMJ/IAWocpYYGit4SIIoILmVZfEEWTORSY=
|
||||
cloud.google.com/go/profiler v0.4.1/go.mod h1:LBrtEX6nbvhv1w/e5CPZmX9ajGG9BGLtGbv56Tg4SHs=
|
||||
cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
|
||||
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
|
||||
cloud.google.com/go/trace v1.10.7 h1:gK8z2BIJQ3KIYGddw9RJLne5Fx0FEXkrEQzPaeEYVvk=
|
||||
@ -313,8 +315,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
|
||||
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
|
||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
|
@ -263,7 +263,7 @@ func TestServer_RemovePhone(t *testing.T) {
|
||||
req *user.RemovePhoneRequest
|
||||
want *user.RemovePhoneResponse
|
||||
wantErr bool
|
||||
dep func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error)
|
||||
dep func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error)
|
||||
}{
|
||||
{
|
||||
name: "remove phone",
|
||||
@ -303,7 +303,7 @@ func TestServer_RemovePhone(t *testing.T) {
|
||||
dep: func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error) {
|
||||
return Client.RemovePhone(ctx, &user.RemovePhoneRequest{
|
||||
UserId: doubleRemoveUser.GetUserId(),
|
||||
});
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -45,3 +45,36 @@ func ZitadelErrorToHTTPStatusCode(err error) (statusCode int, ok bool) {
|
||||
return http.StatusInternalServerError, false
|
||||
}
|
||||
}
|
||||
|
||||
func HTTPStatusCodeToZitadelError(parent error, statusCode int, id string, message string) error {
|
||||
if statusCode == http.StatusOK {
|
||||
return nil
|
||||
}
|
||||
var errorFunc func(parent error, id, message string) error
|
||||
switch statusCode {
|
||||
case http.StatusConflict:
|
||||
errorFunc = zerrors.ThrowAlreadyExists
|
||||
case http.StatusGatewayTimeout:
|
||||
errorFunc = zerrors.ThrowDeadlineExceeded
|
||||
case http.StatusInternalServerError:
|
||||
errorFunc = zerrors.ThrowInternal
|
||||
case http.StatusBadRequest:
|
||||
errorFunc = zerrors.ThrowInvalidArgument
|
||||
case http.StatusNotFound:
|
||||
errorFunc = zerrors.ThrowNotFound
|
||||
case http.StatusForbidden:
|
||||
errorFunc = zerrors.ThrowPermissionDenied
|
||||
case http.StatusUnauthorized:
|
||||
errorFunc = zerrors.ThrowUnauthenticated
|
||||
case http.StatusServiceUnavailable:
|
||||
errorFunc = zerrors.ThrowUnavailable
|
||||
case http.StatusNotImplemented:
|
||||
errorFunc = zerrors.ThrowUnimplemented
|
||||
case http.StatusTooManyRequests:
|
||||
errorFunc = zerrors.ThrowResourceExhausted
|
||||
default:
|
||||
errorFunc = zerrors.ThrowUnknown
|
||||
}
|
||||
|
||||
return errorFunc(parent, id, message)
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
@ -136,3 +138,152 @@ func TestZitadelErrorToHTTPStatusCode(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPStatusCodeToZitadelError(t *testing.T) {
|
||||
type args struct {
|
||||
statusCode int
|
||||
id string
|
||||
message string
|
||||
parent error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "StatusOK",
|
||||
args: args{
|
||||
statusCode: http.StatusOK,
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "StatusConflict",
|
||||
args: args{
|
||||
statusCode: http.StatusConflict,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowAlreadyExists(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusGatewayTimeout",
|
||||
args: args{
|
||||
statusCode: http.StatusGatewayTimeout,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowDeadlineExceeded(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusInternalServerError",
|
||||
args: args{
|
||||
statusCode: http.StatusInternalServerError,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowInternal(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusBadRequest",
|
||||
args: args{
|
||||
statusCode: http.StatusBadRequest,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowInvalidArgument(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusNotFound",
|
||||
args: args{
|
||||
statusCode: http.StatusNotFound,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowNotFound(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusForbidden",
|
||||
args: args{
|
||||
statusCode: http.StatusForbidden,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusUnauthorized",
|
||||
args: args{
|
||||
statusCode: http.StatusUnauthorized,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowUnauthenticated(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusServiceUnavailable",
|
||||
args: args{
|
||||
statusCode: http.StatusServiceUnavailable,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowUnavailable(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusNotImplemented",
|
||||
args: args{
|
||||
statusCode: http.StatusNotImplemented,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowUnimplemented(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "StatusTooManyRequests",
|
||||
args: args{
|
||||
statusCode: http.StatusTooManyRequests,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowResourceExhausted(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
args: args{
|
||||
statusCode: 1000,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowUnknown(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "Unknown, test for statuscode",
|
||||
args: args{
|
||||
statusCode: 1000,
|
||||
id: "id",
|
||||
message: "message",
|
||||
},
|
||||
wantErr: zerrors.ThrowError(nil, "id", "message"),
|
||||
},
|
||||
{
|
||||
name: "Unknown with parent",
|
||||
args: args{
|
||||
statusCode: 1000,
|
||||
id: "id",
|
||||
message: "message",
|
||||
parent: errors.New("parent error"),
|
||||
},
|
||||
wantErr: zerrors.ThrowUnknown(nil, "id", "message"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := HTTPStatusCodeToZitadelError(tt.args.parent, tt.args.statusCode, tt.args.id, tt.args.message)
|
||||
assert.ErrorIs(t, err, tt.wantErr)
|
||||
if tt.args.parent != nil {
|
||||
assert.ErrorIs(t, err, tt.args.parent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,14 @@ package execution
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
zhttp "github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@ -114,9 +116,35 @@ func Call(ctx context.Context, url string, timeout time.Duration, body []byte) (
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return HandleResponse(resp)
|
||||
}
|
||||
|
||||
func HandleResponse(resp *http.Response) ([]byte, error) {
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Check for success between 200 and 299, redirect 300 to 399 is handled by the client, return error with statusCode >= 400
|
||||
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
|
||||
return io.ReadAll(resp.Body)
|
||||
var errorBody ErrorBody
|
||||
if err := json.Unmarshal(data, &errorBody); err != nil {
|
||||
// if json unmarshal fails, body has no ErrorBody information, so will be taken as successful response
|
||||
return data, nil
|
||||
}
|
||||
if errorBody.ForwardedStatusCode != 0 || errorBody.ForwardedErrorMessage != "" {
|
||||
if errorBody.ForwardedStatusCode >= 400 && errorBody.ForwardedStatusCode < 500 {
|
||||
return nil, zhttp.HTTPStatusCodeToZitadelError(nil, errorBody.ForwardedStatusCode, "EXEC-reUaUZCzCp", errorBody.ForwardedErrorMessage)
|
||||
}
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "EXEC-bmhNhpcqpF", errorBody.ForwardedErrorMessage)
|
||||
}
|
||||
// no ErrorBody filled in response, so will be taken as successful response
|
||||
return data, nil
|
||||
}
|
||||
return nil, zerrors.ThrowUnknown(nil, "EXEC-dra6yamk98", "Errors.Execution.Failed")
|
||||
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "EXEC-dra6yamk98", "Errors.Execution.Failed")
|
||||
}
|
||||
|
||||
type ErrorBody struct {
|
||||
ForwardedStatusCode int `json:"forwardedStatusCode,omitempty"`
|
||||
ForwardedErrorMessage string `json:"forwardedErrorMessage,omitempty"`
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package execution_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -15,6 +18,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server/middleware"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/execution"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func Test_Call(t *testing.T) {
|
||||
@ -513,3 +517,152 @@ var requestContextInfoBody2 = []byte("{\"request\":{\"request\":\"content2\"}}")
|
||||
type request struct {
|
||||
Request string `json:"request"`
|
||||
}
|
||||
|
||||
func testErrorBody(code int, message string) []byte {
|
||||
body := &execution.ErrorBody{ForwardedStatusCode: code, ForwardedErrorMessage: message}
|
||||
data, _ := json.Marshal(body)
|
||||
return data
|
||||
}
|
||||
|
||||
func Test_handleResponse(t *testing.T) {
|
||||
type args struct {
|
||||
resp *http.Response
|
||||
}
|
||||
type res struct {
|
||||
data []byte
|
||||
wantErr func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"response, statuscode unknown and body",
|
||||
args{
|
||||
resp: &http.Response{
|
||||
StatusCode: 1000,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(""))),
|
||||
},
|
||||
},
|
||||
res{
|
||||
wantErr: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "EXEC-dra6yamk98", "Errors.Execution.Failed"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"response, statuscode >= 400 and no body",
|
||||
args{
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusForbidden,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(""))),
|
||||
},
|
||||
},
|
||||
res{
|
||||
wantErr: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "EXEC-dra6yamk98", "Errors.Execution.Failed"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"response, statuscode >= 400 and body",
|
||||
args{
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusForbidden,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte("body"))),
|
||||
},
|
||||
},
|
||||
res{
|
||||
wantErr: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "EXEC-dra6yamk98", "Errors.Execution.Failed"))
|
||||
}},
|
||||
},
|
||||
{
|
||||
"response, statuscode = 200 and body",
|
||||
args{
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte("body"))),
|
||||
},
|
||||
},
|
||||
res{
|
||||
data: []byte("body"),
|
||||
wantErr: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
"response, statuscode = 200 no body",
|
||||
args{
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(""))),
|
||||
},
|
||||
},
|
||||
res{
|
||||
data: []byte(""),
|
||||
wantErr: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
"response, statuscode = 200, error body >= 400 < 500",
|
||||
args{
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader(testErrorBody(http.StatusForbidden, "forbidden"))),
|
||||
},
|
||||
},
|
||||
res{
|
||||
wantErr: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowPermissionDenied(nil, "EXEC-reUaUZCzCp", "forbidden"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"response, statuscode = 200, error body >= 500",
|
||||
args{
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader(testErrorBody(http.StatusInternalServerError, "internal"))),
|
||||
},
|
||||
},
|
||||
res{
|
||||
wantErr: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "EXEC-bmhNhpcqpF", "internal"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"response, statuscode = 308, no body, should not happen",
|
||||
args{
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader(testErrorBody(http.StatusPermanentRedirect, "redirect"))),
|
||||
},
|
||||
},
|
||||
res{
|
||||
wantErr: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "EXEC-bmhNhpcqpF", "redirect"))
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
respBody, err := execution.HandleResponse(
|
||||
tt.args.resp,
|
||||
)
|
||||
|
||||
if tt.res.wantErr == nil {
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else if !tt.res.wantErr(err) {
|
||||
t.Errorf("got wrong err: %v", err)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.res.data, respBody)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
WebKeyTable = "projections.web_keys"
|
||||
WebKeyTable = "projections.web_keys1"
|
||||
|
||||
WebKeyInstanceIDCol = "instance_id"
|
||||
WebKeyKeyIDCol = "key_id"
|
||||
@ -58,9 +58,6 @@ func (*webKeyProjection) Init() *old_handler.Check {
|
||||
handler.WithIndex(handler.NewIndex(
|
||||
"web_key_state",
|
||||
[]string{WebKeyInstanceIDCol, WebKeyStateCol},
|
||||
handler.WithInclude(
|
||||
WebKeyPrivateKeyCol,
|
||||
),
|
||||
)),
|
||||
),
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
select private_key
|
||||
from projections.web_keys
|
||||
from projections.web_keys1
|
||||
where instance_id = $1
|
||||
and state = $2
|
||||
limit 1;
|
||||
|
@ -1,4 +1,4 @@
|
||||
select key_id, creation_date, change_date, sequence, state, config, config_type
|
||||
from projections.web_keys
|
||||
from projections.web_keys1
|
||||
where instance_id = $1
|
||||
order by creation_date asc;
|
||||
|
@ -1,3 +1,3 @@
|
||||
select public_key
|
||||
from projections.web_keys
|
||||
from projections.web_keys1
|
||||
where instance_id = $1;
|
||||
|
@ -582,6 +582,7 @@ Errors:
|
||||
NotFound: Изпълнението не е намерено
|
||||
IncludeNotFound: Включването не е намерено
|
||||
NoTargets: Няма определени цели
|
||||
Failed: неуспешно изпълнение
|
||||
ResponseIsNotValidJSON: Отговорът не е валиден JSON
|
||||
UserSchema:
|
||||
NotEnabled: Функцията „Потребителска схема“ не е активирана
|
||||
|
@ -563,6 +563,7 @@ Errors:
|
||||
NotFound: Provedení nenalezeno
|
||||
IncludeNotFound: Zahrnout nenalezeno
|
||||
NoTargets: Nejsou definovány žádné cíle
|
||||
Failed: Provedení se nezdařilo
|
||||
ResponseIsNotValidJSON: Odpověď není platný JSON
|
||||
UserSchema:
|
||||
NotEnabled: Funkce "Uživatelské schéma" není povolena
|
||||
|
@ -565,6 +565,7 @@ Errors:
|
||||
NotFound: Ausführung nicht gefunden
|
||||
IncludeNotFound: Einschließen nicht gefunden
|
||||
NoTargets: Keine Ziele definiert
|
||||
Failed: Ausführung fehlgeschlagen
|
||||
ResponseIsNotValidJSON: Antwort ist kein gültiges JSON
|
||||
UserSchema:
|
||||
NotEnabled: Funktion Benutzerschema ist nicht aktiviert
|
||||
|
@ -565,6 +565,7 @@ Errors:
|
||||
NotFound: Execution not found
|
||||
IncludeNotFound: Include not found
|
||||
NoTargets: No targets defined
|
||||
Failed: Execution failed
|
||||
ResponseIsNotValidJSON: Response is not valid JSON
|
||||
UserSchema:
|
||||
NotEnabled: Feature "User Schema" is not enabled
|
||||
@ -595,7 +596,6 @@ Errors:
|
||||
NoActive: No active web key found
|
||||
NotFound: Web key not found
|
||||
|
||||
|
||||
AggregateTypes:
|
||||
action: Action
|
||||
instance: Instance
|
||||
|
@ -565,6 +565,7 @@ Errors:
|
||||
NotFound: Ejecución no encontrada
|
||||
IncludeNotFound: Incluir no encontrado
|
||||
NoTargets: No hay objetivos definidos
|
||||
Failed: Ejecución fallida
|
||||
ResponseIsNotValidJSON: La respuesta no es un JSON válido
|
||||
UserSchema:
|
||||
NotEnabled: La función "Esquema de usuario" no está habilitada
|
||||
|
@ -565,6 +565,7 @@ Errors:
|
||||
NotFound: Exécution introuvable
|
||||
IncludeNotFound: Inclure introuvable
|
||||
NoTargets: Aucune cible définie
|
||||
Failed: Exécution échouée
|
||||
ResponseIsNotValidJSON: La réponse n'est pas un JSON valide
|
||||
UserSchema:
|
||||
NotEnabled: La fonctionnalité "Schéma utilisateur" n'est pas activée
|
||||
|
@ -565,6 +565,7 @@ Errors:
|
||||
NotFound: Esecuzione non trovata
|
||||
IncludeNotFound: Includi non trovato
|
||||
NoTargets: Nessun obiettivo definito
|
||||
Failed: Esecuzione fallita
|
||||
ResponseIsNotValidJSON: La risposta non è un JSON valido
|
||||
UserSchema:
|
||||
NotEnabled: La funzionalità "Schema utente" non è abilitata
|
||||
|
@ -554,6 +554,7 @@ Errors:
|
||||
NotFound: 実行が見つかりませんでした
|
||||
IncludeNotFound: 見つからないものを含める
|
||||
NoTargets: ターゲットが定義されていません
|
||||
Failed: 実行に失敗しました
|
||||
ResponseIsNotValidJSON: 応答は有効な JSON ではありません
|
||||
UserSchema:
|
||||
NotEnabled: 機能「ユーザースキーマ」が有効になっていません
|
||||
|
@ -564,6 +564,7 @@ Errors:
|
||||
NotFound: Извршувањето не е пронајдено
|
||||
IncludeNotFound: Вклучете не е пронајден
|
||||
NoTargets: Не се дефинирани цели
|
||||
Failed: Извршувањето не успеа
|
||||
ResponseIsNotValidJSON: Одговорот не е валиден JSON
|
||||
UserSchema:
|
||||
NotEnabled: Функцијата „Корисничка шема“ не е овозможена
|
||||
|
@ -565,6 +565,7 @@ Errors:
|
||||
NotFound: Uitvoering niet gevonden
|
||||
IncludeNotFound: Inclusief niet gevonden
|
||||
NoTargets: Geen doelstellingen gedefinieerd
|
||||
Failed: Uitvoering mislukt
|
||||
ResponseIsNotValidJSON: Reactie is geen geldige JSON
|
||||
UserSchema:
|
||||
NotEnabled: Functie "Gebruikersschema" is niet ingeschakeld
|
||||
|
@ -565,6 +565,7 @@ Errors:
|
||||
NotFound: Nie znaleziono wykonania
|
||||
IncludeNotFound: Nie znaleziono uwzględnienia
|
||||
NoTargets: Nie zdefiniowano celów
|
||||
Failed: Wykonanie nie powiodło się
|
||||
ResponseIsNotValidJSON: Odpowiedź nie jest prawidłowym JSON-em
|
||||
UserSchema:
|
||||
NotEnabled: Funkcja „Schemat użytkownika” nie jest włączona
|
||||
|
@ -560,6 +560,7 @@ Errors:
|
||||
NotFound: Execução não encontrada
|
||||
IncludeNotFound: Incluir não encontrado
|
||||
NoTargets: Nenhuma meta definida
|
||||
Failed: Falha na execução
|
||||
ResponseIsNotValidJSON: A resposta não é um JSON válido
|
||||
UserSchema:
|
||||
NotEnabled: O recurso "Esquema do usuário" não está habilitado
|
||||
|
@ -554,6 +554,7 @@ Errors:
|
||||
NotFound: Исполнение не найдено
|
||||
IncludeNotFound: Включить не найдено
|
||||
NoTargets: Цели не определены
|
||||
Failed: Выполнение не удалось
|
||||
ResponseIsNotValidJSON: Ответ не является допустимым JSON
|
||||
UserSchema:
|
||||
NotEnabled: Функция «Пользовательская схема» не включена
|
||||
|
@ -564,6 +564,7 @@ Errors:
|
||||
NotFound: Exekveringen hittades inte
|
||||
IncludeNotFound: Inkluderingen hittades inte
|
||||
NoTargets: Inga mål definierade
|
||||
Failed: Utförande misslyckades
|
||||
ResponseIsNotValidJSON: Svaret är inte giltigt JSON
|
||||
UserSchema:
|
||||
NotEnabled: Funktionen "Användarschema" är inte aktiverad
|
||||
|
@ -565,6 +565,7 @@ Errors:
|
||||
NotFound: 未找到执行
|
||||
IncludeNotFound: 包括未找到的内容
|
||||
NoTargets: 没有定义目标
|
||||
Failed: 执行失败
|
||||
ResponseIsNotValidJSON: 响应不是有效的 JSON
|
||||
UserSchema:
|
||||
NotEnabled: 未启用“用户架构”功能
|
||||
|
30
internal/telemetry/profiler/config/config.go
Normal file
30
internal/telemetry/profiler/config/config.go
Normal file
@ -0,0 +1,30 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/telemetry/profiler/google"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Type string
|
||||
Config map[string]interface{} `mapstructure:",remain"`
|
||||
}
|
||||
|
||||
var profiler = map[string]func(map[string]interface{}) error{
|
||||
"google": google.NewProfiler,
|
||||
"none": NoProfiler,
|
||||
"": NoProfiler,
|
||||
}
|
||||
|
||||
func (c *Config) NewProfiler() error {
|
||||
t, ok := profiler[c.Type]
|
||||
if !ok {
|
||||
return zerrors.ThrowInternalf(nil, "PROFI-Dfqsx", "config type %s not supported", c.Type)
|
||||
}
|
||||
|
||||
return t(c.Config)
|
||||
}
|
||||
|
||||
func NoProfiler(_ map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
26
internal/telemetry/profiler/google/profiler.go
Normal file
26
internal/telemetry/profiler/google/profiler.go
Normal file
@ -0,0 +1,26 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"cloud.google.com/go/profiler"
|
||||
|
||||
"github.com/zitadel/zitadel/cmd/build"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ProjectID string
|
||||
}
|
||||
|
||||
func NewProfiler(rawConfig map[string]interface{}) (err error) {
|
||||
c := new(Config)
|
||||
c.ProjectID, _ = rawConfig["projectid"].(string)
|
||||
return c.NewProfiler()
|
||||
}
|
||||
|
||||
func (c *Config) NewProfiler() (err error) {
|
||||
cfg := profiler.Config{
|
||||
Service: "zitadel",
|
||||
ServiceVersion: build.Version(),
|
||||
ProjectID: c.ProjectID,
|
||||
}
|
||||
return profiler.Start(cfg)
|
||||
}
|
@ -172,8 +172,8 @@ service ZITADELWebKeys {
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
summary: "Generate a web key pair for the instance";
|
||||
description: "Delete a web key. Only inactive keys can be deleted. Once a key is deleted, any tokens signed by this key will be invalid."
|
||||
summary: "Delete a web key pair for the instance";
|
||||
description: "Delete a web key pair. Only inactive keys can be deleted. Once a key is deleted, any tokens signed by this key will be invalid."
|
||||
responses: {
|
||||
key: "200"
|
||||
value: {
|
||||
@ -198,7 +198,7 @@ service ZITADELWebKeys {
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
summary: "Generate a web key pair for the instance";
|
||||
summary: "List web key details for the instance";
|
||||
description: "List web key details for the instance"
|
||||
responses: {
|
||||
key: "200"
|
||||
|
Loading…
x
Reference in New Issue
Block a user