diff --git a/build/zitadel/Dockerfile b/build/zitadel/Dockerfile index faaa0f663a..ee47132de5 100644 --- a/build/zitadel/Dockerfile +++ b/build/zitadel/Dockerfile @@ -16,14 +16,6 @@ RUN go mod download COPY tools ./tools RUN ./tools/install.sh -ARG COCKROACH_BINARY=cockroach -RUN apt install openssl tzdata tar - -# cockroach binary used to backup database -RUN mkdir /usr/local/lib/cockroach -RUN wget -qO- https://binaries.cockroachdb.com/cockroach-v21.2.9.linux-amd64.tgz \ - | tar xvz && cp -i cockroach-v21.2.9.linux-amd64/cockroach /usr/local/bin/ -RUN rm -r cockroach-v21.2.9.linux-amd64 ####################### ## generates static files @@ -92,6 +84,15 @@ COPY --from=go-stub /go/src/github.com/zitadel/zitadel/internal/api/assets/route ####################### FROM go-base as go-test +ARG COCKROACH_BINARY=cockroach +RUN apt install openssl tzdata tar + +# cockroach binary used to backup database +RUN mkdir /usr/local/lib/cockroach +RUN wget -qO- https://binaries.cockroachdb.com/cockroach-v21.2.9.linux-amd64.tgz \ + | tar xvz && cp -i cockroach-v21.2.9.linux-amd64/cockroach /usr/local/bin/ +RUN rm -r cockroach-v21.2.9.linux-amd64 + # Migrations for cockroach-secure RUN go install github.com/rakyll/statik \ && go test -race -v -coverprofile=profile.cov $(go list ./... | grep -v /operator/) diff --git a/cmd/admin/start/config.go b/cmd/admin/start/config.go index f3cd144374..d460dc2507 100644 --- a/cmd/admin/start/config.go +++ b/cmd/admin/start/config.go @@ -50,6 +50,7 @@ type Config struct { EncryptionKeys *encryptionKeyConfig DefaultInstance command.InstanceSetup AuditLogRetention time.Duration + SystemAPIUsers map[string]*internal_authz.SystemAPIUser } func MustNewConfig(v *viper.Viper) *Config { diff --git a/cmd/admin/start/start.go b/cmd/admin/start/start.go index 89c6a39bab..c952a0a92f 100644 --- a/cmd/admin/start/start.go +++ b/cmd/admin/start/start.go @@ -135,14 +135,14 @@ func startZitadel(config *Config, masterKey string) error { notification.Start(config.Notification, config.ExternalPort, config.ExternalSecure, commands, queries, dbClient, assets.HandlerPrefix, config.SystemDefaults.Notifications.FileSystemPath, keys.User, keys.SMTP, keys.SMS) router := mux.NewRouter() - err = startAPIs(ctx, router, commands, queries, eventstoreClient, dbClient, config, storage, authZRepo, keys) + err = startAPIs(ctx, router, commands, queries, eventstoreClient, dbClient, config, storage, authZRepo, keys, config.SystemAPIUsers) if err != nil { return err } return listen(ctx, router, config.Port) } -func startAPIs(ctx context.Context, router *mux.Router, commands *command.Commands, queries *query.Queries, eventstore *eventstore.Eventstore, dbClient *sql.DB, config *Config, store static.Storage, authZRepo authz_repo.Repository, keys *encryptionKeys) error { +func startAPIs(ctx context.Context, router *mux.Router, commands *command.Commands, queries *query.Queries, eventstore *eventstore.Eventstore, dbClient *sql.DB, config *Config, store static.Storage, authZRepo authz_repo.Repository, keys *encryptionKeys, systemAPIKeys map[string]*internal_authz.SystemAPIUser) error { repo := struct { authz_repo.Repository *query.Queries @@ -150,9 +150,9 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman authZRepo, queries, } - verifier := internal_authz.Start(repo) + verifier := internal_authz.Start(repo, config.ExternalDomain, systemAPIKeys) - authenticatedAPIs := api.New(config.Port, router, &repo, config.InternalAuthZ, config.ExternalSecure, config.HTTP2HostHeader) + apis := api.New(config.Port, router, queries, verifier, config.InternalAuthZ, config.ExternalSecure, config.HTTP2HostHeader) authRepo, err := auth_es.Start(config.Auth, config.SystemDefaults, commands, queries, dbClient, keys.OIDC, keys.User) if err != nil { return fmt.Errorf("error starting auth repo: %w", err) @@ -161,21 +161,21 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman if err != nil { return fmt.Errorf("error starting admin repo: %w", err) } - if err := authenticatedAPIs.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.Database.Database, config.DefaultInstance)); err != nil { + if err := apis.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.Database.Database, config.DefaultInstance)); err != nil { return err } - if err := authenticatedAPIs.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.ExternalSecure, keys.User)); err != nil { + if err := apis.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.ExternalSecure, keys.User)); err != nil { return err } - if err := authenticatedAPIs.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, keys.User, config.ExternalSecure, oidc.HandlerPrefix, config.AuditLogRetention)); err != nil { + if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, keys.User, config.ExternalSecure, oidc.HandlerPrefix, config.AuditLogRetention)); err != nil { return err } - if err := authenticatedAPIs.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil { + if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil { return err } instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, login.IgnoreInstanceEndpoints...) - authenticatedAPIs.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, instanceInterceptor.Handler)) + apis.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, instanceInterceptor.Handler)) userAgentInterceptor, err := middleware.NewUserAgentHandler(config.UserAgentCookie, keys.UserAgentCookieKey, id.SonyFlakeGenerator(), config.ExternalSecure, login.EndpointResources) if err != nil { @@ -186,25 +186,25 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman if err != nil { return fmt.Errorf("unable to start oidc provider: %w", err) } - authenticatedAPIs.RegisterHandler(oidc.HandlerPrefix, oidcProvider.HttpHandler()) + apis.RegisterHandler(oidc.HandlerPrefix, oidcProvider.HttpHandler()) openAPIHandler, err := openapi.Start() if err != nil { return fmt.Errorf("unable to start openapi handler: %w", err) } - authenticatedAPIs.RegisterHandler(openapi.HandlerPrefix, openAPIHandler) + apis.RegisterHandler(openapi.HandlerPrefix, openAPIHandler) c, err := console.Start(config.Console, config.ExternalSecure, oidcProvider.IssuerFromRequest, instanceInterceptor.Handler) if err != nil { return fmt.Errorf("unable to start console: %w", err) } - authenticatedAPIs.RegisterHandler(console.HandlerPrefix, c) + apis.RegisterHandler(console.HandlerPrefix, c) l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, console.HandlerPrefix+"/", op.AuthCallbackURL(oidcProvider), config.ExternalSecure, userAgentInterceptor, op.NewIssuerInterceptor(oidcProvider.IssuerFromRequest).Handler, instanceInterceptor.Handler, keys.User, keys.IDPConfig, keys.CSRFCookieKey) if err != nil { return fmt.Errorf("unable to start login: %w", err) } - authenticatedAPIs.RegisterHandler(login.HandlerPrefix, l.Handler()) + apis.RegisterHandler(login.HandlerPrefix, l.Handler()) return nil } diff --git a/cmd/admin/start/start_from_init.go b/cmd/admin/start/start_from_init.go index 92bc612d63..85fc4d2c65 100644 --- a/cmd/admin/start/start_from_init.go +++ b/cmd/admin/start/start_from_init.go @@ -4,6 +4,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/zitadel/logging" + "github.com/zitadel/zitadel/cmd/admin/initialise" "github.com/zitadel/zitadel/cmd/admin/key" "github.com/zitadel/zitadel/cmd/admin/setup" diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index 5a7722eca4..eab030eb3f 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -163,6 +163,13 @@ EncryptionKeys: CSRFCookieKeyID: "csrfCookieKey" UserAgentCookieKeyID: "userAgentCookieKey" +SystemAPIUsers: + # add keys for authentication of the systemAPI here: + # - superuser: + # Path: /path/to/superuser/key.pem + # - superuser2: + # Path: /path/to/superuser2/key.pem + #TODO: remove as soon as possible SystemDefaults: SecretGenerators: diff --git a/internal/api/api.go b/internal/api/api.go index 7d15244576..dad6623b87 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -14,7 +14,6 @@ import ( internal_authz "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/grpc/server" http_util "github.com/zitadel/zitadel/internal/api/http" - "github.com/zitadel/zitadel/internal/authz/repository" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/telemetry/tracing" @@ -37,23 +36,20 @@ type health interface { func New( port uint16, router *mux.Router, - repo *struct { - repository.Repository - *query.Queries - }, + queries *query.Queries, + verifier *internal_authz.TokenVerifier, authZ internal_authz.Config, externalSecure bool, http2HostName string, ) *API { - verifier := internal_authz.Start(repo) api := &API{ port: port, verifier: verifier, - health: repo, + health: queries, router: router, externalSecure: externalSecure, } - api.grpcServer = server.CreateServer(api.verifier, authZ, repo.Queries, http2HostName) + api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName) api.routeGRPC() api.RegisterHandler("/debug", api.healthHandler()) @@ -68,9 +64,7 @@ func (a *API) RegisterServer(ctx context.Context, grpcServer server.Server) erro return err } a.RegisterHandler(prefix, handler) - if a.verifier != nil { - a.verifier.RegisterServer(grpcServer.AppName(), grpcServer.MethodPrefix(), grpcServer.AuthMethods()) - } + a.verifier.RegisterServer(grpcServer.AppName(), grpcServer.MethodPrefix(), grpcServer.AuthMethods()) return nil } diff --git a/internal/api/authz/context.go b/internal/api/authz/context.go index 0bbb1be214..938241232e 100644 --- a/internal/api/authz/context.go +++ b/internal/api/authz/context.go @@ -2,6 +2,7 @@ package authz import ( "context" + "strings" "github.com/zitadel/zitadel/internal/api/grpc" http_util "github.com/zitadel/zitadel/internal/api/http" @@ -67,6 +68,9 @@ func VerifyTokenAndCreateCtxData(ctx context.Context, token, orgID string, t *To if err != nil { return CtxData{}, err } + if strings.HasPrefix(method, "/zitadel.system.v1.SystemService") { + return CtxData{UserID: userID}, nil + } var projectID string var origins []string if clientID != "" { diff --git a/internal/api/authz/permissions_test.go b/internal/api/authz/permissions_test.go index 74b9d50ff6..1a1b97d6f8 100644 --- a/internal/api/authz/permissions_test.go +++ b/internal/api/authz/permissions_test.go @@ -68,7 +68,7 @@ func Test_GetUserMethodPermissions(t *testing.T) { { Roles: []string{"ORG_OWNER"}, }, - }}), + }}, "", nil), requiredPerm: "project.read", authConfig: Config{ RolePermissionMappings: []RoleMapping{ @@ -91,7 +91,7 @@ func Test_GetUserMethodPermissions(t *testing.T) { name: "No Grants", args: args{ ctxData: CtxData{}, - verifier: Start(&testVerifier{memberships: []*Membership{}}), + verifier: Start(&testVerifier{memberships: []*Membership{}}, "", nil), requiredPerm: "project.read", authConfig: Config{ RolePermissionMappings: []RoleMapping{ @@ -119,7 +119,7 @@ func Test_GetUserMethodPermissions(t *testing.T) { MemberType: MemberTypeIam, Roles: []string{"IAM_OWNER"}, }, - }}), + }}, "", nil), requiredPerm: "project.read", authConfig: Config{ RolePermissionMappings: []RoleMapping{ diff --git a/internal/api/authz/token.go b/internal/api/authz/token.go index fc2694c029..cad4f54eb5 100644 --- a/internal/api/authz/token.go +++ b/internal/api/authz/token.go @@ -2,9 +2,16 @@ package authz import ( "context" + "crypto/rsa" + "os" "strings" "sync" + "time" + "github.com/zitadel/oidc/v2/pkg/op" + "gopkg.in/square/go-jose.v2" + + "github.com/zitadel/zitadel/internal/crypto" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) @@ -14,9 +21,10 @@ const ( ) type TokenVerifier struct { - authZRepo authZRepo - clients sync.Map - authMethods MethodMapping + authZRepo authZRepo + clients sync.Map + authMethods MethodMapping + systemJWTProfile op.JWTProfileVerifier } type authZRepo interface { @@ -27,15 +35,74 @@ type authZRepo interface { ExistsOrg(ctx context.Context, orgID string) error } -func Start(authZRepo authZRepo) (v *TokenVerifier) { - return &TokenVerifier{authZRepo: authZRepo} +func Start(authZRepo authZRepo, systemAPI string, keys map[string]*SystemAPIUser) (v *TokenVerifier) { + return &TokenVerifier{ + authZRepo: authZRepo, + systemJWTProfile: op.NewJWTProfileVerifier( + &systemJWTStorage{ + keys: keys, + cachedKeys: make(map[string]*rsa.PublicKey), + }, + systemAPI, + 1*time.Hour, + time.Second, + ), + } } func (v *TokenVerifier) VerifyAccessToken(ctx context.Context, token string, method string) (userID, clientID, agentID, prefLang, resourceOwner string, err error) { + if strings.HasPrefix(method, "/zitadel.system.v1.SystemService") { + userID, err := v.verifySystemToken(ctx, token) + if err != nil { + return "", "", "", "", "", err + } + return userID, "", "", "", "", nil + } userID, agentID, clientID, prefLang, resourceOwner, err = v.authZRepo.VerifyAccessToken(ctx, token, "", GetInstance(ctx).ProjectID()) return userID, clientID, agentID, prefLang, resourceOwner, err } +func (v *TokenVerifier) verifySystemToken(ctx context.Context, token string) (string, error) { + jwtReq, err := op.VerifyJWTAssertion(ctx, token, v.systemJWTProfile) + if err != nil { + return "", err + } + return jwtReq.Subject, nil +} + +type systemJWTStorage struct { + keys map[string]*SystemAPIUser + mutex sync.Mutex + cachedKeys map[string]*rsa.PublicKey +} + +type SystemAPIUser struct { + Path string +} + +func (s *systemJWTStorage) GetKeyByIDAndUserID(_ context.Context, _, userID string) (*jose.JSONWebKey, error) { + cachedKey, ok := s.cachedKeys[userID] + if ok { + return &jose.JSONWebKey{KeyID: userID, Key: cachedKey}, nil + } + key, ok := s.keys[userID] + if !ok { + return nil, caos_errs.ThrowNotFound(nil, "AUTHZ-asfd3", "Errors.User.NotFound") + } + defer s.mutex.Unlock() + s.mutex.Lock() + keyData, err := os.ReadFile(key.Path) + if err != nil { + return nil, caos_errs.ThrowInternal(err, "AUTHZ-JK31F", "Errors.NotFound") + } + publicKey, err := crypto.BytesToPublicKey(keyData) + if err != nil { + return nil, err + } + s.cachedKeys[userID] = publicKey + return &jose.JSONWebKey{KeyID: userID, Key: publicKey}, nil +} + type client struct { id string projectID string diff --git a/internal/api/grpc/server/middleware/auth_interceptor.go b/internal/api/grpc/server/middleware/auth_interceptor.go index 44bf40a437..e1c6e6e02d 100644 --- a/internal/api/grpc/server/middleware/auth_interceptor.go +++ b/internal/api/grpc/server/middleware/auth_interceptor.go @@ -15,10 +15,6 @@ import ( func AuthorizationInterceptor(verifier *authz.TokenVerifier, authConfig authz.Config) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { - //TODO: Change as soon as we know how to authenticate system api - if verifier == nil { - return handler(ctx, req) - } return authorize(ctx, req, info, handler, verifier, authConfig) } } diff --git a/internal/api/grpc/server/middleware/auth_interceptor_test.go b/internal/api/grpc/server/middleware/auth_interceptor_test.go index cce501895a..abe50606f9 100644 --- a/internal/api/grpc/server/middleware/auth_interceptor_test.go +++ b/internal/api/grpc/server/middleware/auth_interceptor_test.go @@ -65,7 +65,7 @@ func Test_authorize(t *testing.T) { info: mockInfo("/no/token/needed"), handler: emptyMockHandler, verifier: func() *authz.TokenVerifier { - verifier := authz.Start(&verifierMock{}) + verifier := authz.Start(&verifierMock{}, "", nil) verifier.RegisterServer("need", "need", authz.MethodMapping{}) return verifier }(), @@ -84,7 +84,7 @@ func Test_authorize(t *testing.T) { info: mockInfo("/need/authentication"), handler: emptyMockHandler, verifier: func() *authz.TokenVerifier { - verifier := authz.Start(&verifierMock{}) + verifier := authz.Start(&verifierMock{}, "", nil) verifier.RegisterServer("need", "need", authz.MethodMapping{"/need/authentication": authz.Option{Permission: "authenticated"}}) return verifier }(), @@ -104,7 +104,7 @@ func Test_authorize(t *testing.T) { info: mockInfo("/need/authentication"), handler: emptyMockHandler, verifier: func() *authz.TokenVerifier { - verifier := authz.Start(&verifierMock{}) + verifier := authz.Start(&verifierMock{}, "", nil) verifier.RegisterServer("need", "need", authz.MethodMapping{"/need/authentication": authz.Option{Permission: "authenticated"}}) return verifier }(), @@ -124,7 +124,7 @@ func Test_authorize(t *testing.T) { info: mockInfo("/need/authentication"), handler: emptyMockHandler, verifier: func() *authz.TokenVerifier { - verifier := authz.Start(&verifierMock{}) + verifier := authz.Start(&verifierMock{}, "", nil) verifier.RegisterServer("need", "need", authz.MethodMapping{"/need/authentication": authz.Option{Permission: "authenticated"}}) return verifier }(), diff --git a/internal/api/grpc/server/middleware/instance_interceptor.go b/internal/api/grpc/server/middleware/instance_interceptor.go index 707a4533b6..e5b7aea7ce 100644 --- a/internal/api/grpc/server/middleware/instance_interceptor.go +++ b/internal/api/grpc/server/middleware/instance_interceptor.go @@ -28,6 +28,9 @@ func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInf interceptorCtx, span := tracing.NewServerInterceptorSpan(ctx) defer func() { span.EndWithError(err) }() for _, service := range ignoredServices { + if !strings.HasPrefix(service, "/") { + service = "/" + service + } if strings.HasPrefix(info.FullMethod, service) { return handler(ctx, req) } diff --git a/internal/api/grpc/server/server.go b/internal/api/grpc/server/server.go index a6ad6edeec..8f7ea7ecd4 100644 --- a/internal/api/grpc/server/server.go +++ b/internal/api/grpc/server/server.go @@ -9,6 +9,7 @@ import ( "github.com/zitadel/zitadel/internal/api/grpc/server/middleware" "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/telemetry/metrics" + system_pb "github.com/zitadel/zitadel/pkg/grpc/system" ) type Server interface { @@ -29,8 +30,7 @@ func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, querie middleware.SentryHandler(), middleware.NoCacheInterceptor(), middleware.ErrorHandler(), - //TODO: Handle Ignored Services - middleware.InstanceInterceptor(queries, hostHeaderName, "/zitadel.system.v1.SystemService"), + middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_MethodPrefix), middleware.AuthorizationInterceptor(verifier, authConfig), middleware.TranslationHandler(), middleware.ValidationHandler(), diff --git a/internal/query/query.go b/internal/query/query.go index 921da4be48..328eec42fa 100644 --- a/internal/query/query.go +++ b/internal/query/query.go @@ -73,3 +73,7 @@ func StartQueries(ctx context.Context, es *eventstore.Eventstore, sqlClient *sql return repo, nil } + +func (q *Queries) Health(ctx context.Context) error { + return q.client.Ping() +} diff --git a/proto/zitadel/system.proto b/proto/zitadel/system.proto index c0d7d98c47..6123249abb 100644 --- a/proto/zitadel/system.proto +++ b/proto/zitadel/system.proto @@ -104,6 +104,10 @@ service SystemService { post: "/instances/_search" body: "*" }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } // Returns the detail of an instance @@ -111,6 +115,10 @@ service SystemService { option (google.api.http) = { get: "/instances/{instance_id}"; }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } // Creates a new instance with all needed setup data @@ -120,6 +128,10 @@ service SystemService { post: "/instances" body: "*" }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } // Removes a instances @@ -128,6 +140,10 @@ service SystemService { option (google.api.http) = { delete: "/instances/{instance_id}" }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } // Checks if a domain exists @@ -136,6 +152,10 @@ service SystemService { post: "/domains/{domain}/_exists"; body: "*" }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } // Returns the custom domains of an instance @@ -144,6 +164,10 @@ service SystemService { post: "/instances/{instance_id}/domains/_search"; body: "*" }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } // Returns the domain of an instance @@ -152,6 +176,10 @@ service SystemService { post: "/instances/{instance_id}/domains"; body: "*" }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } // Returns the domain of an instance @@ -159,6 +187,10 @@ service SystemService { option (google.api.http) = { delete: "/instances/{instance_id}/domains/{domain}"; }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } // Returns the domain of an instance @@ -167,6 +199,10 @@ service SystemService { post: "/instances/{instance_id}/domains/_set_primary"; body: "*" }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; } @@ -179,6 +215,10 @@ service SystemService { body: "*" }; + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { tags: "views"; external_docs: { @@ -203,6 +243,9 @@ service SystemService { post: "/views/{database}/{view_name}"; }; + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { tags: "views"; @@ -228,6 +271,10 @@ service SystemService { body: "*" }; + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { tags: "failed events"; external_docs: { @@ -253,6 +300,10 @@ service SystemService { delete: "/failedevents/{database}/{view_name}/{failed_sequence}"; }; + option (zitadel.v1.auth_option) = { + permission: "authenticated"; + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { tags: "failed events"; external_docs: {