diff --git a/Makefile b/Makefile index 8f9be14bc0..8ef48623f6 100644 --- a/Makefile +++ b/Makefile @@ -33,10 +33,10 @@ core_static: .PHONY: core_generate_all core_generate_all: - go install github.com/dmarkham/enumer@v1.5.9 # https://pkg.go.dev/github.com/dmarkham/enumer?tab=versions + go install github.com/dmarkham/enumer@v1.5.10 # https://pkg.go.dev/github.com/dmarkham/enumer?tab=versions go install github.com/rakyll/statik@v0.1.7 # https://pkg.go.dev/github.com/rakyll/statik?tab=versions go install go.uber.org/mock/mockgen@v0.4.0 # https://pkg.go.dev/go.uber.org/mock/mockgen?tab=versions - go install golang.org/x/tools/cmd/stringer@v0.19.0 # https://pkg.go.dev/golang.org/x/tools/cmd/stringer?tab=versions + go install golang.org/x/tools/cmd/stringer@v0.22.0 # https://pkg.go.dev/golang.org/x/tools/cmd/stringer?tab=versions go generate ./... .PHONY: core_assets @@ -57,12 +57,12 @@ endif .PHONY: core_grpc_dependencies core_grpc_dependencies: - go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.33 # https://pkg.go.dev/google.golang.org/protobuf/cmd/protoc-gen-go?tab=versions - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3 # https://pkg.go.dev/google.golang.org/grpc/cmd/protoc-gen-go-grpc?tab=versions - go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v2.19.1 # https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway?tab=versions - go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@v2.19.1 # https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2?tab=versions + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.2 # https://pkg.go.dev/google.golang.org/protobuf/cmd/protoc-gen-go?tab=versions + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.4 # https://pkg.go.dev/google.golang.org/grpc/cmd/protoc-gen-go-grpc?tab=versions + go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v2.20.0 # https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway?tab=versions + go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@v2.20.0 # https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2?tab=versions go install github.com/envoyproxy/protoc-gen-validate@v1.0.4 # https://pkg.go.dev/github.com/envoyproxy/protoc-gen-validate?tab=versions - go install github.com/bufbuild/buf/cmd/buf@v1.30.0 # https://pkg.go.dev/github.com/bufbuild/buf/cmd/buf?tab=versions + go install github.com/bufbuild/buf/cmd/buf@v1.34.0 # https://pkg.go.dev/github.com/bufbuild/buf/cmd/buf?tab=versions .PHONY: core_api core_api: core_api_generator core_grpc_dependencies diff --git a/cmd/setup/28.go b/cmd/setup/28.go new file mode 100644 index 0000000000..3bfcb2d613 --- /dev/null +++ b/cmd/setup/28.go @@ -0,0 +1,27 @@ +package setup + +import ( + "context" + _ "embed" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" +) + +var ( + //go:embed 28.sql + addFieldTable string +) + +type AddFieldTable struct { + dbClient *database.DB +} + +func (mig *AddFieldTable) Execute(ctx context.Context, _ eventstore.Event) error { + _, err := mig.dbClient.ExecContext(ctx, addFieldTable) + return err +} + +func (mig *AddFieldTable) String() string { + return "28_add_search_table" +} diff --git a/cmd/setup/28.sql b/cmd/setup/28.sql new file mode 100644 index 0000000000..ccadf3a0a6 --- /dev/null +++ b/cmd/setup/28.sql @@ -0,0 +1,64 @@ +CREATE TABLE eventstore.fields ( + id TEXT NOT NULL DEFAULT gen_random_uuid() + , instance_id TEXT NOT NULL + , resource_owner TEXT NOT NULL + + , aggregate_type TEXT NOT NULL + , aggregate_id TEXT NOT NULL + + , object_type TEXT NOT NULL + , object_id TEXT NOT NULL + , object_revision INT2 + + , field_name TEXT NOT NULL + + -- all the values of fields are inserted into value column as jsonb and if we need to index something we store it to the type specific column additionally + , "value" JSONB NOT NULL + , number_value NUMERIC GENERATED ALWAYS AS (CASE WHEN should_index AND JSONB_TYPEOF("value") = 'number' THEN "value"::NUMERIC ELSE NULL END) STORED + , text_value TEXT GENERATED ALWAYS AS (CASE WHEN should_index AND JSONB_TYPEOF("value") = 'string' THEN "value" #>> '{}' ELSE NULL END) STORED + , bool_value BOOLEAN GENERATED ALWAYS AS (CASE WHEN should_index AND JSONB_TYPEOF("value") = 'boolean' THEN "value"::BOOLEAN ELSE NULL END) STORED + + -- if true the value must be unique within an instance + , value_must_be_unique BOOLEAN + -- if set to true the primitive value is indexed + , should_index BOOLEAN + + , PRIMARY KEY (instance_id, id) + -- TODO: create issue to enable the foreign key as soon as the objects table is implemented + -- , CONSTRAINT f_objects_fk FOREIGN KEY (instance_id, resource_owner, object_type, object_id, object_revision) REFERENCES eventstore.objects (instance_id, resource_owner, object_type, object_id, object_revision) ON DELETE CASCADE + + -- the constraint ensures that a primitive value is set if the value must be unique + , CONSTRAINT primitive_value_for_unique_check CHECK ( + CASE + WHEN value_must_be_unique THEN num_nonnulls(number_value, text_value, bool_value) = 1 + ELSE true + END + ) + -- the constraint ensures that a primitive value is set if the value must be indexed + , CONSTRAINT primitive_value_for_index CHECK ( + CASE + WHEN should_index THEN num_nonnulls(number_value, text_value, bool_value) = 1 + ELSE true + END + ) +); + +-- unique constraints for primitive values +CREATE UNIQUE INDEX IF NOT EXISTS f_number_unique_idx ON eventstore.fields (instance_id, field_name, number_value) WHERE value_must_be_unique; +CREATE UNIQUE INDEX IF NOT EXISTS f_text_unique_idx ON eventstore.fields (instance_id, field_name, text_value) WHERE value_must_be_unique; +CREATE UNIQUE INDEX IF NOT EXISTS f_bool_unique_idx ON eventstore.fields (instance_id, field_name, bool_value) WHERE value_must_be_unique; + +-- search index for primitive values +CREATE INDEX IF NOT EXISTS f_number_value_idx ON eventstore.fields (instance_id, object_type, field_name, number_value) + INCLUDE (resource_owner, object_id, object_revision, "value") + WHERE number_value IS NOT NULL ; +CREATE INDEX IF NOT EXISTS f_text_value_idx ON eventstore.fields (instance_id, object_type, field_name, text_value) + INCLUDE (resource_owner, object_id, object_revision, "value") + WHERE text_value IS NOT NULL ; +CREATE INDEX IF NOT EXISTS f_bool_value_idx ON eventstore.fields (instance_id, object_type, field_name, bool_value) + INCLUDE (resource_owner, object_id, object_revision, "value") + WHERE bool_value IS NOT NULL ; + +-- search index for object by id +CREATE INDEX IF NOT EXISTS f_object_idx ON eventstore.fields (instance_id, object_type, object_id, object_revision) + INCLUDE (resource_owner, field_name, "value") ; \ No newline at end of file diff --git a/cmd/setup/29.go b/cmd/setup/29.go new file mode 100644 index 0000000000..1d3bfe992d --- /dev/null +++ b/cmd/setup/29.go @@ -0,0 +1,42 @@ +package setup + +import ( + "context" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/query/projection" + "github.com/zitadel/zitadel/internal/repository/instance" +) + +type FillFieldsForProjectGrant struct { + eventstore *eventstore.Eventstore +} + +func (mig *FillFieldsForProjectGrant) Execute(ctx context.Context, _ eventstore.Event) error { + instances, err := mig.eventstore.InstanceIDs( + ctx, + 0, + true, + eventstore.NewSearchQueryBuilder(eventstore.ColumnsInstanceIDs). + OrderDesc(). + AddQuery(). + AggregateTypes("instance"). + EventTypes(instance.InstanceAddedEventType). + Builder(), + ) + if err != nil { + return err + } + for _, instance := range instances { + ctx := authz.WithInstanceID(ctx, instance) + if err := projection.ProjectGrantFields.Trigger(ctx); err != nil { + return err + } + } + return nil +} + +func (mig *FillFieldsForProjectGrant) String() string { + return "29_init_fields_for_project_grant" +} diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 81ec6f2332..ebeaa7a91c 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -111,6 +111,8 @@ type Steps struct { s25User11AddLowerFieldsToVerifiedEmail *User11AddLowerFieldsToVerifiedEmail s26AuthUsers3 *AuthUsers3 s27IDPTemplate6SAMLNameIDFormat *IDPTemplate6SAMLNameIDFormat + s28AddFieldTable *AddFieldTable + s29FillFieldsForProjectGrant *FillFieldsForProjectGrant } func MustNewSteps(v *viper.Viper) *Steps { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index 6ed0cc4dc7..77395e58e2 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -108,8 +108,11 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string) logging.OnError(err).Fatal("unable to connect to database") config.Eventstore.Querier = old_es.NewCRDB(queryDBClient) - config.Eventstore.Pusher = new_es.NewEventstore(esPusherDBClient) + esV3 := new_es.NewEventstore(esPusherDBClient) + config.Eventstore.Pusher = esV3 + config.Eventstore.Searcher = esV3 eventstoreClient := eventstore.NewEventstore(config.Eventstore) + logging.OnError(err).Fatal("unable to start eventstore") eventstoreV4 := es_v4.NewEventstoreFromOne(es_v4_pg.New(queryDBClient, &es_v4_pg.Config{ MaxRetries: config.Eventstore.MaxRetries, @@ -153,6 +156,8 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string) steps.s25User11AddLowerFieldsToVerifiedEmail = &User11AddLowerFieldsToVerifiedEmail{dbClient: esPusherDBClient} steps.s26AuthUsers3 = &AuthUsers3{dbClient: esPusherDBClient} steps.s27IDPTemplate6SAMLNameIDFormat = &IDPTemplate6SAMLNameIDFormat{dbClient: esPusherDBClient} + steps.s28AddFieldTable = &AddFieldTable{dbClient: esPusherDBClient} + steps.s29FillFieldsForProjectGrant = &FillFieldsForProjectGrant{eventstore: eventstoreClient} err = projection.Create(ctx, projectionDBClient, eventstoreClient, config.Projections, nil, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -175,6 +180,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string) steps.s14NewEventsTable, steps.s1ProjectionTable, steps.s2AssetsTable, + steps.s28AddFieldTable, steps.FirstInstance, steps.s5LastFailed, steps.s6OwnerRemoveColumns, @@ -191,6 +197,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string) steps.s23CorrectGlobalUniqueConstraints, steps.s24AddActorToAuthTokens, steps.s26AuthUsers3, + steps.s29FillFieldsForProjectGrant, } { mustExecuteMigration(ctx, eventstoreClient, step, "migration failed") } diff --git a/cmd/setup/steps.yaml b/cmd/setup/steps.yaml index 03476c6648..265549c379 100644 --- a/cmd/setup/steps.yaml +++ b/cmd/setup/steps.yaml @@ -54,3 +54,6 @@ CorrectCreationDate: AddEventCreatedAt: BulkAmount: 100 # ZITADEL_ADDEVENTCREATEDAT_BULKAMOUNT + +FillFields: + BatchSize: 1000 # ZITADEL_EVENTSTORE_FILLFIELDS_BULKLIMIT \ No newline at end of file diff --git a/cmd/start/start.go b/cmd/start/start.go index a7688a83cb..7d2c1e61f6 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -153,6 +153,7 @@ func startZitadel(ctx context.Context, config *Config, masterKey string, server } config.Eventstore.Pusher = new_es.NewEventstore(esPusherDBClient) + config.Eventstore.Searcher = new_es.NewEventstore(queryDBClient) config.Eventstore.Querier = old_es.NewCRDB(queryDBClient) eventstoreClient := eventstore.NewEventstore(config.Eventstore) eventstoreV4 := es_v4.NewEventstoreFromOne(es_v4_pg.New(queryDBClient, &es_v4_pg.Config{ diff --git a/console/src/app/modules/idp-table/idp-table.component.html b/console/src/app/modules/idp-table/idp-table.component.html index 70c94343d5..db4f04216b 100644 --- a/console/src/app/modules/idp-table/idp-table.component.html +++ b/console/src/app/modules/idp-table/idp-table.component.html @@ -87,7 +87,7 @@
- SAML SP + SAML
coming soon
diff --git a/console/src/app/modules/policies/idp-settings/idp-settings.component.html b/console/src/app/modules/policies/idp-settings/idp-settings.component.html index 7424a63924..b49fa598fb 100644 --- a/console/src/app/modules/policies/idp-settings/idp-settings.component.html +++ b/console/src/app/modules/policies/idp-settings/idp-settings.component.html @@ -214,7 +214,7 @@ >
- SAML SP + SAML
diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 240eee8c2b..79b36dc748 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -2047,7 +2047,7 @@ "DESCRIPTION": "Enter the credentials for your Apple Provider" }, "SAML": { - "TITLE": "Sign in with SAML SP", + "TITLE": "Sign in with SAML", "DESCRIPTION": "Enter the credentials for your SAML Provider" } }, diff --git a/console/src/styles/table.scss b/console/src/styles/table.scss index 68457949c3..933366607e 100644 --- a/console/src/styles/table.scss +++ b/console/src/styles/table.scss @@ -76,6 +76,10 @@ padding: 0 0.5rem !important; } + .mat-calendar-table-header tr th { + height: 14px; + } + td .mat-mdc-checkbox, th .mat-checkbox { margin-left: 1rem; diff --git a/docs/docs/guides/integrate/identity-providers/azure-ad-saml.mdx b/docs/docs/guides/integrate/identity-providers/azure-ad-saml.mdx index 45dd00f0ce..236488431f 100644 --- a/docs/docs/guides/integrate/identity-providers/azure-ad-saml.mdx +++ b/docs/docs/guides/integrate/identity-providers/azure-ad-saml.mdx @@ -51,7 +51,7 @@ Configure the sign-on method of the app. ### Go to the IdP Providers Overview - + ### Create a new SAML Service Provider (SP) @@ -70,7 +70,7 @@ Now we configure the identity provider on ZITADEL. ## Configure Basic SAML Configuration -After you created the SAML SP in ZITADEL, you can copy the URLs you need to configure in your Entra ID application. +After you created the SAML provider in ZITADEL, you can copy the URLs you need to configure in your Entra ID application. ![Azure SAML App URLs](/img/guides/zitadel_azure_saml_provider_urls.png) diff --git a/docs/docs/guides/integrate/identity-providers/mocksaml.mdx b/docs/docs/guides/integrate/identity-providers/mocksaml.mdx index 67fa8220e3..4cdfa90ad5 100644 --- a/docs/docs/guides/integrate/identity-providers/mocksaml.mdx +++ b/docs/docs/guides/integrate/identity-providers/mocksaml.mdx @@ -23,23 +23,23 @@ MockSAML is not intended for any production environment, only for test purposes ### Download metadata You can either download the metadata under [https://mocksaml.com/api/saml/metadata?download=true](https://mocksaml.com/api/saml/metadata?download=true) or skip this step and -fill in the URL when creating the SAML SP in ZITADEL. +fill in the URL when creating the SAML Provider in ZITADEL. ## ZITADEL configuration ### Go to the IdP providers overview - + ### Create a new SAML ServiceProvider -The SAML SP provider template has everything you need preconfigured. +The SAML provider template has everything you need preconfigured. Add the metadata.xml or the URL to the metadata which are accessible by you ZITADEL instance. All the necessary configuration is contained in the metadata which has to be exchanged by the ServiceProvider and the IdentityProvider. -![SAML SP Provider](/img/guides/zitadel_saml_create_provider.png) +![SAML Provider](/img/guides/zitadel_saml_create_provider.png) ### Download metadata @@ -50,7 +50,7 @@ They are available under `https://${CUSTOMDOMAIN}/idps/\{ID of the provider in Z -![Activate the SAML SP Provider](/img/guides/zitadel_activate_saml.png) +![Activate the SAML Provider](/img/guides/zitadel_activate_saml.png) ### Ensure your Login Policy allows External IDPs @@ -58,11 +58,11 @@ They are available under `https://${CUSTOMDOMAIN}/idps/\{ID of the provider in Z ## Test the setup - + -![SAML SP Button](/img/guides/zitadel_login_saml.png) +![SAML Button](/img/guides/zitadel_login_saml.png) -![SAML SP Login](/img/guides/mocksaml_login.png) +![SAML Login](/img/guides/mocksaml_login.png) ## Optional: Add ZITADEL action to autofill userdata diff --git a/docs/docs/guides/integrate/identity-providers/okta_saml.mdx b/docs/docs/guides/integrate/identity-providers/okta_saml.mdx index c5ab416eaa..49eb8e6c44 100644 --- a/docs/docs/guides/integrate/identity-providers/okta_saml.mdx +++ b/docs/docs/guides/integrate/identity-providers/okta_saml.mdx @@ -1,6 +1,6 @@ --- title: Configure OKTA as a SAML Identity Provider in ZITADEL -sidebar_label: OKTA SAML SP +sidebar_label: OKTA SAML id: okta-saml --- @@ -18,12 +18,12 @@ import TestSetup from './_test_setup.mdx'; ### Go to the IdP Providers Overview - + -### Create a new SAML Service Provider (SP) +### Create a new SAML Provider To be able to create the application in OKTA we need the provider id from ZITADEL. -1. Create a new SAML SP with a name and a random text in the Metadata Xml field. +1. Create a new SAML Provider with a name and a random text in the Metadata Xml field. We will fill that as soon as we have done the configuration in OKTA. 2. Save Configuration @@ -33,7 +33,7 @@ As an alternative you can add the SAML identity provider through the API, either ![OKTA Provider Empty](/img/guides/zitadel_okta_saml_provider_empty.png) -After you created the SAML SP in ZITADEL, you can copy the URLs you need to configure in your OKTA application. +After you created the SAML Provider in ZITADEL, you can copy the URLs you need to configure in your OKTA application. ![OKTA SAML App URLs](/img/guides/zitadel_okta_saml_provider_urls.png) diff --git a/go.mod b/go.mod index f801360470..1185ffd963 100644 --- a/go.mod +++ b/go.mod @@ -3,31 +3,31 @@ module github.com/zitadel/zitadel go 1.22.2 require ( - cloud.google.com/go/storage v1.40.0 - github.com/BurntSushi/toml v1.3.2 + cloud.google.com/go/storage v1.43.0 + github.com/BurntSushi/toml v1.4.0 github.com/DATA-DOG/go-sqlmock v1.5.2 - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.22.0 + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.24.0 github.com/Masterminds/squirrel v1.5.4 github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b github.com/benbjohnson/clock v1.3.5 - github.com/boombuler/barcode v1.0.1 + github.com/boombuler/barcode v1.0.2 github.com/brianvoe/gofakeit/v6 v6.28.0 - github.com/cockroachdb/cockroach-go/v2 v2.3.7 + github.com/cockroachdb/cockroach-go/v2 v2.3.8 github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be github.com/crewjam/saml v0.4.14 github.com/descope/virtualwebauthn v1.0.2 - github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 - github.com/dop251/goja_nodejs v0.0.0-20240221231712-27eeffc9c235 + github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2 + github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf github.com/drone/envsubst v1.0.3 github.com/envoyproxy/protoc-gen-validate v1.0.4 - github.com/fatih/color v1.16.0 - github.com/gabriel-vasile/mimetype v1.4.3 + github.com/fatih/color v1.17.0 + github.com/gabriel-vasile/mimetype v1.4.4 github.com/go-jose/go-jose/v4 v4.0.2 - github.com/go-ldap/ldap/v3 v3.4.7 + github.com/go-ldap/ldap/v3 v3.4.8 github.com/go-webauthn/webauthn v0.10.2 github.com/gorilla/csrf v1.7.2 github.com/gorilla/mux v1.8.1 - github.com/gorilla/schema v1.3.0 + github.com/gorilla/schema v1.4.1 github.com/gorilla/securecookie v1.1.2 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -35,13 +35,13 @@ require ( github.com/h2non/gock v1.2.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/improbable-eng/grpc-web v0.15.0 - github.com/jackc/pgx/v5 v5.5.5 + github.com/jackc/pgx/v5 v5.6.0 github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 github.com/jinzhu/gorm v1.9.16 github.com/k3a/html2text v1.2.1 - github.com/kevinburke/twilio-go v0.0.0-20231009225535-38b36b35294d + github.com/kevinburke/twilio-go v0.0.0-20240623211326-c7334b537077 github.com/lucasb-eyer/go-colorful v1.2.0 - github.com/minio/minio-go/v7 v7.0.69 + github.com/minio/minio-go/v7 v7.0.73 github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/gamut v0.3.1 github.com/muhlemmer/gu v0.3.1 @@ -52,49 +52,53 @@ require ( github.com/rs/cors v1.11.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sony/sonyflake v1.2.0 - github.com/spf13/cobra v1.8.0 - github.com/spf13/viper v1.18.2 + github.com/spf13/cobra v1.8.1 + github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 github.com/ttacon/libphonenumber v1.2.1 github.com/zitadel/logging v0.6.0 - github.com/zitadel/oidc/v3 v3.25.0 + github.com/zitadel/oidc/v3 v3.25.1 github.com/zitadel/passwap v0.6.0 github.com/zitadel/saml v0.1.3 github.com/zitadel/schema v1.3.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 - go.opentelemetry.io/otel v1.27.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 - go.opentelemetry.io/otel/exporters/prometheus v0.49.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 - go.opentelemetry.io/otel/metric v1.27.0 - go.opentelemetry.io/otel/sdk v1.27.0 - go.opentelemetry.io/otel/sdk/metric v1.27.0 - go.opentelemetry.io/otel/trace v1.27.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 + go.opentelemetry.io/otel v1.28.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 + go.opentelemetry.io/otel/exporters/prometheus v0.50.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 + go.opentelemetry.io/otel/metric v1.28.0 + go.opentelemetry.io/otel/sdk v1.28.0 + go.opentelemetry.io/otel/sdk/metric v1.28.0 + go.opentelemetry.io/otel/trace v1.28.0 go.uber.org/mock v0.4.0 golang.org/x/crypto v0.24.0 - golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 golang.org/x/net v0.26.0 golang.org/x/oauth2 v0.21.0 golang.org/x/sync v0.7.0 golang.org/x/text v0.16.0 - google.golang.org/api v0.172.0 - google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 - google.golang.org/grpc v1.64.0 + google.golang.org/api v0.187.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 + google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 sigs.k8s.io/yaml v1.4.0 ) require ( - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.46.0 // indirect + cloud.google.com/go/auth v0.6.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.0 // indirect github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/crewjam/httperr v0.2.0 // indirect github.com/go-chi/chi/v5 v5.0.12 // indirect + github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/go-webauthn/x v0.1.9 // indirect + github.com/goccy/go-json v0.10.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -102,12 +106,13 @@ require ( github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/lib/pq v1.10.9 // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/pelletier/go-toml/v2 v2.2.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -115,15 +120,15 @@ require ( github.com/zenazn/goji v1.0.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect + google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect ) require ( - cloud.google.com/go v0.112.2 // indirect + cloud.google.com/go v0.115.0 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect - cloud.google.com/go/iam v1.1.7 // indirect - cloud.google.com/go/trace v1.10.6 // indirect + cloud.google.com/go/iam v1.1.8 // indirect + cloud.google.com/go/trace v1.10.7 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/amdonov/xmlsig v0.1.0 // indirect github.com/beevik/etree v1.3.0 @@ -155,7 +160,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/uuid v1.6.0 github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.3 // indirect + github.com/googleapis/gax-go/v2 v2.12.5 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/h2non/filetype v1.1.3 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect @@ -165,24 +170,20 @@ require ( github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jonboulle/clockwork v0.4.0 - github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7 // indirect - github.com/kevinburke/rest v0.0.0-20231107185522-a9c371f90234 // indirect - github.com/klauspost/compress v1.17.8 // indirect + github.com/kevinburke/rest v0.0.0-20240617045629-3ed0ad3487f0 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/sha256-simd v1.0.1 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 // indirect github.com/muesli/kmeans v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.54.0 // indirect + github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russellhaering/goxmldsig v1.4.0 // indirect @@ -195,7 +196,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/sys v0.21.0 gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 77a9df95e5..629064c288 100644 --- a/go.sum +++ b/go.sum @@ -1,36 +1,40 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.112.2 h1:ZaGT6LiG7dBzi6zNOvVZwacaXlmf3lRqnC4DQzqyRQw= -cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= +cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= +cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= +cloud.google.com/go/auth v0.6.1 h1:T0Zw1XM5c1GlpN2HYr2s+m3vr1p2wy+8VN+Z1FKxW38= +cloud.google.com/go/auth v0.6.1/go.mod h1:eFHG7zDzbXHKmjJddFG/rBlcGp6t25SwRUiEQSlO4x4= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= -cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA= -cloud.google.com/go/logging v1.9.0 h1:iEIOXFO9EmSiTjDmfpbRjOxECO7R8C7b8IXUGOj7xZw= -cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= -cloud.google.com/go/longrunning v0.5.6 h1:xAe8+0YaWoCKr9t1+aWe+OeQgN/iJK1fEgZSXmjuEaE= -cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA= -cloud.google.com/go/monitoring v1.18.1 h1:0yvFXK+xQd95VKo6thndjwnJMno7c7Xw1CwMByg0B+8= -cloud.google.com/go/monitoring v1.18.1/go.mod h1:52hTzJ5XOUMRm7jYi7928aEdVxBEmGwA0EjNJXIBvt8= -cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJahPw= -cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g= -cloud.google.com/go/trace v1.10.6 h1:XF0Ejdw0NpRfAvuZUeQe3ClAG4R/9w5JYICo7l2weaw= -cloud.google.com/go/trace v1.10.6/go.mod h1:EABXagUjxGuKcZMy4pXyz0fJpE5Ghog3jzTxcEsVJS4= +cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0= +cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE= +cloud.google.com/go/logging v1.10.0 h1:f+ZXMqyrSJ5vZ5pE/zr0xC8y/M9BLNzQeLBwfeZ+wY4= +cloud.google.com/go/logging v1.10.0/go.mod h1:EHOwcxlltJrYGqMGfghSet736KR3hX1MAj614mrMk9I= +cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU= +cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= +cloud.google.com/go/monitoring v1.19.0 h1:NCXf8hfQi+Kmr56QJezXRZ6GPb80ZI7El1XztyUuLQI= +cloud.google.com/go/monitoring v1.19.0/go.mod h1:25IeMR5cQ5BoZ8j1eogHE5VPJLlReQ7zFp5OiLgiGZw= +cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= +cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= +cloud.google.com/go/trace v1.10.7 h1:gK8z2BIJQ3KIYGddw9RJLne5Fx0FEXkrEQzPaeEYVvk= +cloud.google.com/go/trace v1.10.7/go.mod h1:qk3eiKmZX0ar2dzIJN/3QhY2PIFh1eqcIdaN5uEjQPM= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.22.0 h1:xl4IRfBXPZxwu7dIza8n6wdX5zEJpi0boF5dX22MbYE= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.22.0/go.mod h1:P69hhmQh4zwnU5iEdGVowFWg1DiP9x2KsCYBOIaP4us= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.46.0 h1:vaXjFX09ygxNxAiHwByzPBVKltYFVZR8HN4U3TR4vn8= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.46.0/go.mod h1:V28hx+cUCZC9e3qcqszMb+Sbt8cQZtHTiXOmyDzoDOg= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.46.0 h1:xlfPHZ5QFvHad9KmrVDoaPpJUT/XluwNDMNHn+k7z/s= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.46.0/go.mod h1:mzI44HpPp75Z8/a1sJP1asdHdu7Wui7t10SZ9EEPPnM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.24.0 h1:TBo1ql03qmVkZzEndpfkS4i9dOgCVvO0rQP7HEth110= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.24.0/go.mod h1:pix4dhb6R3oDGZgQhkEGGC+5ZTz6kcxOhS4lhsSJhrE= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.0 h1:3vze4eFE3z2tDy2iSeI7yCQ17L8iLxN4OkXgvTr979s= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.0/go.mod h1:PdB0wkmILI+phhoBhWdrrB4LfORT9tHc03OOn+q3dWU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.0 h1:ng6QH9Z4bAXCf0Z1cjR5hKESyc1BUiOrfIOhN+nHfRU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.0/go.mod h1:ZC7rjqRzdhRKDK223jQ7Tsz89ZtrSSLH/VFzf7k5Sb0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= @@ -77,8 +81,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4= +github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -90,14 +94,11 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cockroachdb/cockroach-go/v2 v2.3.7 h1:nq5GYDuA2zIR/kdLkVLTg7oHTw0UbGU9RWpC+OZVYYU= -github.com/cockroachdb/cockroach-go/v2 v2.3.7/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= +github.com/cockroachdb/cockroach-go/v2 v2.3.8 h1:53yoUo4+EtrC1NrAEgnnad4AS3ntNvGup1PAXZ7UmpE= +github.com/cockroachdb/cockroach-go/v2 v2.3.8/go.mod h1:9uH5jK4yQ3ZQUT9IXe4I2fHzMIF5+JC/oOdzTRgJYJk= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= @@ -106,7 +107,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo= @@ -124,17 +125,12 @@ github.com/descope/virtualwebauthn v1.0.2/go.mod h1:iJvinjD1iZYqQ09J5lF0+795OdDb github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 h1:O7I1iuzEA7SG+dK8ocOBSlYAA9jBUmCYl/Qa7ey7JAM= -github.com/dop251/goja v0.0.0-20240220182346-e401ed450204/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= -github.com/dop251/goja_nodejs v0.0.0-20240221231712-27eeffc9c235 h1:5870ijWGCGCw7Ty4IGCquT6EfTck6f5zriYzFpPwOJ0= -github.com/dop251/goja_nodejs v0.0.0-20240221231712-27eeffc9c235/go.mod h1:bhGPmCgCCTSRfiMYWjpS46IDo9EUZXlsuUaPXSWGbv0= +github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2 h1:4Ew88p5s9dwIk5/woUyqI9BD89NgZoUNH4/rM/h2UDg= +github.com/dop251/goja v0.0.0-20240627195025-eb1f15ee67d2/go.mod h1:o31y53rb/qiIAONF7w3FHJZRqqP3fzHUr1HqanthByw= +github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf h1:2JoVYP9iko8uuIW33BQafzaylDixXbdXCRw/vCoxL+s= +github.com/dop251/goja_nodejs v0.0.0-20240418154818-2aae10d4cbcf/go.mod h1:bhGPmCgCCTSRfiMYWjpS46IDo9EUZXlsuUaPXSWGbv0= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/dsoprea/go-exif v0.0.0-20210131231135-d154f10435cc/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs= @@ -184,8 +180,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -197,8 +193,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= +github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= @@ -212,14 +208,16 @@ github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-ldap/ldap/v3 v3.4.7 h1:3Hbd7mIB1qjd3Ra59fI3JYea/t5kykFu2CVHBca9koE= -github.com/go-ldap/ldap/v3 v3.4.7/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk= +github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ= +github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -232,7 +230,6 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -251,6 +248,8 @@ github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6C github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -312,9 +311,8 @@ github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -326,8 +324,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= -github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= +github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA= +github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -339,8 +337,8 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/schema v1.3.0 h1:rbciOzXAx3IB8stEFnfTwO3sYa6EWlQk79XdyustPDA= -github.com/gorilla/schema v1.3.0/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= +github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= +github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= @@ -393,7 +391,6 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= @@ -405,8 +402,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= -github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 h1:jny9eqYPwkG8IVy7foUoRjQmFLcArCSz+uPsL6KS0HQ= @@ -444,8 +441,6 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -454,21 +449,21 @@ github.com/k3a/html2text v1.2.1 h1:nvnKgBvBR/myqrwfLuiqecUtaK1lB9hGziIJKatNFVY= github.com/k3a/html2text v1.2.1/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA= github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7 h1:K8qael4LemsmJCGt+ccI8b0fCNFDttmEu3qtpFt3G0M= github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7/go.mod h1:/Pk5i/SqYdYv1cie5wGwoZ4P6TpgMi+Yf58mtJSHdOw= -github.com/kevinburke/rest v0.0.0-20231107185522-a9c371f90234 h1:wMgTpL99gp1GUW9n7rtSB7Oad7xAKX+KgHftVctE7RQ= -github.com/kevinburke/rest v0.0.0-20231107185522-a9c371f90234/go.mod h1:dcLMT8KO9krIMJQ4578Lex1Su6ewuJUqEDeQ1nTORug= -github.com/kevinburke/twilio-go v0.0.0-20231009225535-38b36b35294d h1:EtrDnkat2jYA91OGm+dizL8RaF+GGy3EY/zLW59JhV8= -github.com/kevinburke/twilio-go v0.0.0-20231009225535-38b36b35294d/go.mod h1:4ljZgMTVLbuhqWMcxXPQRsGJ/XJ0xdGzbdLOJACnYco= +github.com/kevinburke/rest v0.0.0-20240617045629-3ed0ad3487f0 h1:qksAIHu0d4vkA0rIePBn+K9eO33RxkUMiceFn3T7lO4= +github.com/kevinburke/rest v0.0.0-20240617045629-3ed0ad3487f0/go.mod h1:dcLMT8KO9krIMJQ4578Lex1Su6ewuJUqEDeQ1nTORug= +github.com/kevinburke/twilio-go v0.0.0-20240623211326-c7334b537077 h1:IBBYDggH8Ra+xBzJ/7e+X9MG5TUNQ6kjyU2qf08m8c8= +github.com/kevinburke/twilio-go v0.0.0-20240623211326-c7334b537077/go.mod h1:ywO98mC3XU46KlDLgCDChBOpf5CihdaETKriwam/8eE= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -515,10 +510,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0= -github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ= -github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= -github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/minio/minio-go/v7 v7.0.73 h1:qr2vi96Qm7kZ4v7LLebjte+MQh621fFWnv93p12htEo= +github.com/minio/minio-go/v7 v7.0.73/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -529,12 +522,9 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/muesli/clusters v0.0.0-20180605185049-a07a36e67d36/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 h1:p4A2Jx7Lm3NV98VRMKlyWd3nqf8obft8NfXlAUmqd3I= github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= @@ -546,6 +536,8 @@ github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM= github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM= github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/vWUetyzY= github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -579,8 +571,8 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg= -github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -616,8 +608,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= -github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -676,13 +668,13 @@ github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNo github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -729,8 +721,8 @@ github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8= github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zitadel/logging v0.6.0 h1:t5Nnt//r+m2ZhhoTmoPX+c96pbMarqJvW1Vq6xFTank= github.com/zitadel/logging v0.6.0/go.mod h1:Y4CyAXHpl3Mig6JOszcV5Rqqsojj+3n7y2F591Mp/ow= -github.com/zitadel/oidc/v3 v3.25.0 h1:DosOUc31IPM9ZtKaT58+0iNicwDFTFk5Ctt7mgYtsA8= -github.com/zitadel/oidc/v3 v3.25.0/go.mod h1:UDwD+PRFbUBzabyPd9JORrakty3/wec7VpKZYi9Ahh0= +github.com/zitadel/oidc/v3 v3.25.1 h1:mkGimTWzbb8wARUewIqr6LhTPZnZeL6WOeXWy+iz1aI= +github.com/zitadel/oidc/v3 v3.25.1/go.mod h1:UDwD+PRFbUBzabyPd9JORrakty3/wec7VpKZYi9Ahh0= github.com/zitadel/passwap v0.6.0 h1:m9F3epFC0VkBXu25rihSLGyHvWiNlCzU5kk8RoI+SXQ= github.com/zitadel/passwap v0.6.0/go.mod h1:kqAiJ4I4eZvm3Y6oAk6hlEqlZZOkjMHraGXF90GG7LI= github.com/zitadel/saml v0.1.3 h1:LI4DOCVyyU1qKPkzs3vrGcA5J3H4pH3+CL9zr9ShkpM= @@ -744,28 +736,28 @@ go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/exporters/prometheus v0.49.0 h1:Er5I1g/YhfYv9Affk9nJLfH/+qCCVVg1f2R9AbJfqDQ= -go.opentelemetry.io/otel/exporters/prometheus v0.49.0/go.mod h1:KfQ1wpjf3zsHjzP149P4LyAwWRupc6c7t1ZJ9eXpKQM= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 h1:/0YaXu3755A/cFbtXp+21lkXgI0QE5avTWA2HjU9/WE= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0/go.mod h1:m7SFxp0/7IxmJPLIY3JhOcU9CoFzDaCPL6xxQIxhA+o= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= -go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= +go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= +go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= +go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -804,8 +796,8 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc= -golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -910,7 +902,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -936,7 +927,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= @@ -971,11 +961,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.172.0 h1:/1OcMZGPmW1rX2LCu2CmGUD1KXK1+pfzxotxyRUCCdk= -google.golang.org/api v0.172.0/go.mod h1:+fJZq6QXWfa9pXhnIzsjx4yI22d4aI9ZpLb58gvXjis= +google.golang.org/api v0.187.0 h1:Mxs7VATVC2v7CY+7Xwm4ndkX71hpElcvx0D1Ji/p1eo= +google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SBp2gk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -988,12 +976,12 @@ google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56 h1:LlcUFJ4BLmJVS4Kly+WCK7LQqcevmycHj88EPgyhNx8= -google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56/go.mod h1:n1CaIKYMIlxFt1zJE/1kU40YpSL0drGMbl0Idum1VSs= -google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE= -google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d h1:PksQg4dV6Sem3/HkBX+Ltq8T0ke0PKIRBNBatoDTVls= +google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:s7iA721uChleev562UJO2OYB0PPT9CMFjV+Ce7VJH5M= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1009,8 +997,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/internal/api/grpc/feature/v2/converter.go b/internal/api/grpc/feature/v2/converter.go index 259a6f8a42..15c43655a1 100644 --- a/internal/api/grpc/feature/v2/converter.go +++ b/internal/api/grpc/feature/v2/converter.go @@ -109,6 +109,10 @@ func improvedPerformanceTypeToPb(typ feature.ImprovedPerformanceType) feature_pb return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_UNSPECIFIED case feature.ImprovedPerformanceTypeOrgByID: return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID + case feature.ImprovedPerformanceTypeProjectGrant: + return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_PROJECT_GRANT + case feature.ImprovedPerformanceTypeProject: + return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_PROJECT default: return feature_pb.ImprovedPerformance(typ) } @@ -133,6 +137,10 @@ func improvedPerformanceToDomain(typ feature_pb.ImprovedPerformance) feature.Imp return feature.ImprovedPerformanceTypeUnknown case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID: return feature.ImprovedPerformanceTypeOrgByID + case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_PROJECT_GRANT: + return feature.ImprovedPerformanceTypeProjectGrant + case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_PROJECT: + return feature.ImprovedPerformanceTypeProject default: return feature.ImprovedPerformanceTypeUnknown } diff --git a/internal/api/grpc/settings/v2/settings_converter.go b/internal/api/grpc/settings/v2/settings_converter.go index 05e01bfecf..912df689aa 100644 --- a/internal/api/grpc/settings/v2/settings_converter.go +++ b/internal/api/grpc/settings/v2/settings_converter.go @@ -216,6 +216,8 @@ func idpTypeToPb(idpType domain.IDPType) settings.IdentityProviderType { return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITLAB_SELF_HOSTED case domain.IDPTypeGoogle: return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GOOGLE + case domain.IDPTypeSAML: + return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_SAML default: return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_UNSPECIFIED } diff --git a/internal/api/grpc/settings/v2/settings_converter_test.go b/internal/api/grpc/settings/v2/settings_converter_test.go index f8a99e5df6..99c60f2628 100644 --- a/internal/api/grpc/settings/v2/settings_converter_test.go +++ b/internal/api/grpc/settings/v2/settings_converter_test.go @@ -466,6 +466,10 @@ func Test_idpTypeToPb(t *testing.T) { args: args{domain.IDPTypeGoogle}, want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GOOGLE, }, + { + args: args{domain.IDPTypeSAML}, + want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_SAML, + }, { args: args{99}, want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_UNSPECIFIED, diff --git a/internal/api/oidc/auth_request.go b/internal/api/oidc/auth_request.go index 5053f7c1af..de6ce3c794 100644 --- a/internal/api/oidc/auth_request.go +++ b/internal/api/oidc/auth_request.go @@ -555,7 +555,7 @@ func (s *Server) authResponseToken(authReq *AuthRequest, authorizer op.Authorize authReq.AuthTime, authReq.GetNonce(), authReq.PreferredLanguage, - authReq.BrowserInfo.ToUserAgent(), + authReq.ToUserAgent(), domain.TokenReasonAuthRequest, nil, slices.Contains(scope, oidc.ScopeOfflineAccess), diff --git a/internal/api/oidc/token_code.go b/internal/api/oidc/token_code.go index 2e47c55641..b4705e9f2c 100644 --- a/internal/api/oidc/token_code.go +++ b/internal/api/oidc/token_code.go @@ -81,7 +81,7 @@ func (s *Server) codeExchangeV1(ctx context.Context, client *Client, req *oidc.A authReq.AuthTime, authReq.GetNonce(), authReq.PreferredLanguage, - authReq.BrowserInfo.ToUserAgent(), + authReq.ToUserAgent(), domain.TokenReasonAuthRequest, nil, slices.Contains(scope, oidc.ScopeOfflineAccess), diff --git a/internal/api/ui/login/device_auth.go b/internal/api/ui/login/device_auth.go index 0d5349903e..ca26fb956b 100644 --- a/internal/api/ui/login/device_auth.go +++ b/internal/api/ui/login/device_auth.go @@ -162,7 +162,7 @@ func (l *Login) handleDeviceAuthAction(w http.ResponseWriter, r *http.Request) { action := mux.Vars(r)["action"] switch action { case deviceAuthAllowed: - _, err = l.command.ApproveDeviceAuth(r.Context(), authDev.DeviceCode, authReq.UserID, authReq.UserOrgID, authReq.UserAuthMethodTypes(), authReq.AuthTime, authReq.PreferredLanguage, authReq.BrowserInfo.ToUserAgent()) + _, err = l.command.ApproveDeviceAuth(r.Context(), authDev.DeviceCode, authReq.UserID, authReq.UserOrgID, authReq.UserAuthMethodTypes(), authReq.AuthTime, authReq.PreferredLanguage, authReq.ToUserAgent()) case deviceAuthDenied: _, err = l.command.CancelDeviceAuth(r.Context(), authDev.DeviceCode, domain.DeviceAuthCanceledDenied) default: diff --git a/internal/api/ui/login/static/i18n/de.yaml b/internal/api/ui/login/static/i18n/de.yaml index e10e6bf1cf..25002177a9 100644 --- a/internal/api/ui/login/static/i18n/de.yaml +++ b/internal/api/ui/login/static/i18n/de.yaml @@ -21,7 +21,7 @@ LDAP: SelectAccount: Title: Konto auswählen Description: Wähle dein Konto aus. - TitleLinking: Konto auswählen um zu verknüpfen + TitleLinking: Konto für die Benutzerverknüpfung auswählen DescriptionLinking: Wähle dein Konto, um dieses mit deinem externen Benutzerkonto zu verbinden. OtherUser: Anderer Benutzer SessionState0: aktiv @@ -88,7 +88,7 @@ InitUserDone: InitMFAPrompt: Title: Zweitfaktor hinzufügen - Description: Zwei-Faktor-Authentifizierung gibt dir eine zusätzliche Sicherheit für dein Benutzerkonto. Damit stellst du sicher, dass nur du Zugriff auf dein Konto hast. + Description: Die Zwei-Faktor-Authentifizierung gibt dir eine zusätzliche Sicherheit für dein Benutzerkonto. Damit stellst du sicher, dass nur du Zugriff auf dein Konto hast. Provider0: Authentifizierungs-App (z.B. Google/Microsoft Authenticator, Authy) Provider1: Geräte-gebunden (z.B. FaceID, Windows Hello, Fingerprint) Provider3: Einmalpasswort per SMS @@ -97,7 +97,7 @@ InitMFAPrompt: SkipButtonText: Überspringen InitMFAOTP: - Title: Zwei-Faktor-Anmeldung + Title: Zwei-Faktor-Authentifizierung Description: Erstelle deinen Zweitfaktor. Installiere eine Authentifizierungs-App, wenn du noch keine hast. OTPDescription: Scanne den Code mit einer Authentifizierungs-App (z.B. Google/Mircorsoft Authenticator, Authy) oder kopiere das Secret und gib anschliessend den Code ein. SecretLabel: Secret @@ -120,12 +120,12 @@ InitMFAU2F: Description: Ein Sicherheitsschlüssel ist eine Verifizierungsmethode, die in deinem Telefon integriert sein kann, oder ein externes Gerät, das über Bluetooth oder USB mit deinem Computer verbunden wird. TokenNameLabel: Name des Geräts NotSupported: WebAuthN wird durch deinen Browser nicht unterstützt. Stelle sicher, dass du die aktuelle Version installiert hast oder nutze eine anderen (z.B. Chrome, Safari, Firefox) - RegisterTokenButtonText: Sicherschlüssel hinzufügen + RegisterTokenButtonText: Sicherheitsschlüssel hinzufügen ErrorRetry: Versuche es erneut, erstelle eine neue Abfrage oder wähle einen andere Methode. InitMFADone: Title: Sicherheitsschlüssel eingerichtet - Description: Großartig! Du hast gerade erfolgreich deinen Zweitfaktor eingerichtet und dein Konto viel sicherer gemacht. Der Zweitfaktor muss bei jeder Anmeldung verwendet werden. + Description: Großartig! Du hast gerade erfolgreich deinen Zweitfaktor eingerichtet und dein Konto viel sicherer gemacht. Der Zweitfaktor muss ab sofort bei jeder Anmeldung verwendet werden. NextButtonText: Weiter CancelButtonText: Abbrechen @@ -189,7 +189,7 @@ PasswordlessRegistrationDone: PasswordChange: Title: Passwort ändern - Description: Ändere dein Passwort indem du dein altes und dann dein neues Passwort eingibst. + Description: Ändere dein Passwort, indem du dein altes und dann dein neues Passwort eingibst. ExpiredDescription: Dein Passwort ist abgelaufen und muss geändert werden. Gib dein altes und neues Passwort ein. OldPasswordLabel: Altes Passwort NewPasswordLabel: Neues Passwort @@ -204,12 +204,12 @@ PasswordChangeDone: PasswordResetDone: Title: Resetlink versendet - Description: Prüfe dein E-Mail-Postfach, um ein neues Passwort zu setzen. + Description: Prüfe dein E-Mail-Postfach, um ein neues Passwort festzulegen. NextButtonText: Weiter EmailVerification: Title: E-Mail-Verifizierung - Description: Du hast eine E-Mail zur Verifizierung deiner E-Mail-Adresse bekommen. Gib den Code im untenstehenden Formular ein. Mit erneut versenden, wird dir eine neue E-Mail zugestellt. + Description: Du hast eine E-Mail zur Verifizierung deiner E-Mail-Adresse bekommen. Gib den Code im untenstehenden Feld ein. Mit erneut versenden, wird dir eine neue E-Mail gesendet. CodeLabel: Code NextButtonText: Weiter ResendButtonText: Code erneut senden @@ -222,7 +222,7 @@ EmailVerificationDone: LoginButtonText: Anmelden RegisterOption: - Title: Registrationsmöglichkeiten + Title: Registrieren Description: Wähle aus, wie du dich registrieren möchtest. RegisterUsernamePasswordButtonText: Mit Benutzername/Passwort ExternalLoginDescription: oder registriere dich mit einem externen Benutzerkonto @@ -304,7 +304,7 @@ ExternalRegistrationUserOverview: RegistrationOrg: Title: Organisation registrieren Description: Gib deinen Organisationsnamen und deine Benutzerdaten an. - OrgNameLabel: Organisationsname + OrgNameLabel: Name der Organisation EmailLabel: E-Mail UsernameLabel: Benutzername FirstnameLabel: Vorname @@ -320,7 +320,7 @@ RegistrationOrg: LoginSuccess: Title: Erfolgreich angemeldet - AutoRedirectDescription: Du wirst automatisch zurück in die Anwendung geleitet. Danach kannst du dieses Fenster schließen. + AutoRedirectDescription: Du wirst automatisch zu der Anwendung weitergeleitet. Wenn nicht, klicke auf die Schaltfläche unten. Danach kannst Du das Fenster schließen. RedirectedDescription: Du kannst dieses Fenster nun schließen. NextButtonText: Weiter @@ -331,19 +331,19 @@ LogoutDone: LinkingUserPrompt: Title: Vorhandener Benutzer gefunden - Description: "Möchten Sie Ihr bestehendes Konto verknüpfen:" + Description: "Möchten Sie Ihr bestehendes Konto verknüpfen?" LinkButtonText: Verknüpfen OtherButtonText: Andere Optionen LinkingUsersDone: - Title: Benutzerkonto verknpüfen - Description: Benuzterkonto verknpüft. + Title: Benutzerkonto verknüpfen + Description: Das Benutzerkonto wurde erfolgreich verknüpft. CancelButtonText: Abbrechen NextButtonText: Weiter ExternalNotFound: Title: Externes Benutzerkonto nicht gefunden - Description: Externes Benutzerkonto konnte nicht gefunden werden. Willst du deinen Benutzer mit einem bestehenden verknüpfen oder diesen als neuen Benutzer registrieren. + Description: Externes Benutzerkonto konnte nicht gefunden werden. Möchtest du deinen Benutzer mit einem bestehenden Benutzer verknüpfen oder ihn als neuen Benutzer registrieren? LinkButtonText: Verknüpfen AutoRegisterButtonText: Registrieren TosAndPrivacyLabel: Allgemeine Geschäftsbedingungen und Datenschutz @@ -367,18 +367,18 @@ ExternalNotFound: Dutch: Nederlands Swedish: Svenska DeviceAuth: - Title: Gerätezulassung + Title: Gerät verbinden UserCode: - Label: Benutzercode - Description: Gib den auf dem Gerät angezeigten Benutzercode ein + Label: Gerätecode + Description: Gib den auf dem Gerät angezeigten Code ein ButtonNext: Weiter Action: Description: Gerätezugriff erlauben - GrantDevice: Du bist dabei, das Gerät zu erlauben - AccessToScopes: Zugriff auf die folgenden Daten + GrantDevice: Möchtest du dem Gerät + AccessToScopes: Zugriff auf die folgenden Daten erlauben? Button: Allow: Erlauben - Deny: Verweigern + Deny: Ablehnen Done: Description: Abgeschlossen Approved: Gerätezulassung genehmigt. Sie können jetzt zum Gerät zurückkehren. @@ -406,11 +406,11 @@ Errors: NotFound: Benutzer konnte nicht gefunden werden AlreadyExists: Benutzer existiert bereits Inactive: Benutzer ist inaktiv - NotFoundOnOrg: Benutzer konnte in der gewünschten Organisation nicht gefunden werden + NotFoundOnOrg: Benutzer konnte nicht in der gewünschten Organisation gefunden werden NotAllowedOrg: Benutzer gehört nicht der benötigten Organisation an - NotMatchingUserID: Benutzer stimmt nicht mit Benutzer in Auth Request überein - UserIDMissing: UserID ist leer - Invalid: Userdaten sind ungültig + NotMatchingUserID: Benutzer stimmt nicht mit Benutzer im Auth Request überein + UserIDMissing: User-ID ist leer + Invalid: Benutzerdaten sind ungültig DomainNotAllowedAsUsername: Domäne ist bereits reserviert und kann nicht verwendet werden NotAllowedToLink: Der Benutzer darf nicht mit einem externen Benutzerkonto verknüpft werden Profile: @@ -423,7 +423,7 @@ Errors: Email: NotFound: E-Mail-Adresse nicht gefunden Invalid: E-Mail-Adresse ist ungültig - AlreadyVerified: E-Mail-Adresse ist bereits verifiziert + AlreadyVerified: E-Mail-Adresse wurde bereits verifiziert NotChanged: E-Mail-Adresse wurde nicht geändert Empty: E-Mail-Adresse ist leer IDMissing: E-Mail ID fehlt @@ -449,10 +449,10 @@ Errors: UsernameOrPassword: Invalid: Benutzername oder Passwort ist ungültig PasswordComplexityPolicy: - NotFound: Passwortpolicy konnte nicht gefunden werden + NotFound: Passwortrichtlinie konnte nicht gefunden werden MinLength: Passwort ist zu kurz - HasLower: Passwort beinhaltet keinen Kleinbuchstaben - HasUpper: Passwort beinhaltet keinen Großbuchstaben + HasLower: Passwort beinhaltet keine Kleinbuchstaben + HasUpper: Passwort beinhaltet keine Großbuchstaben HasNumber: Passwort beinhaltet keine Zahl HasSymbol: Passwort beinhaltet kein Symbol Code: @@ -463,11 +463,11 @@ Errors: NotFound: Code konnte nicht gefunden werden GeneratorAlgNotSupported: Generator-Algorithmus wird nicht unterstützt EmailVerify: - UserIDEmpty: UserID ist leer + UserIDEmpty: User-ID ist leer ExternalData: CouldNotRead: Externe Daten konnten nicht korrekt gelesen werden MFA: - NoProviders: Es stehen keine Multifaktorprovider zur Verfügung + NoProviders: Es steht kein Multifaktorprovider zur Verfügung OTP: AlreadyReady: Multifaktor OTP (OneTimePassword) ist bereits eingerichtet NotExisting: Multifaktor OTP (OneTimePassword) existiert nicht @@ -481,21 +481,21 @@ Errors: NotAllowed: Externer Login Provider ist nicht erlaubt IDPConfigIDEmpty: Identity Provider ID ist leer ExternalUserIDEmpty: Externe User ID ist leer - UserDisplayNameEmpty: Benutzer Anzeige Name ist leer - NoExternalUserData: Keine externe User Daten erhalten + UserDisplayNameEmpty: Anzeige-Name des Benutzers ist leer + NoExternalUserData: Keine externen User-Daten erhalten CreationNotAllowed: Erstellen eines neuen Benutzers mit diesem Provider ist nicht erlaubt LinkingNotAllowed: Verknüpfen eines Benutzers mit diesem Provider ist nicht erlaubt - GrantRequired: Die Anmeldung an diese Applikation ist nicht möglich. Der Benutzer benötigt mindestens eine Berechtigung an der Applikation. Bitte melde dich bei deinem Administrator. - ProjectRequired: Die Anmeldung an dieser Applikation ist nicht möglich. Die Organisation des Benutzer benötigt Berechtigung auf das Projekt. Bitte melde dich bei deinem Administrator. + GrantRequired: Die Anmeldung an diese Applikation ist nicht möglich. Der Benutzer benötigt mindestens eine Berechtigung an der Applikation. Bitte wende dich an deinen Administrator. + ProjectRequired: Die Anmeldung an dieser Applikation ist nicht möglich. Die Organisation des Benutzer benötigt Berechtigung auf das Projekt. Bitte wende dich an deinen Administrator. IdentityProvider: InvalidConfig: Konfiguration des Identitätsproviders ist ungültig IAM: LockoutPolicy: - NotExisting: Lockout Policy existiert nicht + NotExisting: Aussperrungs-Richtlinie existiert nicht Org: LoginPolicy: RegistrationNotAllowed: Registrierung ist nicht erlaubt DeviceAuth: - NotExisting: Benutzercode existiert nicht + NotExisting: Gerätecode existiert nicht optional: (optional) diff --git a/internal/command/org.go b/internal/command/org.go index 570455794a..e603531b3f 100644 --- a/internal/command/org.go +++ b/internal/command/org.go @@ -443,6 +443,7 @@ func (c *Commands) prepareRemoveOrg(a *org.Aggregate) preparation.Validation { if a.ID == instance.DefaultOrganisationID() { return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-wG9p1", "Errors.Org.DefaultOrgNotDeletable") } + err := c.checkProjectExists(ctx, instance.ProjectID(), a.ID) // if there is no error, the ZITADEL project was found on the org to be deleted if err == nil { diff --git a/internal/command/project.go b/internal/command/project.go index be8ea242c9..16ede9c7cc 100644 --- a/internal/command/project.go +++ b/internal/command/project.go @@ -6,9 +6,12 @@ import ( "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/feature" + "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/zerrors" @@ -165,16 +168,51 @@ func (c *Commands) getProjectByID(ctx context.Context, projectID, resourceOwner return projectWriteModelToProject(projectWriteModel), nil } +func (c *Commands) projectAggregateByID(ctx context.Context, projectID, resourceOwner string) (*eventstore.Aggregate, domain.ProjectState, error) { + result, err := c.projectState(ctx, projectID, resourceOwner) + if err != nil { + return nil, domain.ProjectStateUnspecified, zerrors.ThrowNotFound(err, "COMMA-NDQoF", "Errors.Project.NotFound") + } + if len(result) == 0 { + _ = projection.ProjectGrantFields.Trigger(ctx) + result, err = c.projectState(ctx, projectID, resourceOwner) + if err != nil || len(result) == 0 { + return nil, domain.ProjectStateUnspecified, zerrors.ThrowNotFound(err, "COMMA-U1nza", "Errors.Project.NotFound") + } + } + + var state domain.ProjectState + err = result[0].Value.Unmarshal(&state) + if err != nil { + return nil, state, zerrors.ThrowNotFound(err, "COMMA-o4n6F", "Errors.Project.NotFound") + } + return &result[0].Aggregate, state, nil +} + +func (c *Commands) projectState(ctx context.Context, projectID, resourceOwner string) ([]*eventstore.SearchResult, error) { + return c.eventstore.Search( + ctx, + map[eventstore.FieldType]any{ + eventstore.FieldTypeObjectType: project.ProjectSearchType, + eventstore.FieldTypeObjectID: projectID, + eventstore.FieldTypeObjectRevision: project.ProjectObjectRevision, + eventstore.FieldTypeFieldName: project.ProjectStateSearchField, + eventstore.FieldTypeResourceOwner: resourceOwner, + }, + ) +} + func (c *Commands) checkProjectExists(ctx context.Context, projectID, resourceOwner string) (err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - projectWriteModel, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner) - if err != nil { - return err + if !authz.GetFeatures(ctx).ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeProject) { + return c.checkProjectExistsOld(ctx, projectID, resourceOwner) } - if projectWriteModel.State == domain.ProjectStateUnspecified || projectWriteModel.State == domain.ProjectStateRemoved { - return zerrors.ThrowPreconditionFailed(nil, "COMMAND-EbFMN", "Errors.Project.NotFound") + + _, state, err := c.projectAggregateByID(ctx, projectID, resourceOwner) + if err != nil || !state.Valid() { + return zerrors.ThrowPreconditionFailed(err, "COMMA-VCnwD", "Errors.Project.NotFound") } return nil } @@ -184,6 +222,10 @@ func (c *Commands) ChangeProject(ctx context.Context, projectChange *domain.Proj return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.Invalid") } + if !authz.GetFeatures(ctx).ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeProject) { + return c.changeProjectOld(ctx, projectChange, resourceOwner) + } + existingProject, err := c.getProjectWriteModelByID(ctx, projectChange.AggregateID, resourceOwner) if err != nil { return nil, err @@ -223,6 +265,27 @@ func (c *Commands) DeactivateProject(ctx context.Context, projectID string, reso return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-88iF0", "Errors.Project.ProjectIDMissing") } + if !authz.GetFeatures(ctx).ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeProject) { + return c.deactivateProjectOld(ctx, projectID, resourceOwner) + } + + projectAgg, state, err := c.projectAggregateByID(ctx, projectID, resourceOwner) + if err != nil { + return nil, err + } + + if state == domain.ProjectStateUnspecified || state == domain.ProjectStateRemoved { + return nil, zerrors.ThrowNotFound(nil, "COMMAND-112M9", "Errors.Project.NotFound") + } + if state != domain.ProjectStateActive { + return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-mki55", "Errors.Project.NotActive") + } + + pushedEvents, err := c.eventstore.Push(ctx, project.NewProjectDeactivatedEvent(ctx, projectAgg)) + if err != nil { + return nil, err + } + existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner) if err != nil { return nil, err @@ -234,16 +297,11 @@ func (c *Commands) DeactivateProject(ctx context.Context, projectID string, reso return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-mki55", "Errors.Project.NotActive") } - projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) - pushedEvents, err := c.eventstore.Push(ctx, project.NewProjectDeactivatedEvent(ctx, projectAgg)) - if err != nil { - return nil, err - } - err = AppendAndReduce(existingProject, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToObjectDetails(&existingProject.WriteModel), nil + return &domain.ObjectDetails{ + ResourceOwner: pushedEvents[0].Aggregate().ResourceOwner, + Sequence: pushedEvents[0].Sequence(), + EventDate: pushedEvents[0].CreatedAt(), + }, nil } func (c *Commands) ReactivateProject(ctx context.Context, projectID string, resourceOwner string) (*domain.ObjectDetails, error) { @@ -251,6 +309,23 @@ func (c *Commands) ReactivateProject(ctx context.Context, projectID string, reso return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-3ihsF", "Errors.Project.ProjectIDMissing") } + if !authz.GetFeatures(ctx).ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeProject) { + return c.reactivateProjectOld(ctx, projectID, resourceOwner) + } + + projectAgg, state, err := c.projectAggregateByID(ctx, projectID, resourceOwner) + if err != nil { + return nil, err + } + + if state == domain.ProjectStateUnspecified || state == domain.ProjectStateRemoved { + return nil, zerrors.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound") + } + + if state != domain.ProjectStateInactive { + return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-5M9bs", "Errors.Project.NotInactive") + } + existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner) if err != nil { return nil, err @@ -262,16 +337,16 @@ func (c *Commands) ReactivateProject(ctx context.Context, projectID string, reso return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-5M9bs", "Errors.Project.NotInactive") } - projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) pushedEvents, err := c.eventstore.Push(ctx, project.NewProjectReactivatedEvent(ctx, projectAgg)) if err != nil { return nil, err } - err = AppendAndReduce(existingProject, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToObjectDetails(&existingProject.WriteModel), nil + + return &domain.ObjectDetails{ + ResourceOwner: pushedEvents[0].Aggregate().ResourceOwner, + Sequence: pushedEvents[0].Sequence(), + EventDate: pushedEvents[0].CreatedAt(), + }, nil } func (c *Commands) RemoveProject(ctx context.Context, projectID, resourceOwner string, cascadingUserGrantIDs ...string) (*domain.ObjectDetails, error) { @@ -279,6 +354,10 @@ func (c *Commands) RemoveProject(ctx context.Context, projectID, resourceOwner s return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-66hM9", "Errors.Project.ProjectIDMissing") } + if !authz.GetFeatures(ctx).ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeProject) { + return c.removeProjectOld(ctx, projectID, resourceOwner) + } + existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner) if err != nil { return nil, err diff --git a/internal/command/project_grant.go b/internal/command/project_grant.go index d0f2d210fb..59f98e3761 100644 --- a/internal/command/project_grant.go +++ b/internal/command/project_grant.go @@ -6,8 +6,11 @@ import ( "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/feature" + "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/zerrors" @@ -143,10 +146,12 @@ func (c *Commands) DeactivateProjectGrant(ctx context.Context, projectID, grantI if grantID == "" || projectID == "" { return details, zerrors.ThrowInvalidArgument(nil, "PROJECT-p0s4V", "Errors.IDMissing") } + err = c.checkProjectExists(ctx, projectID, resourceOwner) if err != nil { - return details, err + return nil, err } + existingGrant, err := c.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner) if err != nil { return details, err @@ -171,10 +176,12 @@ func (c *Commands) ReactivateProjectGrant(ctx context.Context, projectID, grantI if grantID == "" || projectID == "" { return details, zerrors.ThrowInvalidArgument(nil, "PROJECT-p0s4V", "Errors.IDMissing") } + err = c.checkProjectExists(ctx, projectID, resourceOwner) if err != nil { - return details, err + return nil, err } + existingGrant, err := c.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner) if err != nil { return details, err @@ -198,10 +205,12 @@ func (c *Commands) RemoveProjectGrant(ctx context.Context, projectID, grantID, r if grantID == "" || projectID == "" { return details, zerrors.ThrowInvalidArgument(nil, "PROJECT-1m9fJ", "Errors.IDMissing") } + err = c.checkProjectExists(ctx, projectID, resourceOwner) if err != nil { - return details, zerrors.ThrowPreconditionFailed(err, "PROJECT-6mf9s", "Errors.Project.NotFound") + return nil, err } + existingGrant, err := c.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner) if err != nil { return details, err @@ -247,18 +256,76 @@ func (c *Commands) projectGrantWriteModelByID(ctx context.Context, grantID, proj } func (c *Commands) checkProjectGrantPreCondition(ctx context.Context, projectGrant *domain.ProjectGrant) error { - preConditions := NewProjectGrantPreConditionReadModel(projectGrant.AggregateID, projectGrant.GrantedOrgID) - err := c.eventstore.FilterToQueryReducer(ctx, preConditions) + if !authz.GetFeatures(ctx).ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeProjectGrant) { + return c.checkProjectGrantPreConditionOld(ctx, projectGrant) + } + results, err := c.eventstore.Search( + ctx, + // project state query + map[eventstore.FieldType]any{ + eventstore.FieldTypeAggregateType: project.AggregateType, + eventstore.FieldTypeAggregateID: projectGrant.AggregateID, + eventstore.FieldTypeFieldName: project.ProjectStateSearchField, + eventstore.FieldTypeObjectType: project.ProjectSearchType, + }, + // granted org query + map[eventstore.FieldType]any{ + eventstore.FieldTypeAggregateType: org.AggregateType, + eventstore.FieldTypeAggregateID: projectGrant.GrantedOrgID, + eventstore.FieldTypeFieldName: org.OrgStateSearchField, + eventstore.FieldTypeObjectType: org.OrgSearchType, + }, + // role query + map[eventstore.FieldType]any{ + eventstore.FieldTypeAggregateType: project.AggregateType, + eventstore.FieldTypeAggregateID: projectGrant.AggregateID, + eventstore.FieldTypeFieldName: project.ProjectRoleKeySearchField, + eventstore.FieldTypeObjectType: project.ProjectRoleSearchType, + }, + ) if err != nil { return err } - if !preConditions.ProjectExists { + + var ( + existsProject bool + existsGrantedOrg bool + existingRoleKeys []string + ) + + for _, result := range results { + switch result.Object.Type { + case project.ProjectRoleSearchType: + var role string + err := result.Value.Unmarshal(&role) + if err != nil { + return err + } + existingRoleKeys = append(existingRoleKeys, role) + case org.OrgSearchType: + var state domain.OrgState + err := result.Value.Unmarshal(&state) + if err != nil { + return err + } + existsGrantedOrg = state.Valid() && state != domain.OrgStateRemoved + case project.ProjectSearchType: + var state domain.ProjectState + err := result.Value.Unmarshal(&state) + if err != nil { + return err + } + existsProject = state.Valid() && state != domain.ProjectStateRemoved + } + } + + if !existsProject { return zerrors.ThrowPreconditionFailed(err, "COMMAND-m9gsd", "Errors.Project.NotFound") } - if !preConditions.GrantedOrgExists { + if !existsGrantedOrg { return zerrors.ThrowPreconditionFailed(err, "COMMAND-3m9gg", "Errors.Org.NotFound") } - if projectGrant.HasInvalidRoles(preConditions.ExistingRoleKeys) { + if projectGrant.HasInvalidRoles(existingRoleKeys) { return zerrors.ThrowPreconditionFailed(err, "COMMAND-6m9gd", "Errors.Project.Role.NotFound") } return nil diff --git a/internal/command/project_old.go b/internal/command/project_old.go new file mode 100644 index 0000000000..434df38a7f --- /dev/null +++ b/internal/command/project_old.go @@ -0,0 +1,180 @@ +package command + +import ( + "context" + + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/repository/project" + "github.com/zitadel/zitadel/internal/telemetry/tracing" + "github.com/zitadel/zitadel/internal/zerrors" +) + +func (c *Commands) checkProjectExistsOld(ctx context.Context, projectID, resourceOwner string) (err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + projectWriteModel, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner) + if err != nil { + return err + } + if projectWriteModel.State == domain.ProjectStateUnspecified || projectWriteModel.State == domain.ProjectStateRemoved { + return zerrors.ThrowPreconditionFailed(nil, "COMMAND-EbFMN", "Errors.Project.NotFound") + } + return nil +} + +func (c *Commands) changeProjectOld(ctx context.Context, projectChange *domain.Project, resourceOwner string) (*domain.Project, error) { + if !projectChange.IsValid() || projectChange.AggregateID == "" { + return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.Invalid") + } + + existingProject, err := c.getProjectWriteModelByID(ctx, projectChange.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + if existingProject.State == domain.ProjectStateUnspecified || existingProject.State == domain.ProjectStateRemoved { + return nil, zerrors.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound") + } + + //nolint: contextcheck + projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) + changedEvent, hasChanged, err := existingProject.NewChangedEvent( + ctx, + projectAgg, + projectChange.Name, + projectChange.ProjectRoleAssertion, + projectChange.ProjectRoleCheck, + projectChange.HasProjectCheck, + projectChange.PrivateLabelingSetting) + if err != nil { + return nil, err + } + if !hasChanged { + return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-2M0fs", "Errors.NoChangesFound") + } + pushedEvents, err := c.eventstore.Push(ctx, changedEvent) + if err != nil { + return nil, err + } + err = AppendAndReduce(existingProject, pushedEvents...) + if err != nil { + return nil, err + } + return projectWriteModelToProject(existingProject), nil +} + +func (c *Commands) deactivateProjectOld(ctx context.Context, projectID string, resourceOwner string) (*domain.ObjectDetails, error) { + existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner) + if err != nil { + return nil, err + } + if existingProject.State == domain.ProjectStateUnspecified || existingProject.State == domain.ProjectStateRemoved { + return nil, zerrors.ThrowNotFound(nil, "COMMAND-112M9", "Errors.Project.NotFound") + } + if existingProject.State != domain.ProjectStateActive { + return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-mki55", "Errors.Project.NotActive") + } + + //nolint: contextcheck + projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) + pushedEvents, err := c.eventstore.Push(ctx, project.NewProjectDeactivatedEvent(ctx, projectAgg)) + if err != nil { + return nil, err + } + err = AppendAndReduce(existingProject, pushedEvents...) + if err != nil { + return nil, err + } + return writeModelToObjectDetails(&existingProject.WriteModel), nil +} + +func (c *Commands) reactivateProjectOld(ctx context.Context, projectID string, resourceOwner string) (*domain.ObjectDetails, error) { + existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner) + if err != nil { + return nil, err + } + if existingProject.State == domain.ProjectStateUnspecified || existingProject.State == domain.ProjectStateRemoved { + return nil, zerrors.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound") + } + if existingProject.State != domain.ProjectStateInactive { + return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-5M9bs", "Errors.Project.NotInactive") + } + + //nolint: contextcheck + projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) + pushedEvents, err := c.eventstore.Push(ctx, project.NewProjectReactivatedEvent(ctx, projectAgg)) + if err != nil { + return nil, err + } + err = AppendAndReduce(existingProject, pushedEvents...) + if err != nil { + return nil, err + } + return writeModelToObjectDetails(&existingProject.WriteModel), nil +} + +func (c *Commands) removeProjectOld(ctx context.Context, projectID, resourceOwner string, cascadingUserGrantIDs ...string) (*domain.ObjectDetails, error) { + existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner) + if err != nil { + return nil, err + } + if existingProject.State == domain.ProjectStateUnspecified || existingProject.State == domain.ProjectStateRemoved { + return nil, zerrors.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound") + } + + samlEntityIDsAgg, err := c.getSAMLEntityIdsWriteModelByProjectID(ctx, projectID, resourceOwner) + if err != nil { + return nil, err + } + + uniqueConstraints := make([]*eventstore.UniqueConstraint, len(samlEntityIDsAgg.EntityIDs)) + for i, entityID := range samlEntityIDsAgg.EntityIDs { + uniqueConstraints[i] = project.NewRemoveSAMLConfigEntityIDUniqueConstraint(entityID.EntityID) + } + + //nolint: contextcheck + projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) + events := []eventstore.Command{ + project.NewProjectRemovedEvent(ctx, projectAgg, existingProject.Name, uniqueConstraints), + } + + for _, grantID := range cascadingUserGrantIDs { + event, _, err := c.removeUserGrant(ctx, grantID, "", true) + if err != nil { + logging.WithFields("usergrantid", grantID).WithError(err).Warn("could not cascade remove user grant") + continue + } + events = append(events, event) + } + + pushedEvents, err := c.eventstore.Push(ctx, events...) + if err != nil { + return nil, err + } + err = AppendAndReduce(existingProject, pushedEvents...) + if err != nil { + return nil, err + } + return writeModelToObjectDetails(&existingProject.WriteModel), nil +} + +func (c *Commands) checkProjectGrantPreConditionOld(ctx context.Context, projectGrant *domain.ProjectGrant) error { + preConditions := NewProjectGrantPreConditionReadModel(projectGrant.AggregateID, projectGrant.GrantedOrgID) + err := c.eventstore.FilterToQueryReducer(ctx, preConditions) + if err != nil { + return err + } + if !preConditions.ProjectExists { + return zerrors.ThrowPreconditionFailed(err, "COMMAND-m9gsd", "Errors.Project.NotFound") + } + if !preConditions.GrantedOrgExists { + return zerrors.ThrowPreconditionFailed(err, "COMMAND-3m9gg", "Errors.Org.NotFound") + } + if projectGrant.HasInvalidRoles(preConditions.ExistingRoleKeys) { + return zerrors.ThrowPreconditionFailed(err, "COMMAND-6m9gd", "Errors.Project.Role.NotFound") + } + return nil +} diff --git a/internal/command/project_role.go b/internal/command/project_role.go index ec9a426242..3c8f9c725c 100644 --- a/internal/command/project_role.go +++ b/internal/command/project_role.go @@ -41,7 +41,7 @@ func (c *Commands) AddProjectRole(ctx context.Context, projectRole *domain.Proje func (c *Commands) BulkAddProjectRole(ctx context.Context, projectID, resourceOwner string, projectRoles []*domain.ProjectRole) (details *domain.ObjectDetails, err error) { err = c.checkProjectExists(ctx, projectID, resourceOwner) if err != nil { - return details, err + return nil, err } roleWriteModel := NewProjectRoleWriteModel(projectID, resourceOwner) diff --git a/internal/domain/browser_info.go b/internal/domain/browser_info.go index 7261cb3e6e..fd0073183f 100644 --- a/internal/domain/browser_info.go +++ b/internal/domain/browser_info.go @@ -23,14 +23,15 @@ func BrowserInfoFromRequest(r *net_http.Request) *BrowserInfo { } } -func (b *BrowserInfo) ToUserAgent() *UserAgent { - if b == nil { - return nil +func (a *AuthRequest) ToUserAgent() *UserAgent { + agent := &UserAgent{ + FingerprintID: &a.AgentID, } - return &UserAgent{ - FingerprintID: &b.UserAgent, - IP: b.RemoteIP, - Description: &b.UserAgent, - Header: b.Header, + if a.BrowserInfo == nil { + return agent } + agent.IP = a.BrowserInfo.RemoteIP + agent.Description = &a.BrowserInfo.UserAgent + agent.Header = a.BrowserInfo.Header + return agent } diff --git a/internal/domain/org.go b/internal/domain/org.go index 0e87d20a43..d016dde99c 100644 --- a/internal/domain/org.go +++ b/internal/domain/org.go @@ -36,4 +36,10 @@ const ( OrgStateActive OrgStateInactive OrgStateRemoved + + orgStateMax ) + +func (s OrgState) Valid() bool { + return s > OrgStateUnspecified && s < orgStateMax +} diff --git a/internal/eventstore/config.go b/internal/eventstore/config.go index fd39556676..6fc3f79f2c 100644 --- a/internal/eventstore/config.go +++ b/internal/eventstore/config.go @@ -8,6 +8,7 @@ type Config struct { PushTimeout time.Duration MaxRetries uint32 - Pusher Pusher - Querier Querier + Pusher Pusher + Querier Querier + Searcher Searcher } diff --git a/internal/eventstore/event.go b/internal/eventstore/event.go index 6c7f47b738..3df096f069 100644 --- a/internal/eventstore/event.go +++ b/internal/eventstore/event.go @@ -31,6 +31,8 @@ type Command interface { Payload() any // UniqueConstraints should be added for unique attributes of an event, if nil constraints will not be checked UniqueConstraints() []*UniqueConstraint + // Fields should be added for fields which should be indexed for lookup, if nil fields will not be indexed + Fields() []*FieldOperation } // Event is a stored activity diff --git a/internal/eventstore/event_base.go b/internal/eventstore/event_base.go index 5bb117b14a..c2b56128a8 100644 --- a/internal/eventstore/event_base.go +++ b/internal/eventstore/event_base.go @@ -123,3 +123,7 @@ func NewBaseEventForPush(ctx context.Context, aggregate *Aggregate, typ EventTyp EventType: typ, } } + +func (*BaseEvent) Fields() []*FieldOperation { + return nil +} diff --git a/internal/eventstore/eventstore.go b/internal/eventstore/eventstore.go index 34db1e9cf0..e456135828 100644 --- a/internal/eventstore/eventstore.go +++ b/internal/eventstore/eventstore.go @@ -11,6 +11,7 @@ import ( "github.com/zitadel/logging" "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/zerrors" ) // Eventstore abstracts all functions needed to store valid events @@ -19,8 +20,9 @@ type Eventstore struct { PushTimeout time.Duration maxRetries int - pusher Pusher - querier Querier + pusher Pusher + querier Querier + searcher Searcher instances []string lastInstanceQuery time.Time @@ -62,8 +64,9 @@ func NewEventstore(config *Config) *Eventstore { PushTimeout: config.PushTimeout, maxRetries: int(config.MaxRetries), - pusher: config.Pusher, - querier: config.Querier, + pusher: config.Pusher, + querier: config.Querier, + searcher: config.Searcher, instancesMu: sync.Mutex{}, } @@ -127,6 +130,20 @@ func (es *Eventstore) AggregateTypes() []string { return aggregateTypes } +// FillFields implements the [Searcher] interface +func (es *Eventstore) FillFields(ctx context.Context, events ...FillFieldsEvent) error { + return es.searcher.FillFields(ctx, events...) +} + +// Search implements the [Searcher] interface +func (es *Eventstore) Search(ctx context.Context, conditions ...map[FieldType]any) ([]*SearchResult, error) { + if len(conditions) == 0 { + return nil, zerrors.ThrowInvalidArgument(nil, "V3-5Xbr1", "no search conditions") + } + + return es.searcher.Search(ctx, conditions...) +} + // Filter filters the stored events based on the searchQuery // and maps the events to the defined event structs // @@ -262,6 +279,22 @@ type Pusher interface { Push(ctx context.Context, commands ...Command) (_ []Event, err error) } +type FillFieldsEvent interface { + Event + Fields() []*FieldOperation +} + +type Searcher interface { + // Search allows to search for specific fields of objects + // The instance id is taken from the context + // The list of conditions are combined with AND + // The search fields are combined with OR + // At least one must be defined + Search(ctx context.Context, conditions ...map[FieldType]any) (result []*SearchResult, err error) + // FillFields is to insert the fields of previously stored events + FillFields(ctx context.Context, events ...FillFieldsEvent) error +} + func appendEventType(typ EventType) { i := sort.SearchStrings(eventTypes, string(typ)) if i < len(eventTypes) && eventTypes[i] == string(typ) { diff --git a/internal/eventstore/field.go b/internal/eventstore/field.go new file mode 100644 index 0000000000..8ce4616661 --- /dev/null +++ b/internal/eventstore/field.go @@ -0,0 +1,140 @@ +package eventstore + +// FieldOperation if the definition of the operation to be executed on the field +type FieldOperation struct { + // Set a field in the field table + // if [SearchField.UpsertConflictFields] are set the field will be updated if the conflict fields match + // if no [SearchField.UpsertConflictFields] are set the field will be inserted + Set *Field + // Remove fields using the map as `AND`ed conditions + Remove map[FieldType]any +} + +type SearchResult struct { + Aggregate Aggregate + Object Object + FieldName string + // Value represents the stored value + // use the Unmarshal method to parse the value to the desired type + Value interface { + // Unmarshal parses the value to ptr + Unmarshal(ptr any) error + } +} + +// // NumericResultValue marshals the value to the given type + +type Object struct { + // Type of the object + Type string + // ID of the object + ID string + // Revision of the object, if an object evolves the revision should be increased + // analog to current projection versioning + Revision uint8 +} + +type Field struct { + Aggregate *Aggregate + Object Object + UpsertConflictFields []FieldType + FieldName string + Value Value +} + +type Value struct { + Value any + // MustBeUnique defines if the field must be unique + // This field will replace unique constraints in the future + // If MustBeUnique is true the value must be a primitive type + MustBeUnique bool + // ShouldIndex defines if the field should be indexed + // If the field is not indexed it can not be used in search queries + // If ShouldIndex is true the value must be a primitive type + ShouldIndex bool +} + +type SearchValueType int8 + +const ( + SearchValueTypeString SearchValueType = iota + SearchValueTypeNumeric +) + +// SetSearchField sets the field based on the defined parameters +// if conflictFields are set the field will be updated if the conflict fields match +func SetField(aggregate *Aggregate, object Object, fieldName string, value *Value, conflictFields ...FieldType) *FieldOperation { + return &FieldOperation{ + Set: &Field{ + Aggregate: aggregate, + Object: object, + UpsertConflictFields: conflictFields, + FieldName: fieldName, + Value: *value, + }, + } +} + +// RemoveSearchFields removes fields using the map as `AND`ed conditions +func RemoveSearchFields(clause map[FieldType]any) *FieldOperation { + return &FieldOperation{ + Remove: clause, + } +} + +// RemoveSearchFieldsByAggregate removes fields using the aggregate as `AND`ed conditions +func RemoveSearchFieldsByAggregate(aggregate *Aggregate) *FieldOperation { + return &FieldOperation{ + Remove: map[FieldType]any{ + FieldTypeInstanceID: aggregate.InstanceID, + FieldTypeResourceOwner: aggregate.ResourceOwner, + FieldTypeAggregateType: aggregate.Type, + FieldTypeAggregateID: aggregate.ID, + }, + } +} + +// RemoveSearchFieldsByAggregateAndObject removes fields using the aggregate and object as `AND`ed conditions +func RemoveSearchFieldsByAggregateAndObject(aggregate *Aggregate, object Object) *FieldOperation { + return &FieldOperation{ + Remove: map[FieldType]any{ + FieldTypeInstanceID: aggregate.InstanceID, + FieldTypeResourceOwner: aggregate.ResourceOwner, + FieldTypeAggregateType: aggregate.Type, + FieldTypeAggregateID: aggregate.ID, + FieldTypeObjectType: object.Type, + FieldTypeObjectID: object.ID, + FieldTypeObjectRevision: object.Revision, + }, + } +} + +// RemoveSearchFieldsByAggregateAndObjectAndField removes fields using the aggregate, object and field as `AND`ed conditions +func RemoveSearchFieldsByAggregateAndObjectAndField(aggregate *Aggregate, object Object, field string) *FieldOperation { + return &FieldOperation{ + Remove: map[FieldType]any{ + FieldTypeInstanceID: aggregate.InstanceID, + FieldTypeResourceOwner: aggregate.ResourceOwner, + FieldTypeAggregateType: aggregate.Type, + FieldTypeAggregateID: aggregate.ID, + FieldTypeObjectType: object.Type, + FieldTypeObjectID: object.ID, + FieldTypeObjectRevision: object.Revision, + FieldTypeFieldName: field, + }, + } +} + +type FieldType int8 + +const ( + FieldTypeAggregateType FieldType = iota + FieldTypeAggregateID + FieldTypeInstanceID + FieldTypeResourceOwner + FieldTypeObjectType + FieldTypeObjectID + FieldTypeObjectRevision + FieldTypeFieldName + FieldTypeValue +) diff --git a/internal/eventstore/handler/v2/field_handler.go b/internal/eventstore/handler/v2/field_handler.go new file mode 100644 index 0000000000..104b5a40dd --- /dev/null +++ b/internal/eventstore/handler/v2/field_handler.go @@ -0,0 +1,205 @@ +package handler + +import ( + "context" + "database/sql" + "errors" + "sync" + "time" + + "github.com/jackc/pgx/v5/pgconn" + + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/telemetry/tracing" +) + +type FieldHandler struct { + Handler +} + +type fieldProjection struct { + name string +} + +// Name implements Projection. +func (f *fieldProjection) Name() string { + return f.name +} + +// Reducers implements Projection. +func (f *fieldProjection) Reducers() []AggregateReducer { + return nil +} + +var _ Projection = (*fieldProjection)(nil) + +func NewFieldHandler(config *Config, name string, eventTypes map[eventstore.AggregateType][]eventstore.EventType) *FieldHandler { + return &FieldHandler{ + Handler: Handler{ + projection: &fieldProjection{name: name}, + client: config.Client, + es: config.Eventstore, + bulkLimit: config.BulkLimit, + eventTypes: eventTypes, + requeueEvery: config.RequeueEvery, + handleActiveInstances: config.HandleActiveInstances, + now: time.Now, + maxFailureCount: config.MaxFailureCount, + retryFailedAfter: config.RetryFailedAfter, + triggeredInstancesSync: sync.Map{}, + triggerWithoutEvents: config.TriggerWithoutEvents, + txDuration: config.TransactionDuration, + }, + } +} + +func (h *FieldHandler) Trigger(ctx context.Context, opts ...TriggerOpt) (err error) { + config := new(triggerConfig) + for _, opt := range opts { + opt(config) + } + + cancel := h.lockInstance(ctx, config) + if cancel == nil { + return nil + } + defer cancel() + + for i := 0; ; i++ { + additionalIteration, err := h.processEvents(ctx, config) + h.log().OnError(err).Info("process events failed") + h.log().WithField("iteration", i).Debug("trigger iteration") + if !additionalIteration || err != nil { + return err + } + } +} + +func (h *FieldHandler) processEvents(ctx context.Context, config *triggerConfig) (additionalIteration bool, err error) { + defer func() { + pgErr := new(pgconn.PgError) + if errors.As(err, &pgErr) { + // error returned if the row is currently locked by another connection + if pgErr.Code == "55P03" { + h.log().Debug("state already locked") + err = nil + additionalIteration = false + } + } + }() + + txCtx := ctx + if h.txDuration > 0 { + var cancel, cancelTx func() + // add 100ms to store current state if iteration takes too long + txCtx, cancelTx = context.WithTimeout(ctx, h.txDuration+100*time.Millisecond) + defer cancelTx() + ctx, cancel = context.WithTimeout(ctx, h.txDuration) + defer cancel() + } + + ctx, spanBeginTx := tracing.NewNamedSpan(ctx, "db.BeginTx") + tx, err := h.client.BeginTx(txCtx, nil) + spanBeginTx.EndWithError(err) + if err != nil { + return false, err + } + defer func() { + if err != nil && !errors.Is(err, &executionError{}) { + rollbackErr := tx.Rollback() + h.log().OnError(rollbackErr).Debug("unable to rollback tx") + return + } + commitErr := tx.Commit() + if err == nil { + err = commitErr + } + }() + + currentState, err := h.currentState(ctx, tx, config) + if err != nil { + if errors.Is(err, errJustUpdated) { + return false, nil + } + return additionalIteration, err + } + // stop execution if currentState.eventTimestamp >= config.maxCreatedAt + if config.maxPosition != 0 && currentState.position >= config.maxPosition { + return false, nil + } + + events, additionalIteration, err := h.fetchEvents(ctx, tx, currentState) + if err != nil { + return additionalIteration, err + } + if len(events) == 0 { + err = h.setState(tx, currentState) + return additionalIteration, err + } + + err = h.es.FillFields(ctx, events...) + if err != nil { + return false, err + } + + err = h.setState(tx, currentState) + + return additionalIteration, err +} + +func (h *FieldHandler) fetchEvents(ctx context.Context, tx *sql.Tx, currentState *state) (_ []eventstore.FillFieldsEvent, additionalIteration bool, err error) { + events, err := h.es.Filter(ctx, h.eventQuery(currentState).SetTx(tx)) + if err != nil { + h.log().WithError(err).Debug("filter eventstore failed") + return nil, false, err + } + eventAmount := len(events) + + idx, offset := skipPreviouslyReducedEvents(events, currentState) + + if currentState.position == events[len(events)-1].Position() { + offset += currentState.offset + } + currentState.position = events[len(events)-1].Position() + currentState.offset = offset + currentState.aggregateID = events[len(events)-1].Aggregate().ID + currentState.aggregateType = events[len(events)-1].Aggregate().Type + currentState.sequence = events[len(events)-1].Sequence() + currentState.eventTimestamp = events[len(events)-1].CreatedAt() + + if idx+1 == len(events) { + return nil, false, nil + } + events = events[idx+1:] + + additionalIteration = eventAmount == int(h.bulkLimit) + + fillFieldsEvents := make([]eventstore.FillFieldsEvent, len(events)) + highestPosition := events[len(events)-1].Position() + for i, event := range events { + if event.Position() == highestPosition { + offset++ + } + fillFieldsEvents[i] = event.(eventstore.FillFieldsEvent) + } + + return fillFieldsEvents, additionalIteration, nil +} + +func skipPreviouslyReducedEvents(events []eventstore.Event, currentState *state) (index int, offset uint32) { + var position float64 + for i, event := range events { + if event.Position() != position { + offset = 0 + position = event.Position() + } + offset++ + if event.Position() == currentState.position && + event.Aggregate().ID == currentState.aggregateID && + event.Aggregate().Type == currentState.aggregateType && + event.Sequence() == currentState.sequence { + return i, offset + } + } + return -1, 0 +} diff --git a/internal/eventstore/handler/v2/handler.go b/internal/eventstore/handler/v2/handler.go index 0e0f8ae4d1..ed16787aa9 100644 --- a/internal/eventstore/handler/v2/handler.go +++ b/internal/eventstore/handler/v2/handler.go @@ -28,6 +28,7 @@ type EventStore interface { FilterToQueryReducer(ctx context.Context, reducer eventstore.QueryReducer) error Filter(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) Push(ctx context.Context, cmds ...eventstore.Command) ([]eventstore.Event, error) + FillFields(ctx context.Context, events ...eventstore.FillFieldsEvent) error } type Config struct { @@ -542,7 +543,7 @@ func (h *Handler) generateStatements(ctx context.Context, tx *sql.Tx, currentSta return []*Statement{stmt}, false, nil } - events, err := h.es.Filter(ctx, h.eventQuery(currentState)) + events, err := h.es.Filter(ctx, h.eventQuery(currentState).SetTx(tx)) if err != nil { h.log().WithError(err).Debug("filter eventstore failed") return nil, false, err @@ -554,7 +555,7 @@ func (h *Handler) generateStatements(ctx context.Context, tx *sql.Tx, currentSta return nil, false, err } - idx := skipPreviouslyReduced(statements, currentState) + idx := skipPreviouslyReducedStatements(statements, currentState) if idx+1 == len(statements) { currentState.position = statements[len(statements)-1].Position currentState.offset = statements[len(statements)-1].offset @@ -576,7 +577,7 @@ func (h *Handler) generateStatements(ctx context.Context, tx *sql.Tx, currentSta return statements, additionalIteration, nil } -func skipPreviouslyReduced(statements []*Statement, currentState *state) int { +func skipPreviouslyReducedStatements(statements []*Statement, currentState *state) int { for i, statement := range statements { if statement.Position == currentState.position && statement.AggregateID == currentState.aggregateID && @@ -655,12 +656,11 @@ func (h *Handler) eventQuery(currentState *state) *eventstore.SearchQueryBuilder } for aggregateType, eventTypes := range h.eventTypes { - query := builder. + builder = builder. AddQuery(). AggregateTypes(aggregateType). - EventTypes(eventTypes...) - - builder = query.Builder() + EventTypes(eventTypes...). + Builder() } return builder diff --git a/internal/eventstore/repository/event.go b/internal/eventstore/repository/event.go index d55661f157..57b85f15ba 100644 --- a/internal/eventstore/repository/event.go +++ b/internal/eventstore/repository/event.go @@ -120,3 +120,7 @@ func (e *Event) Payload() any { func (e *Event) UniqueConstraints() []*eventstore.UniqueConstraint { return e.Constraints } + +func (e *Event) Fields() []*eventstore.FieldOperation { + return nil +} diff --git a/internal/eventstore/v3/field.go b/internal/eventstore/v3/field.go new file mode 100644 index 0000000000..1298e59e42 --- /dev/null +++ b/internal/eventstore/v3/field.go @@ -0,0 +1,367 @@ +package eventstore + +import ( + "context" + "database/sql" + _ "embed" + "encoding/json" + "reflect" + "slices" + "strconv" + "strings" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/telemetry/tracing" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type fieldValue struct { + value []byte +} + +func (value *fieldValue) Unmarshal(ptr any) error { + return json.Unmarshal(value.value, ptr) +} + +func (es *Eventstore) FillFields(ctx context.Context, events ...eventstore.FillFieldsEvent) (err error) { + ctx, span := tracing.NewSpan(ctx) + defer span.End() + + tx, err := es.client.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + if err != nil { + _ = tx.Rollback() + return + } + err = tx.Commit() + }() + + return handleFieldFillEvents(ctx, tx, events) +} + +// Search implements the [eventstore.Search] method +func (es *Eventstore) Search(ctx context.Context, conditions ...map[eventstore.FieldType]any) (result []*eventstore.SearchResult, err error) { + ctx, span := tracing.NewSpan(ctx) + defer span.EndWithError(err) + + var builder strings.Builder + args := buildSearchStatement(ctx, &builder, conditions...) + + err = es.client.QueryContext( + ctx, + func(rows *sql.Rows) error { + for rows.Next() { + var ( + res eventstore.SearchResult + value fieldValue + ) + err = rows.Scan( + &res.Aggregate.InstanceID, + &res.Aggregate.ResourceOwner, + &res.Aggregate.Type, + &res.Aggregate.ID, + &res.Object.Type, + &res.Object.ID, + &res.Object.Revision, + &res.FieldName, + &value.value, + ) + if err != nil { + return err + } + res.Value = &value + + result = append(result, &res) + } + return nil + }, + builder.String(), + args..., + ) + if err != nil { + return nil, err + } + + return result, nil +} + +const searchQueryPrefix = `SELECT instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value FROM eventstore.fields WHERE instance_id = $1` + +func buildSearchStatement(ctx context.Context, builder *strings.Builder, conditions ...map[eventstore.FieldType]any) []any { + args := make([]any, 0, len(conditions)*4+1) + args = append(args, authz.GetInstance(ctx).InstanceID()) + + builder.WriteString(searchQueryPrefix) + + builder.WriteString(" AND ") + if len(conditions) > 1 { + builder.WriteRune('(') + } + for i, condition := range conditions { + if i > 0 { + builder.WriteString(" OR ") + } + if len(condition) > 1 { + builder.WriteRune('(') + } + args = append(args, buildSearchCondition(builder, len(args)+1, condition)...) + if len(condition) > 1 { + builder.WriteRune(')') + } + } + if len(conditions) > 1 { + builder.WriteRune(')') + } + + return args +} + +func buildSearchCondition(builder *strings.Builder, index int, conditions map[eventstore.FieldType]any) []any { + args := make([]any, 0, len(conditions)) + + orderedCondition := make([]eventstore.FieldType, 0, len(conditions)) + for field := range conditions { + orderedCondition = append(orderedCondition, field) + } + slices.Sort(orderedCondition) + + for _, field := range orderedCondition { + if len(args) > 0 { + builder.WriteString(" AND ") + } + builder.WriteString(fieldNameByType(field, conditions[field])) + builder.WriteString(" = $") + builder.WriteString(strconv.Itoa(index + len(args))) + args = append(args, conditions[field]) + } + + return args +} + +func handleFieldCommands(ctx context.Context, tx *sql.Tx, commands []eventstore.Command) error { + for _, command := range commands { + if len(command.Fields()) > 0 { + if err := handleFieldOperations(ctx, tx, command.Fields()); err != nil { + return err + } + } + } + return nil +} + +func handleFieldFillEvents(ctx context.Context, tx *sql.Tx, events []eventstore.FillFieldsEvent) error { + for _, event := range events { + if len(event.Fields()) > 0 { + if err := handleFieldOperations(ctx, tx, event.Fields()); err != nil { + return err + } + } + } + return nil +} + +func handleFieldOperations(ctx context.Context, tx *sql.Tx, operations []*eventstore.FieldOperation) error { + for _, operation := range operations { + if operation.Set != nil { + if err := handleFieldSet(ctx, tx, operation.Set); err != nil { + return err + } + continue + } + if operation.Remove != nil { + if err := handleSearchDelete(ctx, tx, operation.Remove); err != nil { + return err + } + } + } + + return nil +} + +func handleFieldSet(ctx context.Context, tx *sql.Tx, field *eventstore.Field) error { + if len(field.UpsertConflictFields) == 0 { + return handleSearchInsert(ctx, tx, field) + } + return handleSearchUpsert(ctx, tx, field) +} + +const ( + insertField = `INSERT INTO eventstore.fields (instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value, value_must_be_unique, should_index) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)` +) + +func handleSearchInsert(ctx context.Context, tx *sql.Tx, field *eventstore.Field) error { + value, err := json.Marshal(field.Value.Value) + if err != nil { + return zerrors.ThrowInvalidArgument(err, "V3-fcrW1", "unable to marshal field value") + } + _, err = tx.ExecContext( + ctx, + insertField, + + field.Aggregate.InstanceID, + field.Aggregate.ResourceOwner, + field.Aggregate.Type, + field.Aggregate.ID, + field.Object.Type, + field.Object.ID, + field.Object.Revision, + field.FieldName, + value, + field.Value.MustBeUnique, + field.Value.ShouldIndex, + ) + return err +} + +const ( + fieldsUpsertPrefix = `WITH upsert AS (UPDATE eventstore.fields SET (instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value, value_must_be_unique, should_index) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) WHERE ` + fieldsUpsertSuffix = ` RETURNING * ) INSERT INTO eventstore.fields (instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value, value_must_be_unique, should_index) SELECT $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11 WHERE NOT EXISTS (SELECT 1 FROM upsert)` +) + +func handleSearchUpsert(ctx context.Context, tx *sql.Tx, field *eventstore.Field) error { + value, err := json.Marshal(field.Value.Value) + if err != nil { + return zerrors.ThrowInvalidArgument(err, "V3-fcrW1", "unable to marshal field value") + } + + _, err = tx.ExecContext( + ctx, + writeUpsertField(field.UpsertConflictFields), + + field.Aggregate.InstanceID, + field.Aggregate.ResourceOwner, + field.Aggregate.Type, + field.Aggregate.ID, + field.Object.Type, + field.Object.ID, + field.Object.Revision, + field.FieldName, + value, + field.Value.MustBeUnique, + field.Value.ShouldIndex, + ) + return err +} + +func writeUpsertField(fields []eventstore.FieldType) string { + var builder strings.Builder + + builder.WriteString(fieldsUpsertPrefix) + for i, fieldName := range fields { + if i > 0 { + builder.WriteString(" AND ") + } + name, index := searchFieldNameAndIndexByTypeForPush(fieldName) + + builder.WriteString(name) + builder.WriteString(" = ") + builder.WriteString(index) + } + builder.WriteString(fieldsUpsertSuffix) + + return builder.String() +} + +const removeSearch = `DELETE FROM eventstore.fields WHERE ` + +func handleSearchDelete(ctx context.Context, tx *sql.Tx, clauses map[eventstore.FieldType]any) error { + if len(clauses) == 0 { + return zerrors.ThrowInvalidArgument(nil, "V3-oqlBZ", "no conditions") + } + stmt, args := writeDeleteField(clauses) + _, err := tx.ExecContext(ctx, stmt, args...) + return err +} + +func writeDeleteField(clauses map[eventstore.FieldType]any) (string, []any) { + var ( + builder strings.Builder + args = make([]any, 0, len(clauses)) + ) + builder.WriteString(removeSearch) + + orderedCondition := make([]eventstore.FieldType, 0, len(clauses)) + for field := range clauses { + orderedCondition = append(orderedCondition, field) + } + slices.Sort(orderedCondition) + + for _, fieldName := range orderedCondition { + if len(args) > 0 { + builder.WriteString(" AND ") + } + builder.WriteString(fieldNameByType(fieldName, clauses[fieldName])) + + builder.WriteString(" = $") + builder.WriteString(strconv.Itoa(len(args) + 1)) + + args = append(args, clauses[fieldName]) + } + + return builder.String(), args +} + +func fieldNameByType(typ eventstore.FieldType, value any) string { + switch typ { + case eventstore.FieldTypeAggregateID: + return "aggregate_id" + case eventstore.FieldTypeAggregateType: + return "aggregate_type" + case eventstore.FieldTypeInstanceID: + return "instance_id" + case eventstore.FieldTypeResourceOwner: + return "resource_owner" + case eventstore.FieldTypeFieldName: + return "field_name" + case eventstore.FieldTypeObjectType: + return "object_type" + case eventstore.FieldTypeObjectID: + return "object_id" + case eventstore.FieldTypeObjectRevision: + return "object_revision" + case eventstore.FieldTypeValue: + return valueColumn(value) + } + return "" +} + +func searchFieldNameAndIndexByTypeForPush(typ eventstore.FieldType) (string, string) { + switch typ { + case eventstore.FieldTypeInstanceID: + return "instance_id", "$1" + case eventstore.FieldTypeResourceOwner: + return "resource_owner", "$2" + case eventstore.FieldTypeAggregateType: + return "aggregate_type", "$3" + case eventstore.FieldTypeAggregateID: + return "aggregate_id", "$4" + case eventstore.FieldTypeObjectType: + return "object_type", "$5" + case eventstore.FieldTypeObjectID: + return "object_id", "$6" + case eventstore.FieldTypeObjectRevision: + return "object_revision", "$7" + case eventstore.FieldTypeFieldName: + return "field_name", "$8" + case eventstore.FieldTypeValue: + return "value", "$9" + } + return "", "" +} + +func valueColumn(value any) string { + //nolint: exhaustive + switch reflect.TypeOf(value).Kind() { + case reflect.Bool: + return "bool_value" + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: + return "number_value" + case reflect.String: + return "text_value" + } + return "" +} diff --git a/internal/eventstore/v3/field_test.go b/internal/eventstore/v3/field_test.go new file mode 100644 index 0000000000..0847c1b8ad --- /dev/null +++ b/internal/eventstore/v3/field_test.go @@ -0,0 +1,260 @@ +package eventstore + +import ( + "context" + _ "embed" + "reflect" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/eventstore" +) + +func Test_handleSearchDelete(t *testing.T) { + type args struct { + clauses map[eventstore.FieldType]any + } + type want struct { + stmt string + args []any + } + tests := []struct { + name string + args args + want want + }{ + { + name: "1 condition", + args: args{ + clauses: map[eventstore.FieldType]any{ + eventstore.FieldTypeInstanceID: "i_id", + }, + }, + want: want{ + stmt: "DELETE FROM eventstore.fields WHERE instance_id = $1", + args: []any{"i_id"}, + }, + }, + { + name: "2 conditions", + args: args{ + clauses: map[eventstore.FieldType]any{ + eventstore.FieldTypeInstanceID: "i_id", + eventstore.FieldTypeAggregateID: "a_id", + }, + }, + want: want{ + stmt: "DELETE FROM eventstore.fields WHERE aggregate_id = $1 AND instance_id = $2", + args: []any{"a_id", "i_id"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stmt, args := writeDeleteField(tt.args.clauses) + if stmt != tt.want.stmt { + t.Errorf("handleSearchDelete() stmt = %q, want %q", stmt, tt.want.stmt) + } + assert.Equal(t, tt.want.args, args) + }) + } +} + +func Test_writeUpsertField(t *testing.T) { + type args struct { + fields []eventstore.FieldType + } + tests := []struct { + name string + args args + want string + }{ + { + name: "1 field", + args: args{ + fields: []eventstore.FieldType{ + eventstore.FieldTypeInstanceID, + }, + }, + want: "WITH upsert AS (UPDATE eventstore.fields SET (instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value, value_must_be_unique, should_index) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) WHERE instance_id = $1 RETURNING * ) INSERT INTO eventstore.fields (instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value, value_must_be_unique, should_index) SELECT $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11 WHERE NOT EXISTS (SELECT 1 FROM upsert)", + }, + { + name: "2 fields", + args: args{ + fields: []eventstore.FieldType{ + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeAggregateType, + }, + }, + want: "WITH upsert AS (UPDATE eventstore.fields SET (instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value, value_must_be_unique, should_index) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) WHERE instance_id = $1 AND aggregate_type = $3 RETURNING * ) INSERT INTO eventstore.fields (instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value, value_must_be_unique, should_index) SELECT $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11 WHERE NOT EXISTS (SELECT 1 FROM upsert)", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := writeUpsertField(tt.args.fields); got != tt.want { + t.Errorf("writeUpsertField() = %q, want %q", got, tt.want) + } + }) + } +} + +func Test_buildSearchCondition(t *testing.T) { + type args struct { + index int + conditions map[eventstore.FieldType]any + } + type want struct { + stmt string + args []any + } + tests := []struct { + name string + args args + want want + }{ + { + name: "1 condition", + args: args{ + index: 1, + conditions: map[eventstore.FieldType]any{ + eventstore.FieldTypeAggregateID: "a_id", + }, + }, + want: want{ + stmt: "aggregate_id = $1", + args: []any{"a_id"}, + }, + }, + { + name: "3 condition", + args: args{ + index: 1, + conditions: map[eventstore.FieldType]any{ + eventstore.FieldTypeAggregateID: "a_id", + eventstore.FieldTypeInstanceID: "i_id", + eventstore.FieldTypeAggregateType: "a_type", + }, + }, + want: want{ + stmt: "aggregate_type = $1 AND aggregate_id = $2 AND instance_id = $3", + args: []any{"a_type", "a_id", "i_id"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var builder strings.Builder + + if got := buildSearchCondition(&builder, tt.args.index, tt.args.conditions); !reflect.DeepEqual(got, tt.want.args) { + t.Errorf("buildSearchCondition() = %v, want %v", got, tt.want) + } + if tt.want.stmt != builder.String() { + t.Errorf("buildSearchCondition() stmt = %q, want %q", builder.String(), tt.want.stmt) + } + }) + } +} + +func Test_buildSearchStatement(t *testing.T) { + type args struct { + index int + conditions []map[eventstore.FieldType]any + } + type want struct { + stmt string + args []any + } + tests := []struct { + name string + args args + want want + }{ + { + name: "1 condition with 1 field", + args: args{ + index: 1, + conditions: []map[eventstore.FieldType]any{ + { + eventstore.FieldTypeAggregateID: "a_id", + }, + }, + }, + want: want{ + stmt: "SELECT instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value FROM eventstore.fields WHERE instance_id = $1 AND aggregate_id = $2", + args: []any{"a_id"}, + }, + }, + { + name: "1 condition with 3 fields", + args: args{ + index: 1, + conditions: []map[eventstore.FieldType]any{ + { + eventstore.FieldTypeAggregateID: "a_id", + eventstore.FieldTypeInstanceID: "i_id", + eventstore.FieldTypeAggregateType: "a_type", + }, + }, + }, + want: want{ + stmt: "SELECT instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value FROM eventstore.fields WHERE instance_id = $1 AND (aggregate_type = $2 AND aggregate_id = $3 AND instance_id = $4)", + args: []any{"a_type", "a_id", "i_id"}, + }, + }, + { + name: "2 condition with 1 field", + args: args{ + index: 1, + conditions: []map[eventstore.FieldType]any{ + { + eventstore.FieldTypeAggregateID: "a_id", + }, + { + eventstore.FieldTypeAggregateType: "a_type", + }, + }, + }, + want: want{ + stmt: "SELECT instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value FROM eventstore.fields WHERE instance_id = $1 AND (aggregate_id = $2 OR aggregate_type = $3)", + args: []any{"a_id", "a_type"}, + }, + }, + { + name: "2 condition with 2 fields", + args: args{ + index: 1, + conditions: []map[eventstore.FieldType]any{ + { + eventstore.FieldTypeAggregateID: "a_id1", + eventstore.FieldTypeAggregateType: "a_type1", + }, + { + eventstore.FieldTypeAggregateID: "a_id2", + eventstore.FieldTypeAggregateType: "a_type2", + }, + }, + }, + want: want{ + stmt: "SELECT instance_id, resource_owner, aggregate_type, aggregate_id, object_type, object_id, object_revision, field_name, value FROM eventstore.fields WHERE instance_id = $1 AND ((aggregate_type = $2 AND aggregate_id = $3) OR (aggregate_type = $4 AND aggregate_id = $5))", + args: []any{"a_type1", "a_id1", "a_type2", "a_id2"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var builder strings.Builder + tt.want.args = append([]any{"i_id"}, tt.want.args...) + ctx := authz.WithInstanceID(context.Background(), "i_id") + + if got := buildSearchStatement(ctx, &builder, tt.args.conditions...); !reflect.DeepEqual(got, tt.want.args) { + t.Errorf("buildSearchStatement() = %v, want %v", got, tt.want) + } + if tt.want.stmt != builder.String() { + t.Errorf("buildSearchStatement() stmt = %q, want %q", builder.String(), tt.want.stmt) + } + }) + } +} diff --git a/internal/eventstore/v3/mock_test.go b/internal/eventstore/v3/mock_test.go index 42435d9c44..8de5ae8e2c 100644 --- a/internal/eventstore/v3/mock_test.go +++ b/internal/eventstore/v3/mock_test.go @@ -42,6 +42,10 @@ func (m *mockCommand) UniqueConstraints() []*eventstore.UniqueConstraint { return m.constraints } +func (e *mockCommand) Fields() []*eventstore.FieldOperation { + return nil +} + func mockEvent(aggregate *eventstore.Aggregate, sequence uint64, payload Payload) eventstore.Event { return &event{ aggregate: aggregate, diff --git a/internal/eventstore/v3/push.go b/internal/eventstore/v3/push.go index 22ea9295f6..d49fc408bb 100644 --- a/internal/eventstore/v3/push.go +++ b/internal/eventstore/v3/push.go @@ -41,7 +41,20 @@ func (es *Eventstore) Push(ctx context.Context, commands ...eventstore.Command) return err } - return handleUniqueConstraints(ctx, tx, commands) + if err = handleUniqueConstraints(ctx, tx, commands); err != nil { + return err + } + + // CockroachDB by default does not allow multiple modifications of the same table using ON CONFLICT + // Thats why we enable it manually + if es.client.Type() == "cockroach" { + _, err = tx.Exec("SET enable_multiple_modifications_of_table = on") + if err != nil { + return err + } + } + + return handleFieldCommands(ctx, tx, commands) }) if err != nil { diff --git a/internal/feature/feature.go b/internal/feature/feature.go index dc3e0eb597..d0f8a9f7a3 100644 --- a/internal/feature/feature.go +++ b/internal/feature/feature.go @@ -42,6 +42,8 @@ type ImprovedPerformanceType int32 const ( ImprovedPerformanceTypeUnknown = iota ImprovedPerformanceTypeOrgByID + ImprovedPerformanceTypeProjectGrant + ImprovedPerformanceTypeProject ) func (f Features) ShouldUseImprovedPerformance(typ ImprovedPerformanceType) bool { diff --git a/internal/query/projection/eventstore_field.go b/internal/query/projection/eventstore_field.go new file mode 100644 index 0000000000..5e0a205046 --- /dev/null +++ b/internal/query/projection/eventstore_field.go @@ -0,0 +1,19 @@ +package projection + +import ( + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/handler/v2" + "github.com/zitadel/zitadel/internal/repository/org" + "github.com/zitadel/zitadel/internal/repository/project" +) + +func newFillProjectGrantFields(config handler.Config) *handler.FieldHandler { + return handler.NewFieldHandler( + &config, + "project_grant_fields", + map[eventstore.AggregateType][]eventstore.EventType{ + org.AggregateType: nil, + project.AggregateType: nil, + }, + ) +} diff --git a/internal/query/projection/eventstore_mock_test.go b/internal/query/projection/eventstore_mock_test.go index c1cdf434d4..202664d517 100644 --- a/internal/query/projection/eventstore_mock_test.go +++ b/internal/query/projection/eventstore_mock_test.go @@ -49,3 +49,7 @@ func (m *mockEventStore) Push(ctx context.Context, cmds ...eventstore.Command) ( m.pushCounter++ return m.pushResponse[m.pushCounter-1], nil } + +func (m *mockEventStore) FillFields(ctx context.Context, events ...eventstore.FillFieldsEvent) error { + return nil +} diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go index 30c1df6870..09c3d8d86f 100644 --- a/internal/query/projection/projection.go +++ b/internal/query/projection/projection.go @@ -77,6 +77,8 @@ var ( TargetProjection *handler.Handler ExecutionProjection *handler.Handler UserSchemaProjection *handler.Handler + + ProjectGrantFields *handler.FieldHandler ) type projection interface { @@ -158,6 +160,9 @@ func Create(ctx context.Context, sqlClient *database.DB, es handler.EventStore, TargetProjection = newTargetProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["targets"])) ExecutionProjection = newExecutionProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["executions"])) UserSchemaProjection = newUserSchemaProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["user_schemas"])) + + ProjectGrantFields = newFillProjectGrantFields(applyCustomConfig(projectionConfig, config.Customizations["project_grant_fields"])) + newProjectionsList() return nil } diff --git a/internal/query/user_membership.go b/internal/query/user_membership.go index b45bd5a943..7ba2629cfa 100644 --- a/internal/query/user_membership.go +++ b/internal/query/user_membership.go @@ -68,7 +68,7 @@ func NewMembershipUserIDQuery(userID string) (SearchQuery, error) { } func NewMembershipOrgIDQuery(value string) (SearchQuery, error) { - return NewTextQuery(membershipOrgID, value, TextEquals) + return NewTextQuery(OrgMemberOrgID, value, TextEquals) } func NewMembershipResourceOwnersSearchQuery(ids ...string) (SearchQuery, error) { @@ -84,15 +84,15 @@ func NewMembershipGrantedOrgIDSearchQuery(id string) (SearchQuery, error) { } func NewMembershipProjectIDQuery(value string) (SearchQuery, error) { - return NewTextQuery(membershipProjectID, value, TextEquals) + return NewTextQuery(ProjectMemberProjectID, value, TextEquals) } func NewMembershipProjectGrantIDQuery(value string) (SearchQuery, error) { - return NewTextQuery(membershipGrantID, value, TextEquals) + return NewTextQuery(ProjectGrantMemberGrantID, value, TextEquals) } func NewMembershipIsIAMQuery() (SearchQuery, error) { - return NewNotNullQuery(membershipIAMID) + return NewNotNullQuery(InstanceMemberIAMID) } func (q *MembershipSearchQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder { @@ -357,7 +357,7 @@ func prepareOrgMember(query *MembershipSearchQuery) (string, []interface{}) { ).From(orgMemberTable.identifier()) for _, q := range query.Queries { - if q.Col().table.name == membershipAlias.name { + if q.Col().table.name == membershipAlias.name || q.Col().table.name == orgMemberTable.name { builder = q.toQuery(builder) } } @@ -380,7 +380,7 @@ func prepareIAMMember(query *MembershipSearchQuery) (string, []interface{}) { ).From(instanceMemberTable.identifier()) for _, q := range query.Queries { - if q.Col().table.name == membershipAlias.name { + if q.Col().table.name == membershipAlias.name || q.Col().table.name == instanceMemberTable.name { builder = q.toQuery(builder) } } @@ -403,7 +403,7 @@ func prepareProjectMember(query *MembershipSearchQuery) (string, []interface{}) ).From(projectMemberTable.identifier()) for _, q := range query.Queries { - if q.Col().table.name == membershipAlias.name { + if q.Col().table.name == membershipAlias.name || q.Col().table.name == projectMemberTable.name { builder = q.toQuery(builder) } } @@ -427,7 +427,7 @@ func prepareProjectGrantMember(query *MembershipSearchQuery) (string, []interfac ).From(projectGrantMemberTable.identifier()) for _, q := range query.Queries { - if q.Col().table.name == membershipAlias.name { + if q.Col().table.name == membershipAlias.name || q.Col().table.name == projectMemberTable.name || q.Col().table.name == projectGrantMemberTable.name { builder = q.toQuery(builder) } } diff --git a/internal/repository/org/org.go b/internal/repository/org/org.go index af19fb18f4..95c9c2f3ed 100644 --- a/internal/repository/org/org.go +++ b/internal/repository/org/org.go @@ -17,6 +17,10 @@ const ( OrgDeactivatedEventType = orgEventTypePrefix + "deactivated" OrgReactivatedEventType = orgEventTypePrefix + "reactivated" OrgRemovedEventType = orgEventTypePrefix + "removed" + + OrgSearchType = "org" + OrgNameSearchField = "name" + OrgStateSearchField = "state" ) func NewAddOrgNameUniqueConstraint(orgName string) *eventstore.UniqueConstraint { @@ -46,6 +50,43 @@ func (e *OrgAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return []*eventstore.UniqueConstraint{NewAddOrgNameUniqueConstraint(e.Name)} } +func (e *OrgAddedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + orgSearchObject(e.Aggregate().ID), + OrgNameSearchField, + &eventstore.Value{ + Value: e.Name, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + eventstore.SetField( + e.Aggregate(), + orgSearchObject(e.Aggregate().ID), + OrgStateSearchField, + &eventstore.Value{ + Value: domain.OrgStateActive, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewOrgAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, name string) *OrgAddedEvent { return &OrgAddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -87,6 +128,28 @@ func (e *OrgChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { } } +func (e *OrgChangedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + orgSearchObject(e.Aggregate().ID), + OrgNameSearchField, + &eventstore.Value{ + Value: e.Name, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewOrgChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, oldName, newName string) *OrgChangedEvent { return &OrgChangedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -123,6 +186,28 @@ func (e *OrgDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint return nil } +func (e *OrgDeactivatedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + orgSearchObject(e.Aggregate().ID), + OrgStateSearchField, + &eventstore.Value{ + Value: domain.OrgStateInactive, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewOrgDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *OrgDeactivatedEvent { return &OrgDeactivatedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -143,6 +228,28 @@ type OrgReactivatedEvent struct { eventstore.BaseEvent `json:"-"` } +func (e *OrgReactivatedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + orgSearchObject(e.Aggregate().ID), + OrgStateSearchField, + &eventstore.Value{ + Value: domain.OrgStateActive, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func (e *OrgReactivatedEvent) Payload() interface{} { return e } @@ -200,6 +307,29 @@ func (e *OrgRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return constraints } +func (e *OrgRemovedEvent) Fields() []*eventstore.FieldOperation { + // TODO: project grants are currently not removed because we don't have the relationship between the granted org and the grant + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + orgSearchObject(e.Aggregate().ID), + OrgStateSearchField, + &eventstore.Value{ + Value: domain.OrgStateRemoved, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewOrgRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, name string, usernames []string, loginMustBeDomain bool, domains []string, externalIDPs []*domain.UserIDPLink, samlEntityIDs []string) *OrgRemovedEvent { return &OrgRemovedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -221,3 +351,11 @@ func OrgRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil } + +func orgSearchObject(id string) eventstore.Object { + return eventstore.Object{ + Type: OrgSearchType, + Revision: 1, + ID: id, + } +} diff --git a/internal/repository/project/grant.go b/internal/repository/project/grant.go index 486a62d924..a78172a03e 100644 --- a/internal/repository/project/grant.go +++ b/internal/repository/project/grant.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -17,6 +18,13 @@ var ( GrantDeactivatedType = grantEventTypePrefix + "deactivated" GrantReactivatedType = grantEventTypePrefix + "reactivated" GrantRemovedType = grantEventTypePrefix + "removed" + + ProjectGrantSearchType = "project_grant" + ProjectGrantGrantIDSearchField = "grant_id" + ProjectGrantGrantedOrgIDSearchField = "granted_org_id" + ProjectGrantStateSearchField = "state" + ProjectGrantRoleKeySearchField = "role_key" + ProjectGrantObjectRevision = uint8(1) ) func NewAddProjectGrantUniqueConstraint(grantedOrgID, projectID string) *eventstore.UniqueConstraint { @@ -48,6 +56,76 @@ func (e *GrantAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return []*eventstore.UniqueConstraint{NewAddProjectGrantUniqueConstraint(e.GrantedOrgID, e.Aggregate().ID)} } +func (e *GrantAddedEvent) Fields() []*eventstore.FieldOperation { + fields := make([]*eventstore.FieldOperation, 0, len(e.RoleKeys)+3) + fields = append(fields, + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + ProjectGrantGrantIDSearchField, + &eventstore.Value{ + Value: e.GrantID, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + ProjectGrantGrantedOrgIDSearchField, + &eventstore.Value{ + Value: e.GrantedOrgID, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + ProjectGrantStateSearchField, + &eventstore.Value{ + Value: domain.ProjectGrantStateActive, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + ) + + for _, roleKey := range e.RoleKeys { + fields = append(fields, + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + ProjectGrantRoleKeySearchField, + &eventstore.Value{ + Value: roleKey, + ShouldIndex: true, + }, + ), + ) + } + + return fields +} + func NewGrantAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -95,6 +173,37 @@ func (e *GrantChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } +func (e *GrantChangedEvent) Fields() []*eventstore.FieldOperation { + fields := make([]*eventstore.FieldOperation, 0, len(e.RoleKeys)+1) + + fields = append(fields, + eventstore.RemoveSearchFieldsByAggregateAndObjectAndField( + e.Aggregate(), + grantSearchObject(e.GrantID), + + ProjectGrantRoleKeySearchField, + ), + ) + + for _, roleKey := range e.RoleKeys { + fields = append(fields, + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + + ProjectGrantRoleKeySearchField, + + &eventstore.Value{ + Value: roleKey, + ShouldIndex: true, + }, + ), + ) + } + + return fields +} + func NewGrantChangedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -140,6 +249,43 @@ func (e *GrantCascadeChangedEvent) UniqueConstraints() []*eventstore.UniqueConst return nil } +func (e *GrantCascadeChangedEvent) Fields() []*eventstore.FieldOperation { + fields := make([]*eventstore.FieldOperation, 0, len(e.RoleKeys)+1) + + fields = append(fields, + eventstore.RemoveSearchFieldsByAggregateAndObjectAndField( + e.Aggregate(), + grantSearchObject(e.GrantID), + + ProjectGrantRoleKeySearchField, + ), + ) + + for _, roleKey := range e.RoleKeys { + fields = append(fields, + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + + ProjectGrantRoleKeySearchField, + &eventstore.Value{ + Value: roleKey, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + ) + } + + return fields +} + func NewGrantCascadeChangedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -184,6 +330,29 @@ func (e *GrantDeactivateEvent) UniqueConstraints() []*eventstore.UniqueConstrain return nil } +func (e *GrantDeactivateEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + + ProjectGrantStateSearchField, + &eventstore.Value{ + Value: domain.ProjectGrantStateInactive, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewGrantDeactivateEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -226,6 +395,29 @@ func (e *GrantReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstrai return nil } +func (e *GrantReactivatedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + + ProjectGrantStateSearchField, + &eventstore.Value{ + Value: domain.ProjectGrantStateActive, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewGrantReactivatedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -269,6 +461,29 @@ func (e *GrantRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return []*eventstore.UniqueConstraint{NewRemoveProjectGrantUniqueConstraint(e.grantedOrgID, e.Aggregate().ID)} } +func (e *GrantRemovedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + grantSearchObject(e.GrantID), + + ProjectGrantStateSearchField, + &eventstore.Value{ + Value: domain.ProjectGrantStateRemoved, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewGrantRemovedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -298,3 +513,11 @@ func GrantRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return e, nil } + +func grantSearchObject(id string) eventstore.Object { + return eventstore.Object{ + Type: ProjectGrantSearchType, + Revision: 1, + ID: id, + } +} diff --git a/internal/repository/project/project.go b/internal/repository/project/project.go index 9774877301..6147a632eb 100644 --- a/internal/repository/project/project.go +++ b/internal/repository/project/project.go @@ -16,6 +16,11 @@ const ( ProjectDeactivatedType = projectEventTypePrefix + "deactivated" ProjectReactivatedType = projectEventTypePrefix + "reactivated" ProjectRemovedType = projectEventTypePrefix + "removed" + + ProjectSearchType = "project" + ProjectObjectRevision = uint8(1) + ProjectNameSearchField = "name" + ProjectStateSearchField = "state" ) func NewAddProjectNameUniqueConstraint(projectName, resourceOwner string) *eventstore.UniqueConstraint { @@ -49,6 +54,45 @@ func (e *ProjectAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return []*eventstore.UniqueConstraint{NewAddProjectNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} } +func (e *ProjectAddedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + projectSearchObject(e.Aggregate().ID), + ProjectNameSearchField, + &eventstore.Value{ + Value: e.Name, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeObjectRevision, + eventstore.FieldTypeFieldName, + ), + eventstore.SetField( + e.Aggregate(), + projectSearchObject(e.Aggregate().ID), + ProjectStateSearchField, + &eventstore.Value{ + Value: domain.ProjectStateActive, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeObjectRevision, + eventstore.FieldTypeFieldName, + ), + } +} + func NewProjectAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -110,6 +154,30 @@ func (e *ProjectChangeEvent) UniqueConstraints() []*eventstore.UniqueConstraint return nil } +func (e *ProjectChangeEvent) Fields() []*eventstore.FieldOperation { + if e.Name == nil { + return nil + } + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + projectSearchObject(e.Aggregate().ID), + ProjectNameSearchField, + &eventstore.Value{ + Value: *e.Name, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewProjectChangeEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -190,6 +258,28 @@ func (e *ProjectDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstr return nil } +func (e *ProjectDeactivatedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + projectSearchObject(e.Aggregate().ID), + ProjectStateSearchField, + &eventstore.Value{ + Value: domain.ProjectStateInactive, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewProjectDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *ProjectDeactivatedEvent { return &ProjectDeactivatedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -218,6 +308,28 @@ func (e *ProjectReactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstr return nil } +func (e *ProjectReactivatedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + projectSearchObject(e.Aggregate().ID), + ProjectStateSearchField, + &eventstore.Value{ + Value: domain.ProjectStateRemoved, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewProjectReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *ProjectReactivatedEvent { return &ProjectReactivatedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -255,6 +367,12 @@ func (e *ProjectRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint return constraints } +func (e *ProjectRemovedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.RemoveSearchFieldsByAggregate(e.Aggregate()), + } +} + func NewProjectRemovedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -277,3 +395,11 @@ func ProjectRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil } + +func projectSearchObject(id string) eventstore.Object { + return eventstore.Object{ + Type: ProjectSearchType, + Revision: ProjectObjectRevision, + ID: id, + } +} diff --git a/internal/repository/project/role.go b/internal/repository/project/role.go index 098b32189b..2c7836da2c 100644 --- a/internal/repository/project/role.go +++ b/internal/repository/project/role.go @@ -14,6 +14,12 @@ var ( RoleAddedType = roleEventTypePrefix + "added" RoleChangedType = roleEventTypePrefix + "changed" RoleRemovedType = roleEventTypePrefix + "removed" + + ProjectRoleSearchType = "project_role" + ProjectRoleRevision = uint8(1) + ProjectRoleKeySearchField = "key" + ProjectRoleDisplayNameSearchField = "display_name" + ProjectRoleGroupSearchField = "group" ) func NewAddProjectRoleUniqueConstraint(roleKey, projectID string) *eventstore.UniqueConstraint { @@ -45,6 +51,59 @@ func (e *RoleAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return []*eventstore.UniqueConstraint{NewAddProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)} } +func (e *RoleAddedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.SetField( + e.Aggregate(), + projectRoleSearchObject(e.Key), + ProjectRoleKeySearchField, + &eventstore.Value{ + Value: e.Key, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + eventstore.SetField( + e.Aggregate(), + projectRoleSearchObject(e.Key), + ProjectRoleDisplayNameSearchField, + &eventstore.Value{ + Value: e.DisplayName, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + eventstore.SetField( + e.Aggregate(), + projectRoleSearchObject(e.Key), + ProjectRoleGroupSearchField, + &eventstore.Value{ + Value: e.Group, + ShouldIndex: true, + }, + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + ), + } +} + func NewRoleAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -93,6 +152,50 @@ func (e *RoleChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return nil } +func (e *RoleChangedEvent) Fields() []*eventstore.FieldOperation { + operations := make([]*eventstore.FieldOperation, 0, 2) + if e.DisplayName != nil { + operations = append(operations, eventstore.SetField( + e.Aggregate(), + projectRoleSearchObject(e.Key), + ProjectRoleDisplayNameSearchField, + &eventstore.Value{ + Value: *e.DisplayName, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + )) + } + if e.Group != nil { + operations = append(operations, eventstore.SetField( + e.Aggregate(), + projectRoleSearchObject(e.Key), + ProjectRoleGroupSearchField, + &eventstore.Value{ + Value: *e.Group, + ShouldIndex: true, + }, + + eventstore.FieldTypeInstanceID, + eventstore.FieldTypeResourceOwner, + eventstore.FieldTypeAggregateType, + eventstore.FieldTypeAggregateID, + eventstore.FieldTypeObjectType, + eventstore.FieldTypeObjectID, + eventstore.FieldTypeFieldName, + )) + } + + return operations +} + func NewRoleChangedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -162,6 +265,15 @@ func (e *RoleRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { return []*eventstore.UniqueConstraint{NewRemoveProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)} } +func (e *RoleRemovedEvent) Fields() []*eventstore.FieldOperation { + return []*eventstore.FieldOperation{ + eventstore.RemoveSearchFieldsByAggregateAndObject( + e.Aggregate(), + projectRoleSearchObject(e.Key), + ), + } +} + func NewRoleRemovedEvent( ctx context.Context, aggregate *eventstore.Aggregate, @@ -188,3 +300,11 @@ func RoleRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) { return e, nil } + +func projectRoleSearchObject(id string) eventstore.Object { + return eventstore.Object{ + Type: ProjectRoleSearchType, + Revision: ProjectRoleRevision, + ID: id, + } +} diff --git a/internal/user/repository/view/user_sessions_by_user_agent.sql b/internal/user/repository/view/user_sessions_by_user_agent.sql index d5f5191863..476f43ba81 100644 --- a/internal/user/repository/view/user_sessions_by_user_agent.sql +++ b/internal/user/repository/view/user_sessions_by_user_agent.sql @@ -22,6 +22,6 @@ FROM auth.user_sessions s LEFT JOIN projections.users13 u ON s.user_id = u.id AND s.instance_id = u.instance_id LEFT JOIN projections.users13_humans h ON s.user_id = h.user_id AND s.instance_id = h.instance_id LEFT JOIN projections.login_names3 l ON s.user_id = l.user_id AND s.instance_id = l.instance_id AND l.is_primary = true -WHERE (s.user_agent_id = $1) +WHERE (s.user_agent_id = $1 and s.user_agent_id <> '') AND (s.instance_id = $2) ; \ No newline at end of file diff --git a/proto/zitadel/feature/v2beta/feature.proto b/proto/zitadel/feature/v2beta/feature.proto index 082159b95b..b000092fad 100644 --- a/proto/zitadel/feature/v2beta/feature.proto +++ b/proto/zitadel/feature/v2beta/feature.proto @@ -54,4 +54,9 @@ enum ImprovedPerformance { // Uses the eventstore to query the org by id // instead of the sql table. IMPROVED_PERFORMANCE_ORG_BY_ID = 1; + // Improves performance on write side by using + // optimized processes to query data to determine + // correctnes of data. + IMPROVED_PERFORMANCE_PROJECT_GRANT = 2; + IMPROVED_PERFORMANCE_PROJECT = 3; } \ No newline at end of file diff --git a/proto/zitadel/settings/v2beta/login_settings.proto b/proto/zitadel/settings/v2beta/login_settings.proto index 23315513e0..a31c058931 100644 --- a/proto/zitadel/settings/v2beta/login_settings.proto +++ b/proto/zitadel/settings/v2beta/login_settings.proto @@ -148,4 +148,5 @@ enum IdentityProviderType { IDENTITY_PROVIDER_TYPE_GITLAB = 8; IDENTITY_PROVIDER_TYPE_GITLAB_SELF_HOSTED = 9; IDENTITY_PROVIDER_TYPE_GOOGLE = 10; + IDENTITY_PROVIDER_TYPE_SAML=11; } diff --git a/release-channels.yaml b/release-channels.yaml index bafa7ea3da..cfbb41aef5 100644 --- a/release-channels.yaml +++ b/release-channels.yaml @@ -1 +1 @@ -stable: "v2.49.5" +stable: "v2.50.6"