mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-04 07:55:23 +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
|
# The endpoint of the otel collector endpoint
|
||||||
Endpoint: "" #ZITADEL_TRACING_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:
|
Telemetry:
|
||||||
# As long as Enabled is true, ZITADEL tries to send usage data to the configured Telemetry.Endpoints.
|
# 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.
|
# Data is projected by ZITADEL even if Enabled is false.
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/query/projection"
|
"github.com/zitadel/zitadel/internal/query/projection"
|
||||||
static_config "github.com/zitadel/zitadel/internal/static/config"
|
static_config "github.com/zitadel/zitadel/internal/static/config"
|
||||||
metrics "github.com/zitadel/zitadel/internal/telemetry/metrics/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"
|
tracing "github.com/zitadel/zitadel/internal/telemetry/tracing/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ type Config struct {
|
|||||||
Database database.Config
|
Database database.Config
|
||||||
Tracing tracing.Config
|
Tracing tracing.Config
|
||||||
Metrics metrics.Config
|
Metrics metrics.Config
|
||||||
|
Profiler profiler.Config
|
||||||
Projections projection.Config
|
Projections projection.Config
|
||||||
Auth auth_es.Config
|
Auth auth_es.Config
|
||||||
Admin admin_es.Config
|
Admin admin_es.Config
|
||||||
@ -114,6 +116,9 @@ func MustNewConfig(v *viper.Viper) *Config {
|
|||||||
err = config.Metrics.NewMeter()
|
err = config.Metrics.NewMeter()
|
||||||
logging.OnError(err).Fatal("unable to set meter")
|
logging.OnError(err).Fatal("unable to set meter")
|
||||||
|
|
||||||
|
err = config.Profiler.NewProfiler()
|
||||||
|
logging.OnError(err).Fatal("unable to set profiler")
|
||||||
|
|
||||||
id.Configure(config.Machine)
|
id.Configure(config.Machine)
|
||||||
actions.SetHTTPConfig(&config.Actions.HTTP)
|
actions.SetHTTPConfig(&config.Actions.HTTP)
|
||||||
|
|
||||||
|
@ -57,11 +57,14 @@
|
|||||||
</cnsl-action-keys>
|
</cnsl-action-keys>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
</ng-template>
|
||||||
<cnsl-filter-user
|
<cnsl-filter-user
|
||||||
|
actions
|
||||||
*ngIf="!selection.hasValue()"
|
*ngIf="!selection.hasValue()"
|
||||||
(filterChanged)="applySearchQuery($any($event))"
|
(filterChanged)="applySearchQuery($any($event))"
|
||||||
(filterOpen)="filterOpen = $event"
|
(filterOpen)="filterOpen = $event"
|
||||||
></cnsl-filter-user>
|
></cnsl-filter-user>
|
||||||
|
<ng-template cnslHasRole [hasRole]="['user.write']" actions>
|
||||||
<button
|
<button
|
||||||
(click)="gotoRouterLink(['/users', type === Type.TYPE_HUMAN ? 'create' : 'create-machine'])"
|
(click)="gotoRouterLink(['/users', type === Type.TYPE_HUMAN ? 'create' : 'create-machine'])"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -170,3 +170,18 @@ For event there are 3 levels the condition can be defined:
|
|||||||
- All, handling any event in ZITADEL
|
- 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)",
|
label: "Web key Lifecycle (Preview)",
|
||||||
link: {
|
link: {
|
||||||
type: "generated-index",
|
type: "generated-index",
|
||||||
title: "Action Service API (Preview)",
|
title: "Web Key Service API (Preview)",
|
||||||
slug: "/apis/resources/action_service_v3",
|
slug: "/apis/resources/webkey_service_v3",
|
||||||
description:
|
description:
|
||||||
"This API is intended to manage web keys for a ZITADEL instance, used to sign and validate OIDC tokens.\n" +
|
"This API is intended to manage web keys for a ZITADEL instance, used to sign and validate OIDC tokens.\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
3
go.mod
3
go.mod
@ -3,6 +3,7 @@ module github.com/zitadel/zitadel
|
|||||||
go 1.22.2
|
go 1.22.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cloud.google.com/go/profiler v0.4.1
|
||||||
cloud.google.com/go/storage v1.43.0
|
cloud.google.com/go/storage v1.43.0
|
||||||
github.com/BurntSushi/toml v1.4.0
|
github.com/BurntSushi/toml v1.4.0
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
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-jwt/jwt/v5 v5.2.1 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/google/go-tpm v0.9.0 // 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/google/s2a-go v0.1.7 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.8 // 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/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 h1:NCXf8hfQi+Kmr56QJezXRZ6GPb80ZI7El1XztyUuLQI=
|
||||||
cloud.google.com/go/monitoring v1.19.0/go.mod h1:25IeMR5cQ5BoZ8j1eogHE5VPJLlReQ7zFp5OiLgiGZw=
|
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 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
|
||||||
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
|
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
|
||||||
cloud.google.com/go/trace v1.10.7 h1:gK8z2BIJQ3KIYGddw9RJLne5Fx0FEXkrEQzPaeEYVvk=
|
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/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 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
|
||||||
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
|
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-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
|
||||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
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/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 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||||
|
@ -303,7 +303,7 @@ func TestServer_RemovePhone(t *testing.T) {
|
|||||||
dep: func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error) {
|
dep: func(ctx context.Context, userID string) (*user.RemovePhoneResponse, error) {
|
||||||
return Client.RemovePhone(ctx, &user.RemovePhoneRequest{
|
return Client.RemovePhone(ctx, &user.RemovePhoneRequest{
|
||||||
UserId: doubleRemoveUser.GetUserId(),
|
UserId: doubleRemoveUser.GetUserId(),
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -45,3 +45,36 @@ func ZitadelErrorToHTTPStatusCode(err error) (statusCode int, ok bool) {
|
|||||||
return http.StatusInternalServerError, false
|
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"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
|
zhttp "github.com/zitadel/zitadel/internal/api/http"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"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()
|
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
|
// 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 {
|
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
|
||||||
}
|
}
|
||||||
return nil, zerrors.ThrowUnknown(nil, "EXEC-dra6yamk98", "Errors.Execution.Failed")
|
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.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
|
package execution_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -15,6 +18,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/api/grpc/server/middleware"
|
"github.com/zitadel/zitadel/internal/api/grpc/server/middleware"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/execution"
|
"github.com/zitadel/zitadel/internal/execution"
|
||||||
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Call(t *testing.T) {
|
func Test_Call(t *testing.T) {
|
||||||
@ -513,3 +517,152 @@ var requestContextInfoBody2 = []byte("{\"request\":{\"request\":\"content2\"}}")
|
|||||||
type request struct {
|
type request struct {
|
||||||
Request string `json:"request"`
|
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 (
|
const (
|
||||||
WebKeyTable = "projections.web_keys"
|
WebKeyTable = "projections.web_keys1"
|
||||||
|
|
||||||
WebKeyInstanceIDCol = "instance_id"
|
WebKeyInstanceIDCol = "instance_id"
|
||||||
WebKeyKeyIDCol = "key_id"
|
WebKeyKeyIDCol = "key_id"
|
||||||
@ -58,9 +58,6 @@ func (*webKeyProjection) Init() *old_handler.Check {
|
|||||||
handler.WithIndex(handler.NewIndex(
|
handler.WithIndex(handler.NewIndex(
|
||||||
"web_key_state",
|
"web_key_state",
|
||||||
[]string{WebKeyInstanceIDCol, WebKeyStateCol},
|
[]string{WebKeyInstanceIDCol, WebKeyStateCol},
|
||||||
handler.WithInclude(
|
|
||||||
WebKeyPrivateKeyCol,
|
|
||||||
),
|
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
select private_key
|
select private_key
|
||||||
from projections.web_keys
|
from projections.web_keys1
|
||||||
where instance_id = $1
|
where instance_id = $1
|
||||||
and state = $2
|
and state = $2
|
||||||
limit 1;
|
limit 1;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
select key_id, creation_date, change_date, sequence, state, config, config_type
|
select key_id, creation_date, change_date, sequence, state, config, config_type
|
||||||
from projections.web_keys
|
from projections.web_keys1
|
||||||
where instance_id = $1
|
where instance_id = $1
|
||||||
order by creation_date asc;
|
order by creation_date asc;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
select public_key
|
select public_key
|
||||||
from projections.web_keys
|
from projections.web_keys1
|
||||||
where instance_id = $1;
|
where instance_id = $1;
|
||||||
|
@ -582,6 +582,7 @@ Errors:
|
|||||||
NotFound: Изпълнението не е намерено
|
NotFound: Изпълнението не е намерено
|
||||||
IncludeNotFound: Включването не е намерено
|
IncludeNotFound: Включването не е намерено
|
||||||
NoTargets: Няма определени цели
|
NoTargets: Няма определени цели
|
||||||
|
Failed: неуспешно изпълнение
|
||||||
ResponseIsNotValidJSON: Отговорът не е валиден JSON
|
ResponseIsNotValidJSON: Отговорът не е валиден JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Функцията „Потребителска схема“ не е активирана
|
NotEnabled: Функцията „Потребителска схема“ не е активирана
|
||||||
|
@ -563,6 +563,7 @@ Errors:
|
|||||||
NotFound: Provedení nenalezeno
|
NotFound: Provedení nenalezeno
|
||||||
IncludeNotFound: Zahrnout nenalezeno
|
IncludeNotFound: Zahrnout nenalezeno
|
||||||
NoTargets: Nejsou definovány žádné cíle
|
NoTargets: Nejsou definovány žádné cíle
|
||||||
|
Failed: Provedení se nezdařilo
|
||||||
ResponseIsNotValidJSON: Odpověď není platný JSON
|
ResponseIsNotValidJSON: Odpověď není platný JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Funkce "Uživatelské schéma" není povolena
|
NotEnabled: Funkce "Uživatelské schéma" není povolena
|
||||||
|
@ -565,6 +565,7 @@ Errors:
|
|||||||
NotFound: Ausführung nicht gefunden
|
NotFound: Ausführung nicht gefunden
|
||||||
IncludeNotFound: Einschließen nicht gefunden
|
IncludeNotFound: Einschließen nicht gefunden
|
||||||
NoTargets: Keine Ziele definiert
|
NoTargets: Keine Ziele definiert
|
||||||
|
Failed: Ausführung fehlgeschlagen
|
||||||
ResponseIsNotValidJSON: Antwort ist kein gültiges JSON
|
ResponseIsNotValidJSON: Antwort ist kein gültiges JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Funktion Benutzerschema ist nicht aktiviert
|
NotEnabled: Funktion Benutzerschema ist nicht aktiviert
|
||||||
|
@ -565,6 +565,7 @@ Errors:
|
|||||||
NotFound: Execution not found
|
NotFound: Execution not found
|
||||||
IncludeNotFound: Include not found
|
IncludeNotFound: Include not found
|
||||||
NoTargets: No targets defined
|
NoTargets: No targets defined
|
||||||
|
Failed: Execution failed
|
||||||
ResponseIsNotValidJSON: Response is not valid JSON
|
ResponseIsNotValidJSON: Response is not valid JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Feature "User Schema" is not enabled
|
NotEnabled: Feature "User Schema" is not enabled
|
||||||
@ -595,7 +596,6 @@ Errors:
|
|||||||
NoActive: No active web key found
|
NoActive: No active web key found
|
||||||
NotFound: Web key not found
|
NotFound: Web key not found
|
||||||
|
|
||||||
|
|
||||||
AggregateTypes:
|
AggregateTypes:
|
||||||
action: Action
|
action: Action
|
||||||
instance: Instance
|
instance: Instance
|
||||||
|
@ -565,6 +565,7 @@ Errors:
|
|||||||
NotFound: Ejecución no encontrada
|
NotFound: Ejecución no encontrada
|
||||||
IncludeNotFound: Incluir no encontrado
|
IncludeNotFound: Incluir no encontrado
|
||||||
NoTargets: No hay objetivos definidos
|
NoTargets: No hay objetivos definidos
|
||||||
|
Failed: Ejecución fallida
|
||||||
ResponseIsNotValidJSON: La respuesta no es un JSON válido
|
ResponseIsNotValidJSON: La respuesta no es un JSON válido
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: La función "Esquema de usuario" no está habilitada
|
NotEnabled: La función "Esquema de usuario" no está habilitada
|
||||||
|
@ -565,6 +565,7 @@ Errors:
|
|||||||
NotFound: Exécution introuvable
|
NotFound: Exécution introuvable
|
||||||
IncludeNotFound: Inclure introuvable
|
IncludeNotFound: Inclure introuvable
|
||||||
NoTargets: Aucune cible définie
|
NoTargets: Aucune cible définie
|
||||||
|
Failed: Exécution échouée
|
||||||
ResponseIsNotValidJSON: La réponse n'est pas un JSON valide
|
ResponseIsNotValidJSON: La réponse n'est pas un JSON valide
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: La fonctionnalité "Schéma utilisateur" n'est pas activée
|
NotEnabled: La fonctionnalité "Schéma utilisateur" n'est pas activée
|
||||||
|
@ -565,6 +565,7 @@ Errors:
|
|||||||
NotFound: Esecuzione non trovata
|
NotFound: Esecuzione non trovata
|
||||||
IncludeNotFound: Includi non trovato
|
IncludeNotFound: Includi non trovato
|
||||||
NoTargets: Nessun obiettivo definito
|
NoTargets: Nessun obiettivo definito
|
||||||
|
Failed: Esecuzione fallita
|
||||||
ResponseIsNotValidJSON: La risposta non è un JSON valido
|
ResponseIsNotValidJSON: La risposta non è un JSON valido
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: La funzionalità "Schema utente" non è abilitata
|
NotEnabled: La funzionalità "Schema utente" non è abilitata
|
||||||
|
@ -554,6 +554,7 @@ Errors:
|
|||||||
NotFound: 実行が見つかりませんでした
|
NotFound: 実行が見つかりませんでした
|
||||||
IncludeNotFound: 見つからないものを含める
|
IncludeNotFound: 見つからないものを含める
|
||||||
NoTargets: ターゲットが定義されていません
|
NoTargets: ターゲットが定義されていません
|
||||||
|
Failed: 実行に失敗しました
|
||||||
ResponseIsNotValidJSON: 応答は有効な JSON ではありません
|
ResponseIsNotValidJSON: 応答は有効な JSON ではありません
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: 機能「ユーザースキーマ」が有効になっていません
|
NotEnabled: 機能「ユーザースキーマ」が有効になっていません
|
||||||
|
@ -564,6 +564,7 @@ Errors:
|
|||||||
NotFound: Извршувањето не е пронајдено
|
NotFound: Извршувањето не е пронајдено
|
||||||
IncludeNotFound: Вклучете не е пронајден
|
IncludeNotFound: Вклучете не е пронајден
|
||||||
NoTargets: Не се дефинирани цели
|
NoTargets: Не се дефинирани цели
|
||||||
|
Failed: Извршувањето не успеа
|
||||||
ResponseIsNotValidJSON: Одговорот не е валиден JSON
|
ResponseIsNotValidJSON: Одговорот не е валиден JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Функцијата „Корисничка шема“ не е овозможена
|
NotEnabled: Функцијата „Корисничка шема“ не е овозможена
|
||||||
|
@ -565,6 +565,7 @@ Errors:
|
|||||||
NotFound: Uitvoering niet gevonden
|
NotFound: Uitvoering niet gevonden
|
||||||
IncludeNotFound: Inclusief niet gevonden
|
IncludeNotFound: Inclusief niet gevonden
|
||||||
NoTargets: Geen doelstellingen gedefinieerd
|
NoTargets: Geen doelstellingen gedefinieerd
|
||||||
|
Failed: Uitvoering mislukt
|
||||||
ResponseIsNotValidJSON: Reactie is geen geldige JSON
|
ResponseIsNotValidJSON: Reactie is geen geldige JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Functie "Gebruikersschema" is niet ingeschakeld
|
NotEnabled: Functie "Gebruikersschema" is niet ingeschakeld
|
||||||
|
@ -565,6 +565,7 @@ Errors:
|
|||||||
NotFound: Nie znaleziono wykonania
|
NotFound: Nie znaleziono wykonania
|
||||||
IncludeNotFound: Nie znaleziono uwzględnienia
|
IncludeNotFound: Nie znaleziono uwzględnienia
|
||||||
NoTargets: Nie zdefiniowano celów
|
NoTargets: Nie zdefiniowano celów
|
||||||
|
Failed: Wykonanie nie powiodło się
|
||||||
ResponseIsNotValidJSON: Odpowiedź nie jest prawidłowym JSON-em
|
ResponseIsNotValidJSON: Odpowiedź nie jest prawidłowym JSON-em
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Funkcja „Schemat użytkownika” nie jest włączona
|
NotEnabled: Funkcja „Schemat użytkownika” nie jest włączona
|
||||||
|
@ -560,6 +560,7 @@ Errors:
|
|||||||
NotFound: Execução não encontrada
|
NotFound: Execução não encontrada
|
||||||
IncludeNotFound: Incluir não encontrado
|
IncludeNotFound: Incluir não encontrado
|
||||||
NoTargets: Nenhuma meta definida
|
NoTargets: Nenhuma meta definida
|
||||||
|
Failed: Falha na execução
|
||||||
ResponseIsNotValidJSON: A resposta não é um JSON válido
|
ResponseIsNotValidJSON: A resposta não é um JSON válido
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: O recurso "Esquema do usuário" não está habilitado
|
NotEnabled: O recurso "Esquema do usuário" não está habilitado
|
||||||
|
@ -554,6 +554,7 @@ Errors:
|
|||||||
NotFound: Исполнение не найдено
|
NotFound: Исполнение не найдено
|
||||||
IncludeNotFound: Включить не найдено
|
IncludeNotFound: Включить не найдено
|
||||||
NoTargets: Цели не определены
|
NoTargets: Цели не определены
|
||||||
|
Failed: Выполнение не удалось
|
||||||
ResponseIsNotValidJSON: Ответ не является допустимым JSON
|
ResponseIsNotValidJSON: Ответ не является допустимым JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Функция «Пользовательская схема» не включена
|
NotEnabled: Функция «Пользовательская схема» не включена
|
||||||
|
@ -564,6 +564,7 @@ Errors:
|
|||||||
NotFound: Exekveringen hittades inte
|
NotFound: Exekveringen hittades inte
|
||||||
IncludeNotFound: Inkluderingen hittades inte
|
IncludeNotFound: Inkluderingen hittades inte
|
||||||
NoTargets: Inga mål definierade
|
NoTargets: Inga mål definierade
|
||||||
|
Failed: Utförande misslyckades
|
||||||
ResponseIsNotValidJSON: Svaret är inte giltigt JSON
|
ResponseIsNotValidJSON: Svaret är inte giltigt JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: Funktionen "Användarschema" är inte aktiverad
|
NotEnabled: Funktionen "Användarschema" är inte aktiverad
|
||||||
|
@ -565,6 +565,7 @@ Errors:
|
|||||||
NotFound: 未找到执行
|
NotFound: 未找到执行
|
||||||
IncludeNotFound: 包括未找到的内容
|
IncludeNotFound: 包括未找到的内容
|
||||||
NoTargets: 没有定义目标
|
NoTargets: 没有定义目标
|
||||||
|
Failed: 执行失败
|
||||||
ResponseIsNotValidJSON: 响应不是有效的 JSON
|
ResponseIsNotValidJSON: 响应不是有效的 JSON
|
||||||
UserSchema:
|
UserSchema:
|
||||||
NotEnabled: 未启用“用户架构”功能
|
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) = {
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
summary: "Generate a web key pair for the instance";
|
summary: "Delete 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."
|
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: {
|
responses: {
|
||||||
key: "200"
|
key: "200"
|
||||||
value: {
|
value: {
|
||||||
@ -198,7 +198,7 @@ service ZITADELWebKeys {
|
|||||||
};
|
};
|
||||||
|
|
||||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
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"
|
description: "List web key details for the instance"
|
||||||
responses: {
|
responses: {
|
||||||
key: "200"
|
key: "200"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user