mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 16:37:24 +00:00
feat: System api (#3461)
* feat: start system api * feat: remove auth * feat: change gitignore * feat: run system api * feat: remove clear view form admin api * feat: search instances * feat: add instance * fix: set primary domain * Update .gitignore * fix: add instance * fix: add instance * fix: handle errors * fix: handle instance name * fix: test Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
parent
a7816a43b1
commit
3d5891eb11
2
.gitignore
vendored
2
.gitignore
vendored
@ -58,7 +58,7 @@ openapi/**/*.json
|
||||
/internal/api/ui/console/static/*
|
||||
|
||||
# local
|
||||
build/local/cloud.env
|
||||
build/local/*.env
|
||||
migrations/cockroach/migrate_cloud.go
|
||||
.notifications
|
||||
.artifacts
|
||||
|
@ -24,6 +24,8 @@ type DefaultInstance struct {
|
||||
domain string
|
||||
defaults systemdefaults.SystemDefaults
|
||||
zitadelRoles []authz.RoleMapping
|
||||
baseURL string
|
||||
externalSecure bool
|
||||
}
|
||||
|
||||
func (mig *DefaultInstance) Execute(ctx context.Context) error {
|
||||
@ -45,7 +47,8 @@ func (mig *DefaultInstance) Execute(ctx context.Context) error {
|
||||
mig.zitadelRoles,
|
||||
nil,
|
||||
nil,
|
||||
webauthn_helper.Config{},
|
||||
//TODO: Livio will fix this, but it ZITADEL doesn't run without this
|
||||
webauthn_helper.Config{DisplayName: "HELLO LIVIO", ID: "RPID"},
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
@ -54,8 +57,12 @@ func (mig *DefaultInstance) Execute(ctx context.Context) error {
|
||||
nil,
|
||||
nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = authz.WithRequestedDomain(ctx, mig.domain)
|
||||
_, err = cmd.SetUpInstance(ctx, &mig.InstanceSetup)
|
||||
|
||||
_, _, err = cmd.SetUpInstance(ctx, &mig.InstanceSetup, mig.externalSecure, mig.baseURL)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
@ -15,20 +16,27 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Database database.Config
|
||||
SystemDefaults systemdefaults.SystemDefaults
|
||||
InternalAuthZ authz.Config
|
||||
ExternalPort uint16
|
||||
ExternalDomain string
|
||||
ExternalSecure bool
|
||||
Log *logging.Config
|
||||
EncryptionKeys *encryptionKeyConfig
|
||||
Database database.Config
|
||||
SystemDefaults systemdefaults.SystemDefaults
|
||||
InternalAuthZ authz.Config
|
||||
ExternalPort uint16
|
||||
ExternalDomain string
|
||||
ExternalSecure bool
|
||||
Log *logging.Config
|
||||
EncryptionKeys *encryptionKeyConfig
|
||||
DefaultInstance command.InstanceSetup
|
||||
}
|
||||
|
||||
func MustNewConfig(v *viper.Viper) *Config {
|
||||
config := new(Config)
|
||||
err := v.Unmarshal(config)
|
||||
logging.OnError(err).Fatal("unable to read config")
|
||||
err := v.Unmarshal(config,
|
||||
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
)),
|
||||
)
|
||||
|
||||
err = config.Log.SetLogger()
|
||||
logging.OnError(err).Fatal("unable to set logger")
|
||||
|
@ -51,6 +51,12 @@ func Setup(config *Config, steps *Steps, masterKey string) {
|
||||
steps.s1ProjectionTable = &ProjectionTable{dbClient: dbClient}
|
||||
steps.s2AssetsTable = &AssetTable{dbClient: dbClient}
|
||||
|
||||
instanceSetup := config.DefaultInstance
|
||||
instanceSetup.InstanceName = steps.S3DefaultInstance.InstanceSetup.InstanceName
|
||||
instanceSetup.CustomDomain = steps.S3DefaultInstance.InstanceSetup.CustomDomain
|
||||
instanceSetup.Org = steps.S3DefaultInstance.InstanceSetup.Org
|
||||
steps.S3DefaultInstance.InstanceSetup = instanceSetup
|
||||
|
||||
steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address = strings.TrimSpace(steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address)
|
||||
if steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address == "" {
|
||||
steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address = "admin@" + config.ExternalDomain
|
||||
@ -63,13 +69,14 @@ func Setup(config *Config, steps *Steps, masterKey string) {
|
||||
steps.S3DefaultInstance.domain = config.ExternalDomain
|
||||
steps.S3DefaultInstance.zitadelRoles = config.InternalAuthZ.RolePermissionMappings
|
||||
steps.S3DefaultInstance.userEncryptionKey = config.EncryptionKeys.User
|
||||
steps.S3DefaultInstance.InstanceSetup.Zitadel.IsDevMode = !config.ExternalSecure
|
||||
steps.S3DefaultInstance.InstanceSetup.Zitadel.BaseURL = http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||
steps.S3DefaultInstance.InstanceSetup.Zitadel.IsDevMode = !config.ExternalSecure
|
||||
steps.S3DefaultInstance.InstanceSetup.Zitadel.BaseURL = http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||
steps.S3DefaultInstance.externalSecure = config.ExternalSecure
|
||||
steps.S3DefaultInstance.baseURL = http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||
|
||||
ctx := context.Background()
|
||||
migration.Migrate(ctx, eventstoreClient, steps.s1ProjectionTable)
|
||||
migration.Migrate(ctx, eventstoreClient, steps.s2AssetsTable)
|
||||
migration.Migrate(ctx, eventstoreClient, steps.S3DefaultInstance)
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s1ProjectionTable)
|
||||
logging.OnError(err).Fatal("unable to migrate step 1")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s2AssetsTable)
|
||||
logging.OnError(err).Fatal("unable to migrate step 3")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.S3DefaultInstance)
|
||||
logging.OnError(err).Fatal("unable to migrate step 4")
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -2,6 +2,9 @@ package start
|
||||
|
||||
import (
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/config/hook"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
admin_es "github.com/caos/zitadel/internal/admin/repository/eventsourcing"
|
||||
@ -42,14 +45,20 @@ type Config struct {
|
||||
InternalAuthZ internal_authz.Config
|
||||
SystemDefaults systemdefaults.SystemDefaults
|
||||
EncryptionKeys *encryptionKeyConfig
|
||||
DefaultInstance command.InstanceSetup
|
||||
}
|
||||
|
||||
func MustNewConfig(v *viper.Viper) *Config {
|
||||
config := new(Config)
|
||||
|
||||
err := v.Unmarshal(config)
|
||||
logging.OnError(err).Fatal("unable to read config")
|
||||
|
||||
err := v.Unmarshal(config,
|
||||
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
)),
|
||||
)
|
||||
err = config.Log.SetLogger()
|
||||
logging.OnError(err).Fatal("unable to set logger")
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
"github.com/caos/zitadel/internal/api/grpc/system"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@ -142,7 +143,7 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
|
||||
}
|
||||
verifier := internal_authz.Start(repo)
|
||||
|
||||
apis := api.New(config.Port, router, &repo, config.InternalAuthZ, config.ExternalSecure, config.HTTP2HostHeader)
|
||||
authenticatedAPIs := api.New(config.Port, router, &repo, config.InternalAuthZ, config.ExternalSecure, config.HTTP2HostHeader)
|
||||
authRepo, err := auth_es.Start(config.Auth, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, keys.OIDC, keys.User)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting auth repo: %w", err)
|
||||
@ -151,18 +152,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 := apis.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.SystemDefaults.Domain, assets.HandlerPrefix, keys.User)); err != nil {
|
||||
if err := authenticatedAPIs.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.DefaultInstance, config.ExternalPort, config.ExternalDomain, config.ExternalSecure)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, keys.User)); err != nil {
|
||||
if err := authenticatedAPIs.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.SystemDefaults.Domain, assets.HandlerPrefix, keys.User)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, keys.User)); err != nil {
|
||||
if err := authenticatedAPIs.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, keys.User)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := authenticatedAPIs.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, keys.User)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader)
|
||||
apis.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator, store, queries, instanceInterceptor.Handler))
|
||||
authenticatedAPIs.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator, store, queries, instanceInterceptor.Handler))
|
||||
|
||||
userAgentInterceptor, err := middleware.NewUserAgentHandler(config.UserAgentCookie, keys.UserAgentCookieKey, config.ExternalDomain, id.SonyFlakeGenerator, config.ExternalSecure)
|
||||
if err != nil {
|
||||
@ -174,26 +178,26 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to start oidc provider: %w", err)
|
||||
}
|
||||
apis.RegisterHandler(oidc.HandlerPrefix, oidcProvider.HttpHandler())
|
||||
authenticatedAPIs.RegisterHandler(oidc.HandlerPrefix, oidcProvider.HttpHandler())
|
||||
|
||||
openAPIHandler, err := openapi.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to start openapi handler: %w", err)
|
||||
}
|
||||
apis.RegisterHandler(openapi.HandlerPrefix, openAPIHandler)
|
||||
authenticatedAPIs.RegisterHandler(openapi.HandlerPrefix, openAPIHandler)
|
||||
|
||||
baseURL := http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||
c, err := console.Start(config.Console, config.ExternalDomain, baseURL, issuer, instanceInterceptor.Handler)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to start console: %w", err)
|
||||
}
|
||||
apis.RegisterHandler(console.HandlerPrefix, c)
|
||||
authenticatedAPIs.RegisterHandler(console.HandlerPrefix, c)
|
||||
|
||||
l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, config.SystemDefaults, console.HandlerPrefix+"/", config.ExternalDomain, baseURL, op.AuthCallbackURL(oidcProvider), config.ExternalSecure, userAgentInterceptor, instanceInterceptor.Handler, keys.User, keys.IDPConfig, keys.CSRFCookieKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to start login: %w", err)
|
||||
}
|
||||
apis.RegisterHandler(login.HandlerPrefix, l.Handler())
|
||||
authenticatedAPIs.RegisterHandler(login.HandlerPrefix, l.Handler())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -1427,21 +1427,6 @@ they represent the delta of the event happend on the objects
|
||||
POST: /views/_search
|
||||
|
||||
|
||||
### ClearView
|
||||
|
||||
> **rpc** ClearView([ClearViewRequest](#clearviewrequest))
|
||||
[ClearViewResponse](#clearviewresponse)
|
||||
|
||||
Truncates the delta of the change stream
|
||||
be carefull with this function because ZITADEL has to
|
||||
recompute the deltas after they got cleared.
|
||||
Search requests will return wrong results until all deltas are recomputed
|
||||
|
||||
|
||||
|
||||
POST: /views/{database}/{view_name}
|
||||
|
||||
|
||||
### ListFailedEvents
|
||||
|
||||
> **rpc** ListFailedEvents([ListFailedEventsRequest](#listfailedeventsrequest))
|
||||
@ -1718,24 +1703,6 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### ClearViewRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| database | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| view_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### ClearViewResponse
|
||||
This is an empty response
|
||||
|
||||
|
||||
|
||||
|
||||
### DeactivateIDPRequest
|
||||
|
||||
|
||||
|
@ -70,13 +70,13 @@ DomainPrimaryQuery is always equals
|
||||
|
||||
|
||||
|
||||
### IdQuery
|
||||
### IdsQuery
|
||||
IdQuery is always equals
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| id | string | - | string.max_len: 200<br /> |
|
||||
| ids | repeated string | - | |
|
||||
|
||||
|
||||
|
||||
@ -91,7 +91,6 @@ IdQuery is always equals
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
| state | State | - | |
|
||||
| name | string | - | |
|
||||
| version | string | - | |
|
||||
|
||||
|
||||
|
||||
@ -102,19 +101,7 @@ IdQuery is always equals
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.id_query | IdQuery | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.state_query | StateQuery | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### StateQuery
|
||||
StateQuery is always equals
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| state | State | - | enum.defined_only: true<br /> |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.id_query | IdsQuery | - | |
|
||||
|
||||
|
||||
|
||||
|
@ -29,7 +29,7 @@ Returns a list of ZITADEL instances/tenants
|
||||
|
||||
|
||||
|
||||
POST: /instances
|
||||
POST: /instances/_search
|
||||
|
||||
|
||||
### GetInstance
|
||||
@ -70,18 +70,6 @@ This might take some time
|
||||
DELETE: /instances/{id}
|
||||
|
||||
|
||||
### GetUsage
|
||||
|
||||
> **rpc** GetUsage([GetUsageRequest](#getusagerequest))
|
||||
[GetUsageResponse](#getusageresponse)
|
||||
|
||||
Returns the usage metrics of an instance
|
||||
|
||||
|
||||
|
||||
GET: /instances/{id}/usage
|
||||
|
||||
|
||||
### ListDomains
|
||||
|
||||
> **rpc** ListDomains([ListDomainsRequest](#listdomainsrequest))
|
||||
@ -91,7 +79,7 @@ Returns the custom domains of an instance
|
||||
|
||||
|
||||
|
||||
GET: /instances/{id}/domains
|
||||
POST: /instances/{id}/domains/_search
|
||||
|
||||
|
||||
### AddDomain
|
||||
@ -227,13 +215,12 @@ failed event. You can find out if it worked on the `failure_count`
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| instance_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| first_org_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| first_org_name | string | - | string.max_len: 200<br /> |
|
||||
| custom_domain | string | - | string.max_len: 200<br /> |
|
||||
| owner_first_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| owner_last_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| owner_first_name | string | - | string.max_len: 200<br /> |
|
||||
| owner_last_name | string | - | string.max_len: 200<br /> |
|
||||
| owner_email | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| owner_username | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| password | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| owner_username | string | - | string.max_len: 200<br /> |
|
||||
| request_limit | uint64 | - | |
|
||||
| action_mins_limit | uint64 | - | |
|
||||
|
||||
@ -247,6 +234,7 @@ failed event. You can find out if it worked on the `failure_count`
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| id | string | - | |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
@ -69,7 +69,9 @@ func (a *API) RegisterServer(ctx context.Context, grpcServer server.Server) erro
|
||||
return err
|
||||
}
|
||||
a.RegisterHandler(prefix, handler)
|
||||
a.verifier.RegisterServer(grpcServer.AppName(), grpcServer.MethodPrefix(), grpcServer.AuthMethods())
|
||||
if a.verifier != nil {
|
||||
a.verifier.RegisterServer(grpcServer.AppName(), grpcServer.MethodPrefix(), grpcServer.AuthMethods())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
package admin_test
|
||||
package admin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
admin_grpc "github.com/caos/zitadel/internal/api/grpc/admin"
|
||||
"github.com/caos/zitadel/internal/test"
|
||||
"github.com/caos/zitadel/internal/view/model"
|
||||
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
|
||||
@ -34,7 +33,7 @@ func TestFailedEventsToPbFields(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := admin_grpc.FailedEventsViewToPb(tt.args.failedEvents)
|
||||
got := FailedEventsViewToPb(tt.args.failedEvents)
|
||||
for _, g := range got {
|
||||
test.AssertFieldsMapped(t, g)
|
||||
}
|
||||
@ -64,7 +63,7 @@ func TestFailedEventToPbFields(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
converted := admin_grpc.FailedEventViewToPb(tt.args.failedEvent)
|
||||
converted := FailedEventViewToPb(tt.args.failedEvent)
|
||||
test.AssertFieldsMapped(t, converted)
|
||||
}
|
||||
}
|
||||
@ -89,7 +88,7 @@ func TestRemoveFailedEventRequestToModelFields(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
converted := admin_grpc.RemoveFailedEventRequestToModel(tt.args.req)
|
||||
converted := RemoveFailedEventRequestToModel(tt.args.req)
|
||||
test.AssertFieldsMapped(t, converted, "FailureCount", "ErrMsg")
|
||||
}
|
||||
}
|
||||
|
@ -22,16 +22,3 @@ func (s *Server) ListViews(ctx context.Context, _ *admin_pb.ListViewsRequest) (*
|
||||
convertedCurrentSequences = append(convertedCurrentSequences, convertedViews...)
|
||||
return &admin_pb.ListViewsResponse{Result: convertedCurrentSequences}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ClearView(ctx context.Context, req *admin_pb.ClearViewRequest) (*admin_pb.ClearViewResponse, error) {
|
||||
var err error
|
||||
if req.Database != "zitadel" {
|
||||
err = s.administrator.ClearView(ctx, req.Database, req.ViewName)
|
||||
} else {
|
||||
err = s.query.ClearCurrentSequence(ctx, req.ViewName)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.ClearViewResponse{}, nil
|
||||
}
|
||||
|
@ -7,6 +7,46 @@ import (
|
||||
instance_pb "github.com/caos/zitadel/pkg/grpc/instance"
|
||||
)
|
||||
|
||||
func InstancesToPb(instances []*query.Instance) []*instance_pb.Instance {
|
||||
list := make([]*instance_pb.Instance, len(instances))
|
||||
for i, instance := range instances {
|
||||
list[i] = InstanceToPb(instance)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func InstanceToPb(instance *query.Instance) *instance_pb.Instance {
|
||||
return &instance_pb.Instance{
|
||||
Details: object.ToViewDetailsPb(
|
||||
instance.Sequence,
|
||||
instance.CreationDate,
|
||||
instance.ChangeDate,
|
||||
instance.InstanceID(),
|
||||
),
|
||||
Id: instance.InstanceID(),
|
||||
}
|
||||
}
|
||||
|
||||
func InstanceQueriesToModel(queries []*instance_pb.Query) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i], err = InstanceQueryToModel(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func InstanceQueryToModel(searchQuery *instance_pb.Query) (query.SearchQuery, error) {
|
||||
switch q := searchQuery.Query.(type) {
|
||||
case *instance_pb.Query_IdQuery:
|
||||
return query.NewInstanceIDsListSearchQuery(q.IdQuery.Ids...)
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgument(nil, "INST-3m0se", "List.Query.Invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func DomainQueriesToModel(queries []*instance_pb.DomainSearchQuery) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
@ -27,7 +67,7 @@ func DomainQueryToModel(searchQuery *instance_pb.DomainSearchQuery) (query.Searc
|
||||
case *instance_pb.DomainSearchQuery_PrimaryQuery:
|
||||
return query.NewInstanceDomainPrimarySearchQuery(q.PrimaryQuery.Primary)
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-Ags42", "List.Query.Invalid")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "INST-Ags42", "List.Query.Invalid")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,10 @@ 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)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package middleware
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
@ -16,13 +17,19 @@ type InstanceVerifier interface {
|
||||
GetInstance(ctx context.Context)
|
||||
}
|
||||
|
||||
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string) grpc.UnaryServerInterceptor {
|
||||
func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string, ignoredServices ...string) grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
return setInstance(ctx, req, info, handler, verifier, headerName)
|
||||
return setInstance(ctx, req, info, handler, verifier, headerName, ignoredServices...)
|
||||
}
|
||||
}
|
||||
|
||||
func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName string) (_ interface{}, err error) {
|
||||
func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName string, ignoredServices ...string) (_ interface{}, err error) {
|
||||
for _, service := range ignoredServices {
|
||||
if strings.HasPrefix(info.FullMethod, service) {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
}
|
||||
|
||||
host, err := hostNameFromContext(ctx, headerName)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.PermissionDenied, err.Error())
|
||||
|
@ -30,7 +30,8 @@ func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, querie
|
||||
middleware.SentryHandler(),
|
||||
middleware.NoCacheInterceptor(),
|
||||
middleware.ErrorHandler(),
|
||||
middleware.InstanceInterceptor(queries, hostHeaderName),
|
||||
//TODO: Handle Ignored Services
|
||||
middleware.InstanceInterceptor(queries, hostHeaderName, "/zitadel.system.v1.SystemService"),
|
||||
middleware.AuthorizationInterceptor(verifier, authConfig),
|
||||
middleware.TranslationHandler(queries),
|
||||
middleware.ValidationHandler(),
|
||||
|
37
internal/api/grpc/system/failed_event.go
Normal file
37
internal/api/grpc/system/failed_event.go
Normal file
@ -0,0 +1,37 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
system_pb "github.com/caos/zitadel/pkg/grpc/system"
|
||||
)
|
||||
|
||||
func (s *Server) ListFailedEvents(ctx context.Context, req *system_pb.ListFailedEventsRequest) (*system_pb.ListFailedEventsResponse, error) {
|
||||
failedEventsOld, err := s.administrator.GetFailedEvents(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
convertedOld := FailedEventsViewToPb(failedEventsOld)
|
||||
|
||||
failedEvents, err := s.query.SearchFailedEvents(ctx, new(query.FailedEventSearchQueries))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
convertedNew := FailedEventsToPb(failedEvents)
|
||||
convertedOld = append(convertedOld, convertedNew...)
|
||||
return &system_pb.ListFailedEventsResponse{Result: convertedOld}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveFailedEvent(ctx context.Context, req *system_pb.RemoveFailedEventRequest) (*system_pb.RemoveFailedEventResponse, error) {
|
||||
var err error
|
||||
if req.Database != "zitadel" {
|
||||
err = s.administrator.RemoveFailedEvent(ctx, RemoveFailedEventRequestToModel(req))
|
||||
} else {
|
||||
err = s.query.RemoveFailedEvent(ctx, req.ViewName, req.FailedSequence)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.RemoveFailedEventResponse{}, nil
|
||||
}
|
51
internal/api/grpc/system/failed_event_converter.go
Normal file
51
internal/api/grpc/system/failed_event_converter.go
Normal file
@ -0,0 +1,51 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/internal/view/model"
|
||||
system_pb "github.com/caos/zitadel/pkg/grpc/system"
|
||||
)
|
||||
|
||||
func FailedEventsViewToPb(failedEvents []*model.FailedEvent) []*system_pb.FailedEvent {
|
||||
events := make([]*system_pb.FailedEvent, len(failedEvents))
|
||||
for i, failedEvent := range failedEvents {
|
||||
events[i] = FailedEventViewToPb(failedEvent)
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func FailedEventViewToPb(failedEvent *model.FailedEvent) *system_pb.FailedEvent {
|
||||
return &system_pb.FailedEvent{
|
||||
Database: failedEvent.Database,
|
||||
ViewName: failedEvent.ViewName,
|
||||
FailedSequence: failedEvent.FailedSequence,
|
||||
FailureCount: failedEvent.FailureCount,
|
||||
ErrorMessage: failedEvent.ErrMsg,
|
||||
}
|
||||
}
|
||||
|
||||
func FailedEventsToPb(failedEvents *query.FailedEvents) []*system_pb.FailedEvent {
|
||||
events := make([]*system_pb.FailedEvent, len(failedEvents.FailedEvents))
|
||||
for i, failedEvent := range failedEvents.FailedEvents {
|
||||
events[i] = FailedEventToPb(failedEvent)
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func FailedEventToPb(failedEvent *query.FailedEvent) *system_pb.FailedEvent {
|
||||
return &system_pb.FailedEvent{
|
||||
Database: "zitadel",
|
||||
ViewName: failedEvent.ProjectionName,
|
||||
FailedSequence: failedEvent.FailedSequence,
|
||||
FailureCount: failedEvent.FailureCount,
|
||||
ErrorMessage: failedEvent.Error,
|
||||
}
|
||||
}
|
||||
|
||||
func RemoveFailedEventRequestToModel(req *system_pb.RemoveFailedEventRequest) *model.FailedEvent {
|
||||
return &model.FailedEvent{
|
||||
Database: req.Database,
|
||||
ViewName: req.ViewName,
|
||||
FailedSequence: req.FailedSequence,
|
||||
}
|
||||
}
|
95
internal/api/grpc/system/failed_event_converter_test.go
Normal file
95
internal/api/grpc/system/failed_event_converter_test.go
Normal file
@ -0,0 +1,95 @@
|
||||
package system_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
system_grpc "github.com/caos/zitadel/internal/api/grpc/system"
|
||||
"github.com/caos/zitadel/internal/test"
|
||||
"github.com/caos/zitadel/internal/view/model"
|
||||
system_pb "github.com/caos/zitadel/pkg/grpc/system"
|
||||
)
|
||||
|
||||
func TestFailedEventsToPbFields(t *testing.T) {
|
||||
type args struct {
|
||||
failedEvents []*model.FailedEvent
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "all fields",
|
||||
args: args{
|
||||
failedEvents: []*model.FailedEvent{
|
||||
{
|
||||
Database: "admin",
|
||||
ViewName: "users",
|
||||
FailedSequence: 456,
|
||||
FailureCount: 5,
|
||||
ErrMsg: "some error",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := system_grpc.FailedEventsViewToPb(tt.args.failedEvents)
|
||||
for _, g := range got {
|
||||
test.AssertFieldsMapped(t, g)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailedEventToPbFields(t *testing.T) {
|
||||
type args struct {
|
||||
failedEvent *model.FailedEvent
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
"all fields",
|
||||
args{
|
||||
failedEvent: &model.FailedEvent{
|
||||
Database: "admin",
|
||||
ViewName: "users",
|
||||
FailedSequence: 456,
|
||||
FailureCount: 5,
|
||||
ErrMsg: "some error",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
converted := system_grpc.FailedEventViewToPb(tt.args.failedEvent)
|
||||
test.AssertFieldsMapped(t, converted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveFailedEventRequestToModelFields(t *testing.T) {
|
||||
type args struct {
|
||||
req *system_pb.RemoveFailedEventRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
"all fields",
|
||||
args{
|
||||
req: &system_pb.RemoveFailedEventRequest{
|
||||
Database: "admin",
|
||||
ViewName: "users",
|
||||
FailedSequence: 456,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
converted := system_grpc.RemoveFailedEventRequestToModel(tt.args.req)
|
||||
test.AssertFieldsMapped(t, converted, "FailureCount", "ErrMsg")
|
||||
}
|
||||
}
|
127
internal/api/grpc/system/instance.go
Normal file
127
internal/api/grpc/system/instance.go
Normal file
@ -0,0 +1,127 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
instance_grpc "github.com/caos/zitadel/internal/api/grpc/instance"
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
object_pb "github.com/caos/zitadel/pkg/grpc/object"
|
||||
system_pb "github.com/caos/zitadel/pkg/grpc/system"
|
||||
)
|
||||
|
||||
func (s *Server) ListInstances(ctx context.Context, req *system_pb.ListInstancesRequest) (*system_pb.ListInstancesResponse, error) {
|
||||
queries, err := ListInstancesRequestToModel(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := s.query.SearchInstances(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.ListInstancesResponse{
|
||||
Result: instance_grpc.InstancesToPb(result.Instances),
|
||||
Details: &object_pb.ListDetails{
|
||||
TotalResult: result.Count,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetInstance(ctx context.Context, req *system_pb.GetInstanceRequest) (*system_pb.GetInstanceResponse, error) {
|
||||
ctx = authz.WithInstanceID(ctx, req.Id)
|
||||
instance, err := s.query.Instance(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.GetInstanceResponse{
|
||||
Instance: instance_grpc.InstanceToPb(instance),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddInstance(ctx context.Context, req *system_pb.AddInstanceRequest) (*system_pb.AddInstanceResponse, error) {
|
||||
id, details, err := s.command.SetUpInstance(ctx, AddInstancePbToSetupInstance(req, s.DefaultInstance), s.ExternalSecure, s.BaseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.AddInstanceResponse{
|
||||
Id: id,
|
||||
Details: object.AddToDetailsPb(
|
||||
details.Sequence,
|
||||
details.EventDate,
|
||||
details.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *Server) ListDomains(ctx context.Context, req *system_pb.ListDomainsRequest) (*system_pb.ListDomainsResponse, error) {
|
||||
ctx = authz.WithInstanceID(ctx, req.Id)
|
||||
queries, err := ListInstanceDomainsRequestToModel(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains, err := s.query.SearchInstanceDomains(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.ListDomainsResponse{
|
||||
Result: instance_grpc.DomainsToPb(domains.Domains),
|
||||
Details: object.ToListDetails(
|
||||
domains.Count,
|
||||
domains.Sequence,
|
||||
domains.Timestamp,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddDomain(ctx context.Context, req *system_pb.AddDomainRequest) (*system_pb.AddDomainResponse, error) {
|
||||
ctx = authz.WithInstanceID(ctx, req.Id)
|
||||
instance, err := s.query.Instance(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx = authz.WithInstance(ctx, instance)
|
||||
details, err := s.command.AddInstanceDomain(ctx, req.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.AddDomainResponse{
|
||||
Details: object.AddToDetailsPb(
|
||||
details.Sequence,
|
||||
details.EventDate,
|
||||
details.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveDomain(ctx context.Context, req *system_pb.RemoveDomainRequest) (*system_pb.RemoveDomainResponse, error) {
|
||||
ctx = authz.WithInstanceID(ctx, req.Id)
|
||||
details, err := s.command.RemoveInstanceDomain(ctx, req.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.RemoveDomainResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
details.Sequence,
|
||||
details.EventDate,
|
||||
details.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetPrimaryDomain(ctx context.Context, req *system_pb.SetPrimaryDomainRequest) (*system_pb.SetPrimaryDomainResponse, error) {
|
||||
ctx = authz.WithInstanceID(ctx, req.Id)
|
||||
details, err := s.command.SetPrimaryInstanceDomain(ctx, req.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.SetPrimaryDomainResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
details.Sequence,
|
||||
details.EventDate,
|
||||
details.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
97
internal/api/grpc/system/instance_converter.go
Normal file
97
internal/api/grpc/system/instance_converter.go
Normal file
@ -0,0 +1,97 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
instance_grpc "github.com/caos/zitadel/internal/api/grpc/instance"
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
instance_pb "github.com/caos/zitadel/pkg/grpc/instance"
|
||||
system_pb "github.com/caos/zitadel/pkg/grpc/system"
|
||||
)
|
||||
|
||||
func AddInstancePbToSetupInstance(req *system_pb.AddInstanceRequest, defaultInstance command.InstanceSetup) *command.InstanceSetup {
|
||||
if req.InstanceName != "" {
|
||||
defaultInstance.InstanceName = req.InstanceName
|
||||
defaultInstance.Org.Name = req.InstanceName
|
||||
}
|
||||
if req.CustomDomain != "" {
|
||||
defaultInstance.CustomDomain = req.CustomDomain
|
||||
}
|
||||
if req.FirstOrgName != "" {
|
||||
defaultInstance.Org.Name = req.FirstOrgName
|
||||
}
|
||||
if req.OwnerEmail != "" {
|
||||
defaultInstance.Org.Human.Email.Address = req.OwnerEmail
|
||||
}
|
||||
if req.OwnerUsername != "" {
|
||||
defaultInstance.Org.Human.Username = req.OwnerUsername
|
||||
}
|
||||
if req.OwnerFirstName != "" {
|
||||
defaultInstance.Org.Human.FirstName = req.OwnerFirstName
|
||||
}
|
||||
if req.OwnerLastName != "" {
|
||||
defaultInstance.Org.Human.LastName = req.OwnerLastName
|
||||
}
|
||||
return &defaultInstance
|
||||
}
|
||||
func ListInstancesRequestToModel(req *system_pb.ListInstancesRequest) (*query.InstanceSearchQueries, error) {
|
||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||
queries, err := instance_grpc.InstanceQueriesToModel(req.Queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.InstanceSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: fieldNameToInstanceColumn(req.SortingColumn),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func fieldNameToInstanceColumn(fieldName instance_pb.FieldName) query.Column {
|
||||
switch fieldName {
|
||||
case instance_pb.FieldName_FIELD_NAME_ID:
|
||||
return query.InstanceColumnID
|
||||
case instance_pb.FieldName_FIELD_NAME_NAME:
|
||||
return query.InstanceColumnName
|
||||
case instance_pb.FieldName_FIELD_NAME_CREATION_DATE:
|
||||
return query.InstanceColumnCreationDate
|
||||
default:
|
||||
return query.Column{}
|
||||
}
|
||||
}
|
||||
|
||||
func ListInstanceDomainsRequestToModel(req *system_pb.ListDomainsRequest) (*query.InstanceDomainSearchQueries, error) {
|
||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||
queries, err := instance_grpc.DomainQueriesToModel(req.Queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.InstanceDomainSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: fieldNameToInstanceDomainColumn(req.SortingColumn),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func fieldNameToInstanceDomainColumn(fieldName instance_pb.DomainFieldName) query.Column {
|
||||
switch fieldName {
|
||||
case instance_pb.DomainFieldName_DOMAIN_FIELD_NAME_DOMAIN:
|
||||
return query.InstanceDomainDomainCol
|
||||
case instance_pb.DomainFieldName_DOMAIN_FIELD_NAME_GENERATED:
|
||||
return query.InstanceDomainIsGeneratedCol
|
||||
case instance_pb.DomainFieldName_DOMAIN_FIELD_NAME_PRIMARY:
|
||||
return query.InstanceDomainIsPrimaryCol
|
||||
case instance_pb.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE:
|
||||
return query.InstanceDomainCreationDateCol
|
||||
default:
|
||||
return query.Column{}
|
||||
}
|
||||
}
|
75
internal/api/grpc/system/server.go
Normal file
75
internal/api/grpc/system/server.go
Normal file
@ -0,0 +1,75 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/admin/repository"
|
||||
http_util "github.com/caos/zitadel/internal/api/http"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/caos/zitadel/internal/admin/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/grpc/server"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/pkg/grpc/system"
|
||||
)
|
||||
|
||||
const (
|
||||
systemAPI = "System-API"
|
||||
)
|
||||
|
||||
var _ system.SystemServiceServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
system.UnimplementedSystemServiceServer
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
administrator repository.AdministratorRepository
|
||||
DefaultInstance command.InstanceSetup
|
||||
ExternalSecure bool
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Repository eventsourcing.Config
|
||||
}
|
||||
|
||||
func CreateServer(command *command.Commands,
|
||||
query *query.Queries,
|
||||
repo repository.Repository,
|
||||
defaultInstance command.InstanceSetup,
|
||||
externalPort uint16,
|
||||
externalDomain string,
|
||||
externalSecure bool) *Server {
|
||||
return &Server{
|
||||
command: command,
|
||||
query: query,
|
||||
administrator: repo,
|
||||
DefaultInstance: defaultInstance,
|
||||
ExternalSecure: externalSecure,
|
||||
BaseURL: http_util.BuildHTTP(externalDomain, externalPort, externalSecure),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
|
||||
system.RegisterSystemServiceServer(grpcServer, s)
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return systemAPI
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return system.SystemService_MethodPrefix
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return system.SystemService_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.GatewayFunc {
|
||||
return system.RegisterSystemServiceHandlerFromEndpoint
|
||||
}
|
||||
|
||||
func (s *Server) GatewayPathPrefix() string {
|
||||
return "/system/v1"
|
||||
}
|
37
internal/api/grpc/system/view.go
Normal file
37
internal/api/grpc/system/view.go
Normal file
@ -0,0 +1,37 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
system_pb "github.com/caos/zitadel/pkg/grpc/system"
|
||||
)
|
||||
|
||||
func (s *Server) ListViews(ctx context.Context, _ *system_pb.ListViewsRequest) (*system_pb.ListViewsResponse, error) {
|
||||
currentSequences, err := s.query.SearchCurrentSequences(ctx, new(query.CurrentSequencesSearchQueries))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
convertedCurrentSequences := CurrentSequencesToPb(currentSequences)
|
||||
views, err := s.administrator.GetViews()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
convertedViews := ViewsToPb(views)
|
||||
|
||||
convertedCurrentSequences = append(convertedCurrentSequences, convertedViews...)
|
||||
return &system_pb.ListViewsResponse{Result: convertedCurrentSequences}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ClearView(ctx context.Context, req *system_pb.ClearViewRequest) (*system_pb.ClearViewResponse, error) {
|
||||
var err error
|
||||
if req.Database != "zitadel" {
|
||||
err = s.administrator.ClearView(ctx, req.Database, req.ViewName)
|
||||
} else {
|
||||
err = s.query.ClearCurrentSequence(ctx, req.ViewName)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &system_pb.ClearViewResponse{}, nil
|
||||
}
|
43
internal/api/grpc/system/view_converter.go
Normal file
43
internal/api/grpc/system/view_converter.go
Normal file
@ -0,0 +1,43 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/internal/view/model"
|
||||
system_pb "github.com/caos/zitadel/pkg/grpc/system"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func ViewsToPb(views []*model.View) []*system_pb.View {
|
||||
v := make([]*system_pb.View, len(views))
|
||||
for i, view := range views {
|
||||
v[i] = ViewToPb(view)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func ViewToPb(view *model.View) *system_pb.View {
|
||||
return &system_pb.View{
|
||||
Database: view.Database,
|
||||
ViewName: view.ViewName,
|
||||
LastSuccessfulSpoolerRun: timestamppb.New(view.LastSuccessfulSpoolerRun),
|
||||
ProcessedSequence: view.CurrentSequence,
|
||||
EventTimestamp: timestamppb.New(view.EventTimestamp),
|
||||
}
|
||||
}
|
||||
|
||||
func CurrentSequencesToPb(currentSequences *query.CurrentSequences) []*system_pb.View {
|
||||
v := make([]*system_pb.View, len(currentSequences.CurrentSequences))
|
||||
for i, currentSequence := range currentSequences.CurrentSequences {
|
||||
v[i] = CurrentSequenceToPb(currentSequence)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func CurrentSequenceToPb(currentSequence *query.CurrentSequence) *system_pb.View {
|
||||
return &system_pb.View{
|
||||
Database: "zitadel",
|
||||
ViewName: currentSequence.ProjectionName,
|
||||
ProcessedSequence: currentSequence.CurrentSequence,
|
||||
EventTimestamp: timestamppb.New(currentSequence.Timestamp),
|
||||
}
|
||||
}
|
@ -28,10 +28,22 @@ const (
|
||||
consolePostLogoutPath = console.HandlerPrefix + "/signedout"
|
||||
)
|
||||
|
||||
type AddInstance struct {
|
||||
InstanceName string
|
||||
CustomDomain string
|
||||
FirstOrgName string
|
||||
OwnerEmail string
|
||||
OwnerUsername string
|
||||
OwnerFirstName string
|
||||
OwnerLastName string
|
||||
}
|
||||
|
||||
type InstanceSetup struct {
|
||||
Org OrgSetup
|
||||
Zitadel ZitadelConfig
|
||||
Features struct {
|
||||
zitadel ZitadelConfig
|
||||
InstanceName string
|
||||
CustomDomain string
|
||||
Org OrgSetup
|
||||
Features struct {
|
||||
TierName string
|
||||
TierDescription string
|
||||
Retention time.Duration
|
||||
@ -120,9 +132,6 @@ type InstanceSetup struct {
|
||||
}
|
||||
|
||||
type ZitadelConfig struct {
|
||||
IsDevMode bool
|
||||
BaseURL string
|
||||
|
||||
projectID string
|
||||
mgmtAppID string
|
||||
adminAppID string
|
||||
@ -131,41 +140,41 @@ type ZitadelConfig struct {
|
||||
}
|
||||
|
||||
func (s *InstanceSetup) generateIDs() (err error) {
|
||||
s.Zitadel.projectID, err = id.SonyFlakeGenerator.Next()
|
||||
s.zitadel.projectID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Zitadel.mgmtAppID, err = id.SonyFlakeGenerator.Next()
|
||||
s.zitadel.mgmtAppID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Zitadel.adminAppID, err = id.SonyFlakeGenerator.Next()
|
||||
s.zitadel.adminAppID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Zitadel.authAppID, err = id.SonyFlakeGenerator.Next()
|
||||
s.zitadel.authAppID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Zitadel.consoleAppID, err = id.SonyFlakeGenerator.Next()
|
||||
s.zitadel.consoleAppID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, externalSecure bool, baseURL string) (string, *domain.ObjectDetails, error) {
|
||||
instanceID, err := id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err = c.eventstore.NewInstance(ctx, instanceID); err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
ctx = authz.SetCtxData(authz.WithInstanceID(ctx, instanceID), authz.CtxData{OrgID: instanceID, ResourceOwner: instanceID})
|
||||
@ -174,16 +183,16 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*do
|
||||
|
||||
orgID, err := id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
userID, err := id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err = setup.generateIDs(); err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
setup.Org.Human.PasswordChangeRequired = true
|
||||
@ -191,9 +200,11 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*do
|
||||
instanceAgg := instance.NewAggregate(instanceID)
|
||||
orgAgg := org.NewAggregate(orgID)
|
||||
userAgg := user.NewAggregate(userID, orgID)
|
||||
projectAgg := project.NewAggregate(setup.Zitadel.projectID, orgID)
|
||||
projectAgg := project.NewAggregate(setup.zitadel.projectID, orgID)
|
||||
|
||||
validations := []preparation.Validation{
|
||||
addInstance(instanceAgg, setup.InstanceName),
|
||||
c.addGeneratedInstanceDomain(instanceAgg, setup.InstanceName),
|
||||
SetDefaultFeatures(
|
||||
instanceAgg,
|
||||
setup.Features.TierName,
|
||||
@ -289,20 +300,24 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*do
|
||||
validations = append(validations, SetInstanceCustomTexts(instanceAgg, msg))
|
||||
}
|
||||
|
||||
if setup.CustomDomain != "" {
|
||||
validations = append(validations, c.addInstanceDomain(instanceAgg, setup.CustomDomain, false))
|
||||
}
|
||||
|
||||
console := &addOIDCApp{
|
||||
AddApp: AddApp{
|
||||
Aggregate: *projectAgg,
|
||||
ID: setup.Zitadel.consoleAppID,
|
||||
ID: setup.zitadel.consoleAppID,
|
||||
Name: consoleAppName,
|
||||
},
|
||||
Version: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{setup.Zitadel.BaseURL + consoleRedirectPath},
|
||||
RedirectUris: []string{baseURL + consoleRedirectPath},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
ApplicationType: domain.OIDCApplicationTypeUserAgent,
|
||||
AuthMethodType: domain.OIDCAuthMethodTypeNone,
|
||||
PostLogoutRedirectUris: []string{setup.Zitadel.BaseURL + consolePostLogoutPath},
|
||||
DevMode: setup.Zitadel.IsDevMode,
|
||||
PostLogoutRedirectUris: []string{baseURL + consolePostLogoutPath},
|
||||
DevMode: !externalSecure,
|
||||
AccessTokenType: domain.OIDCTokenTypeBearer,
|
||||
AccessTokenRoleAssertion: false,
|
||||
IDTokenRoleAssertion: false,
|
||||
@ -323,7 +338,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*do
|
||||
&addAPIApp{
|
||||
AddApp: AddApp{
|
||||
Aggregate: *projectAgg,
|
||||
ID: setup.Zitadel.mgmtAppID,
|
||||
ID: setup.zitadel.mgmtAppID,
|
||||
Name: mgmtAppName,
|
||||
},
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
@ -335,7 +350,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*do
|
||||
&addAPIApp{
|
||||
AddApp: AddApp{
|
||||
Aggregate: *projectAgg,
|
||||
ID: setup.Zitadel.adminAppID,
|
||||
ID: setup.zitadel.adminAppID,
|
||||
Name: adminAppName,
|
||||
},
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
@ -347,7 +362,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*do
|
||||
&addAPIApp{
|
||||
AddApp: AddApp{
|
||||
Aggregate: *projectAgg,
|
||||
ID: setup.Zitadel.authAppID,
|
||||
ID: setup.zitadel.authAppID,
|
||||
Name: authAppName,
|
||||
},
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
@ -356,25 +371,35 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*do
|
||||
),
|
||||
|
||||
AddOIDCAppCommand(console, nil),
|
||||
SetIAMConsoleID(instanceAgg, &console.ClientID, &setup.Zitadel.consoleAppID),
|
||||
SetIAMConsoleID(instanceAgg, &console.ClientID, &setup.zitadel.consoleAppID),
|
||||
)
|
||||
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validations...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
events, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
return &domain.ObjectDetails{
|
||||
return instanceID, &domain.ObjectDetails{
|
||||
Sequence: events[len(events)-1].Sequence(),
|
||||
EventDate: events[len(events)-1].CreationDate(),
|
||||
ResourceOwner: orgID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func addInstance(a *instance.Aggregate, instanceName string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{
|
||||
instance.NewInstanceAddedEvent(ctx, &a.Aggregate, instanceName),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetIAMProject defines the command to set the id of the IAM project onto the instance
|
||||
func SetIAMProject(a *instance.Aggregate, projectID string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
|
@ -67,6 +67,11 @@ func (c *Commands) RemoveInstanceDomain(ctx context.Context, instanceDomain stri
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Commands) addGeneratedInstanceDomain(a *instance.Aggregate, instanceName string) preparation.Validation {
|
||||
domain := domain.NewGeneratedInstanceDomain(instanceName, c.iamDomain)
|
||||
return c.addInstanceDomain(a, domain, true)
|
||||
}
|
||||
|
||||
func (c *Commands) addInstanceDomain(a *instance.Aggregate, instanceDomain string, generated bool) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if instanceDomain = strings.TrimSpace(instanceDomain); instanceDomain == "" {
|
||||
@ -80,28 +85,32 @@ func (c *Commands) addInstanceDomain(a *instance.Aggregate, instanceDomain strin
|
||||
if domainWriteModel.State == domain.InstanceDomainStateActive {
|
||||
return nil, errors.ThrowAlreadyExists(nil, "INST-i2nl", "Errors.Instance.Domain.AlreadyExists")
|
||||
}
|
||||
events := []eventstore.Command{
|
||||
instance.NewDomainAddedEvent(ctx, &a.Aggregate, instanceDomain, generated),
|
||||
}
|
||||
appWriteModel, err := c.getOIDCAppWriteModel(ctx, authz.GetInstance(ctx).ProjectID(), authz.GetInstance(ctx).ConsoleApplicationID(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
redirectUrls := append(appWriteModel.RedirectUris, instanceDomain+consoleRedirectPath)
|
||||
logoutUrls := append(appWriteModel.PostLogoutRedirectUris, instanceDomain+consolePostLogoutPath)
|
||||
consoleChangeEvent, err := project.NewOIDCConfigChangedEvent(
|
||||
ctx,
|
||||
ProjectAggregateFromWriteModel(&appWriteModel.WriteModel),
|
||||
appWriteModel.AppID,
|
||||
[]project.OIDCConfigChanges{
|
||||
project.ChangeRedirectURIs(redirectUrls),
|
||||
project.ChangePostLogoutRedirectURIs(logoutUrls),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if appWriteModel.State.Exists() {
|
||||
redirectUrls := append(appWriteModel.RedirectUris, instanceDomain+consoleRedirectPath)
|
||||
logoutUrls := append(appWriteModel.PostLogoutRedirectUris, instanceDomain+consolePostLogoutPath)
|
||||
consoleChangeEvent, err := project.NewOIDCConfigChangedEvent(
|
||||
ctx,
|
||||
ProjectAggregateFromWriteModel(&appWriteModel.WriteModel),
|
||||
appWriteModel.AppID,
|
||||
[]project.OIDCConfigChanges{
|
||||
project.ChangeRedirectURIs(redirectUrls),
|
||||
project.ChangePostLogoutRedirectURIs(logoutUrls),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, consoleChangeEvent)
|
||||
}
|
||||
return []eventstore.Command{
|
||||
instance.NewDomainAddedEvent(ctx, &a.Aggregate, instanceDomain, generated),
|
||||
consoleChangeEvent,
|
||||
}, nil
|
||||
|
||||
return events, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ func (c *Commands) VerifyOIDCClientSecret(ctx context.Context, projectID, appID,
|
||||
return err
|
||||
}
|
||||
if !app.State.Exists() {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-D6hba", "Errors.Project.App.NoExisting")
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-D6hba", "Errors.Project.App.NotExisting")
|
||||
}
|
||||
if !app.IsOIDC() {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-BHgn2", "Errors.Project.App.IsNotOIDC")
|
||||
|
@ -23,5 +23,7 @@ func (f InstanceDomainState) Exists() bool {
|
||||
}
|
||||
|
||||
func NewGeneratedInstanceDomain(instanceName, iamDomain string) string {
|
||||
//TODO: Add random number/string to be unique
|
||||
instanceName = strings.TrimSpace(instanceName)
|
||||
return strings.ToLower(strings.ReplaceAll(instanceName, " ", "-") + "." + iamDomain)
|
||||
}
|
||||
|
@ -34,8 +34,12 @@ func Migrate(ctx context.Context, es *eventstore.Eventstore, migration Migration
|
||||
err = migration.Execute(ctx)
|
||||
logging.OnError(err).Error("migration failed")
|
||||
|
||||
_, err = es.Push(ctx, setupDoneCmd(migration, err))
|
||||
return err
|
||||
_, pushErr := es.Push(ctx, setupDoneCmd(migration, err))
|
||||
logging.OnError(pushErr).Error("migration failed")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pushErr
|
||||
}
|
||||
|
||||
func shouldExec(ctx context.Context, es *eventstore.Eventstore, migration Migration) (should bool, err error) {
|
||||
|
@ -23,6 +23,14 @@ var (
|
||||
name: projection.InstanceColumnID,
|
||||
table: instanceTable,
|
||||
}
|
||||
InstanceColumnName = Column{
|
||||
name: projection.InstanceColumnName,
|
||||
table: instanceTable,
|
||||
}
|
||||
InstanceColumnCreationDate = Column{
|
||||
name: projection.InstanceColumnCreationDate,
|
||||
table: instanceTable,
|
||||
}
|
||||
InstanceColumnChangeDate = Column{
|
||||
name: projection.InstanceColumnChangeDate,
|
||||
table: instanceTable,
|
||||
@ -62,9 +70,10 @@ var (
|
||||
)
|
||||
|
||||
type Instance struct {
|
||||
ID string
|
||||
ChangeDate time.Time
|
||||
Sequence uint64
|
||||
ID string
|
||||
ChangeDate time.Time
|
||||
CreationDate time.Time
|
||||
Sequence uint64
|
||||
|
||||
GlobalOrgID string
|
||||
IAMProjectID string
|
||||
@ -76,6 +85,11 @@ type Instance struct {
|
||||
Host string
|
||||
}
|
||||
|
||||
type Instances struct {
|
||||
SearchResponse
|
||||
Instances []*Instance
|
||||
}
|
||||
|
||||
func (i *Instance) InstanceID() string {
|
||||
return i.ID
|
||||
}
|
||||
@ -101,6 +115,14 @@ type InstanceSearchQueries struct {
|
||||
Queries []SearchQuery
|
||||
}
|
||||
|
||||
func NewInstanceIDsListSearchQuery(ids ...string) (SearchQuery, error) {
|
||||
list := make([]interface{}, len(ids))
|
||||
for i, value := range ids {
|
||||
list[i] = value
|
||||
}
|
||||
return NewListQuery(InstanceColumnID, list, ListIn)
|
||||
}
|
||||
|
||||
func (q *InstanceSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
query = q.SearchRequest.toQuery(query)
|
||||
for _, q := range q.Queries {
|
||||
@ -109,6 +131,24 @@ func (q *InstanceSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder
|
||||
return query
|
||||
}
|
||||
|
||||
func (q *Queries) SearchInstances(ctx context.Context, queries *InstanceSearchQueries) (instances *Instances, err error) {
|
||||
query, scan := prepareInstancesQuery()
|
||||
stmt, args, err := queries.toQuery(query).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInvalidArgument(err, "QUERY-M9fow", "Errors.Query.SQLStatement")
|
||||
}
|
||||
|
||||
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-3j98f", "Errors.Internal")
|
||||
}
|
||||
instances, err = scan(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return instances, err
|
||||
}
|
||||
|
||||
func (q *Queries) Instance(ctx context.Context) (*Instance, error) {
|
||||
stmt, scan := prepareInstanceQuery(authz.GetInstance(ctx).RequestedDomain())
|
||||
query, args, err := stmt.Where(sq.Eq{
|
||||
@ -146,6 +186,7 @@ func (q *Queries) GetDefaultLanguage(ctx context.Context) language.Tag {
|
||||
func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Instance, error)) {
|
||||
return sq.Select(
|
||||
InstanceColumnID.identifier(),
|
||||
InstanceColumnCreationDate.identifier(),
|
||||
InstanceColumnChangeDate.identifier(),
|
||||
InstanceColumnSequence.identifier(),
|
||||
InstanceColumnGlobalOrgID.identifier(),
|
||||
@ -162,6 +203,7 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta
|
||||
lang := ""
|
||||
err := row.Scan(
|
||||
&instance.ID,
|
||||
&instance.CreationDate,
|
||||
&instance.ChangeDate,
|
||||
&instance.Sequence,
|
||||
&instance.GlobalOrgID,
|
||||
@ -182,3 +224,58 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta
|
||||
return instance, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareInstancesQuery() (sq.SelectBuilder, func(*sql.Rows) (*Instances, error)) {
|
||||
return sq.Select(
|
||||
InstanceColumnID.identifier(),
|
||||
InstanceColumnCreationDate.identifier(),
|
||||
InstanceColumnChangeDate.identifier(),
|
||||
InstanceColumnSequence.identifier(),
|
||||
InstanceColumnGlobalOrgID.identifier(),
|
||||
InstanceColumnProjectID.identifier(),
|
||||
InstanceColumnConsoleID.identifier(),
|
||||
InstanceColumnConsoleAppID.identifier(),
|
||||
InstanceColumnSetupStarted.identifier(),
|
||||
InstanceColumnSetupDone.identifier(),
|
||||
InstanceColumnDefaultLanguage.identifier(),
|
||||
countColumn.identifier(),
|
||||
).From(instanceTable.identifier()).PlaceholderFormat(sq.Dollar),
|
||||
func(rows *sql.Rows) (*Instances, error) {
|
||||
instances := make([]*Instance, 0)
|
||||
var count uint64
|
||||
for rows.Next() {
|
||||
instance := new(Instance)
|
||||
lang := ""
|
||||
//TODO: Get Host
|
||||
err := rows.Scan(
|
||||
&instance.ID,
|
||||
&instance.CreationDate,
|
||||
&instance.ChangeDate,
|
||||
&instance.Sequence,
|
||||
&instance.GlobalOrgID,
|
||||
&instance.IAMProjectID,
|
||||
&instance.ConsoleID,
|
||||
&instance.ConsoleAppID,
|
||||
&instance.SetupStarted,
|
||||
&instance.SetupDone,
|
||||
&lang,
|
||||
&count,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instances = append(instances, instance)
|
||||
}
|
||||
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-8nlWW", "Errors.Query.CloseRows")
|
||||
}
|
||||
|
||||
return &Instances{
|
||||
Instances: instances,
|
||||
SearchResponse: SearchResponse{
|
||||
Count: count,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
regexp.QuoteMeta(`SELECT projections.instances.id,`+
|
||||
` projections.instances.creation_date,`+
|
||||
` projections.instances.change_date,`+
|
||||
` projections.instances.sequence,`+
|
||||
` projections.instances.global_org_id,`+
|
||||
@ -64,6 +65,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
regexp.QuoteMeta(`SELECT projections.instances.id,`+
|
||||
` projections.instances.creation_date,`+
|
||||
` projections.instances.change_date,`+
|
||||
` projections.instances.sequence,`+
|
||||
` projections.instances.global_org_id,`+
|
||||
@ -76,6 +78,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
` FROM projections.instances`),
|
||||
[]string{
|
||||
"id",
|
||||
"creation_date",
|
||||
"change_date",
|
||||
"sequence",
|
||||
"global_org_id",
|
||||
@ -89,6 +92,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
uint64(20211108),
|
||||
"global-org-id",
|
||||
"project-id",
|
||||
@ -102,6 +106,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
},
|
||||
object: &Instance{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211108,
|
||||
GlobalOrgID: "global-org-id",
|
||||
@ -121,6 +126,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
want: want{
|
||||
sqlExpectations: mockQueryErr(
|
||||
regexp.QuoteMeta(`SELECT projections.instances.id,`+
|
||||
` projections.instances.creation_date,`+
|
||||
` projections.instances.change_date,`+
|
||||
` projections.instances.sequence,`+
|
||||
` projections.instances.global_org_id,`+
|
||||
|
@ -14,7 +14,9 @@ const (
|
||||
InstanceProjectionTable = "projections.instances"
|
||||
|
||||
InstanceColumnID = "id"
|
||||
InstanceColumnName = "name"
|
||||
InstanceColumnChangeDate = "change_date"
|
||||
InstanceColumnCreationDate = "creation_date"
|
||||
InstanceColumnGlobalOrgID = "global_org_id"
|
||||
InstanceColumnProjectID = "iam_project_id"
|
||||
InstanceColumnConsoleID = "console_client_id"
|
||||
@ -36,10 +38,13 @@ func NewInstanceProjection(ctx context.Context, config crdb.StatementHandlerConf
|
||||
config.InitCheck = crdb.NewTableCheck(
|
||||
crdb.NewTable([]*crdb.Column{
|
||||
crdb.NewColumn(InstanceColumnID, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(InstanceColumnName, crdb.ColumnTypeText, crdb.Default("")),
|
||||
crdb.NewColumn(InstanceColumnChangeDate, crdb.ColumnTypeTimestamp),
|
||||
crdb.NewColumn(InstanceColumnCreationDate, crdb.ColumnTypeTimestamp),
|
||||
crdb.NewColumn(InstanceColumnGlobalOrgID, crdb.ColumnTypeText, crdb.Default("")),
|
||||
crdb.NewColumn(InstanceColumnProjectID, crdb.ColumnTypeText, crdb.Default("")),
|
||||
crdb.NewColumn(InstanceColumnConsoleID, crdb.ColumnTypeText, crdb.Default("")),
|
||||
crdb.NewColumn(InstanceColumnConsoleAppID, crdb.ColumnTypeText, crdb.Default("")),
|
||||
crdb.NewColumn(InstanceColumnSequence, crdb.ColumnTypeInt64),
|
||||
crdb.NewColumn(InstanceColumnSetUpStarted, crdb.ColumnTypeInt64, crdb.Default(0)),
|
||||
crdb.NewColumn(InstanceColumnSetUpDone, crdb.ColumnTypeInt64, crdb.Default(0)),
|
||||
@ -57,6 +62,10 @@ func (p *InstanceProjection) reducers() []handler.AggregateReducer {
|
||||
{
|
||||
Aggregate: instance.AggregateType,
|
||||
EventRedusers: []handler.EventReducer{
|
||||
{
|
||||
Event: instance.InstanceAddedEventType,
|
||||
Reduce: p.reduceInstanceAdded,
|
||||
},
|
||||
{
|
||||
Event: instance.GlobalOrgSetEventType,
|
||||
Reduce: p.reduceGlobalOrgSet,
|
||||
@ -86,19 +95,38 @@ func (p *InstanceProjection) reducers() []handler.AggregateReducer {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *InstanceProjection) reduceInstanceAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*instance.InstanceAddedEvent)
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-29nlS", "reduce.wrong.event.type %s", instance.InstanceAddedEventType)
|
||||
}
|
||||
return crdb.NewCreateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
handler.NewCol(InstanceColumnCreationDate, e.CreationDate()),
|
||||
handler.NewCol(InstanceColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(InstanceColumnSequence, e.Sequence()),
|
||||
handler.NewCol(InstanceColumnName, e.Name),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *InstanceProjection) reduceGlobalOrgSet(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*instance.GlobalOrgSetEvent)
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-2n9f2", "reduce.wrong.event.type %s", instance.GlobalOrgSetEventType)
|
||||
}
|
||||
return crdb.NewUpsertStatement(
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
handler.NewCol(InstanceColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(InstanceColumnSequence, e.Sequence()),
|
||||
handler.NewCol(InstanceColumnGlobalOrgID, e.OrgID),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
@ -107,14 +135,16 @@ func (p *InstanceProjection) reduceIAMProjectSet(event eventstore.Event) (*handl
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-30o0e", "reduce.wrong.event.type %s", instance.ProjectSetEventType)
|
||||
}
|
||||
return crdb.NewUpsertStatement(
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
handler.NewCol(InstanceColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(InstanceColumnSequence, e.Sequence()),
|
||||
handler.NewCol(InstanceColumnProjectID, e.ProjectID),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
@ -123,15 +153,17 @@ func (p *InstanceProjection) reduceConsoleSet(event eventstore.Event) (*handler.
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Dgf11", "reduce.wrong.event.type %s", instance.ConsoleSetEventType)
|
||||
}
|
||||
return crdb.NewUpsertStatement(
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
handler.NewCol(InstanceColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(InstanceColumnSequence, e.Sequence()),
|
||||
handler.NewCol(InstanceColumnConsoleID, e.ClientID),
|
||||
handler.NewCol(InstanceColumnConsoleAppID, e.AppID),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
@ -140,14 +172,16 @@ func (p *InstanceProjection) reduceDefaultLanguageSet(event eventstore.Event) (*
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-30o0e", "reduce.wrong.event.type %s", instance.DefaultLanguageSetEventType)
|
||||
}
|
||||
return crdb.NewUpsertStatement(
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
handler.NewCol(InstanceColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(InstanceColumnSequence, e.Sequence()),
|
||||
handler.NewCol(InstanceColumnDefaultLanguage, e.Language.String()),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(InstanceColumnID, e.Aggregate().InstanceID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ func (p *InstanceDomainProjection) reducers() []handler.AggregateReducer {
|
||||
Reduce: p.reduceDomainAdded,
|
||||
},
|
||||
{
|
||||
Event: instance.InstanceDomainAddedEventType,
|
||||
Event: instance.InstanceDomainPrimarySetEventType,
|
||||
Reduce: p.reduceDomainPrimarySet,
|
||||
},
|
||||
{
|
||||
|
@ -20,7 +20,37 @@ func TestInstanceProjection_reduces(t *testing.T) {
|
||||
args args
|
||||
reduce func(event eventstore.Event) (*handler.Statement, error)
|
||||
want wantReduce
|
||||
}{
|
||||
}{{
|
||||
name: "reduceInstanceAdded",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.InstanceAddedEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{"name": "Name"}`),
|
||||
), instance.InstanceAddedEventMapper),
|
||||
},
|
||||
reduce: (&InstanceProjection{}).reduceInstanceAdded,
|
||||
want: wantReduce{
|
||||
projection: InstanceProjectionTable,
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.instances (id, creation_date, change_date, sequence, name) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"Name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reduceGlobalOrgSet",
|
||||
args: args{
|
||||
@ -39,12 +69,12 @@ func TestInstanceProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPSERT INTO projections.instances (id, change_date, sequence, global_org_id) VALUES ($1, $2, $3, $4)",
|
||||
expectedStmt: "UPDATE projections.instances SET (change_date, sequence, global_org_id) = ($1, $2, $3) WHERE (id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"orgid",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -69,12 +99,12 @@ func TestInstanceProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPSERT INTO projections.instances (id, change_date, sequence, iam_project_id) VALUES ($1, $2, $3, $4)",
|
||||
expectedStmt: "UPDATE projections.instances SET (change_date, sequence, iam_project_id) = ($1, $2, $3) WHERE (id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"project-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -99,12 +129,12 @@ func TestInstanceProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPSERT INTO projections.instances (id, change_date, sequence, default_language) VALUES ($1, $2, $3, $4)",
|
||||
expectedStmt: "UPDATE projections.instances SET (change_date, sequence, default_language) = ($1, $2, $3) WHERE (id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"en",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -96,7 +96,7 @@ func NewDomainPrimarySetEvent(ctx context.Context, aggregate *eventstore.Aggrega
|
||||
}
|
||||
|
||||
func DomainPrimarySetEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
orgDomainAdded := &DomainAddedEvent{
|
||||
orgDomainAdded := &DomainPrimarySetEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
err := json.Unmarshal(event.Data, orgDomainAdded)
|
||||
|
@ -2525,34 +2525,6 @@ service AdminService {
|
||||
};
|
||||
}
|
||||
|
||||
//Truncates the delta of the change stream
|
||||
// be carefull with this function because ZITADEL has to
|
||||
// recompute the deltas after they got cleared.
|
||||
// Search requests will return wrong results until all deltas are recomputed
|
||||
rpc ClearView(ClearViewRequest) returns (ClearViewResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/views/{database}/{view_name}";
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.write";
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
tags: "views";
|
||||
external_docs: {
|
||||
url: "https://docs.zitadel.ch/concepts#Software_Architecture";
|
||||
description: "details of ZITADEL's event driven software concepts";
|
||||
};
|
||||
responses: {
|
||||
key: "200";
|
||||
value: {
|
||||
description: "View cleared";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
//Returns event descriptions which cannot be processed.
|
||||
// It's possible that some events need some retries.
|
||||
// For example if the SMTP-API wasn't able to send an email at the first time
|
||||
@ -4512,34 +4484,6 @@ message ListViewsResponse {
|
||||
repeated View result = 1;
|
||||
}
|
||||
|
||||
message ClearViewRequest {
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
|
||||
json_schema: {
|
||||
required: ["database", "view_name"]
|
||||
};
|
||||
};
|
||||
|
||||
string database = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"adminapi\"";
|
||||
min_length: 1;
|
||||
max_length: 200;
|
||||
}
|
||||
];
|
||||
string view_name = 2 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"iam_members\"";
|
||||
min_length: 1;
|
||||
max_length: 200;
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
//This is an empty response
|
||||
message ClearViewResponse {}
|
||||
|
||||
//This is an empty request
|
||||
message ListFailedEventsRequest {}
|
||||
|
||||
|
@ -25,11 +25,6 @@ message Instance {
|
||||
example: "\"ZITADEL\"";
|
||||
}
|
||||
];
|
||||
string version = 5 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"v1.0.0\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
enum State {
|
||||
@ -44,31 +39,19 @@ message Query {
|
||||
oneof query {
|
||||
option (validate.required) = true;
|
||||
|
||||
IdQuery id_query = 1;
|
||||
StateQuery state_query = 2;
|
||||
IdsQuery id_query = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//IdQuery is always equals
|
||||
message IdQuery {
|
||||
string id = 1 [
|
||||
(validate.rules).string = {max_len: 200},
|
||||
message IdsQuery {
|
||||
repeated string ids = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "4820840938402429";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
//StateQuery is always equals
|
||||
message StateQuery {
|
||||
State state = 1 [
|
||||
(validate.rules).enum.defined_only = true,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "current state of the instance";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
enum FieldName {
|
||||
FIELD_NAME_UNSPECIFIED = 0;
|
||||
FIELD_NAME_ID = 1;
|
||||
|
@ -105,7 +105,7 @@ service SystemService {
|
||||
// Returns a list of ZITADEL instances/tenants
|
||||
rpc ListInstances(ListInstancesRequest) returns (ListInstancesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/instances"
|
||||
post: "/instances/_search"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
@ -134,17 +134,11 @@ service SystemService {
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the usage metrics of an instance
|
||||
rpc GetUsage(GetUsageRequest) returns (GetUsageResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/instances/{id}/usage";
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the custom domains of an instance
|
||||
rpc ListDomains(ListDomainsRequest) returns (ListDomainsResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/instances/{id}/domains";
|
||||
post: "/instances/{id}/domains/_search";
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
@ -178,6 +172,7 @@ service SystemService {
|
||||
rpc ListViews(ListViewsRequest) returns (ListViewsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/views/_search";
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
@ -229,6 +224,7 @@ service SystemService {
|
||||
rpc ListFailedEvents(ListFailedEventsRequest) returns (ListFailedEventsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/failedevents/_search";
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
@ -322,19 +318,19 @@ message GetInstanceResponse {
|
||||
|
||||
message AddInstanceRequest {
|
||||
string instance_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string first_org_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string first_org_name = 2 [(validate.rules).string = {max_len: 200}];
|
||||
string custom_domain = 3 [(validate.rules).string = {max_len: 200}];
|
||||
string owner_first_name = 4 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string owner_last_name = 5 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string owner_first_name = 4 [(validate.rules).string = {max_len: 200}];
|
||||
string owner_last_name = 5 [(validate.rules).string = {max_len: 200}];
|
||||
string owner_email = 6 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string owner_username = 7 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string password = 8 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
uint64 request_limit = 9;
|
||||
uint64 action_mins_limit = 10;
|
||||
string owner_username = 7 [(validate.rules).string = {max_len: 200}];
|
||||
uint64 request_limit = 8;
|
||||
uint64 action_mins_limit = 9;
|
||||
}
|
||||
|
||||
message AddInstanceResponse {
|
||||
string id = 1;
|
||||
zitadel.v1.ObjectDetails details = 2;
|
||||
}
|
||||
|
||||
message RemoveInstanceRequest {
|
||||
|
Loading…
x
Reference in New Issue
Block a user