From cf9d74f4d7c014922950f1036b0498e1620e941a Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Wed, 5 Apr 2023 11:08:01 +0200 Subject: [PATCH 01/36] ci(helm): inform charts repo about releases (#4544) * ci(helm): inform charts repo about releases * typo * use gh app token --------- Co-authored-by: Livio Spring --- .github/workflows/zitadel.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/zitadel.yml b/.github/workflows/zitadel.yml index 34fd88696f..11e01fdc03 100644 --- a/.github/workflows/zitadel.yml +++ b/.github/workflows/zitadel.yml @@ -78,3 +78,16 @@ jobs: with: file: .artifacts/codecov/profile.cov name: go-codecov + - uses: tibdex/github-app-token@v1 + id: generate-token + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Bump Chart Version + uses: peter-evans/repository-dispatch@v2 + if: steps.semantic.outputs.new_release_published == 'true' && github.ref == 'refs/heads/main' + with: + token: ${{ steps.generate-token.outputs.token }} + repository: zitadel/zitadel-charts + event-type: zitadel-released + client-payload: '{"semanticoutputs": "${{ steps.semantic.outputs }}"}' From 4c1169b562d25af2d9082c76fecbfcf6959f3152 Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 5 Apr 2023 11:34:24 +0200 Subject: [PATCH 02/36] feat(eventstore): order by `creation_date` and `sequence` (#5568) * feat(eventstore): order by `creation_date` and `sequence` * fix(logstore): use correct event type --------- Co-authored-by: Livio Spring --- internal/eventstore/repository/sql/crdb.go | 4 ++-- internal/eventstore/repository/sql/crdb_test.go | 2 +- internal/eventstore/repository/sql/query_test.go | 14 +++++++------- .../v1/internal/repository/sql/db_mock_test.go | 10 +++++----- .../eventstore/v1/internal/repository/sql/query.go | 5 +++-- .../v1/internal/repository/sql/query_test.go | 6 +++--- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/internal/eventstore/repository/sql/crdb.go b/internal/eventstore/repository/sql/crdb.go index 91b5cd06ef..8cbd1046de 100644 --- a/internal/eventstore/repository/sql/crdb.go +++ b/internal/eventstore/repository/sql/crdb.go @@ -255,10 +255,10 @@ func (db *CRDB) db() *sql.DB { func (db *CRDB) orderByEventSequence(desc bool) string { if desc { - return " ORDER BY event_sequence DESC" + return " ORDER BY creation_date DESC, event_sequence DESC" } - return " ORDER BY event_sequence" + return " ORDER BY creation_date, event_sequence" } func (db *CRDB) eventQuery() string { diff --git a/internal/eventstore/repository/sql/crdb_test.go b/internal/eventstore/repository/sql/crdb_test.go index 17deaa0cea..80e3472135 100644 --- a/internal/eventstore/repository/sql/crdb_test.go +++ b/internal/eventstore/repository/sql/crdb_test.go @@ -1166,7 +1166,7 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) { } } - rows, err := testCRDBClient.Query("SELECT resource_owner FROM eventstore.events WHERE aggregate_type = $1 AND aggregate_id = ANY($2) ORDER BY event_sequence", tt.fields.aggregateType, tt.fields.aggregateIDs) + rows, err := testCRDBClient.Query("SELECT resource_owner FROM eventstore.events WHERE aggregate_type = $1 AND aggregate_id = ANY($2) ORDER BY creation_date, event_sequence", tt.fields.aggregateType, tt.fields.aggregateIDs) if err != nil { t.Error("unable to query inserted rows: ", err) return diff --git a/internal/eventstore/repository/sql/query_test.go b/internal/eventstore/repository/sql/query_test.go index 396de93349..9560ff0918 100644 --- a/internal/eventstore/repository/sql/query_test.go +++ b/internal/eventstore/repository/sql/query_test.go @@ -595,7 +595,7 @@ func Test_query_events_mocked(t *testing.T) { }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY event_sequence DESC`, + `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC`, []driver.Value{repository.AggregateType("user")}, ), }, @@ -624,7 +624,7 @@ func Test_query_events_mocked(t *testing.T) { }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY event_sequence LIMIT \$2`, + `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date, event_sequence LIMIT \$2`, []driver.Value{repository.AggregateType("user"), uint64(5)}, ), }, @@ -653,7 +653,7 @@ func Test_query_events_mocked(t *testing.T) { }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY event_sequence DESC LIMIT \$2`, + `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC LIMIT \$2`, []driver.Value{repository.AggregateType("user"), uint64(5)}, ), }, @@ -683,7 +683,7 @@ func Test_query_events_mocked(t *testing.T) { }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE \( aggregate_type = \$1 \) ORDER BY event_sequence DESC LIMIT \$2`, + `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC LIMIT \$2`, []driver.Value{repository.AggregateType("user"), uint64(5)}, ), }, @@ -712,7 +712,7 @@ func Test_query_events_mocked(t *testing.T) { }, fields: fields{ mock: newMockClient(t).expectQueryErr(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY event_sequence DESC`, + `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC`, []driver.Value{repository.AggregateType("user")}, sql.ErrConnDone), }, @@ -741,7 +741,7 @@ func Test_query_events_mocked(t *testing.T) { }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY event_sequence DESC`, + `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC`, []driver.Value{repository.AggregateType("user")}, &repository.Event{Sequence: 100}), }, @@ -809,7 +809,7 @@ func Test_query_events_mocked(t *testing.T) { }, fields: fields{ mock: newMockClient(t).expectQuery(t, - `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) OR \( aggregate_type = \$2 AND aggregate_id = \$3 \) ORDER BY event_sequence DESC LIMIT \$4`, + `SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) OR \( aggregate_type = \$2 AND aggregate_id = \$3 \) ORDER BY creation_date DESC, event_sequence DESC LIMIT \$4`, []driver.Value{repository.AggregateType("user"), repository.AggregateType("org"), "asdf42", uint64(5)}, ), }, diff --git a/internal/eventstore/v1/internal/repository/sql/db_mock_test.go b/internal/eventstore/v1/internal/repository/sql/db_mock_test.go index 6c271a572e..e1271fc0d5 100644 --- a/internal/eventstore/v1/internal/repository/sql/db_mock_test.go +++ b/internal/eventstore/v1/internal/repository/sql/db_mock_test.go @@ -17,11 +17,11 @@ const ( var ( eventColumns = []string{"creation_date", "event_type", "event_sequence", "previous_aggregate_sequence", "event_data", "editor_service", "editor_user", "resource_owner", "instance_id", "aggregate_type", "aggregate_id", "aggregate_version"} - expectedFilterEventsLimitFormat = regexp.MustCompile(selectEscaped + ` \) ORDER BY event_sequence LIMIT \$2`).String() - expectedFilterEventsDescFormat = regexp.MustCompile(selectEscaped + ` \) ORDER BY event_sequence DESC`).String() - expectedFilterEventsAggregateIDLimit = regexp.MustCompile(selectEscaped + ` AND aggregate_id = \$2 \) ORDER BY event_sequence LIMIT \$3`).String() - expectedFilterEventsAggregateIDTypeLimit = regexp.MustCompile(selectEscaped + ` AND aggregate_id = \$2 \) ORDER BY event_sequence LIMIT \$3`).String() - expectedGetAllEvents = regexp.MustCompile(selectEscaped + ` \) ORDER BY event_sequence`).String() + expectedFilterEventsLimitFormat = regexp.MustCompile(selectEscaped + ` \) ORDER BY creation_date, event_sequence LIMIT \$2`).String() + expectedFilterEventsDescFormat = regexp.MustCompile(selectEscaped + ` \) ORDER BY creation_date DESC, event_sequence DESC`).String() + expectedFilterEventsAggregateIDLimit = regexp.MustCompile(selectEscaped + ` AND aggregate_id = \$2 \) ORDER BY creation_date, event_sequence LIMIT \$3`).String() + expectedFilterEventsAggregateIDTypeLimit = regexp.MustCompile(selectEscaped + ` AND aggregate_id = \$2 \) ORDER BY creation_date, event_sequence LIMIT \$3`).String() + expectedGetAllEvents = regexp.MustCompile(selectEscaped + ` \) ORDER BY creation_date, event_sequence`).String() expectedInsertStatement = regexp.MustCompile(`INSERT INTO eventstore\.events ` + `\(event_type, aggregate_type, aggregate_id, aggregate_version, creation_date, event_data, editor_user, editor_service, resource_owner, instance_id, previous_aggregate_sequence, previous_aggregate_type_sequence\) ` + diff --git a/internal/eventstore/v1/internal/repository/sql/query.go b/internal/eventstore/v1/internal/repository/sql/query.go index 798cbdd417..ed4510e7f7 100644 --- a/internal/eventstore/v1/internal/repository/sql/query.go +++ b/internal/eventstore/v1/internal/repository/sql/query.go @@ -51,10 +51,11 @@ func buildQuery(ctx context.Context, db dialect.Database, queryFactory *es_model query += where if searchQuery.Columns == es_models.Columns_Event { - query += " ORDER BY event_sequence" + order := " ORDER BY creation_date, event_sequence" if searchQuery.Desc { - query += " DESC" + order = " ORDER BY creation_date DESC, event_sequence DESC" } + query += order } if searchQuery.Limit > 0 { diff --git a/internal/eventstore/v1/internal/repository/sql/query_test.go b/internal/eventstore/v1/internal/repository/sql/query_test.go index 8197893b9c..f0b13bc701 100644 --- a/internal/eventstore/v1/internal/repository/sql/query_test.go +++ b/internal/eventstore/v1/internal/repository/sql/query_test.go @@ -436,7 +436,7 @@ func Test_buildQuery(t *testing.T) { queryFactory: es_models.NewSearchQueryFactory().OrderDesc().AddQuery().AggregateTypes("user").Factory(), }, res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY event_sequence DESC", + query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY creation_date DESC, event_sequence DESC", rowScanner: true, values: []interface{}{es_models.AggregateType("user")}, }, @@ -447,7 +447,7 @@ func Test_buildQuery(t *testing.T) { queryFactory: es_models.NewSearchQueryFactory().Limit(5).AddQuery().AggregateTypes("user").Factory(), }, res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY event_sequence LIMIT $2", + query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY creation_date, event_sequence LIMIT $2", rowScanner: true, values: []interface{}{es_models.AggregateType("user"), uint64(5)}, limit: 5, @@ -459,7 +459,7 @@ func Test_buildQuery(t *testing.T) { queryFactory: es_models.NewSearchQueryFactory().Limit(5).OrderDesc().AddQuery().AggregateTypes("user").Factory(), }, res: res{ - query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY event_sequence DESC LIMIT $2", + query: "SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events AS OF SYSTEM TIME '-1 ms' WHERE ( aggregate_type = $1 ) ORDER BY creation_date DESC, event_sequence DESC LIMIT $2", rowScanner: true, values: []interface{}{es_models.AggregateType("user"), uint64(5)}, limit: 5, From ea9223a2b060424af01d0ca1353af4c866b2d677 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Wed, 5 Apr 2023 13:18:00 +0200 Subject: [PATCH 03/36] fix: remove instance IDPs correctly from org policies (#5609) Co-authored-by: Silvan --- internal/query/idp_login_policy_link_test.go | 6 +-- .../query/projection/idp_login_policy_link.go | 44 ++++++++++--------- .../projection/idp_login_policy_link_test.go | 28 ++++++------ 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/internal/query/idp_login_policy_link_test.go b/internal/query/idp_login_policy_link_test.go index 609b5b0019..6a436c5a27 100644 --- a/internal/query/idp_login_policy_link_test.go +++ b/internal/query/idp_login_policy_link_test.go @@ -12,13 +12,13 @@ import ( ) var ( - loginPolicyIDPLinksQuery = regexp.QuoteMeta(`SELECT projections.idp_login_policy_links4.idp_id,` + + loginPolicyIDPLinksQuery = regexp.QuoteMeta(`SELECT projections.idp_login_policy_links5.idp_id,` + ` projections.idp_templates4.name,` + ` projections.idp_templates4.type,` + ` projections.idp_templates4.owner_type,` + ` COUNT(*) OVER ()` + - ` FROM projections.idp_login_policy_links4` + - ` LEFT JOIN projections.idp_templates4 ON projections.idp_login_policy_links4.idp_id = projections.idp_templates4.id AND projections.idp_login_policy_links4.instance_id = projections.idp_templates4.instance_id` + + ` FROM projections.idp_login_policy_links5` + + ` LEFT JOIN projections.idp_templates4 ON projections.idp_login_policy_links5.idp_id = projections.idp_templates4.id AND projections.idp_login_policy_links5.instance_id = projections.idp_templates4.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) loginPolicyIDPLinksCols = []string{ "idp_id", diff --git a/internal/query/projection/idp_login_policy_link.go b/internal/query/projection/idp_login_policy_link.go index 4ddf815571..62118106d8 100644 --- a/internal/query/projection/idp_login_policy_link.go +++ b/internal/query/projection/idp_login_policy_link.go @@ -14,7 +14,7 @@ import ( ) const ( - IDPLoginPolicyLinkTable = "projections.idp_login_policy_links4" + IDPLoginPolicyLinkTable = "projections.idp_login_policy_links5" IDPLoginPolicyLinkIDPIDCol = "idp_id" IDPLoginPolicyLinkAggregateIDCol = "aggregate_id" @@ -197,45 +197,47 @@ func (p *idpLoginPolicyLinkProjection) reduceCascadeRemoved(event eventstore.Eve } func (p *idpLoginPolicyLinkProjection) reduceIDPConfigRemoved(event eventstore.Event) (*handler.Statement, error) { - var idpID string + var conditions []handler.Condition switch e := event.(type) { case *org.IDPConfigRemovedEvent: - idpID = e.ConfigID + conditions = []handler.Condition{ + handler.NewCond(IDPLoginPolicyLinkIDPIDCol, e.ConfigID), + handler.NewCond(IDPLoginPolicyLinkResourceOwnerCol, event.Aggregate().ResourceOwner), + handler.NewCond(IDPLoginPolicyLinkInstanceIDCol, event.Aggregate().InstanceID), + } case *instance.IDPConfigRemovedEvent: - idpID = e.ConfigID + conditions = []handler.Condition{ + handler.NewCond(IDPLoginPolicyLinkIDPIDCol, e.ConfigID), + handler.NewCond(IDPLoginPolicyLinkInstanceIDCol, event.Aggregate().InstanceID), + } default: return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-u6tze", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigRemovedEventType, instance.IDPConfigRemovedEventType}) } - return crdb.NewDeleteStatement(event, - []handler.Condition{ - handler.NewCond(IDPLoginPolicyLinkIDPIDCol, idpID), - handler.NewCond(IDPLoginPolicyLinkResourceOwnerCol, event.Aggregate().ResourceOwner), - handler.NewCond(IDPLoginPolicyLinkInstanceIDCol, event.Aggregate().InstanceID), - }, - ), nil + return crdb.NewDeleteStatement(event, conditions), nil } func (p *idpLoginPolicyLinkProjection) reduceIDPRemoved(event eventstore.Event) (*handler.Statement, error) { - var idpID string + var conditions []handler.Condition switch e := event.(type) { case *org.IDPRemovedEvent: - idpID = e.ID + conditions = []handler.Condition{ + handler.NewCond(IDPLoginPolicyLinkIDPIDCol, e.ID), + handler.NewCond(IDPLoginPolicyLinkResourceOwnerCol, event.Aggregate().ResourceOwner), + handler.NewCond(IDPLoginPolicyLinkInstanceIDCol, event.Aggregate().InstanceID), + } case *instance.IDPRemovedEvent: - idpID = e.ID + conditions = []handler.Condition{ + handler.NewCond(IDPLoginPolicyLinkIDPIDCol, e.ID), + handler.NewCond(IDPLoginPolicyLinkInstanceIDCol, event.Aggregate().InstanceID), + } default: return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SFED3", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPRemovedEventType, instance.IDPRemovedEventType}) } - return crdb.NewDeleteStatement(event, - []handler.Condition{ - handler.NewCond(IDPLoginPolicyLinkIDPIDCol, idpID), - handler.NewCond(IDPLoginPolicyLinkResourceOwnerCol, event.Aggregate().ResourceOwner), - handler.NewCond(IDPLoginPolicyLinkInstanceIDCol, event.Aggregate().InstanceID), - }, - ), nil + return crdb.NewDeleteStatement(event, conditions), nil } func (p *idpLoginPolicyLinkProjection) reducePolicyRemoved(event eventstore.Event) (*handler.Statement, error) { diff --git a/internal/query/projection/idp_login_policy_link_test.go b/internal/query/projection/idp_login_policy_link_test.go index 43a6edb175..f3d02c4730 100644 --- a/internal/query/projection/idp_login_policy_link_test.go +++ b/internal/query/projection/idp_login_policy_link_test.go @@ -42,7 +42,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_login_policy_links4 (idp_id, aggregate_id, creation_date, change_date, sequence, resource_owner, instance_id, provider_type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedStmt: "INSERT INTO projections.idp_login_policy_links5 (idp_id, aggregate_id, creation_date, change_date, sequence, resource_owner, instance_id, provider_type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", expectedArgs: []interface{}{ "idp-config-id", "agg-id", @@ -78,7 +78,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (idp_id = $1) AND (aggregate_id = $2) AND (instance_id = $3)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (idp_id = $1) AND (aggregate_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "idp-config-id", "agg-id", @@ -109,7 +109,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (idp_id = $1) AND (aggregate_id = $2) AND (instance_id = $3)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (idp_id = $1) AND (aggregate_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "idp-config-id", "agg-id", @@ -140,7 +140,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_login_policy_links4 (idp_id, aggregate_id, creation_date, change_date, sequence, resource_owner, instance_id, provider_type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedStmt: "INSERT INTO projections.idp_login_policy_links5 (idp_id, aggregate_id, creation_date, change_date, sequence, resource_owner, instance_id, provider_type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", expectedArgs: []interface{}{ "idp-config-id", "agg-id", @@ -176,7 +176,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (idp_id = $1) AND (aggregate_id = $2) AND (instance_id = $3)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (idp_id = $1) AND (aggregate_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "idp-config-id", "agg-id", @@ -204,7 +204,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (instance_id = $1)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (instance_id = $1)", expectedArgs: []interface{}{ "agg-id", }, @@ -233,7 +233,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (idp_id = $1) AND (aggregate_id = $2) AND (instance_id = $3)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (idp_id = $1) AND (aggregate_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "idp-config-id", "agg-id", @@ -261,7 +261,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (aggregate_id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (aggregate_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -290,7 +290,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (idp_id = $1) AND (resource_owner = $2) AND (instance_id = $3)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (idp_id = $1) AND (resource_owner = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "idp-config-id", "ro-id", @@ -320,10 +320,9 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (idp_id = $1) AND (resource_owner = $2) AND (instance_id = $3)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (idp_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "idp-config-id", - "ro-id", "instance-id", }, }, @@ -350,7 +349,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (idp_id = $1) AND (resource_owner = $2) AND (instance_id = $3)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (idp_id = $1) AND (resource_owner = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "id", "ro-id", @@ -380,10 +379,9 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_login_policy_links4 WHERE (idp_id = $1) AND (resource_owner = $2) AND (instance_id = $3)", + expectedStmt: "DELETE FROM projections.idp_login_policy_links5 WHERE (idp_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "id", - "ro-id", "instance-id", }, }, @@ -408,7 +406,7 @@ func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_login_policy_links4 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", + expectedStmt: "UPDATE projections.idp_login_policy_links5 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), From d981f0d34813144179cc8061661937c4a5478aa1 Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 5 Apr 2023 13:42:00 +0200 Subject: [PATCH 04/36] fix(saml): correct handling of remove (#5606) --- internal/command/project_application_saml_model.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/command/project_application_saml_model.go b/internal/command/project_application_saml_model.go index ee697a029e..2652acc617 100644 --- a/internal/command/project_application_saml_model.go +++ b/internal/command/project_application_saml_model.go @@ -241,12 +241,11 @@ func (wm *SAMLEntityIDsWriteModel) Reduce() error { for _, event := range wm.Events { switch e := event.(type) { case *project.ApplicationRemovedEvent: - removeAppIDFromEntityIDs(wm.EntityIDs, e.AppID) + wm.EntityIDs = removeAppIDFromEntityIDs(wm.EntityIDs, e.AppID) case *project.SAMLConfigAddedEvent: wm.EntityIDs = append(wm.EntityIDs, &AppIDToEntityID{AppID: e.AppID, EntityID: e.EntityID}) case *project.SAMLConfigChangedEvent: - for i := range wm.EntityIDs { - item := wm.EntityIDs[i] + for _, item := range wm.EntityIDs { if e.AppID == item.AppID && e.EntityID != "" { item.EntityID = e.EntityID } From 8b5217c06dc346301d6a7373a74998d2addc6846 Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 5 Apr 2023 14:06:26 +0200 Subject: [PATCH 05/36] fix(query): only active by org by primary domain (#5610) --- internal/query/org.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/query/org.go b/internal/query/org.go index 08931a8f8f..b3dc88b9dc 100644 --- a/internal/query/org.go +++ b/internal/query/org.go @@ -118,6 +118,7 @@ func (q *Queries) OrgByPrimaryDomain(ctx context.Context, domain string) (_ *Org query, args, err := stmt.Where(sq.Eq{ OrgColumnDomain.identifier(): domain, OrgColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(), + OrgColumnState.identifier(): domain_pkg.OrgStateActive, }).ToSql() if err != nil { return nil, errors.ThrowInternal(err, "QUERY-TYUCE", "Errors.Query.SQLStatement") From 5c8748d76956c9565f2cc0c771393e7a1dbc7ad2 Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 5 Apr 2023 16:27:48 +0200 Subject: [PATCH 06/36] chore: change action timeout (#5604) --- .github/workflows/zitadel.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zitadel.yml b/.github/workflows/zitadel.yml index 11e01fdc03..7ec943ec03 100644 --- a/.github/workflows/zitadel.yml +++ b/.github/workflows/zitadel.yml @@ -66,7 +66,7 @@ jobs: with: distribution: goreleaser version: v1.11.0 - args: release + args: release --timeout 50m env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GORELEASER_TOKEN_TAP: ${{ steps.generate-token.outputs.token }} From 29c0adb650dc025e252c20c6c2f9f3c8e3a58a4e Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Wed, 5 Apr 2023 19:56:11 +0200 Subject: [PATCH 07/36] fix: ignore 0 retention on event search (#5614) * fix: filter all search events if retention * test(e2e): test event api filter --- .../filter-events.component.html | 4 +++- .../app/pages/events/events.component.html | 2 +- e2e/cypress/e2e/events/events.cy.ts | 18 ++++++++++++++++ internal/query/event.go | 21 ++++++++++++------- 4 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 e2e/cypress/e2e/events/events.cy.ts diff --git a/console/src/app/modules/filter-events/filter-events.component.html b/console/src/app/modules/filter-events/filter-events.component.html index 7f73b8eb17..07deb4d0bc 100644 --- a/console/src/app/modules/filter-events/filter-events.component.html +++ b/console/src/app/modules/filter-events/filter-events.component.html @@ -5,6 +5,7 @@ (click)="showFilter = !showFilter" class="cnsl-action-button" #triggereventfilter="cdkOverlayOrigin" + data-e2e="open-filter-button" > {{ 'ACTIONS.FILTER' | translate }} @@ -29,7 +30,7 @@
{{ 'FILTER.TITLE' | translate }} -
@@ -96,6 +97,7 @@ name="eventTypesFilterSet" class="cb" formControlName="eventTypesFilterSet" + data-e2e="event-type-filter-checkbox" >{{ 'IAM.EVENTS.FILTERS.TYPE.CHECKBOX' | translate }} diff --git a/console/src/app/pages/events/events.component.html b/console/src/app/pages/events/events.component.html index 4c0033188d..155c42dbef 100644 --- a/console/src/app/pages/events/events.component.html +++ b/console/src/app/pages/events/events.component.html @@ -91,7 +91,7 @@ {{ 'IAM.EVENTS.TYPE' | translate }} - + {{ event.type.localized.localizedMessage }} diff --git a/e2e/cypress/e2e/events/events.cy.ts b/e2e/cypress/e2e/events/events.cy.ts new file mode 100644 index 0000000000..65362b6129 --- /dev/null +++ b/e2e/cypress/e2e/events/events.cy.ts @@ -0,0 +1,18 @@ +describe('events', () => { + beforeEach(() => { + cy.context().as('ctx'); + }); + + it('events can be filtered', () => { + const eventTypeEnglish = 'Instance added'; + cy.visit('/events'); + cy.get('[data-e2e="event-type-cell"]').should('have.length', 20); + cy.get('[data-e2e="open-filter-button"]').click(); + cy.get('[data-e2e="event-type-filter-checkbox"]').click(); + cy.get('#mat-select-value-1').click(); + cy.contains('mat-option', eventTypeEnglish).click(); + cy.get('body').click(); + cy.get('[data-e2e="filter-finish-button"]').click(); + cy.contains('[data-e2e="event-type-cell"]', eventTypeEnglish).should('have.length.at.least', 1); + }); +}); diff --git a/internal/query/event.go b/internal/query/event.go index 6d238ebabe..b543228825 100644 --- a/internal/query/event.go +++ b/internal/query/event.go @@ -34,18 +34,25 @@ func (q *Queries) SearchEvents(ctx context.Context, query *eventstore.SearchQuer return nil, err } + if auditLogRetention != 0 { + events = filterAuditLogRetention(ctx, events, auditLogRetention) + } + + return q.convertEvents(ctx, events), nil +} + +func filterAuditLogRetention(ctx context.Context, events []eventstore.Event, auditLogRetention time.Duration) []eventstore.Event { callTime := call.FromContext(ctx) if callTime.IsZero() { callTime = time.Now() } - for i, event := range events { - if event.CreationDate().Before(callTime.Add(-auditLogRetention)) { - events = events[:i] - break + filteredEvents := make([]eventstore.Event, 0, len(events)) + for _, event := range events { + if event.CreationDate().After(callTime.Add(-auditLogRetention)) { + filteredEvents = append(filteredEvents, event) } } - - return q.convertEvents(ctx, events, auditLogRetention), nil + return filteredEvents } func (q *Queries) SearchEventTypes(ctx context.Context) []string { @@ -56,7 +63,7 @@ func (q *Queries) SearchAggregateTypes(ctx context.Context) []string { return q.eventstore.AggregateTypes() } -func (q *Queries) convertEvents(ctx context.Context, events []eventstore.Event, auditLogRetention time.Duration) []*Event { +func (q *Queries) convertEvents(ctx context.Context, events []eventstore.Event) []*Event { result := make([]*Event, len(events)) users := make(map[string]*EventEditor) for i, event := range events { From 23e6cc325e00d14d74b8f1276aa0b7b4e5169278 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Wed, 5 Apr 2023 21:31:51 +0200 Subject: [PATCH 08/36] fix: update correct current sequence for refresh tokens (#5608) --- internal/auth/repository/eventsourcing/view/refresh_token.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/auth/repository/eventsourcing/view/refresh_token.go b/internal/auth/repository/eventsourcing/view/refresh_token.go index e63fa0ec0e..8c1342f6f8 100644 --- a/internal/auth/repository/eventsourcing/view/refresh_token.go +++ b/internal/auth/repository/eventsourcing/view/refresh_token.go @@ -30,7 +30,7 @@ func (v *View) PutRefreshToken(token *model.RefreshTokenView, event *models.Even if err != nil { return err } - return v.ProcessedTokenSequence(event) + return v.ProcessedRefreshTokenSequence(event) } func (v *View) PutRefreshTokens(token []*model.RefreshTokenView, event *models.Event) error { From 8141d902b8b883c48e4e117a330767c224ad356c Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Thu, 6 Apr 2023 07:46:12 +0200 Subject: [PATCH 09/36] fix: delete org project mapping by grant id (#5607) * fix: delete org project mapping by grant id * fix: check for project on authentication using projections * fix tests --------- Co-authored-by: Livio Spring --- .../eventsourcing/eventstore/auth_request.go | 37 +++-- .../eventstore/auth_request_test.go | 25 ++-- .../eventsourcing/handler/handler.go | 4 +- .../handler/org_project_mapping.go | 130 ------------------ .../repository/eventsourcing/repository.go | 2 +- .../eventsourcing/spooler/spooler.go | 5 +- .../eventsourcing/view/org_project_mapping.go | 81 ----------- 7 files changed, 41 insertions(+), 243 deletions(-) delete mode 100644 internal/auth/repository/eventsourcing/handler/org_project_mapping.go delete mode 100644 internal/auth/repository/eventsourcing/view/org_project_mapping.go diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 4b1ca88910..6a879a3e8e 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -18,7 +18,6 @@ import ( v1 "github.com/zitadel/zitadel/internal/eventstore/v1" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" - project_view_model "github.com/zitadel/zitadel/internal/project/repository/view/model" "github.com/zitadel/zitadel/internal/query" user_repo "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/telemetry/tracing" @@ -106,7 +105,7 @@ type userGrantProvider interface { type projectProvider interface { ProjectByClientID(context.Context, string, bool) (*query.Project, error) - OrgProjectMappingByIDs(orgID, projectID, instanceID string) (*project_view_model.OrgProjectMapping, error) + SearchProjectGrants(ctx context.Context, queries *query.ProjectGrantSearchQueries, withOwnerRemoved bool) (projects *query.ProjectGrants, err error) } type applicationProvider interface { @@ -1153,11 +1152,11 @@ func privacyPolicyToDomain(p *query.PrivacyPolicy) *domain.PrivacyPolicy { CreationDate: p.CreationDate, ChangeDate: p.ChangeDate, }, - State: p.State, - Default: p.IsDefault, - TOSLink: p.TOSLink, - PrivacyLink: p.PrivacyLink, - HelpLink: p.HelpLink, + State: p.State, + Default: p.IsDefault, + TOSLink: p.TOSLink, + PrivacyLink: p.PrivacyLink, + HelpLink: p.HelpLink, SupportEmail: p.SupportEmail, } } @@ -1465,7 +1464,7 @@ func userGrantRequired(ctx context.Context, request *domain.AuthRequest, user *u return len(grants) == 0, nil } -func projectRequired(ctx context.Context, request *domain.AuthRequest, projectProvider projectProvider) (_ bool, err error) { +func projectRequired(ctx context.Context, request *domain.AuthRequest, projectProvider projectProvider) (missingGrant bool, err error) { var project *query.Project switch request.Request.Type() { case domain.AuthRequestTypeOIDC, domain.AuthRequestTypeSAML: @@ -1476,13 +1475,23 @@ func projectRequired(ctx context.Context, request *domain.AuthRequest, projectPr default: return false, errors.ThrowPreconditionFailed(nil, "EVENT-dfrw2", "Errors.AuthRequest.RequestTypeNotSupported") } - if !project.HasProjectCheck { + // if the user and project are part of the same organisation we do not need to check if the project exists on that org + if !project.HasProjectCheck || project.ResourceOwner == request.UserOrgID { return false, nil } - _, err = projectProvider.OrgProjectMappingByIDs(request.UserOrgID, project.ID, request.InstanceID) - if errors.IsNotFound(err) { - // if not found there is no error returned - return true, nil + + // else just check if there is a project grant for that org + projectID, err := query.NewProjectGrantProjectIDSearchQuery(project.ID) + if err != nil { + return false, err } - return false, err + grantedOrg, err := query.NewProjectGrantGrantedOrgIDSearchQuery(request.UserOrgID) + if err != nil { + return false, err + } + grants, err := projectProvider.SearchProjectGrants(ctx, &query.ProjectGrantSearchQueries{Queries: []query.SearchQuery{projectID, grantedOrg}}, false) + if err != nil { + return false, err + } + return len(grants.ProjectGrants) != 1, nil } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index bc6983bfba..04d9e06646 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -14,7 +14,6 @@ import ( "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/errors" es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" - proj_view_model "github.com/zitadel/zitadel/internal/project/repository/view/model" "github.com/zitadel/zitadel/internal/query" user_repo "github.com/zitadel/zitadel/internal/repository/user" user_model "github.com/zitadel/zitadel/internal/user/model" @@ -208,19 +207,21 @@ func (m *mockUserGrants) UserGrantsByProjectAndUserID(ctx context.Context, s str } type mockProject struct { - hasProject bool - projectCheck bool + hasProject bool + projectCheck bool + resourceOwner string } func (m *mockProject) ProjectByClientID(ctx context.Context, s string, _ bool) (*query.Project, error) { - return &query.Project{HasProjectCheck: m.projectCheck}, nil + return &query.Project{ResourceOwner: m.resourceOwner, HasProjectCheck: m.projectCheck}, nil } -func (m *mockProject) OrgProjectMappingByIDs(orgID, projectID, instanceID string) (*proj_view_model.OrgProjectMapping, error) { +func (m *mockProject) SearchProjectGrants(ctx context.Context, queries *query.ProjectGrantSearchQueries, _ bool) (*query.ProjectGrants, error) { if m.hasProject { - return &proj_view_model.OrgProjectMapping{OrgID: orgID, ProjectID: projectID}, nil + mockProjectGrant := new(query.ProjectGrant) + return &query.ProjectGrants{ProjectGrants: []*query.ProjectGrant{mockProjectGrant}}, nil } - return nil, errors.ThrowNotFound(nil, "ERROR", "error") + return &query.ProjectGrants{}, nil } type mockApp struct { @@ -1258,8 +1259,9 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, userGrantProvider: &mockUserGrants{}, projectProvider: &mockProject{ - projectCheck: true, - hasProject: false, + projectCheck: true, + hasProject: false, + resourceOwner: "other-org", }, lockoutPolicyProvider: &mockLockoutPolicy{ policy: &query.LockoutPolicy{ @@ -1297,8 +1299,9 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { orgViewProvider: &mockViewOrg{State: domain.OrgStateActive}, userGrantProvider: &mockUserGrants{}, projectProvider: &mockProject{ - projectCheck: true, - hasProject: true, + projectCheck: true, + hasProject: true, + resourceOwner: "other-org", }, applicationProvider: &mockApp{app: &query.App{OIDCConfig: &query.OIDCApp{AppType: domain.OIDCApplicationTypeWeb}}}, lockoutPolicyProvider: &mockLockoutPolicy{ diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go index 161500de3a..3f0baecdba 100644 --- a/internal/auth/repository/eventsourcing/handler/handler.go +++ b/internal/auth/repository/eventsourcing/handler/handler.go @@ -6,7 +6,6 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" - sd "github.com/zitadel/zitadel/internal/config/systemdefaults" v1 "github.com/zitadel/zitadel/internal/eventstore/v1" "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/eventstore/v1/query" @@ -33,7 +32,7 @@ func (h *handler) Eventstore() v1.Eventstore { return h.es } -func Register(ctx context.Context, configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, systemDefaults sd.SystemDefaults, queries *query2.Queries) []query.Handler { +func Register(ctx context.Context, configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, queries *query2.Queries) []query.Handler { return []query.Handler{ newUser(ctx, handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, queries), @@ -42,7 +41,6 @@ func Register(ctx context.Context, configs Configs, bulkLimit, errorCount uint64 newToken(ctx, handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount, es}), newRefreshToken(ctx, handler{view, bulkLimit, configs.cycleDuration("RefreshToken"), errorCount, es}), - newOrgProjectMapping(ctx, handler{view, bulkLimit, configs.cycleDuration("OrgProjectMapping"), errorCount, es}), } } diff --git a/internal/auth/repository/eventsourcing/handler/org_project_mapping.go b/internal/auth/repository/eventsourcing/handler/org_project_mapping.go deleted file mode 100644 index ffa7688866..0000000000 --- a/internal/auth/repository/eventsourcing/handler/org_project_mapping.go +++ /dev/null @@ -1,130 +0,0 @@ -package handler - -import ( - "context" - - "github.com/zitadel/logging" - - "github.com/zitadel/zitadel/internal/eventstore" - v1 "github.com/zitadel/zitadel/internal/eventstore/v1" - es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/eventstore/v1/query" - "github.com/zitadel/zitadel/internal/eventstore/v1/spooler" - view_model "github.com/zitadel/zitadel/internal/project/repository/view/model" - "github.com/zitadel/zitadel/internal/repository/instance" - "github.com/zitadel/zitadel/internal/repository/org" - "github.com/zitadel/zitadel/internal/repository/project" -) - -const ( - orgProjectMappingTable = "auth.org_project_mapping2" -) - -type OrgProjectMapping struct { - handler - subscription *v1.Subscription -} - -func newOrgProjectMapping( - ctx context.Context, - handler handler, -) *OrgProjectMapping { - h := &OrgProjectMapping{ - handler: handler, - } - - h.subscribe(ctx) - - return h -} - -func (p *OrgProjectMapping) subscribe(ctx context.Context) { - p.subscription = p.es.Subscribe(p.AggregateTypes()...) - go func() { - for event := range p.subscription.Events { - query.ReduceEvent(ctx, p, event) - } - }() -} - -func (p *OrgProjectMapping) ViewModel() string { - return orgProjectMappingTable -} - -func (p *OrgProjectMapping) Subscription() *v1.Subscription { - return p.subscription -} - -func (_ *OrgProjectMapping) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{project.AggregateType, instance.AggregateType} -} - -func (p *OrgProjectMapping) CurrentSequence(instanceID string) (uint64, error) { - sequence, err := p.view.GetLatestOrgProjectMappingSequence(instanceID) - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (p *OrgProjectMapping) EventQuery(instanceIDs []string) (*es_models.SearchQuery, error) { - sequences, err := p.view.GetLatestOrgProjectMappingSequences(instanceIDs) - if err != nil { - return nil, err - } - return newSearchQuery(sequences, p.AggregateTypes(), instanceIDs), nil -} - -func (p *OrgProjectMapping) Reduce(event *es_models.Event) (err error) { - mapping := new(view_model.OrgProjectMapping) - switch eventstore.EventType(event.Type) { - case project.ProjectAddedType: - mapping.OrgID = event.ResourceOwner - mapping.ProjectID = event.AggregateID - mapping.InstanceID = event.InstanceID - case project.ProjectRemovedType: - err := p.view.DeleteOrgProjectMappingsByProjectID(event.AggregateID, event.InstanceID) - if err == nil { - return p.view.ProcessedOrgProjectMappingSequence(event) - } - case project.GrantAddedType: - projectGrant := new(view_model.ProjectGrant) - err := projectGrant.SetData(event) - if err != nil { - return err - } - mapping.OrgID = projectGrant.GrantedOrgID - mapping.ProjectID = event.AggregateID - mapping.ProjectGrantID = projectGrant.GrantID - mapping.InstanceID = event.InstanceID - case project.GrantRemovedType: - projectGrant := new(view_model.ProjectGrant) - err := projectGrant.SetData(event) - if err != nil { - return err - } - err = p.view.DeleteOrgProjectMappingsByProjectGrantID(event.AggregateID, event.InstanceID) - if err == nil { - return p.view.ProcessedOrgProjectMappingSequence(event) - } - case instance.InstanceRemovedEventType: - return p.view.DeleteInstanceOrgProjectMappings(event) - case org.OrgRemovedEventType: - return p.view.UpdateOwnerRemovedOrgProjectMappings(event) - default: - return p.view.ProcessedOrgProjectMappingSequence(event) - } - if err != nil { - return err - } - return p.view.PutOrgProjectMapping(mapping, event) -} - -func (p *OrgProjectMapping) OnError(event *es_models.Event, err error) error { - logging.WithFields("id", event.AggregateID).WithError(err).Warn("something went wrong in org project mapping handler") - return spooler.HandleError(event, err, p.view.GetLatestOrgProjectMappingFailedEvent, p.view.ProcessedOrgProjectMappingFailedEvent, p.view.ProcessedOrgProjectMappingSequence, p.errorCountUntilSkip) -} - -func (p *OrgProjectMapping) OnSuccess(instanceIDs []string) error { - return spooler.HandleSuccess(p.view.UpdateOrgProjectMappingSpoolerRunTimestamp, instanceIDs) -} diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go index b06e46a139..852ebcca91 100644 --- a/internal/auth/repository/eventsourcing/repository.go +++ b/internal/auth/repository/eventsourcing/repository.go @@ -48,7 +48,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, c authReq := cache.Start(dbClient) - spool := spooler.StartSpooler(ctx, conf.Spooler, es, esV2, view, dbClient, systemDefaults, queries) + spool := spooler.StartSpooler(ctx, conf.Spooler, es, esV2, view, dbClient, queries) userRepo := eventstore.UserRepo{ SearchLimit: conf.SearchLimit, diff --git a/internal/auth/repository/eventsourcing/spooler/spooler.go b/internal/auth/repository/eventsourcing/spooler/spooler.go index 4da8340154..dfbfc73b27 100644 --- a/internal/auth/repository/eventsourcing/spooler/spooler.go +++ b/internal/auth/repository/eventsourcing/spooler/spooler.go @@ -5,7 +5,6 @@ import ( "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/handler" "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view" - sd "github.com/zitadel/zitadel/internal/config/systemdefaults" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/eventstore" v1 "github.com/zitadel/zitadel/internal/eventstore/v1" @@ -21,14 +20,14 @@ type SpoolerConfig struct { Handlers handler.Configs } -func StartSpooler(ctx context.Context, c SpoolerConfig, es v1.Eventstore, esV2 *eventstore.Eventstore, view *view.View, client *database.DB, systemDefaults sd.SystemDefaults, queries *query.Queries) *spooler.Spooler { +func StartSpooler(ctx context.Context, c SpoolerConfig, es v1.Eventstore, esV2 *eventstore.Eventstore, view *view.View, client *database.DB, queries *query.Queries) *spooler.Spooler { spoolerConfig := spooler.Config{ Eventstore: es, EventstoreV2: esV2, Locker: &locker{dbClient: client.DB}, ConcurrentWorkers: c.ConcurrentWorkers, ConcurrentInstances: c.ConcurrentInstances, - ViewHandlers: handler.Register(ctx, c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, systemDefaults, queries), + ViewHandlers: handler.Register(ctx, c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, queries), } spool := spoolerConfig.New() spool.Start() diff --git a/internal/auth/repository/eventsourcing/view/org_project_mapping.go b/internal/auth/repository/eventsourcing/view/org_project_mapping.go deleted file mode 100644 index 9cd3f20899..0000000000 --- a/internal/auth/repository/eventsourcing/view/org_project_mapping.go +++ /dev/null @@ -1,81 +0,0 @@ -package view - -import ( - "github.com/zitadel/zitadel/internal/errors" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" - "github.com/zitadel/zitadel/internal/project/repository/view" - "github.com/zitadel/zitadel/internal/project/repository/view/model" - "github.com/zitadel/zitadel/internal/view/repository" -) - -const ( - orgProjectMappingTable = "auth.org_project_mapping2" -) - -func (v *View) OrgProjectMappingByIDs(orgID, projectID, instanceID string) (*model.OrgProjectMapping, error) { - return view.OrgProjectMappingByIDs(v.Db, orgProjectMappingTable, orgID, projectID, instanceID) -} - -func (v *View) PutOrgProjectMapping(mapping *model.OrgProjectMapping, event *models.Event) error { - err := view.PutOrgProjectMapping(v.Db, orgProjectMappingTable, mapping) - if err != nil { - return err - } - return v.ProcessedOrgProjectMappingSequence(event) -} - -func (v *View) DeleteOrgProjectMapping(orgID, projectID, instanceID string, event *models.Event) error { - err := view.DeleteOrgProjectMapping(v.Db, orgProjectMappingTable, orgID, projectID, instanceID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedOrgProjectMappingSequence(event) -} - -func (v *View) DeleteInstanceOrgProjectMappings(event *models.Event) error { - err := view.DeleteInstanceOrgProjectMappings(v.Db, orgProjectMappingTable, event.InstanceID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedOrgProjectMappingSequence(event) -} - -func (v *View) UpdateOwnerRemovedOrgProjectMappings(event *models.Event) error { - err := view.UpdateOwnerRemovedOrgProjectMappings(v.Db, orgProjectMappingTable, event.InstanceID, event.AggregateID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedOrgProjectMappingSequence(event) -} - -func (v *View) DeleteOrgProjectMappingsByProjectID(projectID, instanceID string) error { - return view.DeleteOrgProjectMappingsByProjectID(v.Db, orgProjectMappingTable, projectID, instanceID) -} - -func (v *View) DeleteOrgProjectMappingsByProjectGrantID(projectGrantID, instanceID string) error { - return view.DeleteOrgProjectMappingsByProjectGrantID(v.Db, orgProjectMappingTable, projectGrantID, instanceID) -} - -func (v *View) GetLatestOrgProjectMappingSequence(instanceID string) (*repository.CurrentSequence, error) { - return v.latestSequence(orgProjectMappingTable, instanceID) -} - -func (v *View) GetLatestOrgProjectMappingSequences(instanceIDs []string) ([]*repository.CurrentSequence, error) { - return v.latestSequences(orgProjectMappingTable, instanceIDs) -} - -func (v *View) ProcessedOrgProjectMappingSequence(event *models.Event) error { - return v.saveCurrentSequence(orgProjectMappingTable, event) -} - -func (v *View) UpdateOrgProjectMappingSpoolerRunTimestamp(instanceIDs []string) error { - return v.updateSpoolerRunSequence(orgProjectMappingTable, instanceIDs) -} - -func (v *View) GetLatestOrgProjectMappingFailedEvent(sequence uint64, instanceID string) (*repository.FailedEvent, error) { - return v.latestFailedEvent(orgProjectMappingTable, instanceID, sequence) -} - -func (v *View) ProcessedOrgProjectMappingFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) -} From 698f46fe6a39873b38a81ebd20f99e66020f18d6 Mon Sep 17 00:00:00 2001 From: Silvan Date: Thu, 6 Apr 2023 08:29:55 +0200 Subject: [PATCH 10/36] chore: update dependencies (#5401) * chore(backend): update dependencies * chore(pipeline): update golangci-lint --- .github/workflows/test-code.yml | 2 +- go.mod | 111 ++++--- go.sum | 288 ++++++++---------- internal/telemetry/metrics/metrics.go | 9 +- .../telemetry/metrics/otel/open_telemetry.go | 57 ++-- internal/telemetry/otel/resource.go | 7 +- 6 files changed, 223 insertions(+), 251 deletions(-) diff --git a/.github/workflows/test-code.yml b/.github/workflows/test-code.yml index b6a9f4b434..7a4793dbe0 100644 --- a/.github/workflows/test-code.yml +++ b/.github/workflows/test-code.yml @@ -37,7 +37,7 @@ jobs: - name: linting uses: golangci/golangci-lint-action@v3 with: - version: v1.50.1 + version: v1.52 only-new-issues: true skip-pkg-cache: true - name: Publish go coverage diff --git a/go.mod b/go.mod index 3ac3be8409..7edde06553 100644 --- a/go.mod +++ b/go.mod @@ -3,46 +3,46 @@ module github.com/zitadel/zitadel go 1.19 require ( - cloud.google.com/go/storage v1.29.0 + cloud.google.com/go/storage v1.30.1 github.com/BurntSushi/toml v1.2.1 github.com/DATA-DOG/go-sqlmock v1.5.0 - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.11.2 + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0 github.com/Masterminds/sprig v2.22.0+incompatible - github.com/Masterminds/squirrel v1.5.3 + github.com/Masterminds/squirrel v1.5.4 github.com/VictoriaMetrics/fastcache v1.12.1 github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b github.com/allegro/bigcache v1.2.1 github.com/benbjohnson/clock v1.3.0 github.com/boombuler/barcode v1.0.1 - github.com/cockroachdb/cockroach-go/v2 v2.2.20 - github.com/dop251/goja v0.0.0-20230216180835-5937a312edda - github.com/dop251/goja_nodejs v0.0.0-20230207183254-2229640ea097 + github.com/cockroachdb/cockroach-go/v2 v2.3.3 + github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079 + github.com/dop251/goja_nodejs v0.0.0-20230322100729-2550c7b6c124 github.com/drone/envsubst v1.0.3 github.com/duo-labs/webauthn v0.0.0-20221205164246-ebaf9b74c6ec - github.com/envoyproxy/protoc-gen-validate v0.9.1 + github.com/envoyproxy/protoc-gen-validate v0.10.1 github.com/go-ldap/ldap/v3 v3.4.4 - github.com/golang/glog v1.0.0 + github.com/golang/glog v1.1.1 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 github.com/gorilla/csrf v1.7.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/schema v1.2.0 github.com/gorilla/securecookie v1.1.1 - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.1 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 github.com/h2non/gock v1.2.0 github.com/improbable-eng/grpc-web v0.15.0 github.com/jackc/pgconn v1.14.0 github.com/jackc/pgtype v1.14.0 - github.com/jackc/pgx/v4 v4.18.0 + github.com/jackc/pgx/v4 v4.18.1 github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 github.com/jinzhu/gorm v1.9.16 github.com/k3a/html2text v1.1.0 github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2 github.com/lib/pq v1.10.7 github.com/lucasb-eyer/go-colorful v1.2.0 - github.com/minio/minio-go/v7 v7.0.49 + github.com/minio/minio-go/v7 v7.0.50 github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/gamut v0.3.1 github.com/nicksnyder/go-i18n/v2 v2.2.1 @@ -51,57 +51,57 @@ require ( github.com/rakyll/statik v0.1.7 github.com/rs/cors v1.8.3 github.com/sony/sonyflake v1.1.0 - github.com/spf13/cobra v1.6.1 + github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 github.com/ttacon/libphonenumber v1.2.1 github.com/zitadel/logging v0.3.4 - github.com/zitadel/oidc/v2 v2.2.5 + github.com/zitadel/oidc/v2 v2.2.6 github.com/zitadel/saml v0.0.10 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.27.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 - go.opentelemetry.io/otel v1.11.2 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0 - go.opentelemetry.io/otel/exporters/prometheus v0.25.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0 - go.opentelemetry.io/otel/metric v0.25.0 - go.opentelemetry.io/otel/sdk v1.11.2 - go.opentelemetry.io/otel/sdk/export/metric v0.25.0 - go.opentelemetry.io/otel/sdk/metric v0.25.0 - go.opentelemetry.io/otel/trace v1.11.2 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 + go.opentelemetry.io/otel v1.14.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 + go.opentelemetry.io/otel/exporters/prometheus v0.37.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 + go.opentelemetry.io/otel/metric v0.37.0 + go.opentelemetry.io/otel/sdk v1.14.0 + go.opentelemetry.io/otel/sdk/metric v0.37.0 + go.opentelemetry.io/otel/trace v1.14.0 golang.org/x/crypto v0.7.0 golang.org/x/net v0.8.0 golang.org/x/oauth2 v0.6.0 golang.org/x/sync v0.1.0 golang.org/x/text v0.8.0 - golang.org/x/tools v0.6.0 - google.golang.org/api v0.110.0 - google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 - google.golang.org/grpc v1.53.0 - google.golang.org/protobuf v1.29.1 + golang.org/x/tools v0.7.0 + google.golang.org/api v0.115.0 + google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd + google.golang.org/grpc v1.54.0 + google.golang.org/protobuf v1.30.0 gopkg.in/square/go-jose.v2 v2.6.0 sigs.k8s.io/yaml v1.3.0 ) require ( - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.35.2 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0 // indirect github.com/cloudflare/cfssl v1.6.3 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/klauspost/cpuid/v2 v2.2.3 // indirect + github.com/google/pprof v0.0.0-20230323073829-e72429f035bd // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/muhlemmer/gu v0.3.1 // indirect - github.com/pelletier/go-toml/v2 v2.0.6 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.7 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect + go.uber.org/multierr v1.11.0 // indirect ) require ( cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.18.0 // indirect + cloud.google.com/go/compute v1.19.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.12.0 // indirect - cloud.google.com/go/trace v1.8.0 // indirect + cloud.google.com/go/iam v1.0.0 // indirect + cloud.google.com/go/trace v1.9.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect @@ -133,20 +133,20 @@ require ( github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect + github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/certificate-transparency-go v1.1.4 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/googleapis/gax-go/v2 v2.8.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/h2non/filetype v1.1.3 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -154,11 +154,11 @@ require ( github.com/jackc/pgproto3/v2 v2.3.2 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jonboulle/clockwork v0.3.0 // indirect + github.com/jonboulle/clockwork v0.4.0 // indirect 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-20230118171807-ac09c3f0ec45 // indirect - github.com/klauspost/compress v1.15.15 // indirect + github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0 // indirect + github.com/klauspost/compress v1.16.4 // 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 @@ -173,14 +173,14 @@ require ( 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.0 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect github.com/rs/xid v1.4.0 // indirect - github.com/russellhaering/goxmldsig v1.2.0 // indirect + github.com/russellhaering/goxmldsig v1.3.0 // indirect github.com/sirupsen/logrus v1.9.0 - github.com/spf13/afero v1.9.3 // indirect + github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -189,11 +189,10 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 // indirect - go.opentelemetry.io/otel/internal/metric v0.25.0 // indirect - go.opentelemetry.io/proto/otlp v0.15.0 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect + go.opentelemetry.io/proto/otlp v0.19.0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/sys v0.7.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index bd231ad384..2011473465 100644 --- a/go.sum +++ b/go.sum @@ -32,18 +32,18 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/logging v1.6.1 h1:ZBsZK+JG+oCDT+vaxwqF2egKNRjz8soXiS6Xv79benI= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= -cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= +cloud.google.com/go/iam v1.0.0 h1:hlQJMovyJJwYjZcTohUH4o1L8Z8kYz+E+W/zktiLCBc= +cloud.google.com/go/iam v1.0.0/go.mod h1:ikbQ4f1r91wTmBmmOtBCOtuEOei6taatNXytzB7Cxew= +cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= +cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= +cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -55,10 +55,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/trace v1.8.0 h1:GFPLxbp5/FzdgTzor3nlNYNxMd6hLmzkE7sA9F0qQcA= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= @@ -85,11 +85,11 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.11.2 h1:Z5/bjyZhqXd7dNI76Z/KnyxtqavKF1UgGhInFSAIa8s= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.11.2/go.mod h1:mHbBqn8wTt1/+bTA55xe2mLX5b+RKz2dVcGz8sH2HsY= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.35.2 h1:xXo67CnDmiDMhgD3zAiuejKKMQ9nNkAQZZ4RS5U5jJY= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.35.2 h1:uH5W8NML+jD8iFiBbNUh5X7Nt0FVDK44Hel+Ux0ZEqU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.35.2/go.mod h1:H785fvlgotVZqht+1rHhXSs8EJ8uPVmpBYkTYO3ccpc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0 h1:aRQEQ57Mw12h0tG8oo2UGC5d8fpUFCvD1lcS9fdGh6I= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.13.0/go.mod h1:WzE/bKzbWw91rEv1w53y4taJheFSkUzp2Mu8uItorHg= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.37.0 h1:+3ioRuJPNQk6WkbdClWVo8FyfpXu/ZXePQ3syDwrN4o= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0 h1:k5x4SiDgKS8wVQO/ww1fnoh5gwYEg6Wsi+1z5kB9uDM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.37.0/go.mod h1:oEccMakRmMNrayCPR+5OmZE/aeXmTPzUtmomEXIPBdI= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -104,8 +104,8 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= -github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= @@ -159,7 +159,7 @@ github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZw github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= -github.com/benbjohnson/clock v1.2.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -187,12 +187,14 @@ github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6 github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +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/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY= @@ -210,8 +212,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go/v2 v2.2.20 h1:TLSzwdTdIwgsbdApHzaxunhSMrmbGf5YY6oxtaP2kvw= -github.com/cockroachdb/cockroach-go/v2 v2.2.20/go.mod h1:73vQi5H/H7kE8SgOt+XA6729Tubvj5hxKIEgbQQhp4c= +github.com/cockroachdb/cockroach-go/v2 v2.3.3 h1:fNmtG6XhoA1DhdDCIu66YyGSsNb1szj4CaAsbDxRmy4= +github.com/cockroachdb/cockroach-go/v2 v2.3.3/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= @@ -255,12 +257,12 @@ github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0 github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= -github.com/dop251/goja v0.0.0-20230216180835-5937a312edda h1:yWEvdMtib3RbPysHDTNf/c3gerF5r+iMcmhlAeE6hEk= -github.com/dop251/goja v0.0.0-20230216180835-5937a312edda/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= +github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079 h1:xkbJGxVnk5sM8/LXeTKaBOfAZrI+iqvIPyH8oK1c6CQ= +github.com/dop251/goja v0.0.0-20230402114112-623f9dda9079/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-20230207183254-2229640ea097 h1:WsLyDk8yHsVT1puf/32883ZxEb6Pgqd19AlQH9mxVK0= -github.com/dop251/goja_nodejs v0.0.0-20230207183254-2229640ea097/go.mod h1:0tlktQL7yHfYEtjcRGi/eiOkbDR5XF7gyFFvbC5//E0= +github.com/dop251/goja_nodejs v0.0.0-20230322100729-2550c7b6c124 h1:QDuDMgEkC/lnmvk0d/fZfcUUml18uUbS9TY5QtbdFhs= +github.com/dop251/goja_nodejs v0.0.0-20230322100729-2550c7b6c124/go.mod h1:0tlktQL7yHfYEtjcRGi/eiOkbDR5XF7gyFFvbC5//E0= 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= @@ -316,15 +318,14 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.1/go.mod h1:txg5va2Qkip90uYoSKH+nkAAmXrb2j3iq4FLwdrCbXQ= -github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= +github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= 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/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= @@ -365,16 +366,14 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= 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= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -423,12 +422,13 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2V github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= -github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo= -github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc h1:WkAZHSmcnJhZyutVoVXe7lDSQBbISxITcm57tYf22PE= +github.com/golang/geo v0.0.0-20230404232722-c4acd7a044dc/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= +github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -515,6 +515,9 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= +github.com/google/pprof v0.0.0-20230323073829-e72429f035bd h1:r8yyd+DJDmsUhGrRBxH5Pj7KeFK5l+Y3FsgT8keqKtk= +github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -533,8 +536,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5 github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 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= @@ -562,8 +565,9 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -573,8 +577,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4G github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.1 h1:I6ITHEanAwjB0FvaxmGm8pKqmCLR7QIe05ZmO4QAXMw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.1/go.mod h1:gYC+WX4YJFarA2ie73G2epzt7TBWpo9pzcBnK1g0MSw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08= github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= @@ -617,15 +621,15 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 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/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= @@ -639,8 +643,6 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.12.0/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= -github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= @@ -658,7 +660,6 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= @@ -668,21 +669,17 @@ github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01C github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.16.0/go.mod h1:N0A9sFdWzkw/Jy1lwoiB64F2+ugFZi987zRxcPez/wI= -github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ= -github.com/jackc/pgx/v4 v4.18.0 h1:Ltaa1ePvc7msFGALnCrqKJVEByu/qYh5jJBYcDtAno4= -github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= +github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jarcoal/jpath v0.0.0-20140328210829-f76b8b2dbf52 h1:jny9eqYPwkG8IVy7foUoRjQmFLcArCSz+uPsL6KS0HQ= @@ -698,17 +695,15 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= -github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo= github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= -github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -730,8 +725,8 @@ github.com/k3a/html2text v1.1.0 h1:ks4hKSTdiTRsLr0DM771mI5TvsoG6zH7m1Ulv7eJRHw= github.com/k3a/html2text v1.1.0/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-20230118171807-ac09c3f0ec45 h1:WMM9MCVDgEtKsp7eQe0DCCkMaqykkNwdx38wn86NlVk= -github.com/kevinburke/rest v0.0.0-20230118171807-ac09c3f0ec45/go.mod h1:pD+iEcdAGVXld5foVN4e24zb/6fnb60tgZPZ3P/3T/I= +github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0 h1:2b9anKtyO/UTUQb+TAdPAW+w0p9xCIvng4fdZJ2xsYk= +github.com/kevinburke/rest v0.0.0-20230306061549-8f487d822ad0/go.mod h1:pD+iEcdAGVXld5foVN4e24zb/6fnb60tgZPZ3P/3T/I= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2 h1:k+lYMvS9cAl7e4Ea78qodfa6QZfXNa4QlFS/0GYpanI= github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2/go.mod h1:PDdDH7RSKjjy9iFyoMzfeChOSmXpXuMEUqmAJSihxx4= @@ -743,12 +738,12 @@ github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCy github.com/kisom/goutils v1.4.3/go.mod h1:Lp5qrquG7yhYnWzZCI/68Pa/GpFynw//od6EkGnWpac= 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.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= +github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 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.2/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= @@ -778,7 +773,6 @@ github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -825,8 +819,8 @@ github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= 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.49 h1:dE5DfOtnXMXCjr/HWI6zN9vCrY6Sv666qhhiwUMvGV4= -github.com/minio/minio-go/v7 v7.0.49/go.mod h1:UI34MvQEiob3Cf/gGExGMmzugkM/tNgbFypNDy5LMVc= +github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k= +github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -913,8 +907,8 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= +github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= 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= @@ -940,17 +934,16 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -961,10 +954,8 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8 github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.24.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= 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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -974,9 +965,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pseudomuto/protoc-gen-doc v1.4.1/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= @@ -990,8 +980,8 @@ github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -1000,8 +990,8 @@ github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg= -github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= +github.com/russellhaering/goxmldsig v1.3.0 h1:DllIWUgMy0cRUMfGiASiYEa35nsieyD3cigIwLonTPM= +github.com/russellhaering/goxmldsig v1.3.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1014,9 +1004,8 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -1044,8 +1033,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= -github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= @@ -1054,8 +1043,8 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -1141,8 +1130,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zitadel/logging v0.3.4 h1:9hZsTjMMTE3X2LUi0xcF9Q9EdLo+FAezeu52ireBbHM= github.com/zitadel/logging v0.3.4/go.mod h1:aPpLQhE+v6ocNK0TWrBrd363hZ95KcI17Q1ixAQwZF0= -github.com/zitadel/oidc/v2 v2.2.5 h1:vxSVowLnvbujqmJRWsBxC7lCBZngrK0zVlQKznb4IxI= -github.com/zitadel/oidc/v2 v2.2.5/go.mod h1:tGkj9lQk6KVj5hsM89XPadvi6I06666sMy3KtykvSFM= +github.com/zitadel/oidc/v2 v2.2.6 h1:L2k5q1X8Rucax5Ynp3B3lz7JQDJxUwfWCOmgc9Bh0BM= +github.com/zitadel/oidc/v2 v2.2.6/go.mod h1:tGkj9lQk6KVj5hsM89XPadvi6I06666sMy3KtykvSFM= github.com/zitadel/saml v0.0.10 h1:cyKd78Vat9vz55S74lggJrXMSqbAPsnJDrPFTPScNYY= github.com/zitadel/saml v0.0.10/go.mod h1:Hze1/zRN9j1uh7U+89vweP/OwLNO8BLHg3zU1Jtycdg= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= @@ -1176,57 +1165,53 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 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.27.0 h1:TON1iU3Y5oIytGQHIejDYLam5uoSMsmA0UV9Yupb5gQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.27.0/go.mod h1:T/zQwBldOpoAEpE3HMbLnI8ydESZVz4ggw6Is4FF9LI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 h1:0BgiNWjN7rUWO9HdjF4L12r8OW86QkVQcYmCjnayJLo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0/go.mod h1:bdvm3YpMxWAgEfQhtTBaVR8ceXPRuRBSQrvOBnIlHxc= -go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= -go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= -go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 h1:xzbcGykysUh776gzD1LUPsNNHKWN0kQWDnJhn1ddUuk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0/go.mod h1:14T5gr+Y6s2AgHPqBMgnGwp04csUjQmYXFWPeiBoq5s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0 h1:VsgsSCDwOSuO8eMVh63Cd4nACMqgjpmAeJSIvVNneD0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0/go.mod h1:9mLBBnPRf3sf+ASVH2p9xREXVBvwib02FxcKnavtExg= -go.opentelemetry.io/otel/exporters/prometheus v0.25.0 h1:8f9PiHQ2yqRRWktEJ/u2cIPLD8yUagIuNOaFpSsCefI= -go.opentelemetry.io/otel/exporters/prometheus v0.25.0/go.mod h1:TmEyKmTplB/cdILsJBqD9/JDK9ssGXWjsrpmMHodFLw= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0 h1:OiYdrCq1Ctwnovp6EofSPwlp5aGy4LgKNbkg7PtEUw8= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0/go.mod h1:DUFCmFkXr0VtAHl5Zq2JRx24G6ze5CAq8YfdD36RdX8= -go.opentelemetry.io/otel/internal/metric v0.25.0 h1:w/7RXe16WdPylaIXDgcYM6t/q0K5lXgSdZOEbIEyliE= -go.opentelemetry.io/otel/internal/metric v0.25.0/go.mod h1:Nhuw26QSX7d6n4duoqAFi5KOQR4AuzyMcl5eXOgwxtc= -go.opentelemetry.io/otel/metric v0.25.0 h1:7cXOnCADUsR3+EOqxPaSKwhEuNu0gz/56dRN1hpIdKw= -go.opentelemetry.io/otel/metric v0.25.0/go.mod h1:E884FSpQfnJOMMUaq+05IWlJ4rjZpk2s/F1Ju+TEEm8= -go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= -go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU= -go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= -go.opentelemetry.io/otel/sdk/export/metric v0.25.0 h1:6UjAFmVB5Fza3K5qUJpYWGrk8QMPIqlSnya5FI46VBY= -go.opentelemetry.io/otel/sdk/export/metric v0.25.0/go.mod h1:Ej7NOa+WpN49EIcr1HMUYRvxXXCCnQCg2+ovdt2z8Pk= -go.opentelemetry.io/otel/sdk/metric v0.25.0 h1:J+Ta+4IAA5W9AdWhGQLfciEpavBqqSkBzTDeYvJLFNU= -go.opentelemetry.io/otel/sdk/metric v0.25.0/go.mod h1:G4xzj4LvC6xDDSsVXpvRVclQCbofGGg4ZU2VKKtDRfg= -go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= -go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= -go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 h1:5jD3teb4Qh7mx/nfzq4jO2WFFpvXD0vYWFDrdvNWmXk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0/go.mod h1:UMklln0+MRhZC4e3PwmN3pCtq4DyIadWw4yikh6bNrw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 h1:lE9EJyw3/JhrjWH/hEy9FptnalDQgj7vpbgC2KCCCxE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0/go.mod h1:pcQ3MM3SWvrA71U4GDqv9UFDJ3HQsW7y5ZO3tDTlUdI= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0/go.mod h1:UFG7EBMRdXyFstOwH028U0sVf+AvukSGhF0g8+dmNG8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0/go.mod h1:HrbCVv40OOLTABmOn1ZWty6CHXkU8DK/Urc43tHug70= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0/go.mod h1:5w41DY6S9gZrbjuq6Y+753e96WfPha5IcsOSZTtullM= +go.opentelemetry.io/otel/exporters/prometheus v0.37.0 h1:NQc0epfL0xItsmGgSXgfbH2C1fq2VLXkZoDFsfRNHpc= +go.opentelemetry.io/otel/exporters/prometheus v0.37.0/go.mod h1:hB8qWjsStK36t50/R0V2ULFb4u95X/Q6zupXLgvjTh8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 h1:sEL90JjOO/4yhquXl5zTAkLLsZ5+MycAgX99SDsxGc8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0/go.mod h1:oCslUcizYdpKYyS9e8srZEqM6BB8fq41VJBjLAE6z1w= +go.opentelemetry.io/otel/metric v0.37.0 h1:pHDQuLQOZwYD+Km0eb657A25NaRzy0a+eLyKfDXedEs= +go.opentelemetry.io/otel/metric v0.37.0/go.mod h1:DmdaHfGt54iV6UKxsV9slj2bBRJcKC1B1uvDLIioc1s= +go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY= +go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM= +go.opentelemetry.io/otel/sdk/metric v0.37.0 h1:haYBBtZZxiI3ROwSmkZnI+d0+AVzBWeviuYQDeBWosU= +go.opentelemetry.io/otel/sdk/metric v0.37.0/go.mod h1:mO2WV1AZKKwhwHTV3AKOoIEb9LbUaENZDuGUQd+j4A0= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.10.0/go.mod h1:zG20xCK0szZ1xdokeSOwEcmlXu+x9kkdRe6N1DhKcfU= -go.opentelemetry.io/proto/otlp v0.15.0 h1:h0bKrvdrT/9sBwEJ6iWUqT/N/xPcS66bL4u3isneJ6w= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1253,9 +1238,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= @@ -1298,8 +1282,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1353,11 +1337,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -1378,9 +1359,7 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1466,13 +1445,11 @@ golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/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-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1480,8 +1457,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1496,6 +1473,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= @@ -1533,6 +1511,7 @@ golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1576,8 +1555,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1612,8 +1591,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= -google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.115.0 h1:6FFkVvStt4YqXSx3azKyzj7fXerGnVlLJ/eud01nBDE= +google.golang.org/api v0.115.0/go.mod h1:9cD4/t6uvd9naoEJFA+M96d0IuB6BqFuyhpw68+mRGg= 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.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1678,8 +1657,8 @@ google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaE google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210510173355-fb37daa5cd7a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc= -google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU= +google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 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= @@ -1708,10 +1687,9 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1727,8 +1705,8 @@ google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX7 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1765,12 +1743,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.3.5/go.mod h1:EGCWefLFQSVFrHGy4J8EtiHCWX5Q8t0yz2Jt9aKkGzU= -gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/telemetry/metrics/metrics.go b/internal/telemetry/metrics/metrics.go index 675cc35c9f..1ebbae0c02 100644 --- a/internal/telemetry/metrics/metrics.go +++ b/internal/telemetry/metrics/metrics.go @@ -6,6 +6,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/instrument" ) const ( @@ -22,8 +23,8 @@ type Metrics interface { GetMetricsProvider() metric.MeterProvider RegisterCounter(name, description string) error AddCount(ctx context.Context, name string, value int64, labels map[string]attribute.Value) error - RegisterUpDownSumObserver(name, description string, callbackFunc metric.Int64ObserverFunc) error - RegisterValueObserver(name, description string, callbackFunc metric.Int64ObserverFunc) error + RegisterUpDownSumObserver(name, description string, callbackFunc instrument.Int64Callback) error + RegisterValueObserver(name, description string, callbackFunc instrument.Int64Callback) error } var M Metrics @@ -56,14 +57,14 @@ func AddCount(ctx context.Context, name string, value int64, labels map[string]a return M.AddCount(ctx, name, value, labels) } -func RegisterUpDownSumObserver(name, description string, callbackFunc metric.Int64ObserverFunc) error { +func RegisterUpDownSumObserver(name, description string, callbackFunc instrument.Int64Callback) error { if M == nil { return nil } return M.RegisterUpDownSumObserver(name, description, callbackFunc) } -func RegisterValueObserver(name, description string, callbackFunc metric.Int64ObserverFunc) error { +func RegisterValueObserver(name, description string, callbackFunc instrument.Int64Callback) error { if M == nil { return nil } diff --git a/internal/telemetry/metrics/otel/open_telemetry.go b/internal/telemetry/metrics/otel/open_telemetry.go index 9150e6cd6b..4a18c6171c 100644 --- a/internal/telemetry/metrics/otel/open_telemetry.go +++ b/internal/telemetry/metrics/otel/open_telemetry.go @@ -5,13 +5,12 @@ import ( "net/http" "sync" + "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/sdk/export/metric/aggregation" - controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" - processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" - selector "go.opentelemetry.io/otel/sdk/metric/selector/simple" + "go.opentelemetry.io/otel/metric/instrument" + sdk_metric "go.opentelemetry.io/otel/sdk/metric" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/telemetry/metrics" @@ -31,39 +30,35 @@ func NewMetrics(meterName string) (metrics.Metrics, error) { if err != nil { return nil, err } - exporter, err := prometheus.New( - prometheus.Config{}, - controller.New( - processor.NewFactory( - selector.NewWithHistogramDistribution(), - aggregation.CumulativeTemporalitySelector(), - processor.WithMemory(true), - ), - controller.WithResource(resource), - ), - ) + exporter, err := prometheus.New() if err != nil { return &Metrics{}, err } return &Metrics{ Exporter: exporter, - Meter: exporter.MeterProvider().Meter(meterName), + Meter: sdk_metric.NewMeterProvider( + sdk_metric.WithReader(exporter), + sdk_metric.WithResource(resource), + ).Meter(meterName), }, nil } func (m *Metrics) GetExporter() http.Handler { - return m.Exporter + return promhttp.Handler() } func (m *Metrics) GetMetricsProvider() metric.MeterProvider { - return m.Exporter.MeterProvider() + return sdk_metric.NewMeterProvider(sdk_metric.WithReader(m.Exporter)) } func (m *Metrics) RegisterCounter(name, description string) error { if _, exists := m.Counters.Load(name); exists { return nil } - counter := metric.Must(m.Meter).NewInt64Counter(name, metric.WithDescription(description)) + counter, err := m.Meter.Int64Counter(name, instrument.WithDescription(description)) + if err != nil { + return err + } m.Counters.Store(name, counter) return nil } @@ -73,29 +68,35 @@ func (m *Metrics) AddCount(ctx context.Context, name string, value int64, labels if !exists { return caos_errs.ThrowNotFound(nil, "METER-4u8fs", "Errors.Metrics.Counter.NotFound") } - counter.(metric.Int64Counter).Add(ctx, value, MapToKeyValue(labels)...) + counter.(instrument.Int64Counter).Add(ctx, value, MapToKeyValue(labels)...) return nil } -func (m *Metrics) RegisterUpDownSumObserver(name, description string, callbackFunc metric.Int64ObserverFunc) error { +func (m *Metrics) RegisterUpDownSumObserver(name, description string, callbackFunc instrument.Int64Callback) error { if _, exists := m.UpDownSumObserver.Load(name); exists { return nil } - sumObserver := metric.Must(m.Meter).NewInt64UpDownCounterObserver( - name, callbackFunc, metric.WithDescription(description)) - m.UpDownSumObserver.Store(name, sumObserver) + counter, err := m.Meter.Int64ObservableUpDownCounter(name, instrument.WithInt64Callback(callbackFunc), instrument.WithDescription(description)) + if err != nil { + return err + } + + m.UpDownSumObserver.Store(name, counter) return nil } -func (m *Metrics) RegisterValueObserver(name, description string, callbackFunc metric.Int64ObserverFunc) error { +func (m *Metrics) RegisterValueObserver(name, description string, callbackFunc instrument.Int64Callback) error { if _, exists := m.UpDownSumObserver.Load(name); exists { return nil } - sumObserver := metric.Must(m.Meter).NewInt64GaugeObserver( - name, callbackFunc, metric.WithDescription(description)) - m.UpDownSumObserver.Store(name, sumObserver) + gauge, err := m.Meter.Int64ObservableGauge(name, instrument.WithInt64Callback(callbackFunc), instrument.WithDescription(description)) + if err != nil { + return err + } + + m.UpDownSumObserver.Store(name, gauge) return nil } diff --git a/internal/telemetry/otel/resource.go b/internal/telemetry/otel/resource.go index e4c8478cba..7a3f249191 100644 --- a/internal/telemetry/otel/resource.go +++ b/internal/telemetry/otel/resource.go @@ -3,7 +3,7 @@ package otel import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.12.0" + semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "github.com/zitadel/zitadel/cmd/build" ) @@ -17,9 +17,6 @@ func ResourceWithService() (*resource.Resource, error) { } return resource.Merge( resource.Default(), - resource.NewWithAttributes( - semconv.SchemaURL, - attributes..., - ), + resource.NewWithAttributes("", attributes...), ) } From 2ee7b9c381c626eb493db86b727e5e7a7bad5e8b Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Thu, 6 Apr 2023 14:02:46 +0200 Subject: [PATCH 11/36] fix(console): refine onboarding styles and behavior, i18n (#5616) * fix: console onboarding styles and behavior, i18n * action-row classes * fix: only consider real user events in the onboarding view (#5617) * fix(onboarding): only human events * explicitly exclude system api events --------- Co-authored-by: Max Peintner --------- Co-authored-by: Elio Bischof Co-authored-by: Elio Bischof --- .../src/app/modules/nav/nav.component.scss | 1 + .../onboarding-card.component.scss | 1 + .../onboarding/onboarding.component.html | 34 +++++++--- .../onboarding/onboarding.component.scss | 60 ++++++++++++----- .../onboarding/onboarding.component.ts | 3 +- .../shortcuts/shortcuts.component.scss | 5 ++ .../src/app/pages/home/home.component.html | 62 +++++++++++++++--- .../src/app/pages/home/home.component.scss | 44 +++++++++---- console/src/app/pages/home/home.component.ts | 13 +++- console/src/app/services/admin.service.ts | 35 ++++++++-- console/src/app/utils/onboarding.ts | 65 +++++++++++++++++-- console/src/assets/i18n/de.json | 19 ++++-- console/src/assets/i18n/en.json | 23 ++++--- console/src/assets/i18n/fr.json | 23 ++++--- console/src/assets/i18n/it.json | 19 ++++-- console/src/assets/i18n/ja.json | 19 ++++-- console/src/assets/i18n/pl.json | 19 ++++-- console/src/assets/i18n/zh.json | 19 ++++-- 18 files changed, 364 insertions(+), 100 deletions(-) diff --git a/console/src/app/modules/nav/nav.component.scss b/console/src/app/modules/nav/nav.component.scss index 689a37f2d8..dff42fccb3 100644 --- a/console/src/app/modules/nav/nav.component.scss +++ b/console/src/app/modules/nav/nav.component.scss @@ -189,6 +189,7 @@ border: none; padding: 2px; border-radius: 50vw; + cursor: pointer; &:hover { background: if($is-dark-theme, #ffffff, $primary-color); diff --git a/console/src/app/modules/onboarding-card/onboarding-card.component.scss b/console/src/app/modules/onboarding-card/onboarding-card.component.scss index d4eb3d59d1..85afb7158f 100644 --- a/console/src/app/modules/onboarding-card/onboarding-card.component.scss +++ b/console/src/app/modules/onboarding-card/onboarding-card.component.scss @@ -72,6 +72,7 @@ margin-right: 1rem; background-color: if($is-dark-theme, map-get($background, state), #e4e7e4); box-shadow: 0 0 3px #0000001a; + border: 1px solid rgba(#8795a1, 0.2); i { font-size: 1rem; diff --git a/console/src/app/modules/onboarding/onboarding.component.html b/console/src/app/modules/onboarding/onboarding.component.html index 6004daf22c..8ec6111af8 100644 --- a/console/src/app/modules/onboarding/onboarding.component.html +++ b/console/src/app/modules/onboarding/onboarding.component.html @@ -1,7 +1,5 @@
-

{{ 'HOME.WELCOME' | translate }}

- -

{{ 'ONBOARDING.DESCRIPTION' | translate }}

+

{{ 'ONBOARDING.DESCRIPTION' | translate }}

@@ -38,16 +36,34 @@
-
- {{ 'ONBOARDING.EVENTS.' + action[0] + '.title' | translate }} - {{ - 'ONBOARDING.EVENTS.' + action[0] + '.description' | translate - }} +
+
+
+ +
+
+
+ {{ 'ONBOARDING.EVENTS.' + action[0] + '.title' | translate }} + {{ + 'ONBOARDING.EVENTS.' + action[0] + '.description' | translate + }} +
- {{ 'ACTIONS.SETUP' | translate }} + {{ 'ONBOARDING.EVENTS.' + action[0] + '.action' | translate }} keyboard_arrow_right
diff --git a/console/src/app/modules/onboarding/onboarding.component.scss b/console/src/app/modules/onboarding/onboarding.component.scss index ab2602d388..23872223d5 100644 --- a/console/src/app/modules/onboarding/onboarding.component.scss +++ b/console/src/app/modules/onboarding/onboarding.component.scss @@ -23,14 +23,10 @@ flex-direction: column; margin-bottom: 2rem; - .title { - font-size: 2rem; - margin-bottom: 1rem; - } - .desc { - font-size: 14px; + font-size: 1.2rem; margin-top: 0; + text-transform: uppercase; } .onboarding-progress-bar-wrapper { @@ -71,7 +67,6 @@ .action-card { position: relative; - z-index: 100; margin: 1rem; flex-basis: 270px; text-decoration: none; @@ -91,18 +86,46 @@ height: 100%; padding-right: 0.5rem; - .text-block { + .action-content-row { display: flex; - flex-direction: column; - color: map-get($foreground, text); - padding-top: 1rem; + flex-direction: row; + align-items: flex-start; - .name { - margin-bottom: 1rem; + .icon-wrapper { + display: flex; + justify-content: center; + align-items: center; + padding: 0.5rem; + border-radius: 50vw; + flex-shrink: 0; + margin-right: 1rem; + margin-top: 1rem; + + .inner { + border-radius: 50vw; + height: 40px; + width: 40px; + display: flex; + justify-content: center; + align-items: center; + font-size: 1.75rem; + } } - .description { - font-size: 14px; + .text-block { + display: flex; + flex-direction: column; + color: map-get($foreground, text); + padding-top: 1rem; + flex: 1; + + .name { + margin-bottom: 1rem; + } + + .description { + font-size: 14px; + } } } @@ -111,6 +134,12 @@ } } + &.done { + .action-content { + opacity: 0.5; + } + } + .state-circle { position: absolute; top: 0; @@ -126,6 +155,7 @@ margin-right: 1rem; background-color: if($is-dark-theme, map-get($background, state), #e4e7e4); box-shadow: 0 0 3px #0000001a; + border: 1px solid rgba(#8795a1, 0.2); i { font-size: 1rem; diff --git a/console/src/app/modules/onboarding/onboarding.component.ts b/console/src/app/modules/onboarding/onboarding.component.ts index 4918458c58..9f77f84154 100644 --- a/console/src/app/modules/onboarding/onboarding.component.ts +++ b/console/src/app/modules/onboarding/onboarding.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { AdminService } from 'src/app/services/admin.service'; +import { ThemeService } from 'src/app/services/theme.service'; import { ONBOARDING_EVENTS } from 'src/app/utils/onboarding'; @Component({ @@ -10,7 +11,7 @@ import { ONBOARDING_EVENTS } from 'src/app/utils/onboarding'; export class OnboardingComponent { public actions = this.adminService.progressEvents; - constructor(public adminService: AdminService) { + constructor(public adminService: AdminService, public themeService: ThemeService) { this.adminService.loadEvents.next(ONBOARDING_EVENTS); } } diff --git a/console/src/app/modules/shortcuts/shortcuts.component.scss b/console/src/app/modules/shortcuts/shortcuts.component.scss index 64f6c3d5de..044e4fca3a 100644 --- a/console/src/app/modules/shortcuts/shortcuts.component.scss +++ b/console/src/app/modules/shortcuts/shortcuts.component.scss @@ -28,6 +28,11 @@ display: flex; align-items: center; + h2 { + text-transform: uppercase; + font-size: 1.2rem; + } + .shortcut-btn { margin-left: 0.5rem; } diff --git a/console/src/app/pages/home/home.component.html b/console/src/app/pages/home/home.component.html index 5ac7a7d735..207251c40b 100644 --- a/console/src/app/pages/home/home.component.html +++ b/console/src/app/pages/home/home.component.html @@ -1,4 +1,6 @@
+

{{ 'HOME.WELCOME' | translate }}

+
@@ -10,24 +12,66 @@

{{ 'HOME.DISCLAIMER' | translate }}

+

{{ 'ONBOARDING.MOREDESCRIPTION' | translate }}

+
- -
- + +
+
+ +
+ {{ 'HOME.DOCUMENTATION.TITLE' | translate }}
- -
- + +
+
+ +
{{ 'HOME.GETSTARTED.TITLE' | translate }}
- -
- + +
+
+ +
{{ 'HOME.QUICKSTARTS.TITLE' | translate }}
diff --git a/console/src/app/pages/home/home.component.scss b/console/src/app/pages/home/home.component.scss index 0f6f875466..68a8a8742c 100644 --- a/console/src/app/pages/home/home.component.scss +++ b/console/src/app/pages/home/home.component.scss @@ -18,6 +18,11 @@ $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); $border-selected-color: if($is-dark-theme, #fff, #000); + .home-title { + font-size: 2rem; + margin-bottom: 1rem; + } + .home-wrapper { position: relative; display: flex; @@ -34,6 +39,12 @@ } } + .desc { + font-size: 1.2rem; + margin-top: 0; + text-transform: uppercase; + } + .home-grid-container { display: grid; grid-template-columns: 1fr; @@ -58,24 +69,15 @@ font-size: 14px; background-color: $card-background-color; border: 1px solid $border-color; - border-radius: 1rem; + border-radius: 0.5rem; margin: 0; text-decoration: none; - color: white; - box-shadow: 0 0 15px #0000001a; + color: inherit; &.edit-state { cursor: move; } - &.green { - background: linear-gradient(40deg, #059669 30%, #047857); - } - - &.blue { - background: linear-gradient(40deg, #3b82f6 30%, #4f46e5); - } - .grid-item-avatar { height: 40px; width: 40px; @@ -118,6 +120,26 @@ color: white; } } + + .icon-wrapper { + display: flex; + justify-content: center; + align-items: center; + padding: 0.5rem; + border-radius: 50vw; + flex-shrink: 0; + margin-right: 1rem; + + .inner { + border-radius: 50vw; + height: 40px; + width: 40px; + display: flex; + justify-content: center; + align-items: center; + font-size: 1.75rem; + } + } } } diff --git a/console/src/app/pages/home/home.component.ts b/console/src/app/pages/home/home.component.ts index d0cd4cf8a7..f834856547 100644 --- a/console/src/app/pages/home/home.component.ts +++ b/console/src/app/pages/home/home.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; +import { ThemeService } from 'src/app/services/theme.service'; +import { COLORS } from 'src/app/utils/color'; @Component({ selector: 'cnsl-home', @@ -8,9 +10,18 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; styleUrls: ['./home.component.scss'], }) export class HomeComponent { + public greendark: string = COLORS[6][700]; + public greenlight = COLORS[6][200]; + + public cyandark: string = COLORS[7][700]; + public cyanlight = COLORS[7][200]; + + public bluedark: string = COLORS[9][700]; + public bluelight = COLORS[9][200]; + public dark: boolean = true; - constructor(public authService: GrpcAuthService, breadcrumbService: BreadcrumbService) { + constructor(public authService: GrpcAuthService, breadcrumbService: BreadcrumbService, public themeService: ThemeService) { const bread: Breadcrumb = { type: BreadcrumbType.ORG, routerLink: ['/org'], diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts index 25b3620efb..c47c576979 100644 --- a/console/src/app/services/admin.service.ts +++ b/console/src/app/services/admin.service.ts @@ -267,9 +267,20 @@ export interface OnboardingActions { oneof: string[]; link: string | string[]; fragment?: string | undefined; + iconClasses?: string; + darkcolor: string; + lightcolor: string; } -type OnboardingEvent = { order: number; link: string; fragment: string | undefined; event: Event.AsObject | undefined }; +type OnboardingEvent = { + order: number; + link: string; + fragment: string | undefined; + event: Event.AsObject | undefined; + iconClasses?: string; + darkcolor: string; + lightcolor: string; +}; type OnboardingEventEntries = Array<[string, OnboardingEvent]> | []; @Injectable({ @@ -286,14 +297,30 @@ export class AdminService { const eventsReq = new ListEventsRequest().setAsc(true).setEventTypesList(searchForTypes).setAsc(false); return from(this.listEvents(eventsReq)).pipe( map((events) => { - const el = events.toObject().eventsList.filter((e) => e.editor?.service !== 'System-API'); + const el = events.toObject().eventsList.filter((e) => e.editor?.service !== 'System-API' && e.editor?.userId); let obj: { [type: string]: OnboardingEvent } = {}; actions.map((action) => { const filtered = el.filter((event) => event.type?.type && action.oneof.includes(event.type.type)); (obj as any)[action.eventType] = filtered.length - ? { order: action.order, link: action.link, fragment: action.fragment, event: filtered[0] } - : { order: action.order, link: action.link, fragment: action.fragment, event: undefined }; + ? { + order: action.order, + link: action.link, + fragment: action.fragment, + event: filtered[0], + iconClasses: action.iconClasses, + darkcolor: action.darkcolor, + lightcolor: action.lightcolor, + } + : { + order: action.order, + link: action.link, + fragment: action.fragment, + event: undefined, + iconClasses: action.iconClasses, + darkcolor: action.darkcolor, + lightcolor: action.lightcolor, + }; }); const toArray = Object.entries(obj).sort(([key0, a], [key1, b]) => a.order - b.order); diff --git a/console/src/app/utils/onboarding.ts b/console/src/app/utils/onboarding.ts index 1fa1e31154..4f66c15198 100644 --- a/console/src/app/utils/onboarding.ts +++ b/console/src/app/utils/onboarding.ts @@ -1,16 +1,70 @@ import { OnboardingActions } from '../services/admin.service'; +import { COLORS } from './color'; + +const reddark: string = COLORS[0][700]; +const redlight = COLORS[0][200]; + +const yellowdark: string = COLORS[3][700]; +const yellowlight = COLORS[3][200]; + +const greendark: string = COLORS[6][700]; +const greenlight = COLORS[6][200]; + +const bluedark: string = COLORS[9][700]; +const bluelight = COLORS[9][200]; + +const purpledark: string = COLORS[12][700]; +const purplelight = COLORS[12][200]; + +const pinkdark: string = COLORS[15][700]; +const pinklight = COLORS[15][200]; export const ONBOARDING_EVENTS: OnboardingActions[] = [ - { order: 0, eventType: 'project.added', oneof: ['project.added'], link: ['/projects/create'] }, - { order: 1, eventType: 'project.application.added', oneof: ['project.application.added'], link: ['/projects/app-create'] }, - { order: 2, eventType: 'user.human.added', oneof: ['user.human.added'], link: ['/users/create'] }, - { order: 3, eventType: 'user.grant.added', oneof: ['user.grant.added'], link: ['/grant-create'] }, + { + order: 0, + eventType: 'project.added', + oneof: ['project.added'], + link: ['/projects/create'], + iconClasses: 'las la-database', + darkcolor: greendark, + lightcolor: greenlight, + }, + { + order: 1, + eventType: 'project.application.added', + oneof: ['project.application.added'], + link: ['/projects/app-create'], + iconClasses: 'lab la-openid', + darkcolor: purpledark, + lightcolor: purplelight, + }, + { + order: 2, + eventType: 'user.human.added', + oneof: ['user.human.added'], + link: ['/users/create'], + iconClasses: 'las la-user', + darkcolor: bluedark, + lightcolor: bluelight, + }, + { + order: 3, + eventType: 'user.grant.added', + oneof: ['user.grant.added'], + link: ['/grant-create'], + iconClasses: 'las la-shield-alt', + darkcolor: reddark, + lightcolor: redlight, + }, { order: 4, eventType: 'instance.policy.label.added', oneof: ['instance.policy.label.added', 'instance.policy.label.changed'], link: ['/settings'], fragment: 'branding', + iconClasses: 'las la-swatchbook', + darkcolor: pinkdark, + lightcolor: pinklight, }, { order: 5, @@ -18,5 +72,8 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [ oneof: ['instance.smtp.config.added', 'instance.smtp.config.changed'], link: ['/settings'], fragment: 'notifications', + iconClasses: 'las la-envelope', + darkcolor: yellowdark, + lightcolor: yellowlight, }, ]; diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index c4277b0367..b550168418 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -44,6 +44,7 @@ }, "ONBOARDING": { "DESCRIPTION": "Dein Onboarding-prozess", + "MOREDESCRIPTION": "mehr Shortcuts", "COMPLETED": "abgeschlossen", "DISMISS": "schließen", "CARD": { @@ -53,27 +54,33 @@ "EVENTS": { "instance.policy.label.added": { "title": "Branding anpassen", - "description": "Definiere Farben und Form des Login-UIs und uploade deine Logos und Icons." + "description": "Definiere Farben und Form des Login-UIs und uploade deine Logos und Icons.", + "action": "Branding anpassen" }, "instance.smtp.config.added": { "title": "SMTP Benachrichtigungseinstellungen", - "description": "Konfiguriere deinen Mailserver." + "description": "Konfiguriere deinen Mailserver.", + "action": "SMTP einrichten" }, "project.added": { "title": "Erstelle ein Projekt", - "description": "Erstelle dein erstes Projekt und definiere Rollen" + "description": "Erstelle dein erstes Projekt und definiere Rollen", + "action": "Projekt erstellen" }, "project.application.added": { "title": "Erstelle eine App", - "description": "Erstelle deine erste Web-, native, API oder SAML-applikation und konfiguriere den Authentification-flow." + "description": "Erstelle deine erste Web-, native, API oder SAML-applikation und konfiguriere den Authentification-flow.", + "action": "App erstellen" }, "user.human.added": { "title": "Erfasse Benutzer", - "description": "Erstelle Benutzer die später deine Apps nutzen können." + "description": "Erstelle Benutzer die später deine Apps nutzen können.", + "action": "Benutzer erfassen" }, "user.grant.added": { "title": "Berechtige Benutzer", - "description": "Erlaube es deinen Nutzern auf deine Apps zuzugreifen und gebe ihnen Rollen." + "description": "Erlaube es deinen Nutzern auf deine Apps zuzugreifen und gebe ihnen Rollen.", + "action": "Benutzer berechtigen" } } }, diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 41039461f0..df86a19aaa 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -44,6 +44,7 @@ }, "ONBOARDING": { "DESCRIPTION": "Your onboarding process", + "MOREDESCRIPTION": "more shortcuts", "COMPLETED": "completed", "DISMISS": "No thanks, I'm a pro.", "CARD": { @@ -53,27 +54,33 @@ "EVENTS": { "instance.policy.label.added": { "title": "Setup your brand", - "description": "Define coloring and shape of your login and upload your logo and icons." + "description": "Define coloring and shape of your login and upload your logo and icons.", + "action": "Setup branding" }, "instance.smtp.config.added": { "title": "Setup your SMTP settings", - "description": "Set your own mail server settings." + "description": "Set your own mail server settings.", + "action": "Setup SMTP" }, "project.added": { - "title": "Create your first project", - "description": "Add your first project and define its roles and authorizations." + "title": "Create a project", + "description": "Add a project and define its roles and authorizations.", + "action": "Create project" }, "project.application.added": { - "title": "Create your first application", - "description": "Create a web, native, api or saml application and setup your authentication flow." + "title": "Create an application", + "description": "Create a web, native, api or saml application and setup your authentication flow.", + "action": "Create app" }, "user.human.added": { "title": "Add users", - "description": "Add your application users" + "description": "Add your application users", + "action": "Add user" }, "user.grant.added": { "title": "Grant users", - "description": "Allow users to access your application and setup their role." + "description": "Allow users to access your application and setup their role.", + "action": "Grant user" } } }, diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 5e516d3433..9443fb891a 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -44,6 +44,7 @@ }, "ONBOARDING": { "DESCRIPTION": "Votre processus d'intégration", + "MOREDESCRIPTION": "plus de raccourcis", "COMPLETED": "terminé", "DISMISS": "fermer", "CARD": { @@ -53,27 +54,33 @@ "EVENTS": { "instance.policy.label.added": { "title": "Créez votre marque", - "description": "Définissez la couleur et la forme de votre connexion et téléchargez votre logo et vos icônes." + "description": "Définissez la couleur et la forme de votre connexion et téléchargez votre logo et vos icônes.", + "action": "Définissez" }, "instance.smtp.config.added": { - "title": "Configurez vos paramètres SMTP", - "description": "Définissez vos propres paramètres de serveur de messagerie" + "title": "Configurez paramètres SMTP", + "description": "Définissez paramètres de serveur de messagerie", + "action": "Configurez" }, "project.added": { - "title": "Créez votre premier projet", - "description": "Ajoutez votre premier projet et définissez ses rôles et autorisations." + "title": "Créez projet", + "description": "Ajoutez projet et définissez ses rôles et autorisations.", + "action": "Créez projet" }, "project.application.added": { "title": "Créez votre première application", - "description": "Créez une application web, native, api ou saml et configurez votre flux d'authentification." + "description": "Créez une application web, native, api ou saml et configurez votre flux d'authentification.", + "action": "Créez application" }, "user.human.added": { "title": "Ajouter des utilisateurs", - "description": "Ajouter les utilisateurs de votre application" + "description": "Ajouter les utilisateurs de application", + "action": "Ajuter utilisateur" }, "user.grant.added": { "title": "Utilisateurs de subventions", - "description": "Autorisez les utilisateurs à accéder à votre application et définissez leur rôle." + "description": "Autorisez les utilisateurs à accéder à votre application et définissez leur rôle.", + "action": "Autorisez" } } }, diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 12ad955388..0e9e5b41cb 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -44,6 +44,7 @@ }, "ONBOARDING": { "DESCRIPTION": "Il tuo processo di onboarding", + "MOREDESCRIPTION": "più scorciatoie", "COMPLETED": "completato", "DISMISS": "chiudi", "CARD": { @@ -53,27 +54,33 @@ "EVENTS": { "instance.policy.label.added": { "title": "Imposta il tuo marchio", - "description": "Definisci la colorazione e il design del vostro login e caricate il vostro logo e le vostre icone." + "description": "Definisci la colorazione e il design del vostro login e caricate il vostro logo e le vostre icone.", + "action": "Imposta marchio" }, "instance.smtp.config.added": { "title": "Configura le impostazioni SMTP", - "description": "Imposta il proprio server di posta" + "description": "Imposta il proprio server di posta", + "action": "Configura SMTP" }, "project.added": { "title": "Crea il tuo primo progetto", - "description": "Aggiungere il primo progetto e definire i ruoli e le autorizzazioni." + "description": "Aggiungere il primo progetto e definire i ruoli e le autorizzazioni.", + "action": "Crea progetto" }, "project.application.added": { "title": "Crea la tua prima applicazione", - "description": "Crea un'applicazione web, nativa, api o saml e imposta il flusso di autenticazione." + "description": "Crea un'applicazione web, nativa, api o saml e imposta il flusso di autenticazione.", + "action": "Crea applicazione" }, "user.human.added": { "title": "Aggiungi utenti", - "description": "Aggiungi gli utenti dell'applicazione" + "description": "Aggiungi gli utenti dell'applicazione", + "action": "Aggiungi utente" }, "user.grant.added": { "title": "Crea autorizzazioni per gli utenti", - "description": "Consenti agli utenti di accedere alla tua applicazione e imposta il loro ruolo." + "description": "Consenti agli utenti di accedere alla tua applicazione e imposta il loro ruolo.", + "action": "Crea autorizzazione" } } }, diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index bab05f88ad..3c8d8fdfa9 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -44,6 +44,7 @@ }, "ONBOARDING": { "DESCRIPTION": "オンボーディングの手順", + "MOREDESCRIPTION": "より多くのショートカット", "COMPLETED": "完了", "DISMISS": "いいえ、私はプロです。", "CARD": { @@ -53,27 +54,33 @@ "EVENTS": { "instance.policy.label.added": { "title": "ブランドをセットアップする", - "description": "ログインの色と形状を定義し、ロゴとアイコンをアップロードします。" + "description": "ログインの色と形状を定義し、ロゴとアイコンをアップロードします。", + "action": "ブランディングの設定" }, "instance.smtp.config.added": { "title": "SMTP設定をセットアップする", - "description": "独自のメールサーバーを設定します。" + "description": "独自のメールサーバーを設定します。", + "action": "SMTP 設定を設定する" }, "project.added": { "title": "最初のプロジェクトを作成する", - "description": "最初のプロジェクトを追加し、ロールと認証を定義します。" + "description": "最初のプロジェクトを追加し、ロールと認証を定義します。", + "action": "プロジェクトを作成" }, "project.application.added": { "title": "最初のアプリケーションを作成する", - "description": "Web、ネイティブ、API、またはSAMLアプリケーションを作成し、認証フローをセットアップします。" + "description": "Web、ネイティブ、API、またはSAMLアプリケーションを作成し、認証フローをセットアップします。", + "action": "アプリケーションを作成" }, "user.human.added": { "title": "ユーザーを追加する", - "description": "アプリケーションユーザーを追加します。" + "description": "アプリケーションユーザーを追加します。", + "action": "ユーザーを作成" }, "user.grant.added": { "title": "ユーザーにグラントする", - "description": "ユーザーがアプリケーションにアクセスし、ロールをセットアップできるようにします。" + "description": "ユーザーがアプリケーションにアクセスし、ロールをセットアップできるようにします。", + "action": "承認の作成" } } }, diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 25bcb24da8..0c9761c766 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -44,6 +44,7 @@ }, "ONBOARDING": { "DESCRIPTION": "Twój proces wprowadzania na rynek", + "MOREDESCRIPTION": "więcej skrótów", "COMPLETED": "zakończone", "DISMISS": "zamknąć", "CARD": { @@ -53,27 +54,33 @@ "EVENTS": { "instance.policy.label.added": { "title": "Skonfiguruj swoją markę", - "description": "Zdefiniuj kolorystykę i kształt swojego loginu oraz wgraj swoje logo i ikony." + "description": "Zdefiniuj kolorystykę i kształt swojego loginu oraz wgraj swoje logo i ikony.", + "action": "Skonfiguruj branding" }, "instance.smtp.config.added": { "title": "Ustawienia SMTP", - "description": "Ustawienie własnego serwera pocztowego" + "description": "Ustawienie własnego serwera pocztowego", + "action": "skonfiguruj ustawienia SMTP" }, "project.added": { "title": "Stwórz swój pierwszy projekt", - "description": "Dodaj swój pierwszy projekt i określ jego role i uprawnienia." + "description": "Dodaj swój pierwszy projekt i określ jego role i uprawnienia.", + "action": "Utwórz projekt" }, "project.application.added": { "title": "Utwórz swoją pierwszą aplikację", - "description": "Utwórz aplikację internetową, natywną, api lub saml i skonfiguruj swój przepływ uwierzytelniania." + "description": "Utwórz aplikację internetową, natywną, api lub saml i skonfiguruj swój przepływ uwierzytelniania.", + "action": "Utwórz aplikację" }, "user.human.added": { "title": "Dodaj użytkowników", - "description": "Dodaj użytkowników aplikacji" + "description": "Dodaj użytkowników aplikacji", + "action": "Stwórz użytkownika" }, "user.grant.added": { "title": "Użytkownicy dotacji", - "description": "Pozwól użytkownikom na dostęp do Twojej aplikacji i ustaw ich rolę." + "description": "Pozwól użytkownikom na dostęp do Twojej aplikacji i ustaw ich rolę.", + "action": "Utwórz autoryzację" } } }, diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 226064f369..c7bd3eb226 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -44,6 +44,7 @@ }, "ONBOARDING": { "DESCRIPTION": "你的入职过程", + "MOREDESCRIPTION": "更多捷径", "COMPLETED": "已完成", "DISMISS": "隐藏", "CARD": { @@ -53,27 +54,33 @@ "EVENTS": { "instance.policy.label.added": { "title": "设置你的品牌", - "description": "定义你的登录的颜色和形状,上传你的标志和图标。" + "description": "定义你的登录的颜色和形状,上传你的标志和图标。", + "action": "设置品牌" }, "instance.smtp.config.added": { "title": "SMTP设置", - "description": "设置你自己的邮件服务器设置" + "description": "设置你自己的邮件服务器设置", + "action": "设置 SMTP 设置" }, "project.added": { "title": "创建你的第一个项目", - "description": "添加你的第一个项目并定义其角色和授权。" + "description": "添加你的第一个项目并定义其角色和授权。", + "action": "创建项目" }, "project.application.added": { "title": "创建你的第一个应用程序", - "description": "创建一个web、native、api或saml应用程序并设置你的认证流程。" + "description": "创建一个web、native、api或saml应用程序并设置你的认证流程。", + "action": "创建应用程序" }, "user.human.added": { "title": "添加用户", - "description": "添加你的应用程序用户" + "description": "添加你的应用程序用户", + "action": "创建用户" }, "user.grant.added": { "title": "授予用户", - "description": "允许用户访问你的应用程序并设置他们的角色。" + "description": "允许用户访问你的应用程序并设置他们的角色。", + "action": "创建授权" } } }, From 1e63c2eea3ffad4a03127b72330247be076b3ae8 Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Thu, 6 Apr 2023 15:46:21 +0200 Subject: [PATCH 12/36] chore: remove duplicate step (#5624) --- .github/workflows/zitadel.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/zitadel.yml b/.github/workflows/zitadel.yml index 7ec943ec03..2691109f80 100644 --- a/.github/workflows/zitadel.yml +++ b/.github/workflows/zitadel.yml @@ -78,11 +78,6 @@ jobs: with: file: .artifacts/codecov/profile.cov name: go-codecov - - uses: tibdex/github-app-token@v1 - id: generate-token - with: - app_id: ${{ secrets.APP_ID }} - private_key: ${{ secrets.APP_PRIVATE_KEY }} - name: Bump Chart Version uses: peter-evans/repository-dispatch@v2 if: steps.semantic.outputs.new_release_published == 'true' && github.ref == 'refs/heads/main' From 440ba9f5efa11802f27d1ad39db79581101398a9 Mon Sep 17 00:00:00 2001 From: Stefan Benz <46600784+stebenz@users.noreply.github.com> Date: Tue, 11 Apr 2023 09:39:12 +0200 Subject: [PATCH 13/36] fix: update saml to v0.0.11 (#5628) * fix: update saml to v0.0.11 * chore: remove unused sum --------- Co-authored-by: adlerhurst --- cmd/start/start.go | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- internal/api/oidc/op.go | 2 +- internal/api/saml/provider.go | 4 +--- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cmd/start/start.go b/cmd/start/start.go index f01060bef4..61d35540e5 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -260,12 +260,12 @@ func startAPIs( } apis.RegisterHandler(openapi.HandlerPrefix, openAPIHandler) - oidcProvider, err := oidc.NewProvider(ctx, config.OIDC, login.DefaultLoggedOutPath, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.OIDCKey, eventstore, dbClient, userAgentInterceptor, instanceInterceptor.Handler, accessInterceptor.Handle) + oidcProvider, err := oidc.NewProvider(config.OIDC, login.DefaultLoggedOutPath, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.OIDCKey, eventstore, dbClient, userAgentInterceptor, instanceInterceptor.Handler, accessInterceptor.Handle) if err != nil { return fmt.Errorf("unable to start oidc provider: %w", err) } - samlProvider, err := saml.NewProvider(ctx, config.SAML, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.SAML, eventstore, dbClient, instanceInterceptor.Handler, userAgentInterceptor, accessInterceptor.Handle) + samlProvider, err := saml.NewProvider(config.SAML, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.SAML, eventstore, dbClient, instanceInterceptor.Handler, userAgentInterceptor, accessInterceptor.Handle) if err != nil { return fmt.Errorf("unable to start saml provider: %w", err) } diff --git a/go.mod b/go.mod index 7edde06553..eb61ce26e2 100644 --- a/go.mod +++ b/go.mod @@ -58,7 +58,7 @@ require ( github.com/ttacon/libphonenumber v1.2.1 github.com/zitadel/logging v0.3.4 github.com/zitadel/oidc/v2 v2.2.6 - github.com/zitadel/saml v0.0.10 + github.com/zitadel/saml v0.0.11 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 go.opentelemetry.io/otel v1.14.0 diff --git a/go.sum b/go.sum index 2011473465..4efcc17bb2 100644 --- a/go.sum +++ b/go.sum @@ -1132,8 +1132,8 @@ github.com/zitadel/logging v0.3.4 h1:9hZsTjMMTE3X2LUi0xcF9Q9EdLo+FAezeu52ireBbHM github.com/zitadel/logging v0.3.4/go.mod h1:aPpLQhE+v6ocNK0TWrBrd363hZ95KcI17Q1ixAQwZF0= github.com/zitadel/oidc/v2 v2.2.6 h1:L2k5q1X8Rucax5Ynp3B3lz7JQDJxUwfWCOmgc9Bh0BM= github.com/zitadel/oidc/v2 v2.2.6/go.mod h1:tGkj9lQk6KVj5hsM89XPadvi6I06666sMy3KtykvSFM= -github.com/zitadel/saml v0.0.10 h1:cyKd78Vat9vz55S74lggJrXMSqbAPsnJDrPFTPScNYY= -github.com/zitadel/saml v0.0.10/go.mod h1:Hze1/zRN9j1uh7U+89vweP/OwLNO8BLHg3zU1Jtycdg= +github.com/zitadel/saml v0.0.11 h1:kObucnBrcu1PHCO7RGT0iVeuJL/5I50gUgr40S41nMs= +github.com/zitadel/saml v0.0.11/go.mod h1:YGWAvPZRv4DbEZ78Ht/2P0AWzGn+6WGhFf90PMXl0Po= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= diff --git a/internal/api/oidc/op.go b/internal/api/oidc/op.go index c8d72a12ed..48167f402c 100644 --- a/internal/api/oidc/op.go +++ b/internal/api/oidc/op.go @@ -73,7 +73,7 @@ type OPStorage struct { assetAPIPrefix func(ctx context.Context) string } -func NewProvider(ctx context.Context, config Config, defaultLogoutRedirectURI string, externalSecure bool, command *command.Commands, query *query.Queries, repo repository.Repository, encryptionAlg crypto.EncryptionAlgorithm, cryptoKey []byte, es *eventstore.Eventstore, projections *database.DB, userAgentCookie, instanceHandler, accessHandler func(http.Handler) http.Handler) (op.OpenIDProvider, error) { +func NewProvider(config Config, defaultLogoutRedirectURI string, externalSecure bool, command *command.Commands, query *query.Queries, repo repository.Repository, encryptionAlg crypto.EncryptionAlgorithm, cryptoKey []byte, es *eventstore.Eventstore, projections *database.DB, userAgentCookie, instanceHandler, accessHandler func(http.Handler) http.Handler) (op.OpenIDProvider, error) { opConfig, err := createOPConfig(config, defaultLogoutRedirectURI, cryptoKey) if err != nil { return nil, caos_errs.ThrowInternal(err, "OIDC-EGrqd", "cannot create op config: %w") diff --git a/internal/api/saml/provider.go b/internal/api/saml/provider.go index 8158846214..f46802f6aa 100644 --- a/internal/api/saml/provider.go +++ b/internal/api/saml/provider.go @@ -1,7 +1,6 @@ package saml import ( - "context" "fmt" "net/http" @@ -29,7 +28,6 @@ type Config struct { } func NewProvider( - ctx context.Context, conf Config, externalSecure bool, command *command.Commands, @@ -68,13 +66,13 @@ func NewProvider( accessHandler, http_utils.CopyHeadersToContext, ), + provider.WithCustomTimeFormat("2006-01-02T15:04:05.999Z"), } if !externalSecure { options = append(options, provider.WithAllowInsecure()) } return provider.NewProvider( - ctx, provStorage, HandlerPrefix, conf.ProviderConfig, From ef7c53a6dc80abf19a7effc48c6a017e4cc0a542 Mon Sep 17 00:00:00 2001 From: mffap Date: Tue, 11 Apr 2023 11:24:02 +0200 Subject: [PATCH 14/36] docs(migrate): Migrate to ZITADEL (#5625) * docs(guide): migration process guide * outline users * intro * wip * wip * wip - basics * structure migrate * importing users * structure * sidebar * move technical considerations to intro * update users * update sidebar * migrate from auth0 * Apply suggestions from code review Co-authored-by: Fabi * remove mermaid * add concepts * replace bulk user * add note on code snippets --------- Co-authored-by: Fabi --- docs/docs/guides/migrate/introduction.md | 79 ++++++ docs/docs/guides/migrate/sources/auth0.md | 41 +++ .../sources/zitadel-v1.md} | 3 +- docs/docs/guides/migrate/users.md | 236 ++++++++++++++++++ docs/sidebars.js | 19 +- 5 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 docs/docs/guides/migrate/introduction.md create mode 100644 docs/docs/guides/migrate/sources/auth0.md rename docs/docs/guides/{integrate/export-and-import.md => migrate/sources/zitadel-v1.md} (98%) create mode 100644 docs/docs/guides/migrate/users.md diff --git a/docs/docs/guides/migrate/introduction.md b/docs/docs/guides/migrate/introduction.md new file mode 100644 index 0000000000..887a374a23 --- /dev/null +++ b/docs/docs/guides/migrate/introduction.md @@ -0,0 +1,79 @@ +--- +title: Migrate to ZITADEL +sidebar_label: Introduction +--- + +This section of guides shows you how to migrate from your current auth system to ZITADEL. +The individual guides in this section should give you an overview of things to consider before you start the migration. + +When moving from a previous auth solution to ZITADEL, it is important to note that some decisions and features are unique to ZITADEL. +Without duplicating too much content here are some important features and patterns to consider in terms of solution architecture. +You can read more about the basic structure and important concepts of ZITADEL in our [concepts section](https://zitadel.com/docs/concepts/introduction). + +## Multi-Tenancy Architecture + +Multi-tenancy in ZITADEL can be achieved through either [Instances](/docs/concepts/structure/instance) or [Organizations](/docs/concepts/structure/organizations). +Where instances represent isolated ZITADEL instances, Organizations provide a more permeable approach to multi-tenancy. + +In most cases, when you want to achieve multi-tenancy, you use Organizations. Each organization can have their own set of Settings (eg, Security Policies, IDPs, Branding), Managers, and Users. +Please also consult our guide on [Solution Scenarios](/docs/guides/solution-scenarios/introduction +) for B2C and B2B for more details. + +## Delegated Access Management + +Some solutions, that offer multi-tenancy, require you to copy applications and settings to each tenant and manage changes individually. +ZITADEL works differently by using [Granted Projects](/docs/concepts/structure/granted_projects). + +Projects can be granted to [Organization](/docs/concepts/structure/projects#granted-organizations) or even to individual users. +You can think of it as a logical link to a Project, which can be used by the receiving Organization or User as if it was their own project, except privileges to modify the Project itself. + +Delegated access management is a great way of keeping the management overhead low and enabling [self-service](/docs/concepts/features/selfservice#managers-in-delegation) for Organizations to manage their own Settings and Authorizations. + +## Actions + +ZITADEL [Actions](/docs/apis/actions/introduction) is the key feature to customize and create workflows and change the default behavior of the platform. + +You define custom code that should be run on a specific Trigger. +A trigger could be the creation of a new user, getting profile information about a user, or a login attempt. + +With the [HTTP module](/docs/apis/actions/modules) you can even make calls to third party systems for example to receive additional user information from a backend system, or triggering a workflow in other systems (Webhook). + +You can also create custom claims or manipulate tokens and information on the userinfo endpoint by using the [Complement Token Flow](/docs/apis/actions/complement-token). +This might be required, if an application expects roles/permissions in a certain format or additional attributes (eg, a backend user-id) as claims. + +## Metadata + +You can store arbitrary key-value pairs of data on objects such as Users or Organizations. +Metadata could link a user to a specific backend user-id or represent an "organizational unit" for your business logic. +Metadata can be access directly with the correct [scopes](/docs/apis/openidoauth/scopes#reserved-scopes) or transformed to custom claims (see above). + +## Migrating users + +Migrating users with minimal impact on users can be a challenging task. +We provide some more information on migrating users and secrets in [this guide](./users.md). + +## Technical considerations + +### Batch migration + +**Batch migration** is the easiest way, if you can afford some minimal downtime to move all users and applications over to ZITADEL. +See the [User guide](./users.md) for batch migration of users. + +### Just-in-time migration + +In case all your applications depend on ZITADEL after the migration date, and ZITADEL is able to retrieve the required user information, including secrets, from the legacy system, then the recommended way is to let **ZITADEL orchestrate the user migration just-in-time**: + +- Create a pre-authentication [Action](/docs/apis/actions/introduction) to request user data from the legacy system and create a new user in ZITADEL. +- Optionally, create a post-authentication Action to flag successfully migrated users in your legacy system + +For all other cases, we recommend that the **legacy system orchestrates the migration** of users to ZITADEL for more flexibility: + +- Update your legacy system to create a user in ZITADEL on their next login, if not already flagged as migrated, by using our APIs (you can set the password and a verified email) +- Redirect migrated users with a login hint in the [auth request](/docs/apis/openidoauth/authrequest.mdx) to ZITADEL to pre-select the user + +In this case the migration can also be done as an import job or also allowing to create user session in both the legacy auth solution and ZITADEL in parallel with identity brokering: + +- Setup ZITADEL to use your legacy system as external identity provider (note: you can also use JWT-IDP, if you only have a token). +- Configure your app to use ZITADEL, which will redirect users automatically to the external identity provider to login. +- A session will be created both on the legacy system and ZITADEL +- If a user does not exist already in ZITADEL you can auto-register new users and use an Action to pull additional information (eg, Secrets) from your legacy system. Note: ZITADEL links external identity information to users, meaning you can have users use both a password and external identity providers to login with the same user. diff --git a/docs/docs/guides/migrate/sources/auth0.md b/docs/docs/guides/migrate/sources/auth0.md new file mode 100644 index 0000000000..dca95a7c3a --- /dev/null +++ b/docs/docs/guides/migrate/sources/auth0.md @@ -0,0 +1,41 @@ +--- +title: Migrate from Auth0 +sidebar_label: From Auth0 +--- + +Migrating users from Auth0 to ZITADEL requires the following steps: + +- Request and download hashed passwords +- Export all user data +- Import users and password hashes to ZITADEL + +## Export hashed passwords + +Auth0 does not export hashed passwords as part of the bulk user export. +You must create a support ticket to download password hashes and password-related information. +Please also refer to the Auth0 guide on how to [Export Data](https://auth0.com/docs/troubleshoot/customer-support/manage-subscriptions/export-data#user-passwords). + +:::info +You can also import users into ZITADEL with an verified email but without the passwords. +Users will be prompted to create a new password after they login for the first time after migration. +::: + +1. Go to https://support.auth0.com/tickets and click on **Open Ticket** +2. Issue Type: **I have a question regarding my Auth0 account** +3. What can we help you with?: **I would like to obtain an export of my tenant password hashes** +4. Fill out the form: Request password hashes as bcrypt +5. **Submit ticket** + +You will receive a JSON file including the password hashes. +See this [community post](https://community.auth0.com/t/password-hashes-export-data-format/58730) for more information about the contents and format. + +## Export all user data + +Create a [bulk user export](https://auth0.com/docs/manage-users/user-migration/bulk-user-exports) from the Auth0 Management API. +You will receive a newline-delimited JSON with the requested user data. + +## Import users and password hashes to ZITADEL + +You will need to merge the received password hashes with the user bulk export. + +After you successfully merged the datasets, you can follow the instructions described in the [Migrate Users](../users) guide to import users to ZITADEL. diff --git a/docs/docs/guides/integrate/export-and-import.md b/docs/docs/guides/migrate/sources/zitadel-v1.md similarity index 98% rename from docs/docs/guides/integrate/export-and-import.md rename to docs/docs/guides/migrate/sources/zitadel-v1.md index f091baad62..176f231ccb 100644 --- a/docs/docs/guides/integrate/export-and-import.md +++ b/docs/docs/guides/migrate/sources/zitadel-v1.md @@ -1,5 +1,6 @@ --- -title: Export and import with ZITADEL +title: Migrate from v1 +sidebar_label: From ZITADEL v1 --- ## Export from V1 to Import into V2 diff --git a/docs/docs/guides/migrate/users.md b/docs/docs/guides/migrate/users.md new file mode 100644 index 0000000000..c01bbfb380 --- /dev/null +++ b/docs/docs/guides/migrate/users.md @@ -0,0 +1,236 @@ +--- +title: Migrate Users +sidebar_label: Users +--- + +Migrating users from an existing system, while minimizing impact on said users, can be a challenging task. + +## Individual Users + +Creating individual users can be done with this endpoint: [ImportHumanUser](/docs/apis/mgmt/management-service-import-human-user). +Please also consult our [guide](/docs/guides/manage/user/reg-create-user) on how to create users. + +```json +{ + "userName": "test9@test9", + "profile": { + "firstName": "Road", + "lastName": "Runner", + "displayName": "Road Runner", + "preferredLanguage": "en" + }, + "email": { + "email": "test@test.com", + "isEmailVerified": false + }, + "hashedPassword": { + "value": "$2a$14$aPbwhMVJSVrRRW2NoM/5.esSJO6o/EIGzGxWiM5SAEZlGqCsr9DAK", + "algorithm": "bcrypt" + }, + "passwordChangeRequired": false, + "otpCode": "testotp", + "requestPasswordlessRegistration": false, + "idps": [ + { + "configId": "124425861423228496", + "externalUserId": "roadrunner@mailonline.com", + "displayName": "name" + } + ] +} +``` + +## Bulk import + +For bulk import use the [import endpoint](https://zitadel.com/docs/apis/admin/admin-service-import-data) on the admin API: + +```json +{ + "timeout": "10m", + "data_orgs": { + "orgs": [ + { + "orgId": "104133391254874632", + "org": { + "name": "ACME" + }, + "humanUsers": [ + { + "userId": "104133391271651848", + "user": { + "userName": "test9@test9", + "profile": { + "firstName": "Road", + "lastName": "Runner", + "displayName": "Road Runner", + "preferredLanguage": "de" + }, + "email": { + "email": "test@acme.tld", + "isEmailVerified": true + }, + "hashedPassword": { + "value": "$2a$14$aPbwhMVJSVrRRW2NoM/5.esSJO6o/EIGzGxWiM5SAEZlGqCsr9DAK", + "algorithm": "bcrypt" + } + } + }, + { + "userId": "120080115081209416", + "user": { + "userName": "testuser", + "profile": { + "firstName": "Test", + "lastName": "User", + "displayName": "Test User", + "preferredLanguage": "und" + }, + "email": { + "email": "fabienne@caos.ch", + "isEmailVerified": true + }, + "hashedPassword": { + "value": "$2a$14$785Fcdbpo9rn5L7E21nIAOJvGCPgWFrZhIAIfDonYXzWuZIKRAQkO", + "algorithm": "bcrypt" + } + } + }, + { + "userId": "145195347319252359", + "user": { + "userName": "wile@test9", + "profile": { + "firstName": "Wile E.", + "lastName": "Coyote", + "displayName": "Wile E. Coyote", + "preferredLanguage": "en" + }, + "email": { + "email": "wile.e@acme.tld" + } + } + } + ] + } + ] + } +} +``` + +:::info +We will improve the bulk import interface for users in the future. +You can show your interest or join the discussion on [this issue](https://github.com/zitadel/zitadel/issues/5524). +::: + +## Migrate secrets + +Besides user data you need to migrate secrets, such as password hashes, OTP seeds, and public keys for passkeys (FIDO2). +The snippets in the sections below are parts from the bulk import endpoint, to clarify how the different objects can be imported. + +### Passwords + +Passwords are stored only as hash. +You can transfer the hashes as long as ZITADEL [supports the same hash algorithm](/docs/concepts/architecture/secrets#hashed-secrets). +Password change on the next sign-in can be enforced. + +_snippet from [bulk-import](#bulk-import) example:_ +```json +{ + "userName": "test9@test9", + ..., + "hashedPassword": { + "value": "$2a$14$aPbwhMVJSVrRRW2NoM/5.esSJO6o/EIGzGxWiM5SAEZlGqCsr9DAK", + "algorithm": "bcrypt" + }, + "passwordChangeRequired": false, + ..., +} +``` + +In case the hashes can't be transferred directly, you always have the option to create a user in ZITADEL without password and prompt users to create a new password. + +If your legacy system receives the passwords in clear text (eg, login form) you could also directly create users via ZITADEL API. We will explain this pattern in more detail in this guide. + +:::info +In case the hash algorithm you are using is not supported by ZITADEL, please let us know after searching our discussions, issues, and chat for similar requests. +::: + +### One-time-passwords (OTP) + +You can pass the OTP secret when creating users: + +_snippet from [bulk-import](#bulk-import) example:_ +```json +{ + "userName": "test9@test9", + ..., + "otpCode": "testotp", + ..., +} +``` + +### Passkeys + +When creating new users, you can trigger a workflow that prompts the users to setup a passkey authenticator. + +_snippet from [bulk-import](#bulk-import) example:_ +```json +{ + "userName": "test9@test9", + ..., + "requestPasswordlessRegistration": false, + ..., +} +``` + +For passkeys to work on the new system you need to make sure that the new auth server has the same domain as the legacy auth server. + +:::info +Currently it is not possible to migrate passkeys directly from another system. +::: + +## Users linked to an external IDP + +A users `sub` is bound to the external [IDP's Client ID](https://zitadel.com/docs/guides/manage/console/instance-settings#identity-providers). +This means that the IDP Client ID configured in ZITADEL must be the same ID as in the legacy system. + +Users should be imported with their `externalUserId`. + +_snippet from [bulk-import](#bulk-import) example:_ +```json +{ + "userName": "test9@test9", + ..., + "idps": [ + { + "configId": "124425861423228496", + "externalUserId": "roadrunner@mailonline.com", + "displayName": "name" + } + ..., +} +``` + +You can use an Action with [post-creation flow](https://zitadel.com/docs/apis/actions/external-authentication#post-creation) to pull information such as roles from the old system and apply them to the user in ZITADEL. + +## Metadata + +You can store arbitrary key-value information on a user (or Organization) in ZITADEL. +Use metadata to store additional attributes of the users, such as organizational unit, backend-id, etc. + +:::info +Metadata must be added to users after the users were created. Currently metadata can't be added during user creation. +[API reference: User Metadata](https://zitadel.com/docs/category/apis/mgmt/user-metadata) +::: + +Request metadata from the userinfo endpoint by passing the required [reserved scope](/docs/apis/openidoauth/scopes#reserved-scopes) in your auth request. +With the [complement token flow](/docs/apis/actions/complement-token), you can also transform metadata (or roles) to custom claims. + +## Authorizations / Roles + +You can assign roles from owned or granted projects to a user. + +:::info +Authorizations must be added to users after the users were created. Currently metadata can't be added during user creation. +[API reference: User Authorization / Grants](https://zitadel.com/docs/category/apis/auth/user-authorizations-grants) +::: \ No newline at end of file diff --git a/docs/sidebars.js b/docs/sidebars.js index d8d45b3a60..7508a5a90a 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -105,6 +105,24 @@ module.exports = { }, ], }, + { + type: "category", + label: "Migrate", + collapsed: false, + items: [ + "guides/migrate/introduction", + "guides/migrate/users", + { + type: "category", + label: "Sources", + collapsed: true, + items: [ + "guides/migrate/sources/zitadel-v1", + "guides/migrate/sources/auth0", + ] + }, + ] + }, { type: "category", label: "Integrate", @@ -154,7 +172,6 @@ module.exports = { "guides/integrate/access-zitadel-apis", "guides/integrate/access-zitadel-system-api", "guides/integrate/event-api", - "guides/integrate/export-and-import", { type: "category", label: "Example Code", From c0c76a8ea9f592ac9273840323187895b75834a5 Mon Sep 17 00:00:00 2001 From: Miguel Cabrerizo <30386061+doncicuto@users.noreply.github.com> Date: Tue, 11 Apr 2023 15:07:35 +0200 Subject: [PATCH 15/36] chore(console): change icon of past steps to checkbox (#5580) checkbox for app create stepper --- .../create-layout.component.scss | 30 +++++++++++++++++++ .../apps/app-create/app-create.component.html | 8 ++++- .../apps/app-create/app-create.component.scss | 8 +++++ console/src/component-themes.scss | 2 ++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/console/src/app/modules/create-layout/create-layout.component.scss b/console/src/app/modules/create-layout/create-layout.component.scss index 63696fcb2e..172d993cb8 100644 --- a/console/src/app/modules/create-layout/create-layout.component.scss +++ b/console/src/app/modules/create-layout/create-layout.component.scss @@ -1,3 +1,33 @@ +@use '@angular/material' as mat; + +@mixin app-create-theme($theme) { + $primary: map-get($theme, primary); + $primary-color: mat.get-color-from-palette($primary, 500); + + // Number of steps creating app + $steps: 3; + + .app-create-wrapper { + // Reference: https://github.com/angular/components/issues/10681#issuecomment-695185806 + @for $i from 1 through $steps { + @for $j from 1 through $i { + .last-edited-step-#{$i} + .mat-horizontal-stepper-header-container + .mat-step-header:nth-child(#{1 + 2 * ($j - 1)})::after, + .last-edited-step-#{$i} + .mat-horizontal-stepper-header-container + .mat-stepper-horizontal-line:nth-child(#{2 + 2 * ($j - 1)}), + .last-edited-step-#{$i} + .mat-horizontal-stepper-header-container + .mat-step-header:nth-child(#{3 + 2 * ($j - 1)})::before { + border-top-width: 3px; + border-top-color: $primary-color; + } + } + } + } +} + .create-layout-container { display: flex; align-items: center; diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.html b/console/src/app/pages/projects/apps/app-create/app-create.component.html index e078c5a67e..d11e908a16 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.html +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.html @@ -1,5 +1,6 @@
+ + + + check +
diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.scss b/console/src/app/pages/projects/apps/app-create/app-create.component.scss index 9a5baa1ec5..ae66cc1172 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.scss +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.scss @@ -50,6 +50,14 @@ p.desc { .step-description { font-size: 0.9rem; } + + .mat-step-icon-content { + position: absolute; + transform: translate(-50%, -50%); + display: flex; + top: 50%; + left: 50%; + } } .checkbox-container { diff --git a/console/src/component-themes.scss b/console/src/component-themes.scss index 88b4201422..68efa9d12b 100644 --- a/console/src/component-themes.scss +++ b/console/src/component-themes.scss @@ -65,6 +65,7 @@ @import 'src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss'; @import 'src/app/modules/policies/login-policy/factor-table/factor-table.component.scss'; @import 'src/app/modules/info-overlay/info-overlay.component.scss'; +@import 'src/app/modules/create-layout/create-layout.component.scss'; @import './styles/codemirror.scss'; @mixin component-themes($theme) { @@ -136,4 +137,5 @@ @include action-keys-theme($theme); @include codemirror-theme($theme); @include contact-theme($theme); + @include app-create-theme($theme); } From b3d878792193631e8b213a4b900a939d095aa575 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 11 Apr 2023 15:37:42 +0200 Subject: [PATCH 16/36] feat: add new api services (#5619) * feat: add new services * improve demos and comments * remove unused field * add comment to demo proto calls * Apply suggestions from code review Co-authored-by: Silvan --------- Co-authored-by: Silvan --- build/zitadel/generate-grpc.sh | 32 ++++++++ cmd/start/start.go | 32 +++++--- internal/api/api.go | 79 +++++++++++++++---- internal/api/grpc/admin/server.go | 4 +- internal/api/grpc/auth/server.go | 4 +- internal/api/grpc/management/server.go | 4 +- internal/api/grpc/server/gateway.go | 72 +++++++++++++++-- .../server/middleware/service_interceptor.go | 8 +- internal/api/grpc/server/server.go | 14 +++- internal/api/grpc/session/v2/server.go | 51 ++++++++++++ internal/api/grpc/session/v2/session.go | 18 +++++ internal/api/grpc/system/server.go | 4 +- internal/api/grpc/user/v2/server.go | 51 ++++++++++++ internal/api/grpc/user/v2/test.go | 55 +++++++++++++ proto/zitadel/session/v2alpha/session.proto | 12 +++ .../session/v2alpha/session_service.proto | 33 ++++++++ proto/zitadel/user/v2alpha/user.proto | 9 +++ proto/zitadel/user/v2alpha/user_service.proto | 78 ++++++++++++++++++ 18 files changed, 516 insertions(+), 44 deletions(-) create mode 100644 internal/api/grpc/session/v2/server.go create mode 100644 internal/api/grpc/session/v2/session.go create mode 100644 internal/api/grpc/user/v2/server.go create mode 100644 internal/api/grpc/user/v2/test.go create mode 100644 proto/zitadel/session/v2alpha/session.proto create mode 100644 proto/zitadel/session/v2alpha/session_service.proto create mode 100644 proto/zitadel/user/v2alpha/user.proto create mode 100644 proto/zitadel/user/v2alpha/user_service.proto diff --git a/build/zitadel/generate-grpc.sh b/build/zitadel/generate-grpc.sh index 93053eedb5..02d1fa86b1 100755 --- a/build/zitadel/generate-grpc.sh +++ b/build/zitadel/generate-grpc.sh @@ -93,4 +93,36 @@ protoc \ mv ${ZITADEL_PATH}/pkg/grpc/auth/zitadel/* ${ZITADEL_PATH}/pkg/grpc/auth rm -r ${ZITADEL_PATH}/pkg/grpc/auth/zitadel +protoc \ + -I=/proto/include \ + --grpc-gateway_out ${GOPATH}/src \ + --grpc-gateway_opt logtostderr=true \ + --grpc-gateway_opt allow_delete_body=true \ + --openapiv2_out ${OPENAPI_PATH} \ + --openapiv2_opt logtostderr=true \ + --openapiv2_opt allow_delete_body=true \ + --authoption_out=${GRPC_PATH}/user \ + --validate_out=lang=go:${GOPATH}/src \ + ${PROTO_PATH}/user/v2alpha/user_service.proto + +# authoptions are generated into the wrong folder +cp -r ${ZITADEL_PATH}/pkg/grpc/user/zitadel/* ${ZITADEL_PATH}/pkg/grpc +rm -r ${ZITADEL_PATH}/pkg/grpc/user/zitadel + +protoc \ + -I=/proto/include \ + --grpc-gateway_out ${GOPATH}/src \ + --grpc-gateway_opt logtostderr=true \ + --grpc-gateway_opt allow_delete_body=true \ + --openapiv2_out ${OPENAPI_PATH} \ + --openapiv2_opt logtostderr=true \ + --openapiv2_opt allow_delete_body=true \ + --authoption_out=${GRPC_PATH}/session \ + --validate_out=lang=go:${GOPATH}/src \ + ${PROTO_PATH}/session/v2alpha/session_service.proto + +# authoptions are generated into the wrong folder +cp -r ${ZITADEL_PATH}/pkg/grpc/session/zitadel/* ${ZITADEL_PATH}/pkg/grpc +rm -r ${ZITADEL_PATH}/pkg/grpc/session/zitadel + echo "done generating grpc" diff --git a/cmd/start/start.go b/cmd/start/start.go index 61d35540e5..f80fc6fef1 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -33,7 +33,9 @@ import ( "github.com/zitadel/zitadel/internal/api/grpc/admin" "github.com/zitadel/zitadel/internal/api/grpc/auth" "github.com/zitadel/zitadel/internal/api/grpc/management" + "github.com/zitadel/zitadel/internal/api/grpc/session/v2" "github.com/zitadel/zitadel/internal/api/grpc/system" + "github.com/zitadel/zitadel/internal/api/grpc/user/v2" http_util "github.com/zitadel/zitadel/internal/api/http" "github.com/zitadel/zitadel/internal/api/http/middleware" "github.com/zitadel/zitadel/internal/api/oidc" @@ -223,7 +225,10 @@ func startAPIs( logging.Warn("access logs are currently in beta") } accessInterceptor := middleware.NewAccessInterceptor(accessSvc, config.Quotas.Access) - apis := api.New(config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, accessSvc) + apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, accessSvc) + if err != nil { + return fmt.Errorf("error creating api %w", err) + } authRepo, err := auth_es.Start(ctx, config.Auth, config.SystemDefaults, commands, queries, dbClient, eventstore, keys.OIDC, keys.User) if err != nil { return fmt.Errorf("error starting auth repo: %w", err) @@ -244,9 +249,15 @@ func startAPIs( if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil { return err } + if err := apis.RegisterService(ctx, user.CreateServer(commands, queries)); err != nil { + return err + } + if err := apis.RegisterService(ctx, session.CreateServer(commands, queries)); err != nil { + return err + } instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, login.IgnoreInstanceEndpoints...) assetsCache := middleware.AssetsCacheInterceptor(config.AssetStorage.Cache.MaxAge, config.AssetStorage.Cache.SharedMaxAge) - apis.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, middleware.CallDurationHandler, instanceInterceptor.Handler, assetsCache.Handler, accessInterceptor.Handle)) + apis.RegisterHandlerOnPrefix(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, middleware.CallDurationHandler, instanceInterceptor.Handler, assetsCache.Handler, accessInterceptor.Handle)) userAgentInterceptor, err := middleware.NewUserAgentHandler(config.UserAgentCookie, keys.UserAgentCookieKey, id.SonyFlakeGenerator(), config.ExternalSecure, login.EndpointResources) if err != nil { @@ -258,37 +269,34 @@ func startAPIs( if err != nil { return fmt.Errorf("unable to start openapi handler: %w", err) } - apis.RegisterHandler(openapi.HandlerPrefix, openAPIHandler) + apis.RegisterHandlerOnPrefix(openapi.HandlerPrefix, openAPIHandler) oidcProvider, err := oidc.NewProvider(config.OIDC, login.DefaultLoggedOutPath, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.OIDCKey, eventstore, dbClient, userAgentInterceptor, instanceInterceptor.Handler, accessInterceptor.Handle) if err != nil { return fmt.Errorf("unable to start oidc provider: %w", err) } + apis.RegisterHandlerPrefixes(oidcProvider.HttpHandler(), "/.well-known/openid-configuration", "/oidc/v1", "/oauth/v2") samlProvider, err := saml.NewProvider(config.SAML, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.SAML, eventstore, dbClient, instanceInterceptor.Handler, userAgentInterceptor, accessInterceptor.Handle) if err != nil { return fmt.Errorf("unable to start saml provider: %w", err) } - apis.RegisterHandler(saml.HandlerPrefix, samlProvider.HttpHandler()) + apis.RegisterHandlerOnPrefix(saml.HandlerPrefix, samlProvider.HttpHandler()) c, err := console.Start(config.Console, config.ExternalSecure, oidcProvider.IssuerFromRequest, middleware.CallDurationHandler, instanceInterceptor.Handler, accessInterceptor.Handle, config.CustomerPortal) if err != nil { return fmt.Errorf("unable to start console: %w", err) } - apis.RegisterHandler(console.HandlerPrefix, c) + apis.RegisterHandlerOnPrefix(console.HandlerPrefix, c) l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, console.HandlerPrefix+"/", op.AuthCallbackURL(oidcProvider), provider.AuthCallbackURL(samlProvider), config.ExternalSecure, userAgentInterceptor, op.NewIssuerInterceptor(oidcProvider.IssuerFromRequest).Handler, provider.NewIssuerInterceptor(samlProvider.IssuerFromRequest).Handler, instanceInterceptor.Handler, assetsCache.Handler, accessInterceptor.Handle, keys.User, keys.IDPConfig, keys.CSRFCookieKey) if err != nil { return fmt.Errorf("unable to start login: %w", err) } - apis.RegisterHandler(login.HandlerPrefix, l.Handler()) + apis.RegisterHandlerOnPrefix(login.HandlerPrefix, l.Handler()) - //handle oidc at last, to be able to handle the root - //we might want to change that in the future - //esp. if we want to have multiple well-known endpoints - //it might make sense to handle the discovery endpoint and oauth and oidc prefixes individually - //but this will require a change in the oidc lib - apis.RegisterHandler("", oidcProvider.HttpHandler()) + // handle grpc at last to be able to handle the root, because grpc and gateway require a lot of different prefixes + apis.RouteGRPC() return nil } diff --git a/internal/api/api.go b/internal/api/api.go index 78044e5b6b..3cb851e3d0 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -10,6 +10,8 @@ import ( "github.com/improbable-eng/grpc-web/go/grpcweb" "github.com/zitadel/logging" "google.golang.org/grpc" + "google.golang.org/grpc/health" + healthpb "google.golang.org/grpc/health/grpc_health_v1" internal_authz "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/grpc/server" @@ -26,17 +28,19 @@ type API struct { port uint16 grpcServer *grpc.Server verifier *internal_authz.TokenVerifier - health health + health healthCheck router *mux.Router http1HostName string + grpcGateway *server.Gateway + healthServer *health.Server } -type health interface { +type healthCheck interface { Health(ctx context.Context) error - Instance(ctx context.Context, shouldTriggerBulk bool) (*query.Instance, error) } func New( + ctx context.Context, port uint16, router *mux.Router, queries *query.Queries, @@ -44,7 +48,7 @@ func New( authZ internal_authz.Config, tlsConfig *tls.Config, http2HostName, http1HostName string, accessSvc *logstore.Service, -) *API { +) (_ *API, err error) { api := &API{ port: port, verifier: verifier, @@ -54,43 +58,89 @@ func New( } api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName, tlsConfig, accessSvc) - api.routeGRPC() + api.grpcGateway, err = server.CreateGateway(ctx, port, http1HostName) + if err != nil { + return nil, err + } + api.registerHealthServer() - api.RegisterHandler("/debug", api.healthHandler()) + api.RegisterHandlerOnPrefix("/debug", api.healthHandler()) api.router.Handle("/", http.RedirectHandler(login.HandlerPrefix, http.StatusFound)) - return api + return api, nil } -func (a *API) RegisterServer(ctx context.Context, grpcServer server.Server) error { +// RegisterServer registers a grpc service on the grpc server, +// creates a new grpc gateway and registers it as a separate http handler +// +// used for v1 api (system, admin, mgmt, auth) +func (a *API) RegisterServer(ctx context.Context, grpcServer server.WithGatewayPrefix) error { grpcServer.RegisterServer(a.grpcServer) - handler, prefix, err := server.CreateGateway(ctx, grpcServer, a.port, a.http1HostName) + handler, prefix, err := server.CreateGatewayWithPrefix(ctx, grpcServer, a.port, a.http1HostName) if err != nil { return err } - a.RegisterHandler(prefix, handler) + a.RegisterHandlerOnPrefix(prefix, handler) a.verifier.RegisterServer(grpcServer.AppName(), grpcServer.MethodPrefix(), grpcServer.AuthMethods()) + a.healthServer.SetServingStatus(grpcServer.MethodPrefix(), healthpb.HealthCheckResponse_SERVING) return nil } -func (a *API) RegisterHandler(prefix string, handler http.Handler) { +// RegisterService registers a grpc service on the grpc server, +// and its gateway on the gateway handler +// +// used for >= v2 api (e.g. user, session, ...) +func (a *API) RegisterService(ctx context.Context, grpcServer server.Server) error { + grpcServer.RegisterServer(a.grpcServer) + err := server.RegisterGateway(ctx, a.grpcGateway, grpcServer) + if err != nil { + return err + } + a.verifier.RegisterServer(grpcServer.AppName(), grpcServer.MethodPrefix(), grpcServer.AuthMethods()) + a.healthServer.SetServingStatus(grpcServer.MethodPrefix(), healthpb.HealthCheckResponse_SERVING) + return nil +} + +// RegisterHandlerOnPrefix registers a http handler on a path prefix +// the prefix will not be passed to the actual handler +func (a *API) RegisterHandlerOnPrefix(prefix string, handler http.Handler) { prefix = strings.TrimSuffix(prefix, "/") subRouter := a.router.PathPrefix(prefix).Name(prefix).Subrouter() subRouter.PathPrefix("").Handler(http.StripPrefix(prefix, handler)) } -func (a *API) routeGRPC() { +// RegisterHandlerPrefixes registers a http handler on a multiple path prefixes +// the prefix will remain when calling the actual handler +func (a *API) RegisterHandlerPrefixes(handler http.Handler, prefixes ...string) { + for _, prefix := range prefixes { + prefix = strings.TrimSuffix(prefix, "/") + subRouter := a.router.PathPrefix(prefix).Name(prefix).Subrouter() + subRouter.PathPrefix("").Handler(handler) + } +} + +func (a *API) registerHealthServer() { + healthServer := health.NewServer() + healthpb.RegisterHealthServer(a.grpcServer, healthServer) + a.healthServer = healthServer +} + +func (a *API) RouteGRPC() { http2Route := a.router. MatcherFunc(func(r *http.Request, _ *mux.RouteMatch) bool { return r.ProtoMajor == 2 }). - Subrouter() + Subrouter(). + Name("grpc") http2Route. Methods(http.MethodPost). Headers("Content-Type", "application/grpc"). Handler(a.grpcServer) a.routeGRPCWeb() + a.router.NewRoute(). + Handler(a.grpcGateway.Handler()). + Name("grpc-gateway") } func (a *API) routeGRPCWeb() { @@ -117,7 +167,8 @@ func (a *API) routeGRPCWeb() { func(r *http.Request, _ *mux.RouteMatch) bool { return grpcWebServer.IsGrpcWebRequest(r) || grpcWebServer.IsAcceptableGrpcCorsRequest(r) }). - Handler(grpcWebServer) + Handler(grpcWebServer). + Name("grpc-web") } func (a *API) healthHandler() http.Handler { diff --git a/internal/api/grpc/admin/server.go b/internal/api/grpc/admin/server.go index 42429cb124..267fad2f45 100644 --- a/internal/api/grpc/admin/server.go +++ b/internal/api/grpc/admin/server.go @@ -78,8 +78,8 @@ func (s *Server) AuthMethods() authz.MethodMapping { return admin.AdminService_AuthMethods } -func (s *Server) RegisterGateway() server.GatewayFunc { - return admin.RegisterAdminServiceHandlerFromEndpoint +func (s *Server) RegisterGateway() server.RegisterGatewayFunc { + return admin.RegisterAdminServiceHandler } func (s *Server) GatewayPathPrefix() string { diff --git a/internal/api/grpc/auth/server.go b/internal/api/grpc/auth/server.go index e30ab12892..ff7d7ae7b6 100644 --- a/internal/api/grpc/auth/server.go +++ b/internal/api/grpc/auth/server.go @@ -76,8 +76,8 @@ func (s *Server) AuthMethods() authz.MethodMapping { return auth.AuthService_AuthMethods } -func (s *Server) RegisterGateway() server.GatewayFunc { - return auth.RegisterAuthServiceHandlerFromEndpoint +func (s *Server) RegisterGateway() server.RegisterGatewayFunc { + return auth.RegisterAuthServiceHandler } func (s *Server) GatewayPathPrefix() string { diff --git a/internal/api/grpc/management/server.go b/internal/api/grpc/management/server.go index 30d177a5e8..732f44ea29 100644 --- a/internal/api/grpc/management/server.go +++ b/internal/api/grpc/management/server.go @@ -70,8 +70,8 @@ func (s *Server) AuthMethods() authz.MethodMapping { return management.ManagementService_AuthMethods } -func (s *Server) RegisterGateway() server.GatewayFunc { - return management.RegisterManagementServiceHandlerFromEndpoint +func (s *Server) RegisterGateway() server.RegisterGatewayFunc { + return management.RegisterManagementServiceHandler } func (s *Server) GatewayPathPrefix() string { diff --git a/internal/api/grpc/server/gateway.go b/internal/api/grpc/server/gateway.go index 6c3b2fbdea..7fae522d95 100644 --- a/internal/api/grpc/server/gateway.go +++ b/internal/api/grpc/server/gateway.go @@ -7,8 +7,10 @@ import ( "strings" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/zitadel/logging" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + healthpb "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/protobuf/encoding/protojson" client_middleware "github.com/zitadel/zitadel/internal/api/grpc/client/middleware" @@ -50,26 +52,84 @@ var ( ) ) -type Gateway interface { - RegisterGateway() GatewayFunc - GatewayPathPrefix() string +type Gateway struct { + mux *runtime.ServeMux + http1HostName string + connection *grpc.ClientConn } -type GatewayFunc func(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) error +func (g *Gateway) Handler() http.Handler { + return addInterceptors(g.mux, g.http1HostName) +} -func CreateGateway(ctx context.Context, g Gateway, port uint16, http1HostName string) (http.Handler, string, error) { +type RegisterGatewayFunc func(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error + +func CreateGatewayWithPrefix(ctx context.Context, g WithGatewayPrefix, port uint16, http1HostName string) (http.Handler, string, error) { runtimeMux := runtime.NewServeMux(serveMuxOptions...) opts := []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor(client_middleware.DefaultTracingClient()), } - err := g.RegisterGateway()(ctx, runtimeMux, fmt.Sprintf("localhost:%d", port), opts) + connection, err := dial(ctx, port, opts) + if err != nil { + return nil, "", err + } + err = g.RegisterGateway()(ctx, runtimeMux, connection) if err != nil { return nil, "", fmt.Errorf("failed to register grpc gateway: %w", err) } return addInterceptors(runtimeMux, http1HostName), g.GatewayPathPrefix(), nil } +func CreateGateway(ctx context.Context, port uint16, http1HostName string) (*Gateway, error) { + connection, err := dial(ctx, + port, + []grpc.DialOption{ + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithUnaryInterceptor(client_middleware.DefaultTracingClient()), + }) + if err != nil { + return nil, err + } + runtimeMux := runtime.NewServeMux(append(serveMuxOptions, runtime.WithHealthzEndpoint(healthpb.NewHealthClient(connection)))...) + return &Gateway{ + mux: runtimeMux, + http1HostName: http1HostName, + connection: connection, + }, nil +} + +func RegisterGateway(ctx context.Context, gateway *Gateway, server Server) error { + err := server.RegisterGateway()(ctx, gateway.mux, gateway.connection) + if err != nil { + return fmt.Errorf("failed to register grpc gateway: %w", err) + } + return nil +} + +func dial(ctx context.Context, port uint16, opts []grpc.DialOption) (*grpc.ClientConn, error) { + endpoint := fmt.Sprintf("localhost:%d", port) + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + logging.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + logging.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + return conn, nil +} + func addInterceptors(handler http.Handler, http1HostName string) http.Handler { handler = http_mw.CallDurationHandler(handler) handler = http1Host(handler, http1HostName) diff --git a/internal/api/grpc/server/middleware/service_interceptor.go b/internal/api/grpc/server/middleware/service_interceptor.go index 99291e936b..a32fcfa989 100644 --- a/internal/api/grpc/server/middleware/service_interceptor.go +++ b/internal/api/grpc/server/middleware/service_interceptor.go @@ -3,14 +3,18 @@ package middleware import ( "context" + "google.golang.org/grpc" + "github.com/zitadel/zitadel/internal/api/service" _ "github.com/zitadel/zitadel/internal/statik" - "google.golang.org/grpc" ) func ServiceHandler() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { - namer := info.Server.(interface{ AppName() string }) + namer, ok := info.Server.(interface{ AppName() string }) + if !ok { + return handler(ctx, req) + } ctx = service.WithService(ctx, namer.AppName()) return handler(ctx, req) } diff --git a/internal/api/grpc/server/server.go b/internal/api/grpc/server/server.go index 86fb625928..71eef0afa6 100644 --- a/internal/api/grpc/server/server.go +++ b/internal/api/grpc/server/server.go @@ -2,9 +2,11 @@ package server import ( "crypto/tls" + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" "google.golang.org/grpc" "google.golang.org/grpc/credentials" + healthpb "google.golang.org/grpc/health/grpc_health_v1" "github.com/zitadel/zitadel/internal/api/authz" grpc_api "github.com/zitadel/zitadel/internal/api/grpc" @@ -16,13 +18,21 @@ import ( ) type Server interface { - Gateway RegisterServer(*grpc.Server) + RegisterGateway() RegisterGatewayFunc AppName() string MethodPrefix() string AuthMethods() authz.MethodMapping } +// WithGatewayPrefix extends the server interface with a prefix for the grpc gateway +// +// it's used for the System, Admin, Mgmt and Auth API +type WithGatewayPrefix interface { + Server + GatewayPathPrefix() string +} + func CreateServer( verifier *authz.TokenVerifier, authConfig authz.Config, @@ -40,7 +50,7 @@ func CreateServer( middleware.MetricsHandler(metricTypes, grpc_api.Probes...), middleware.NoCacheInterceptor(), middleware.ErrorHandler(), - middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_MethodPrefix), + middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_MethodPrefix, healthpb.Health_ServiceDesc.ServiceName), middleware.AccessStorageInterceptor(accessSvc), middleware.AuthorizationInterceptor(verifier, authConfig), middleware.TranslationHandler(), diff --git a/internal/api/grpc/session/v2/server.go b/internal/api/grpc/session/v2/server.go new file mode 100644 index 0000000000..22919fdb7d --- /dev/null +++ b/internal/api/grpc/session/v2/server.go @@ -0,0 +1,51 @@ +package session + +import ( + "google.golang.org/grpc" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/api/grpc/server" + "github.com/zitadel/zitadel/internal/command" + "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/pkg/grpc/session/v2alpha" +) + +var _ session.SessionServiceServer = (*Server)(nil) + +type Server struct { + session.UnimplementedSessionServiceServer + command *command.Commands + query *query.Queries +} + +type Config struct{} + +func CreateServer( + command *command.Commands, + query *query.Queries, +) *Server { + return &Server{ + command: command, + query: query, + } +} + +func (s *Server) RegisterServer(grpcServer *grpc.Server) { + session.RegisterSessionServiceServer(grpcServer, s) +} + +func (s *Server) AppName() string { + return session.SessionService_ServiceDesc.ServiceName +} + +func (s *Server) MethodPrefix() string { + return session.SessionService_ServiceDesc.ServiceName +} + +func (s *Server) AuthMethods() authz.MethodMapping { + return session.SessionService_AuthMethods +} + +func (s *Server) RegisterGateway() server.RegisterGatewayFunc { + return session.RegisterSessionServiceHandler +} diff --git a/internal/api/grpc/session/v2/session.go b/internal/api/grpc/session/v2/session.go new file mode 100644 index 0000000000..3e90e76a99 --- /dev/null +++ b/internal/api/grpc/session/v2/session.go @@ -0,0 +1,18 @@ +package session + +import ( + "context" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/pkg/grpc/session/v2alpha" + "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" +) + +func (s *Server) GetSession(ctx context.Context, req *session.GetSessionRequest) (*session.GetSessionResponse, error) { + return &session.GetSessionResponse{ + Session: &session.Session{ + Id: req.Id, + User: &user.User{Id: authz.GetCtxData(ctx).UserID}, + }, + }, nil +} diff --git a/internal/api/grpc/system/server.go b/internal/api/grpc/system/server.go index 2d9745181a..a954cfce43 100644 --- a/internal/api/grpc/system/server.go +++ b/internal/api/grpc/system/server.go @@ -67,8 +67,8 @@ func (s *Server) AuthMethods() authz.MethodMapping { return system.SystemService_AuthMethods } -func (s *Server) RegisterGateway() server.GatewayFunc { - return system.RegisterSystemServiceHandlerFromEndpoint +func (s *Server) RegisterGateway() server.RegisterGatewayFunc { + return system.RegisterSystemServiceHandler } func (s *Server) GatewayPathPrefix() string { diff --git a/internal/api/grpc/user/v2/server.go b/internal/api/grpc/user/v2/server.go new file mode 100644 index 0000000000..6a7a96d31d --- /dev/null +++ b/internal/api/grpc/user/v2/server.go @@ -0,0 +1,51 @@ +package user + +import ( + "google.golang.org/grpc" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/api/grpc/server" + "github.com/zitadel/zitadel/internal/command" + "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" +) + +var _ user.UserServiceServer = (*Server)(nil) + +type Server struct { + user.UnimplementedUserServiceServer + command *command.Commands + query *query.Queries +} + +type Config struct{} + +func CreateServer( + command *command.Commands, + query *query.Queries, +) *Server { + return &Server{ + command: command, + query: query, + } +} + +func (s *Server) RegisterServer(grpcServer *grpc.Server) { + user.RegisterUserServiceServer(grpcServer, s) +} + +func (s *Server) AppName() string { + return user.UserService_ServiceDesc.ServiceName +} + +func (s *Server) MethodPrefix() string { + return user.UserService_ServiceDesc.ServiceName +} + +func (s *Server) AuthMethods() authz.MethodMapping { + return user.UserService_AuthMethods +} + +func (s *Server) RegisterGateway() server.RegisterGatewayFunc { + return user.RegisterUserServiceHandler +} diff --git a/internal/api/grpc/user/v2/test.go b/internal/api/grpc/user/v2/test.go new file mode 100644 index 0000000000..8b5bc38654 --- /dev/null +++ b/internal/api/grpc/user/v2/test.go @@ -0,0 +1,55 @@ +package user + +import ( + "context" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" +) + +func (s *Server) TestGet(ctx context.Context, req *user.TestGetRequest) (*user.TestGetResponse, error) { + return &user.TestGetResponse{ + Ctx: req.Ctx.String(), + }, nil +} + +func (s *Server) TestPost(ctx context.Context, req *user.TestPostRequest) (*user.TestPostResponse, error) { + return &user.TestPostResponse{ + Ctx: req.Ctx.String(), + }, nil +} + +func (s *Server) TestAuth(ctx context.Context, req *user.TestAuthRequest) (*user.TestAuthResponse, error) { + reqCtx, err := authDemo(ctx, req.Ctx) + if err != nil { + return nil, err + } + return &user.TestAuthResponse{ + User: &user.User{Id: authz.GetCtxData(ctx).UserID}, + Ctx: reqCtx, + }, nil +} + +func authDemo(ctx context.Context, reqCtx *user.Context) (*user.Context, error) { + ro := authz.GetCtxData(ctx).ResourceOwner + if reqCtx == nil { + return &user.Context{Ctx: &user.Context_OrgId{OrgId: ro}}, nil + } + switch c := reqCtx.Ctx.(type) { + case *user.Context_OrgId: + if c.OrgId == ro { + return reqCtx, nil + } + return nil, errors.ThrowPermissionDenied(nil, "USER-dg4g", "Errors.User.NotAllowedOrg") + case *user.Context_OrgDomain: + if c.OrgDomain == "forbidden.com" { + return nil, errors.ThrowPermissionDenied(nil, "USER-SDg4g", "Errors.User.NotAllowedOrg") + } + return reqCtx, nil + case *user.Context_Instance: + return reqCtx, nil + default: + return reqCtx, nil + } +} diff --git a/proto/zitadel/session/v2alpha/session.proto b/proto/zitadel/session/v2alpha/session.proto new file mode 100644 index 0000000000..8d4c168b17 --- /dev/null +++ b/proto/zitadel/session/v2alpha/session.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package zitadel.session.v2alpha; + +import "zitadel/user/v2alpha/user.proto"; + +option go_package = "github.com/zitadel/zitadel/pkg/grpc/session/v2alpha;session"; + +message Session { + string id = 1; + zitadel.user.v2alpha.User user = 2; +} diff --git a/proto/zitadel/session/v2alpha/session_service.proto b/proto/zitadel/session/v2alpha/session_service.proto new file mode 100644 index 0000000000..aefd47e21b --- /dev/null +++ b/proto/zitadel/session/v2alpha/session_service.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; + +package zitadel.session.v2alpha; + +import "zitadel/options.proto"; +import "zitadel/session/v2alpha/session.proto"; +import "google/api/annotations.proto"; +import "validate/validate.proto"; + +option go_package = "github.com/zitadel/zitadel/pkg/grpc/session/v2alpha;session"; + +service SessionService { + + // GetSession is to demonstrate an authenticated request, where the authenticated user (usage of another grpc package) is returned + // + // this request is subject to change and currently used for demonstration only + rpc GetSession (GetSessionRequest) returns (GetSessionResponse) { + option (google.api.http) = { + get: "/v2alpha/sessions/{id}" + }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated" + }; + } +} + +message GetSessionRequest{ + string id = 1; +} +message GetSessionResponse{ + Session session = 1; +} diff --git a/proto/zitadel/user/v2alpha/user.proto b/proto/zitadel/user/v2alpha/user.proto new file mode 100644 index 0000000000..9b8426f517 --- /dev/null +++ b/proto/zitadel/user/v2alpha/user.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package zitadel.user.v2alpha; + +option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha;user"; + +message User { + string id = 1; +} diff --git a/proto/zitadel/user/v2alpha/user_service.proto b/proto/zitadel/user/v2alpha/user_service.proto new file mode 100644 index 0000000000..f16bb8ecfa --- /dev/null +++ b/proto/zitadel/user/v2alpha/user_service.proto @@ -0,0 +1,78 @@ +syntax = "proto3"; + +package zitadel.user.v2alpha; + +import "zitadel/options.proto"; +import "zitadel/user/v2alpha/user.proto"; +import "google/api/annotations.proto"; +import "validate/validate.proto"; + +option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha;user"; + +service UserService { + + // TestGet simply demonstrates how the context (org, instance) could be handled in a GET request // + // + // this request is subject to change and currently used for demonstration only + rpc TestGet (TestGetRequest) returns (TestGetResponse) { + option (google.api.http) = { + get: "/v2alpha/users/test" + }; + } + + // TestPOST simply demonstrates how the context (org, instance) could be handled in a POST request + // + // this request is subject to change and currently used for demonstration only + rpc TestPost (TestPostRequest) returns (TestPostResponse) { + option (google.api.http) = { + post: "/v2alpha/users/test" + body: "*" + }; + } + + // TestAuth demonstrates how the context (org, instance) could be handled in combination of the authorized context + // + // this request is subject to change and currently used for demonstration only + rpc TestAuth (TestAuthRequest) returns (TestAuthResponse) { + option (google.api.http) = { + get: "/v2alpha/users/test_auth" + }; + + option (zitadel.v1.auth_option) = { + permission: "authenticated" + }; + } +} + +message TestGetRequest{ + Context ctx = 1; +} + +message TestGetResponse{ + string ctx = 1; +} + +message TestPostRequest{ + Context ctx = 1; +} + +message TestPostResponse{ + string ctx = 1; +} + +message TestAuthRequest{ + Context ctx = 1; +} + +message TestAuthResponse{ + User user = 1; + Context ctx = 2; +} + +message Context { + oneof ctx { + bool instance = 1; + string org_id = 2; + string org_domain = 3; + } +} From 8bf36301ed6134c4a1c3f90522e7e44c0ff3e660 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 11 Apr 2023 17:07:32 +0200 Subject: [PATCH 17/36] feat: allow skip of success page for native apps (#5627) add possibility to return to callback directly after login without rendering the successful login page --- .../apps/app-create/app-create.component.html | 2 +- .../apps/app-detail/app-detail.component.html | 17 +- .../apps/app-detail/app-detail.component.ts | 19 +- console/src/assets/i18n/de.json | 2 + console/src/assets/i18n/en.json | 2 + console/src/assets/i18n/fr.json | 2 + console/src/assets/i18n/it.json | 2 + console/src/assets/i18n/ja.json | 2 + console/src/assets/i18n/pl.json | 2 + console/src/assets/i18n/zh.json | 2 + .../guides/manage/console/applications.mdx | 1 + internal/api/grpc/admin/export.go | 1 + .../project_application_converter.go | 2 + internal/api/grpc/project/application.go | 1 + .../api/ui/login/login_success_handler.go | 32 +- .../static/resources/scripts/login_success.js | 7 - .../login/static/templates/login_success.html | 2 +- .../eventsourcing/eventstore/auth_request.go | 2 +- internal/command/instance_domain_test.go | 4 +- internal/command/project_application_oidc.go | 38 +- .../command/project_application_oidc_model.go | 10 + .../command/project_application_oidc_test.go | 23 +- internal/command/project_converter.go | 9 +- internal/command/user_human_otp.go | 4 +- internal/domain/application_oidc.go | 1 + internal/query/app.go | 75 +- internal/query/app_test.go | 661 +++++++++++------- internal/query/projection/app.go | 8 +- internal/query/projection/app_test.go | 70 +- internal/repository/project/oidc_config.go | 13 +- proto/zitadel/app.proto | 5 + proto/zitadel/management.proto | 10 + 32 files changed, 641 insertions(+), 390 deletions(-) diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.html b/console/src/app/pages/projects/apps/app-create/app-create.component.html index d11e908a16..7bd4729e67 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.html +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.html @@ -306,7 +306,7 @@ - {{ 'APP.API.AUTHMETHOD.' + authMethodType?.value | translate }} + {{ 'APP.API.AUTHMETHOD.' + apiAppRequest.toObject().authMethodType | translate }}
diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index bc18c9bcfc..f910e129d3 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -331,11 +331,24 @@
+ + {{ 'APP.OIDC.SKIPNATIVEAPPSUCCESSPAGE' | translate }} + + {{ 'APP.OIDC.SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION' | translate }} + + | null { + return this.oidcForm.get('devMode') as FormControl; + } + + public get skipNativeAppSuccessPage(): FormControl | null { + return this.oidcForm.get('skipNativeAppSuccessPage') as FormControl; } public get accessTokenType(): AbstractControl | null { diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index b550168418..5eb01ed24b 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1937,6 +1937,8 @@ "REGENERATESECRET": "Client Secret neu generieren", "DEVMODE": "Entwicklermodus", "DEVMODEDESC": "Bei eingeschaltetem Entwicklermodus werden die Weiterleitungs-URIs im OIDC-Flow nicht validiert.", + "SKIPNATIVEAPPSUCCESSPAGE": "Login Erfolgseite überspringen", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Erfolgseite nach dem Login für diese Native Applikation überspringen.", "REDIRECT": "Weiterleitungs-URIs", "REDIRECTSECTION": "Weiterleitungs-URIs", "POSTLOGOUTREDIRECT": "URIs für Post-Log-out", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index df86a19aaa..c9864c9222 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1930,6 +1930,8 @@ "REGENERATESECRET": "Regenerate Client Secret", "DEVMODE": "Development Mode", "DEVMODEDESC": "Beware: With development mode enabled redirect URIs will not be validated.", + "SKIPNATIVEAPPSUCCESSPAGE": "Skip Login Success Page", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Skip the success page after a login for this native app.", "REDIRECT": "Redirect URIs", "REDIRECTSECTION": "Redirect URIs", "POSTLOGOUTREDIRECT": "Post Logout URIs", diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 9443fb891a..2cd44a1b67 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -1938,6 +1938,8 @@ "REGENERATESECRET": "Régénérer le secret du client", "DEVMODE": "Mode développement", "DEVMODEDESC": "Attention", + "SKIPNATIVEAPPSUCCESSPAGE": "Sauter la page de succès de connexion", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Sauter la page de succès après la connexion pour cette application native", "REDIRECT": "Rediriger les URI", "REDIRECTSECTION": "URI de redirection", "POSTLOGOUTREDIRECT": "URIs de post-déconnexion", diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 0e9e5b41cb..0e42e01a64 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -1939,6 +1939,8 @@ "REGENERATESECRET": "Rigenera il Client Secret", "DEVMODE": "Modalit\u00e0 di sviluppo (DEV Mode)", "DEVMODEDESC": "Attenzione: Con la modalit\u00e0 di sviluppo abilitata, gli URI di reindirizzamento non saranno convalidati.", + "SKIPNATIVEAPPSUCCESSPAGE": "Salta la pagina di successo dopo il login", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Salta la pagina di successo dopo il login per questa applicazione nativa", "REDIRECT": "URI per il reindrizzamento", "REDIRECTSECTION": "Reindirizzamento", "POSTLOGOUTREDIRECT": "URI post logout", diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 3c8d8fdfa9..f6a262a3ac 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -1929,6 +1929,8 @@ "REGENERATESECRET": "クライアントシークレットを再生成する", "DEVMODE": "開発モード", "DEVMODEDESC": "注意:開発モードを有効にすると、URIが認証されません。", + "SKIPNATIVEAPPSUCCESSPAGE": "ログイン後に成功ページをスキップする", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "このネイティブアプリのログイン後に成功ページをスキップする", "REDIRECT": "リダイレクトURI", "REDIRECTSECTION": "リダイレクトURI", "POSTLOGOUTREDIRECT": "ログアウトURI", diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 0c9761c766..4b5bedde76 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -1938,6 +1938,8 @@ "REGENERATESECRET": "Odtwórz sekret klienta", "DEVMODE": "Tryb rozwoju", "DEVMODEDESC": "Uwaga: przy włączonym trybie rozwoju adresy URI przekierowania nie będą sprawdzane.", + "SKIPNATIVEAPPSUCCESSPAGE": "Pomiń stronę sukcesu po zalogowaniu", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Pomiń stronę sukcesu po zalogowaniu dla tej Natywny aplikację", "REDIRECT": "Adresy URI przekierowania", "REDIRECTSECTION": "Adresy URI przekierowania", "POSTLOGOUTREDIRECT": "Adresy URI po wylogowaniu", diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index c7bd3eb226..9fed206931 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -1937,6 +1937,8 @@ "REGENERATESECRET": "重新生成客户端密钥", "DEVMODE": "开发模式", "DEVMODEDESC": "注意:启用开发模式的重定向 URI 将不会被验证。", + "SKIPNATIVEAPPSUCCESSPAGE": "登录后跳过成功页面", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "登录后跳过本机应用的成功页面", "REDIRECT": "重定向 URLs", "REDIRECTSECTION": "重定向 URLs", "POSTLOGOUTREDIRECT": "退出登录重定向 URLs", diff --git a/docs/docs/guides/manage/console/applications.mdx b/docs/docs/guides/manage/console/applications.mdx index 0266c5ae4d..8cad1c9eb7 100644 --- a/docs/docs/guides/manage/console/applications.mdx +++ b/docs/docs/guides/manage/console/applications.mdx @@ -142,6 +142,7 @@ On the bottom you can optionally set a **ClockSkew** time which is added to the Like on creation, you can modify you redirect settings here. Note that for local development you most likely have to enable development mode, as redirects to http:// are otherwise blocked. +On Native Apps you can also skip the Login Success Page. Redirect URIs {{end}} -{{template "main-bottom" .}} \ No newline at end of file +{{template "main-bottom" .}} diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 6a879a3e8e..34a30a6abf 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -1223,7 +1223,7 @@ func (repo *AuthRequestRepo) hasSucceededPage(ctx context.Context, request *doma if err != nil { return false, err } - return app.OIDCConfig.AppType == domain.OIDCApplicationTypeNative, nil + return app.OIDCConfig.AppType == domain.OIDCApplicationTypeNative && !app.OIDCConfig.SkipNativeAppSuccessPage, nil } func (repo *AuthRequestRepo) getDomainPolicy(ctx context.Context, orgID string) (*query.DomainPolicy, error) { diff --git a/internal/command/instance_domain_test.go b/internal/command/instance_domain_test.go index 6cd03a28ef..90a692519f 100644 --- a/internal/command/instance_domain_test.go +++ b/internal/command/instance_domain_test.go @@ -161,7 +161,9 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + false, + ), ), ), expectPush( diff --git a/internal/command/project_application_oidc.go b/internal/command/project_application_oidc.go index 99eec03d41..44efd279a1 100644 --- a/internal/command/project_application_oidc.go +++ b/internal/command/project_application_oidc.go @@ -19,20 +19,21 @@ import ( type addOIDCApp struct { AddApp - Version domain.OIDCVersion - RedirectUris []string - ResponseTypes []domain.OIDCResponseType - GrantTypes []domain.OIDCGrantType - ApplicationType domain.OIDCApplicationType - AuthMethodType domain.OIDCAuthMethodType - PostLogoutRedirectUris []string - DevMode bool - AccessTokenType domain.OIDCTokenType - AccessTokenRoleAssertion bool - IDTokenRoleAssertion bool - IDTokenUserinfoAssertion bool - ClockSkew time.Duration - AdditionalOrigins []string + Version domain.OIDCVersion + RedirectUris []string + ResponseTypes []domain.OIDCResponseType + GrantTypes []domain.OIDCGrantType + ApplicationType domain.OIDCApplicationType + AuthMethodType domain.OIDCAuthMethodType + PostLogoutRedirectUris []string + DevMode bool + AccessTokenType domain.OIDCTokenType + AccessTokenRoleAssertion bool + IDTokenRoleAssertion bool + IDTokenUserinfoAssertion bool + ClockSkew time.Duration + AdditionalOrigins []string + SkipSuccessPageForNativeApp bool ClientID string ClientSecret *crypto.CryptoValue @@ -109,6 +110,7 @@ func (c *Commands) AddOIDCAppCommand(app *addOIDCApp, clientSecretAlg crypto.Has app.IDTokenUserinfoAssertion, app.ClockSkew, app.AdditionalOrigins, + app.SkipSuccessPageForNativeApp, ), }, nil }, nil @@ -191,7 +193,9 @@ func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain oidcApp.IDTokenRoleAssertion, oidcApp.IDTokenUserinfoAssertion, oidcApp.ClockSkew, - oidcApp.AdditionalOrigins)) + oidcApp.AdditionalOrigins, + oidcApp.SkipNativeAppSuccessPage, + )) addedApplication.AppID = oidcApp.AppID pushedEvents, err := c.eventstore.Push(ctx, events...) @@ -241,7 +245,9 @@ func (c *Commands) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCA oidc.IDTokenRoleAssertion, oidc.IDTokenUserinfoAssertion, oidc.ClockSkew, - oidc.AdditionalOrigins) + oidc.AdditionalOrigins, + oidc.SkipNativeAppSuccessPage, + ) if err != nil { return nil, err } diff --git a/internal/command/project_application_oidc_model.go b/internal/command/project_application_oidc_model.go index d88c79a4bd..7b70d89554 100644 --- a/internal/command/project_application_oidc_model.go +++ b/internal/command/project_application_oidc_model.go @@ -35,6 +35,7 @@ type OIDCApplicationWriteModel struct { ClockSkew time.Duration State domain.AppState AdditionalOrigins []string + SkipNativeAppSuccessPage bool oidc bool } @@ -156,6 +157,7 @@ func (wm *OIDCApplicationWriteModel) appendAddOIDCEvent(e *project.OIDCConfigAdd wm.IDTokenUserinfoAssertion = e.IDTokenUserinfoAssertion wm.ClockSkew = e.ClockSkew wm.AdditionalOrigins = e.AdditionalOrigins + wm.SkipNativeAppSuccessPage = e.SkipNativeAppSuccessPage } func (wm *OIDCApplicationWriteModel) appendChangeOIDCEvent(e *project.OIDCConfigChangedEvent) { @@ -201,6 +203,9 @@ func (wm *OIDCApplicationWriteModel) appendChangeOIDCEvent(e *project.OIDCConfig if e.AdditionalOrigins != nil { wm.AdditionalOrigins = *e.AdditionalOrigins } + if e.SkipNativeAppSuccessPage != nil { + wm.SkipNativeAppSuccessPage = *e.SkipNativeAppSuccessPage + } } func (wm *OIDCApplicationWriteModel) Query() *eventstore.SearchQueryBuilder { @@ -240,6 +245,7 @@ func (wm *OIDCApplicationWriteModel) NewChangedEvent( idTokenUserinfoAssertion bool, clockSkew time.Duration, additionalOrigins []string, + skipNativeAppSuccessPage bool, ) (*project.OIDCConfigChangedEvent, bool, error) { changes := make([]project.OIDCConfigChanges, 0) var err error @@ -286,6 +292,10 @@ func (wm *OIDCApplicationWriteModel) NewChangedEvent( if !reflect.DeepEqual(wm.AdditionalOrigins, additionalOrigins) { changes = append(changes, project.ChangeAdditionalOrigins(additionalOrigins)) } + if wm.SkipNativeAppSuccessPage != skipNativeAppSuccessPage { + changes = append(changes, project.ChangeSkipNativeAppSuccessPage(skipNativeAppSuccessPage)) + } + if len(changes) == 0 { return nil, false, nil } diff --git a/internal/command/project_application_oidc_test.go b/internal/command/project_application_oidc_test.go index d8ed4bb80a..86b3c6e04e 100644 --- a/internal/command/project_application_oidc_test.go +++ b/internal/command/project_application_oidc_test.go @@ -169,6 +169,7 @@ func TestAddOIDCApp(t *testing.T) { false, 0, nil, + false, ), }, }, @@ -325,7 +326,9 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + true, + ), ), }, uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")), @@ -354,6 +357,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: true, ClockSkew: time.Second * 1, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, }, resourceOwner: "org1", secretGenerator: GetMockSecretGenerator(t), @@ -382,6 +386,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: true, ClockSkew: time.Second * 1, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, State: domain.AppStateActive, Compliance: &domain.Compliance{}, }, @@ -558,7 +563,9 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + true, + ), ), ), ), @@ -585,6 +592,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: true, ClockSkew: time.Second * 1, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, }, resourceOwner: "org1", }, @@ -629,7 +637,9 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + true, + ), ), ), expectPush( @@ -666,6 +676,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: false, ClockSkew: time.Second * 2, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, }, resourceOwner: "org1", }, @@ -692,6 +703,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) { IDTokenUserinfoAssertion: false, ClockSkew: time.Second * 2, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: true, Compliance: &domain.Compliance{}, State: domain.AppStateActive, }, @@ -826,7 +838,9 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) { true, true, time.Second*1, - []string{"https://sub.test.ch"}), + []string{"https://sub.test.ch"}, + false, + ), ), ), expectPush( @@ -877,6 +891,7 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) { IDTokenUserinfoAssertion: true, ClockSkew: time.Second * 1, AdditionalOrigins: []string{"https://sub.test.ch"}, + SkipNativeAppSuccessPage: false, State: domain.AppStateActive, }, }, diff --git a/internal/command/project_converter.go b/internal/command/project_converter.go index 02a1b9eac7..35679d8a14 100644 --- a/internal/command/project_converter.go +++ b/internal/command/project_converter.go @@ -25,14 +25,6 @@ func projectGrantWriteModelToProjectGrant(writeModel *ProjectGrantWriteModel) *d } } -func applicationWriteModelToApplication(writeModel *ApplicationWriteModel) domain.Application { - return &domain.ChangeApp{ - AppID: writeModel.AppID, - AppName: writeModel.Name, - State: writeModel.State, - } -} - func oidcWriteModelToOIDCConfig(writeModel *OIDCApplicationWriteModel) *domain.OIDCApp { return &domain.OIDCApp{ ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel), @@ -54,6 +46,7 @@ func oidcWriteModelToOIDCConfig(writeModel *OIDCApplicationWriteModel) *domain.O IDTokenUserinfoAssertion: writeModel.IDTokenUserinfoAssertion, ClockSkew: writeModel.ClockSkew, AdditionalOrigins: writeModel.AdditionalOrigins, + SkipNativeAppSuccessPage: writeModel.SkipNativeAppSuccessPage, } } diff --git a/internal/command/user_human_otp.go b/internal/command/user_human_otp.go index 3ee507f735..02dbdae552 100644 --- a/internal/command/user_human_otp.go +++ b/internal/command/user_human_otp.go @@ -3,9 +3,9 @@ package command import ( "context" - "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore/v1/models" diff --git a/internal/domain/application_oidc.go b/internal/domain/application_oidc.go index 808052c050..ad08b18d7a 100644 --- a/internal/domain/application_oidc.go +++ b/internal/domain/application_oidc.go @@ -45,6 +45,7 @@ type OIDCApp struct { IDTokenUserinfoAssertion bool ClockSkew time.Duration AdditionalOrigins []string + SkipNativeAppSuccessPage bool State AppState } diff --git a/internal/query/app.go b/internal/query/app.go index e08d25e096..6b40f712e3 100644 --- a/internal/query/app.go +++ b/internal/query/app.go @@ -40,23 +40,24 @@ type App struct { } type OIDCApp struct { - RedirectURIs database.StringArray - ResponseTypes database.EnumArray[domain.OIDCResponseType] - GrantTypes database.EnumArray[domain.OIDCGrantType] - AppType domain.OIDCApplicationType - ClientID string - AuthMethodType domain.OIDCAuthMethodType - PostLogoutRedirectURIs database.StringArray - Version domain.OIDCVersion - ComplianceProblems database.StringArray - IsDevMode bool - AccessTokenType domain.OIDCTokenType - AssertAccessTokenRole bool - AssertIDTokenRole bool - AssertIDTokenUserinfo bool - ClockSkew time.Duration - AdditionalOrigins database.StringArray - AllowedOrigins database.StringArray + RedirectURIs database.StringArray + ResponseTypes database.EnumArray[domain.OIDCResponseType] + GrantTypes database.EnumArray[domain.OIDCGrantType] + AppType domain.OIDCApplicationType + ClientID string + AuthMethodType domain.OIDCAuthMethodType + PostLogoutRedirectURIs database.StringArray + Version domain.OIDCVersion + ComplianceProblems database.StringArray + IsDevMode bool + AccessTokenType domain.OIDCTokenType + AssertAccessTokenRole bool + AssertIDTokenRole bool + AssertIDTokenUserinfo bool + ClockSkew time.Duration + AdditionalOrigins database.StringArray + AllowedOrigins database.StringArray + SkipNativeAppSuccessPage bool } type SAMLApp struct { @@ -241,6 +242,10 @@ var ( name: projection.AppOIDCConfigColumnAdditionalOrigins, table: appOIDCConfigsTable, } + AppOIDCConfigColumnSkipNativeAppSuccessPage = Column{ + name: projection.AppOIDCConfigColumnSkipNativeAppSuccessPage, + table: appOIDCConfigsTable, + } ) func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bool, projectID, appID string, withOwnerRemoved bool) (_ *App, err error) { @@ -535,6 +540,7 @@ func prepareAppQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, AppOIDCConfigColumnIDTokenUserinfoAssertion.identifier(), AppOIDCConfigColumnClockSkew.identifier(), AppOIDCConfigColumnAdditionalOrigins.identifier(), + AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(), AppSAMLConfigColumnAppID.identifier(), AppSAMLConfigColumnEntityID.identifier(), @@ -583,6 +589,7 @@ func prepareAppQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, &oidcConfig.iDTokenUserinfoAssertion, &oidcConfig.clockSkew, &oidcConfig.additionalOrigins, + &oidcConfig.skipNativeAppSuccessPage, &samlConfig.appID, &samlConfig.entityID, @@ -703,6 +710,7 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder AppOIDCConfigColumnIDTokenUserinfoAssertion.identifier(), AppOIDCConfigColumnClockSkew.identifier(), AppOIDCConfigColumnAdditionalOrigins.identifier(), + AppOIDCConfigColumnSkipNativeAppSuccessPage.identifier(), AppSAMLConfigColumnAppID.identifier(), AppSAMLConfigColumnEntityID.identifier(), @@ -754,6 +762,7 @@ func prepareAppsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder &oidcConfig.iDTokenUserinfoAssertion, &oidcConfig.clockSkew, &oidcConfig.additionalOrigins, + &oidcConfig.skipNativeAppSuccessPage, &samlConfig.appID, &samlConfig.entityID, @@ -825,6 +834,7 @@ type sqlOIDCConfig struct { additionalOrigins database.StringArray responseTypes database.EnumArray[domain.OIDCResponseType] grantTypes database.EnumArray[domain.OIDCGrantType] + skipNativeAppSuccessPage sql.NullBool } func (c sqlOIDCConfig) set(app *App) { @@ -832,21 +842,22 @@ func (c sqlOIDCConfig) set(app *App) { return } app.OIDCConfig = &OIDCApp{ - Version: domain.OIDCVersion(c.version.Int32), - ClientID: c.clientID.String, - RedirectURIs: c.redirectUris, - AppType: domain.OIDCApplicationType(c.applicationType.Int16), - AuthMethodType: domain.OIDCAuthMethodType(c.authMethodType.Int16), - PostLogoutRedirectURIs: c.postLogoutRedirectUris, - IsDevMode: c.devMode.Bool, - AccessTokenType: domain.OIDCTokenType(c.accessTokenType.Int16), - AssertAccessTokenRole: c.accessTokenRoleAssertion.Bool, - AssertIDTokenRole: c.iDTokenRoleAssertion.Bool, - AssertIDTokenUserinfo: c.iDTokenUserinfoAssertion.Bool, - ClockSkew: time.Duration(c.clockSkew.Int64), - AdditionalOrigins: c.additionalOrigins, - ResponseTypes: c.responseTypes, - GrantTypes: c.grantTypes, + Version: domain.OIDCVersion(c.version.Int32), + ClientID: c.clientID.String, + RedirectURIs: c.redirectUris, + AppType: domain.OIDCApplicationType(c.applicationType.Int16), + AuthMethodType: domain.OIDCAuthMethodType(c.authMethodType.Int16), + PostLogoutRedirectURIs: c.postLogoutRedirectUris, + IsDevMode: c.devMode.Bool, + AccessTokenType: domain.OIDCTokenType(c.accessTokenType.Int16), + AssertAccessTokenRole: c.accessTokenRoleAssertion.Bool, + AssertIDTokenRole: c.iDTokenRoleAssertion.Bool, + AssertIDTokenUserinfo: c.iDTokenUserinfoAssertion.Bool, + ClockSkew: time.Duration(c.clockSkew.Int64), + AdditionalOrigins: c.additionalOrigins, + ResponseTypes: c.responseTypes, + GrantTypes: c.grantTypes, + SkipNativeAppSuccessPage: c.skipNativeAppSuccessPage.Bool, } compliance := domain.GetOIDCCompliance(app.OIDCConfig.Version, app.OIDCConfig.AppType, app.OIDCConfig.GrantTypes, app.OIDCConfig.ResponseTypes, app.OIDCConfig.AuthMethodType, app.OIDCConfig.RedirectURIs) app.OIDCConfig.ComplianceProblems = compliance.Problems diff --git a/internal/query/app_test.go b/internal/query/app_test.go index 3076a6ed0c..e75e4ce06f 100644 --- a/internal/query/app_test.go +++ b/internal/query/app_test.go @@ -15,96 +15,98 @@ import ( ) var ( - expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps4.id,` + - ` projections.apps4.name,` + - ` projections.apps4.project_id,` + - ` projections.apps4.creation_date,` + - ` projections.apps4.change_date,` + - ` projections.apps4.resource_owner,` + - ` projections.apps4.state,` + - ` projections.apps4.sequence,` + + expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps5.id,` + + ` projections.apps5.name,` + + ` projections.apps5.project_id,` + + ` projections.apps5.creation_date,` + + ` projections.apps5.change_date,` + + ` projections.apps5.resource_owner,` + + ` projections.apps5.state,` + + ` projections.apps5.sequence,` + // api config - ` projections.apps4_api_configs.app_id,` + - ` projections.apps4_api_configs.client_id,` + - ` projections.apps4_api_configs.auth_method,` + + ` projections.apps5_api_configs.app_id,` + + ` projections.apps5_api_configs.client_id,` + + ` projections.apps5_api_configs.auth_method,` + // oidc config - ` projections.apps4_oidc_configs.app_id,` + - ` projections.apps4_oidc_configs.version,` + - ` projections.apps4_oidc_configs.client_id,` + - ` projections.apps4_oidc_configs.redirect_uris,` + - ` projections.apps4_oidc_configs.response_types,` + - ` projections.apps4_oidc_configs.grant_types,` + - ` projections.apps4_oidc_configs.application_type,` + - ` projections.apps4_oidc_configs.auth_method_type,` + - ` projections.apps4_oidc_configs.post_logout_redirect_uris,` + - ` projections.apps4_oidc_configs.is_dev_mode,` + - ` projections.apps4_oidc_configs.access_token_type,` + - ` projections.apps4_oidc_configs.access_token_role_assertion,` + - ` projections.apps4_oidc_configs.id_token_role_assertion,` + - ` projections.apps4_oidc_configs.id_token_userinfo_assertion,` + - ` projections.apps4_oidc_configs.clock_skew,` + - ` projections.apps4_oidc_configs.additional_origins,` + + ` projections.apps5_oidc_configs.app_id,` + + ` projections.apps5_oidc_configs.version,` + + ` projections.apps5_oidc_configs.client_id,` + + ` projections.apps5_oidc_configs.redirect_uris,` + + ` projections.apps5_oidc_configs.response_types,` + + ` projections.apps5_oidc_configs.grant_types,` + + ` projections.apps5_oidc_configs.application_type,` + + ` projections.apps5_oidc_configs.auth_method_type,` + + ` projections.apps5_oidc_configs.post_logout_redirect_uris,` + + ` projections.apps5_oidc_configs.is_dev_mode,` + + ` projections.apps5_oidc_configs.access_token_type,` + + ` projections.apps5_oidc_configs.access_token_role_assertion,` + + ` projections.apps5_oidc_configs.id_token_role_assertion,` + + ` projections.apps5_oidc_configs.id_token_userinfo_assertion,` + + ` projections.apps5_oidc_configs.clock_skew,` + + ` projections.apps5_oidc_configs.additional_origins,` + + ` projections.apps5_oidc_configs.skip_native_app_success_page,` + //saml config - ` projections.apps4_saml_configs.app_id,` + - ` projections.apps4_saml_configs.entity_id,` + - ` projections.apps4_saml_configs.metadata,` + - ` projections.apps4_saml_configs.metadata_url` + - ` FROM projections.apps4` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + - ` LEFT JOIN projections.apps4_saml_configs ON projections.apps4.id = projections.apps4_saml_configs.app_id AND projections.apps4.instance_id = projections.apps4_saml_configs.instance_id` + + ` projections.apps5_saml_configs.app_id,` + + ` projections.apps5_saml_configs.entity_id,` + + ` projections.apps5_saml_configs.metadata,` + + ` projections.apps5_saml_configs.metadata_url` + + ` FROM projections.apps5` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + + ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps4.id,` + - ` projections.apps4.name,` + - ` projections.apps4.project_id,` + - ` projections.apps4.creation_date,` + - ` projections.apps4.change_date,` + - ` projections.apps4.resource_owner,` + - ` projections.apps4.state,` + - ` projections.apps4.sequence,` + + expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps5.id,` + + ` projections.apps5.name,` + + ` projections.apps5.project_id,` + + ` projections.apps5.creation_date,` + + ` projections.apps5.change_date,` + + ` projections.apps5.resource_owner,` + + ` projections.apps5.state,` + + ` projections.apps5.sequence,` + // api config - ` projections.apps4_api_configs.app_id,` + - ` projections.apps4_api_configs.client_id,` + - ` projections.apps4_api_configs.auth_method,` + + ` projections.apps5_api_configs.app_id,` + + ` projections.apps5_api_configs.client_id,` + + ` projections.apps5_api_configs.auth_method,` + // oidc config - ` projections.apps4_oidc_configs.app_id,` + - ` projections.apps4_oidc_configs.version,` + - ` projections.apps4_oidc_configs.client_id,` + - ` projections.apps4_oidc_configs.redirect_uris,` + - ` projections.apps4_oidc_configs.response_types,` + - ` projections.apps4_oidc_configs.grant_types,` + - ` projections.apps4_oidc_configs.application_type,` + - ` projections.apps4_oidc_configs.auth_method_type,` + - ` projections.apps4_oidc_configs.post_logout_redirect_uris,` + - ` projections.apps4_oidc_configs.is_dev_mode,` + - ` projections.apps4_oidc_configs.access_token_type,` + - ` projections.apps4_oidc_configs.access_token_role_assertion,` + - ` projections.apps4_oidc_configs.id_token_role_assertion,` + - ` projections.apps4_oidc_configs.id_token_userinfo_assertion,` + - ` projections.apps4_oidc_configs.clock_skew,` + - ` projections.apps4_oidc_configs.additional_origins,` + + ` projections.apps5_oidc_configs.app_id,` + + ` projections.apps5_oidc_configs.version,` + + ` projections.apps5_oidc_configs.client_id,` + + ` projections.apps5_oidc_configs.redirect_uris,` + + ` projections.apps5_oidc_configs.response_types,` + + ` projections.apps5_oidc_configs.grant_types,` + + ` projections.apps5_oidc_configs.application_type,` + + ` projections.apps5_oidc_configs.auth_method_type,` + + ` projections.apps5_oidc_configs.post_logout_redirect_uris,` + + ` projections.apps5_oidc_configs.is_dev_mode,` + + ` projections.apps5_oidc_configs.access_token_type,` + + ` projections.apps5_oidc_configs.access_token_role_assertion,` + + ` projections.apps5_oidc_configs.id_token_role_assertion,` + + ` projections.apps5_oidc_configs.id_token_userinfo_assertion,` + + ` projections.apps5_oidc_configs.clock_skew,` + + ` projections.apps5_oidc_configs.additional_origins,` + + ` projections.apps5_oidc_configs.skip_native_app_success_page,` + //saml config - ` projections.apps4_saml_configs.app_id,` + - ` projections.apps4_saml_configs.entity_id,` + - ` projections.apps4_saml_configs.metadata,` + - ` projections.apps4_saml_configs.metadata_url,` + + ` projections.apps5_saml_configs.app_id,` + + ` projections.apps5_saml_configs.entity_id,` + + ` projections.apps5_saml_configs.metadata,` + + ` projections.apps5_saml_configs.metadata_url,` + ` COUNT(*) OVER ()` + - ` FROM projections.apps4` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + - ` LEFT JOIN projections.apps4_saml_configs ON projections.apps4.id = projections.apps4_saml_configs.app_id AND projections.apps4.instance_id = projections.apps4_saml_configs.instance_id` + + ` FROM projections.apps5` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + + ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedAppIDsQuery = regexp.QuoteMeta(`SELECT projections.apps4_api_configs.client_id,` + - ` projections.apps4_oidc_configs.client_id` + - ` FROM projections.apps4` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + + expectedAppIDsQuery = regexp.QuoteMeta(`SELECT projections.apps5_api_configs.client_id,` + + ` projections.apps5_oidc_configs.client_id` + + ` FROM projections.apps5` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) - expectedProjectIDByAppQuery = regexp.QuoteMeta(`SELECT projections.apps4.project_id` + - ` FROM projections.apps4` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + - ` LEFT JOIN projections.apps4_saml_configs ON projections.apps4.id = projections.apps4_saml_configs.app_id AND projections.apps4.instance_id = projections.apps4_saml_configs.instance_id` + + expectedProjectIDByAppQuery = regexp.QuoteMeta(`SELECT projections.apps5.project_id` + + ` FROM projections.apps5` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + + ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) expectedProjectByAppQuery = regexp.QuoteMeta(`SELECT projections.projects3.id,` + ` projections.projects3.creation_date,` + @@ -118,10 +120,10 @@ var ( ` projections.projects3.has_project_check,` + ` projections.projects3.private_labeling_setting` + ` FROM projections.projects3` + - ` JOIN projections.apps4 ON projections.projects3.id = projections.apps4.project_id AND projections.projects3.instance_id = projections.apps4.instance_id` + - ` LEFT JOIN projections.apps4_api_configs ON projections.apps4.id = projections.apps4_api_configs.app_id AND projections.apps4.instance_id = projections.apps4_api_configs.instance_id` + - ` LEFT JOIN projections.apps4_oidc_configs ON projections.apps4.id = projections.apps4_oidc_configs.app_id AND projections.apps4.instance_id = projections.apps4_oidc_configs.instance_id` + - ` LEFT JOIN projections.apps4_saml_configs ON projections.apps4.id = projections.apps4_saml_configs.app_id AND projections.apps4.instance_id = projections.apps4_saml_configs.instance_id` + + ` JOIN projections.apps5 ON projections.projects3.id = projections.apps5.project_id AND projections.projects3.instance_id = projections.apps5.instance_id` + + ` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` + + ` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` + + ` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) appCols = database.StringArray{ @@ -154,6 +156,7 @@ var ( "id_token_userinfo_assertion", "clock_skew", "additional_origins", + "skip_native_app_success_page", //saml config "app_id", "entity_id", @@ -224,6 +227,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -289,6 +293,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -357,6 +362,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config "app-id", "https://test.com/saml/metadata", @@ -427,6 +433,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -451,23 +458,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -511,6 +519,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -535,23 +544,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: false, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: false, - AssertIDTokenRole: false, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: false, + AssertIDTokenRole: false, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -595,6 +605,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -619,23 +630,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: false, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: false, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -679,6 +691,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -703,23 +716,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: false, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: false, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: false, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -763,6 +777,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -787,23 +802,110 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: false, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, + }, + }, + }, + }, + }, + { + name: "prepareAppsQuery oidc app native success page skip", + prepare: prepareAppsQuery, + want: want{ + sqlExpectations: mockQueries( + expectedAppsQuery, + appsCols, + [][]driver.Value{ + { + "app-id", + "app-name", + "project-id", + testNow, + testNow, + "ro", + domain.AppStateActive, + uint64(20211109), + // api config + nil, + nil, + nil, + // oidc config + "app-id", + domain.OIDCVersionV1, + "oidc-client-id", + database.StringArray{"https://redirect.to/me"}, + database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + domain.OIDCApplicationTypeNative, + domain.OIDCAuthMethodTypeNone, + database.StringArray{"post.logout.ch"}, + false, + domain.OIDCTokenTypeJWT, + false, + false, + true, + 1 * time.Second, + database.StringArray{"additional.origin"}, + true, + // saml config + nil, + nil, + nil, + nil, + }, + }, + ), + }, + object: &Apps{ + SearchResponse: SearchResponse{ + Count: 1, + }, + Apps: []*App{ + { + ID: "app-id", + CreationDate: testNow, + ChangeDate: testNow, + ResourceOwner: "ro", + State: domain.AppStateActive, + Sequence: 20211109, + Name: "app-name", + ProjectID: "project-id", + OIDCConfig: &OIDCApp{ + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeNative, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: false, + AssertIDTokenRole: false, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: true, }, }, }, @@ -847,6 +949,7 @@ func Test_AppsPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -883,6 +986,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -919,6 +1023,7 @@ func Test_AppsPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config "saml-app-id", "https://test.com/saml/metadata", @@ -943,23 +1048,24 @@ func Test_AppsPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, { @@ -1085,6 +1191,7 @@ func Test_AppPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -1142,6 +1249,7 @@ func Test_AppPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config nil, nil, @@ -1204,6 +1312,7 @@ func Test_AppPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1223,23 +1332,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, { @@ -1280,6 +1390,7 @@ func Test_AppPrepare(t *testing.T) { nil, nil, nil, + nil, // saml config "app-id", "https://test.com/saml/metadata", @@ -1343,6 +1454,7 @@ func Test_AppPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1362,23 +1474,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: false, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: false, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -1420,6 +1533,7 @@ func Test_AppPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1439,23 +1553,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: false, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: false, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -1497,6 +1612,7 @@ func Test_AppPrepare(t *testing.T) { true, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1516,23 +1632,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: false, - AssertIDTokenUserinfo: true, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: false, + AssertIDTokenUserinfo: true, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, @@ -1574,6 +1691,7 @@ func Test_AppPrepare(t *testing.T) { false, 1 * time.Second, database.StringArray{"additional.origin"}, + false, // saml config nil, nil, @@ -1593,23 +1711,24 @@ func Test_AppPrepare(t *testing.T) { Name: "app-name", ProjectID: "project-id", OIDCConfig: &OIDCApp{ - Version: domain.OIDCVersionV1, - ClientID: "oidc-client-id", - RedirectURIs: database.StringArray{"https://redirect.to/me"}, - ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, - GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, - AppType: domain.OIDCApplicationTypeUserAgent, - AuthMethodType: domain.OIDCAuthMethodTypeNone, - PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, - IsDevMode: true, - AccessTokenType: domain.OIDCTokenTypeJWT, - AssertAccessTokenRole: true, - AssertIDTokenRole: true, - AssertIDTokenUserinfo: false, - ClockSkew: 1 * time.Second, - AdditionalOrigins: database.StringArray{"additional.origin"}, - ComplianceProblems: nil, - AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + Version: domain.OIDCVersionV1, + ClientID: "oidc-client-id", + RedirectURIs: database.StringArray{"https://redirect.to/me"}, + ResponseTypes: database.EnumArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken}, + GrantTypes: database.EnumArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit}, + AppType: domain.OIDCApplicationTypeUserAgent, + AuthMethodType: domain.OIDCAuthMethodTypeNone, + PostLogoutRedirectURIs: database.StringArray{"post.logout.ch"}, + IsDevMode: true, + AccessTokenType: domain.OIDCTokenTypeJWT, + AssertAccessTokenRole: true, + AssertIDTokenRole: true, + AssertIDTokenUserinfo: false, + ClockSkew: 1 * time.Second, + AdditionalOrigins: database.StringArray{"additional.origin"}, + ComplianceProblems: nil, + AllowedOrigins: database.StringArray{"https://redirect.to", "additional.origin"}, + SkipNativeAppSuccessPage: false, }, }, }, diff --git a/internal/query/projection/app.go b/internal/query/projection/app.go index ee5b34fbca..483764d5d5 100644 --- a/internal/query/projection/app.go +++ b/internal/query/projection/app.go @@ -15,7 +15,7 @@ import ( ) const ( - AppProjectionTable = "projections.apps4" + AppProjectionTable = "projections.apps5" AppAPITable = AppProjectionTable + "_" + appAPITableSuffix AppOIDCTable = AppProjectionTable + "_" + appOIDCTableSuffix AppSAMLTable = AppProjectionTable + "_" + appSAMLTableSuffix @@ -57,6 +57,7 @@ const ( AppOIDCConfigColumnIDTokenUserinfoAssertion = "id_token_userinfo_assertion" AppOIDCConfigColumnClockSkew = "clock_skew" AppOIDCConfigColumnAdditionalOrigins = "additional_origins" + AppOIDCConfigColumnSkipNativeAppSuccessPage = "skip_native_app_success_page" appSAMLTableSuffix = "saml_configs" AppSAMLConfigColumnAppID = "app_id" @@ -122,6 +123,7 @@ func newAppProjection(ctx context.Context, config crdb.StatementHandlerConfig) * crdb.NewColumn(AppOIDCConfigColumnIDTokenUserinfoAssertion, crdb.ColumnTypeBool, crdb.Default(false)), crdb.NewColumn(AppOIDCConfigColumnClockSkew, crdb.ColumnTypeInt64, crdb.Default(0)), crdb.NewColumn(AppOIDCConfigColumnAdditionalOrigins, crdb.ColumnTypeTextArray, crdb.Nullable()), + crdb.NewColumn(AppOIDCConfigColumnSkipNativeAppSuccessPage, crdb.ColumnTypeBool, crdb.Default(false)), }, crdb.NewPrimaryKey(AppOIDCConfigColumnInstanceID, AppOIDCConfigColumnAppID), appOIDCTableSuffix, @@ -463,6 +465,7 @@ func (p *appProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler. handler.NewCol(AppOIDCConfigColumnIDTokenUserinfoAssertion, e.IDTokenUserinfoAssertion), handler.NewCol(AppOIDCConfigColumnClockSkew, e.ClockSkew), handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.StringArray(e.AdditionalOrigins)), + handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, e.SkipNativeAppSuccessPage), }, crdb.WithTableSuffix(appOIDCTableSuffix), ), @@ -528,6 +531,9 @@ func (p *appProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle if e.AdditionalOrigins != nil { cols = append(cols, handler.NewCol(AppOIDCConfigColumnAdditionalOrigins, database.StringArray(*e.AdditionalOrigins))) } + if e.SkipNativeAppSuccessPage != nil { + cols = append(cols, handler.NewCol(AppOIDCConfigColumnSkipNativeAppSuccessPage, *e.SkipNativeAppSuccessPage)) + } if len(cols) == 0 { return crdb.NewNoOpStatement(e), nil diff --git a/internal/query/projection/app_test.go b/internal/query/projection/app_test.go index 9320b62ad3..c605ed6dc4 100644 --- a/internal/query/projection/app_test.go +++ b/internal/query/projection/app_test.go @@ -45,7 +45,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.apps4 (id, name, project_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.apps5 (id, name, project_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "app-id", "my-app", @@ -82,7 +82,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.apps5 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ "my-app", anyArg{}, @@ -95,6 +95,27 @@ func TestAppProjection_reduces(t *testing.T) { }, }, }, + { + name: "project reduceAppChanged no change", + args: args{ + event: getEvent(testEvent( + repository.EventType(project.ApplicationChangedType), + project.AggregateType, + []byte(`{ + "appId": "app-id" + }`), + ), project.ApplicationChangedEventMapper), + }, + reduce: (&appProjection{}).reduceAppChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("project"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{}, + }, + }, + }, { name: "project reduceAppDeactivated", args: args{ @@ -114,7 +135,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.apps5 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.AppStateInactive, anyArg{}, @@ -146,7 +167,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.apps5 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.AppStateActive, anyArg{}, @@ -178,7 +199,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.apps4 WHERE (id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.apps5 WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "app-id", "instance-id", @@ -205,7 +226,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.apps4 WHERE (project_id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.apps5 WHERE (project_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -232,7 +253,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.apps4 WHERE (instance_id = $1)", + expectedStmt: "DELETE FROM projections.apps5 WHERE (instance_id = $1)", expectedArgs: []interface{}{ "agg-id", }, @@ -263,7 +284,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.apps4_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.apps5_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "app-id", "instance-id", @@ -273,7 +294,7 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -307,7 +328,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, domain.APIAuthMethodTypePrivateKeyJWT, @@ -316,7 +337,7 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -369,7 +390,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.apps5_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ anyArg{}, "app-id", @@ -377,7 +398,7 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -412,7 +433,8 @@ func TestAppProjection_reduces(t *testing.T) { "idTokenRoleAssertion": true, "idTokenUserinfoAssertion": true, "clockSkew": 1000, - "additionalOrigins": ["origin.one.ch", "origin.two.ch"] + "additionalOrigins": ["origin.one.ch", "origin.two.ch"], + "skipNativeAppSuccessPage": true }`), ), project.OIDCConfigAddedEventMapper), }, @@ -424,7 +446,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.apps4_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)", + expectedStmt: "INSERT INTO projections.apps5_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)", expectedArgs: []interface{}{ "app-id", "instance-id", @@ -444,10 +466,11 @@ func TestAppProjection_reduces(t *testing.T) { true, 1 * time.Microsecond, database.StringArray{"origin.one.ch", "origin.two.ch"}, + true, }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -480,7 +503,9 @@ func TestAppProjection_reduces(t *testing.T) { "idTokenRoleAssertion": true, "idTokenUserinfoAssertion": true, "clockSkew": 1000, - "additionalOrigins": ["origin.one.ch", "origin.two.ch"] + "additionalOrigins": ["origin.one.ch", "origin.two.ch"], + "skipNativeAppSuccessPage": true + }`), ), project.OIDCConfigChangedEventMapper), }, @@ -492,7 +517,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) WHERE (app_id = $15) AND (instance_id = $16)", + expectedStmt: "UPDATE projections.apps5_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (app_id = $16) AND (instance_id = $17)", expectedArgs: []interface{}{ domain.OIDCVersionV1, database.StringArray{"redirect.one.ch", "redirect.two.ch"}, @@ -508,12 +533,13 @@ func TestAppProjection_reduces(t *testing.T) { true, 1 * time.Microsecond, database.StringArray{"origin.one.ch", "origin.two.ch"}, + true, "app-id", "instance-id", }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -566,7 +592,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.apps5_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ anyArg{}, "app-id", @@ -574,7 +600,7 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -603,7 +629,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps4 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", + expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), diff --git a/internal/repository/project/oidc_config.go b/internal/repository/project/oidc_config.go index df809daa7c..d39e92c2aa 100644 --- a/internal/repository/project/oidc_config.go +++ b/internal/repository/project/oidc_config.go @@ -40,6 +40,7 @@ type OIDCConfigAddedEvent struct { IDTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion,omitempty"` ClockSkew time.Duration `json:"clockSkew,omitempty"` AdditionalOrigins []string `json:"additionalOrigins,omitempty"` + SkipNativeAppSuccessPage bool `json:"skipNativeAppSuccessPage,omitempty"` } func (e *OIDCConfigAddedEvent) Data() interface{} { @@ -70,6 +71,7 @@ func NewOIDCConfigAddedEvent( idTokenUserinfoAssertion bool, clockSkew time.Duration, additionalOrigins []string, + skipNativeAppSuccessPage bool, ) *OIDCConfigAddedEvent { return &OIDCConfigAddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -94,6 +96,7 @@ func NewOIDCConfigAddedEvent( IDTokenUserinfoAssertion: idTokenUserinfoAssertion, ClockSkew: clockSkew, AdditionalOrigins: additionalOrigins, + SkipNativeAppSuccessPage: skipNativeAppSuccessPage, } } @@ -179,8 +182,7 @@ func (e *OIDCConfigAddedEvent) Validate(cmd eventstore.Command) bool { return false } } - - return true + return e.SkipNativeAppSuccessPage == c.SkipNativeAppSuccessPage } func OIDCConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) { @@ -214,6 +216,7 @@ type OIDCConfigChangedEvent struct { IDTokenUserinfoAssertion *bool `json:"idTokenUserinfoAssertion,omitempty"` ClockSkew *time.Duration `json:"clockSkew,omitempty"` AdditionalOrigins *[]string `json:"additionalOrigins,omitempty"` + SkipNativeAppSuccessPage *bool `json:"skipNativeAppSuccessPage,omitempty"` } func (e *OIDCConfigChangedEvent) Data() interface{} { @@ -334,6 +337,12 @@ func ChangeAdditionalOrigins(additionalOrigins []string) func(event *OIDCConfigC } } +func ChangeSkipNativeAppSuccessPage(skipNativeAppSuccessPage bool) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.SkipNativeAppSuccessPage = &skipNativeAppSuccessPage + } +} + func OIDCConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) { e := &OIDCConfigChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), diff --git a/proto/zitadel/app.proto b/proto/zitadel/app.proto index 0a855f9552..d889135bc4 100644 --- a/proto/zitadel/app.proto +++ b/proto/zitadel/app.proto @@ -163,6 +163,11 @@ message OIDCConfig { description: "all allowed origins from where the API can be used"; } ]; + bool skip_native_app_success_page = 20 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Skip the successful login page on native apps and directly redirect the user to the callback."; + } + ]; } enum OIDCResponseType { diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 71dd9b99ff..e410d839ae 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -8773,6 +8773,11 @@ message AddOIDCAppRequest { description: "Additional origins (other than the redirect_uris) from where the API can be used"; } ]; + bool skip_native_app_success_page = 17 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Skip the successful login page on native apps and directly redirect the user to the callback."; + } + ]; } message AddOIDCAppResponse { @@ -8943,6 +8948,11 @@ message UpdateOIDCAppConfigRequest { description: "Additional origins (other than the redirect_uris) from where the API can be used"; } ]; + bool skip_native_app_success_page = 16 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "Skip the successful login page on native apps and directly redirect the user to the callback."; + } + ]; } message UpdateOIDCAppConfigResponse { From 1c1d66cbe806edeff6c7fc432ac3077be983981d Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 11 Apr 2023 17:56:51 +0200 Subject: [PATCH 18/36] chore(console): remove first and lastName fallback from user (#5629) * chore(console): remove first and lastName fallback from user * use display name and ensure it's set without required name fields * add user type to user grant and memberships responses * contributor, members * fix avatar display checks --------- Co-authored-by: Livio Spring --- cmd/defaults.yaml | 24 +++++++++---------- .../accounts-card.component.html | 8 ++----- .../contributors/contributors.component.html | 4 ++-- .../contributors/contributors.component.ts | 3 +++ .../app/modules/header/header.component.html | 10 ++------ .../members-table.component.html | 2 +- .../members-table/members-table.component.ts | 2 ++ .../search-user-autocomplete.component.html | 18 ++------------ .../user-grants/user-grants.component.html | 2 +- .../user-grants/user-grants.component.ts | 1 - .../detail-form/detail-form.component.html | 6 ++--- .../user-table/user-table.component.html | 12 +++------- internal/api/grpc/auth/user_grant.go | 2 ++ internal/api/grpc/member/converter.go | 2 ++ internal/api/grpc/user/converter.go | 13 ++++++++++ internal/api/grpc/user/user_grant.go | 1 + internal/command/user_human.go | 12 ++++++++-- internal/domain/human.go | 9 ++++++- internal/notification/static/i18n/de.yaml | 16 ++++++------- internal/notification/static/i18n/en.yaml | 16 ++++++------- internal/notification/static/i18n/fr.yaml | 14 +++++------ internal/notification/static/i18n/it.yaml | 16 ++++++------- internal/notification/static/i18n/ja.yaml | 14 +++++------ internal/notification/static/i18n/pl.yaml | 14 +++++------ internal/notification/static/i18n/zh.yaml | 14 +++++------ internal/query/iam_member.go | 6 +++++ internal/query/iam_member_test.go | 13 ++++++++++ internal/query/member.go | 2 ++ internal/query/org_member.go | 6 +++++ internal/query/org_member_test.go | 14 +++++++++++ internal/query/project_grant_member.go | 6 +++++ internal/query/project_grant_member_test.go | 14 +++++++++++ internal/query/project_member.go | 6 +++++ internal/query/project_member_test.go | 14 +++++++++++ proto/zitadel/auth.proto | 5 ++++ proto/zitadel/member.proto | 6 +++++ proto/zitadel/user.proto | 5 ++++ 37 files changed, 218 insertions(+), 114 deletions(-) diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index 625a03dafc..8996e384c6 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -554,7 +554,7 @@ DefaultInstance: Title: Zitadel - User initialisieren PreHeader: User initialisieren Subject: User initialisieren - Greeting: Hallo {{.FirstName}} {{.LastName}}, + Greeting: Hallo {{.DisplayName}}, Text: Dieser Benutzer wurde soeben im Zitadel erstellt. Mit dem Benutzernamen <br><strong>{{.PreferredLoginName}}</strong><br> kannst du dich anmelden. Nutze den untenstehenden Button, um die Initialisierung abzuschliessen <br>(Code <strong>{{.Code}}</strong>).<br> Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren. ButtonText: Initialisierung abschliessen - MessageTextType: PasswordReset @@ -562,7 +562,7 @@ DefaultInstance: Title: Zitadel - Passwort zurücksetzen PreHeader: Passwort zurücksetzen Subject: Passwort zurücksetzen - Greeting: Hallo {{.FirstName}} {{.LastName}}, + Greeting: Hallo {{.DisplayName}}, Text: Wir haben eine Anfrage für das Zurücksetzen deines Passwortes bekommen. Du kannst den untenstehenden Button verwenden, um dein Passwort zurückzusetzen <br>(Code <strong>{{.Code}}</strong>).<br> Falls du dieses Mail nicht angefordert hast, kannst du es ignorieren. ButtonText: Passwort zurücksetzen - MessageTextType: VerifyEmail @@ -570,7 +570,7 @@ DefaultInstance: Title: Zitadel - Email verifizieren PreHeader: Email verifizieren Subject: Email verifizieren - Greeting: Hallo {{.FirstName}} {{.LastName}}, + Greeting: Hallo {{.DisplayName}}, Text: Eine neue E-Mail Adresse wurde hinzugefügt. Bitte verwende den untenstehenden Button um diese zu verifizieren <br>(Code <strong>{{.Code}}</strong>).<br> Falls du deine E-Mail Adresse nicht selber hinzugefügt hast, kannst du dieses E-Mail ignorieren. ButtonText: Email verifizieren - MessageTextType: VerifyPhone @@ -578,7 +578,7 @@ DefaultInstance: Title: Zitadel - Telefonnummer verifizieren PreHeader: Telefonnummer verifizieren Subject: Telefonnummer verifizieren - Greeting: Hallo {{.FirstName}} {{.LastName}}, + Greeting: Hallo {{.DisplayName}}, Text: Eine Telefonnummer wurde hinzugefügt. Bitte verifiziere diese in dem du folgenden Code eingibst (Code {{.Code}}) ButtonText: Telefon verifizieren - MessageTextType: DomainClaimed @@ -586,7 +586,7 @@ DefaultInstance: Title: Zitadel - Domain wurde beansprucht PreHeader: Email / Username ändern Subject: Domain wurde beansprucht - Greeting: Hallo {{.FirstName}} {{.LastName}}, + Greeting: Hallo {{.DisplayName}}, Text: Die Domain {{.Domain}} wurde von einer Organisation beansprucht. Dein derzeitiger User {{.Username}} ist nicht Teil dieser Organisation. Daher musst du beim nächsten Login eine neue Email hinterlegen. Für diesen Login haben wir dir einen temporären Usernamen ({{.TempUsername}}) erstellt. ButtonText: Login - MessageTextType: PasswordChange @@ -594,7 +594,7 @@ DefaultInstance: Title: ZITADEL - Passwort von Benutzer wurde geändert PreHeader: Passwort Änderung Subject: Passwort von Benutzer wurde geändert - Greeting: Hallo {{.FirstName}} {{.LastName}}, + Greeting: Hallo {{.DisplayName}}, Text: Das Password vom Benutzer wurde geändert. Wenn diese Änderung von jemand anderem gemacht wurde, empfehlen wir die sofortige Zurücksetzung ihres Passworts. ButtonText: Login - MessageTextType: InitCode @@ -602,7 +602,7 @@ DefaultInstance: Title: Zitadel - Initialize User PreHeader: Initialize User Subject: Initialize User - Greeting: Hello {{.FirstName}} {{.LastName}}, + Greeting: Hello {{.DisplayName}}, Text: This user was created in Zitadel. Use the username {{.PreferredLoginName}} to login. Please click the button below to finish the initialization process. (Code {{.Code}}) If you didn't ask for this mail, please ignore it. ButtonText: Finish initialization - MessageTextType: PasswordReset @@ -610,7 +610,7 @@ DefaultInstance: Title: Zitadel - Reset password PreHeader: Reset password Subject: Reset password - Greeting: Hello {{.FirstName}} {{.LastName}}, + Greeting: Hello {{.DisplayName}}, Text: We received a password reset request. Please use the button below to reset your password. (Code {{.Code}}) If you didn't ask for this mail, please ignore it. ButtonText: Reset password - MessageTextType: VerifyEmail @@ -618,7 +618,7 @@ DefaultInstance: Title: Zitadel - Verify email PreHeader: Verify email Subject: Verify email - Greeting: Hello {{.FirstName}} {{.LastName}}, + Greeting: Hello {{.DisplayName}}, Text: A new email has been added. Please use the button below to verify your mail. (Code {{.Code}}) If you din't add a new email, please ignore this email. ButtonText: Verify email - MessageTextType: VerifyPhone @@ -626,7 +626,7 @@ DefaultInstance: Title: Zitadel - Verify phone PreHeader: Verify phone Subject: Verify phone - Greeting: Hello {{.FirstName}} {{.LastName}}, + Greeting: Hello {{.DisplayName}}, Text: A new phonenumber has been added. Please use the following code to verify it {{.Code}}. ButtonText: Verify phone - MessageTextType: DomainClaimed @@ -634,7 +634,7 @@ DefaultInstance: Title: Zitadel - Domain has been claimed PreHeader: Change email / username Subject: Domain has been claimed - Greeting: Hello {{.FirstName}} {{.LastName}}, + Greeting: Hello {{.DisplayName}}, Text: The domain {{.Domain}} has been claimed by an organisation. Your current user {{.UserName}} is not part of this organisation. Therefore you'll have to change your email when you login. We have created a temporary username ({{.TempUsername}}) for this login. ButtonText: Login - MessageTextType: PasswordChange @@ -642,7 +642,7 @@ DefaultInstance: Title: ZITADEL - Password of user has changed PreHeader: Change password Subject: Password of user has changed - Greeting: Hello {{.FirstName}} {{.LastName}}, + Greeting: Hello {{.DisplayName}}, Text: The password of your user has changed. If this change was not done by you, please be advised to immediately reset your password. ButtonText: Login diff --git a/console/src/app/modules/accounts-card/accounts-card.component.html b/console/src/app/modules/accounts-card/accounts-card.component.html index 7780fd323f..b4a9439c36 100644 --- a/console/src/app/modules/accounts-card/accounts-card.component.html +++ b/console/src/app/modules/accounts-card/accounts-card.component.html @@ -1,16 +1,12 @@
diff --git a/console/src/app/modules/contributors/contributors.component.html b/console/src/app/modules/contributors/contributors.component.html index 58d6f6355a..0ef25b708c 100644 --- a/console/src/app/modules/contributors/contributors.component.html +++ b/console/src/app/modules/contributors/contributors.component.html @@ -13,10 +13,10 @@ data-e2e="member-avatar" > diff --git a/console/src/app/modules/contributors/contributors.component.ts b/console/src/app/modules/contributors/contributors.component.ts index d0dd487fd8..bdb3fe4540 100644 --- a/console/src/app/modules/contributors/contributors.component.ts +++ b/console/src/app/modules/contributors/contributors.component.ts @@ -2,6 +2,7 @@ import { animate, animateChild, keyframes, query, stagger, style, transition, tr import { Component, EventEmitter, Input, Output } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { Member } from 'src/app/proto/generated/zitadel/member_pb'; +import { Type } from 'src/app/proto/generated/zitadel/user_pb'; @Component({ selector: 'cnsl-contributors', @@ -36,6 +37,8 @@ export class ContributorsComponent { @Output() showDetailClicked: EventEmitter = new EventEmitter(); @Output() refreshClicked: EventEmitter = new EventEmitter(); + public UserType: any = Type; + public emitAddMember(): void { this.addClicked.emit(); } diff --git a/console/src/app/modules/header/header.component.html b/console/src/app/modules/header/header.component.html index d9043c1381..de31e202eb 100644 --- a/console/src/app/modules/header/header.component.html +++ b/console/src/app/modules/header/header.component.html @@ -197,9 +197,7 @@
- + diff --git a/console/src/app/modules/filter-org/filter-org.component.ts b/console/src/app/modules/filter-org/filter-org.component.ts index 0f1a33e76d..a4592bdf7e 100644 --- a/console/src/app/modules/filter-org/filter-org.component.ts +++ b/console/src/app/modules/filter-org/filter-org.component.ts @@ -3,13 +3,14 @@ import { MatLegacyCheckboxChange as MatCheckboxChange } from '@angular/material/ import { ActivatedRoute, Router } from '@angular/router'; import { take } from 'rxjs'; import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb'; -import { OrgNameQuery, OrgQuery, OrgState } from 'src/app/proto/generated/zitadel/org_pb'; +import { OrgNameQuery, OrgQuery, OrgState, OrgStateQuery } from 'src/app/proto/generated/zitadel/org_pb'; import { UserNameQuery } from 'src/app/proto/generated/zitadel/user_pb'; import { FilterComponent } from '../filter/filter.component'; enum SubQuery { NAME, + STATE, } @Component({ @@ -21,7 +22,7 @@ export class FilterOrgComponent extends FilterComponent implements OnInit { public SubQuery: any = SubQuery; public searchQueries: OrgQuery[] = []; - public states: OrgState[] = [OrgState.ORG_STATE_ACTIVE, OrgState.ORG_STATE_INACTIVE]; + public states: OrgState[] = [OrgState.ORG_STATE_ACTIVE, OrgState.ORG_STATE_INACTIVE, OrgState.ORG_STATE_REMOVED]; constructor(router: Router, protected route: ActivatedRoute) { super(router, route); @@ -37,13 +38,17 @@ export class FilterOrgComponent extends FilterComponent implements OnInit { const orgQueries = filters.map((filter) => { if (filter.nameQuery) { const orgQuery = new OrgQuery(); - const orgNameQuery = new OrgNameQuery(); orgNameQuery.setName(filter.nameQuery.name); orgNameQuery.setMethod(filter.nameQuery.method); - orgQuery.setNameQuery(orgNameQuery); return orgQuery; + } else if (filter.stateQuery) { + const orgQuery = new OrgQuery(); + const orgStateQuery = new OrgStateQuery(); + orgStateQuery.setState(filter.stateQuery.state); + orgQuery.setStateQuery(orgStateQuery); + return orgQuery; } else { return undefined; } @@ -64,12 +69,17 @@ export class FilterOrgComponent extends FilterComponent implements OnInit { const nq = new OrgNameQuery(); nq.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE); nq.setName(''); - const oq = new OrgQuery(); oq.setNameQuery(nq); - this.searchQueries.push(oq); break; + case SubQuery.STATE: + const sq = new OrgStateQuery(); + sq.setState(OrgState.ORG_STATE_ACTIVE); + const osq = new OrgQuery(); + osq.setStateQuery(sq); + this.searchQueries.push(osq); + break; } } else { switch (subquery) { @@ -79,6 +89,12 @@ export class FilterOrgComponent extends FilterComponent implements OnInit { this.searchQueries.splice(index_dn, 1); } break; + case SubQuery.STATE: + const index_sn = this.searchQueries.findIndex((q) => (q as OrgQuery).toObject().stateQuery !== undefined); + if (index_sn > -1) { + this.searchQueries.splice(index_sn, 1); + } + break; } } } @@ -90,6 +106,10 @@ export class FilterOrgComponent extends FilterComponent implements OnInit { (query as OrgNameQuery).setName(value); this.filterChanged.emit(this.searchQueries ? this.searchQueries : []); break; + case SubQuery.STATE: + (query as OrgStateQuery).setState(value); + this.filterChanged.emit(this.searchQueries ? this.searchQueries : []); + break; } } @@ -102,6 +122,13 @@ export class FilterOrgComponent extends FilterComponent implements OnInit { } else { return undefined; } + case SubQuery.STATE: + const sn = this.searchQueries.find((q) => (q as OrgQuery).toObject().stateQuery !== undefined); + if (sn) { + return (sn as OrgQuery).getStateQuery(); + } else { + return undefined; + } } } diff --git a/console/src/app/modules/org-context/org-context.component.ts b/console/src/app/modules/org-context/org-context.component.ts index 2c78846948..c5ad2dbe9b 100644 --- a/console/src/app/modules/org-context/org-context.component.ts +++ b/console/src/app/modules/org-context/org-context.component.ts @@ -3,7 +3,7 @@ import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } import { UntypedFormControl } from '@angular/forms'; import { BehaviorSubject, catchError, debounceTime, finalize, from, map, Observable, of, pipe, tap } from 'rxjs'; import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb'; -import { Org, OrgNameQuery, OrgQuery } from 'src/app/proto/generated/zitadel/org_pb'; +import { Org, OrgNameQuery, OrgQuery, OrgState, OrgStateQuery } from 'src/app/proto/generated/zitadel/org_pb'; import { AuthenticationService } from 'src/app/services/authentication.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; @@ -47,9 +47,12 @@ export class OrgContextComponent implements OnInit { } } - let query; + let query = new OrgQuery(); + const orgStateQuery = new OrgStateQuery(); + orgStateQuery.setState(OrgState.ORG_STATE_ACTIVE); + query.setStateQuery(orgStateQuery); + if (filter) { - query = new OrgQuery(); const orgNameQuery = new OrgNameQuery(); orgNameQuery.setName(filter); orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE); diff --git a/console/src/app/pages/users/user-list/user-table/user-table.component.ts b/console/src/app/pages/users/user-list/user-table/user-table.component.ts index 948eee8022..777fa5ad81 100644 --- a/console/src/app/pages/users/user-list/user-table/user-table.component.ts +++ b/console/src/app/pages/users/user-list/user-table/user-table.component.ts @@ -204,6 +204,7 @@ export class UserTableComponent implements OnInit { sortingField = UserFieldName.USER_FIELD_NAME_CREATION_DATE; break; } + this.userService .listUsers( limit, diff --git a/internal/api/grpc/org/converter.go b/internal/api/grpc/org/converter.go index f98c97e4ff..a73d77a279 100644 --- a/internal/api/grpc/org/converter.go +++ b/internal/api/grpc/org/converter.go @@ -26,7 +26,7 @@ func OrgQueryToModel(apiQuery *org_pb.OrgQuery) (query.SearchQuery, error) { case *org_pb.OrgQuery_NameQuery: return query.NewOrgNameSearchQuery(object.TextMethodToQuery(q.NameQuery.Method), q.NameQuery.Name) case *org_pb.OrgQuery_StateQuery: - return query.NewOrgStateSearchQuery(int32(q.StateQuery.State)) + return query.NewOrgStateSearchQuery(OrgStateToDomain(q.StateQuery.State)) default: return nil, errors.ThrowInvalidArgument(nil, "ORG-vR9nC", "List.Query.Invalid") } @@ -49,6 +49,8 @@ func OrgQueryToQuery(search *org_pb.OrgQuery) (query.SearchQuery, error) { return query.NewOrgDomainSearchQuery(object.TextMethodToQuery(q.DomainQuery.Method), q.DomainQuery.Domain) case *org_pb.OrgQuery_NameQuery: return query.NewOrgNameSearchQuery(object.TextMethodToQuery(q.NameQuery.Method), q.NameQuery.Name) + case *org_pb.OrgQuery_StateQuery: + return query.NewOrgStateSearchQuery(OrgStateToDomain(q.StateQuery.State)) default: return nil, errors.ThrowInvalidArgument(nil, "ADMIN-ADvsd", "List.Query.Invalid") } @@ -108,6 +110,21 @@ func OrgStateToPb(state domain.OrgState) org_pb.OrgState { } } +func OrgStateToDomain(state org_pb.OrgState) domain.OrgState { + switch state { + case org_pb.OrgState_ORG_STATE_ACTIVE: + return domain.OrgStateActive + case org_pb.OrgState_ORG_STATE_INACTIVE: + return domain.OrgStateInactive + case org_pb.OrgState_ORG_STATE_REMOVED: + return domain.OrgStateRemoved + case org_pb.OrgState_ORG_STATE_UNSPECIFIED: + fallthrough + default: + return domain.OrgStateUnspecified + } +} + func DomainQueriesToModel(queries []*org_pb.DomainSearchQuery) (_ []query.SearchQuery, err error) { q := make([]query.SearchQuery, len(queries)) for i, query := range queries { diff --git a/internal/query/org.go b/internal/query/org.go index b3dc88b9dc..26bd3cd670 100644 --- a/internal/query/org.go +++ b/internal/query/org.go @@ -221,7 +221,7 @@ func NewOrgNameSearchQuery(method TextComparison, value string) (SearchQuery, er return NewTextQuery(OrgColumnName, value, method) } -func NewOrgStateSearchQuery(value int32) (SearchQuery, error) { +func NewOrgStateSearchQuery(value domain_pkg.OrgState) (SearchQuery, error) { return NewNumberQuery(OrgColumnState, value, NumberEquals) } From 24e3695e086db9438c0715b3006123ea2bb383c3 Mon Sep 17 00:00:00 2001 From: mffap Date: Thu, 13 Apr 2023 10:34:01 +0200 Subject: [PATCH 25/36] docs(readme): update features (#5676) * docs(readme): update features - add LDAP - structure and update features * Apply suggestions from code review Co-authored-by: Florian Forster --------- Co-authored-by: Florian Forster --- README.md | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 996e5120f3..18c9ae3e4a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Do you have project that requires a multi-tenant user management with self-servi Look no further — ZITADEL combines the ease of Auth0 with the versatility of Keycloak. We provide you with a wide range of out of the box features to accelerate your project. -Multi-tenancy with branding customization, secure login, self-service, OpenID Connect, OAuth2.x, SAML2, Passwordless with FIDO2 (including Passkeys), OTP, U2F, and an unlimited audit trail is there for you, ready to use. +Multi-tenancy with branding customization, secure login, self-service, OpenID Connect, OAuth2.x, SAML2, LDAP, Passwordless with FIDO2 (including Passkeys), OTP, U2F, and an unlimited audit trail is there for you, ready to use. With ZITADEL you can rely on a hardened and extensible turnkey solution to solve all of your authentication and authorization needs. @@ -90,20 +90,34 @@ Yet it offers everything you need for a customer identity ([CIAM](https://zitade ## Features +Authentication - Single Sign On (SSO) - Passwordless with FIDO2 support (Including Passkeys) - Username / Password -- Multifactor authentication with OTP, U2F -- [Identity Brokering](https://zitadel.com/docs/guides/integrate/identity-brokering) -- [Machine-to-machine (JWT profile)](https://zitadel.com/docs/guides/integrate/serviceusers) -- Personal Access Tokens (PAT) -- Role Based Access Control (RBAC) -- [Delegate role management to third-parties](https://zitadel.com/docs/guides/manage/console/projects) -- [Self-registration](https://zitadel.com/docs/concepts/features/selfservice#registration) including verification -- [Self-service](https://zitadel.com/docs/concepts/features/selfservice) for end-users, business customers, and administrators +- Multifactor authentication with OTP, U2F, SMS +- LDAP - [OpenID Connect certified](https://openid.net/certification/#OPs) => [OIDC Endpoints](https://zitadel.com/docs/apis/openidoauth/endpoints) - [SAML 2.0](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) => [SAML Endpoints](https://zitadel.com/docs/apis/saml/endpoints) +- [Machine-to-machine](https://zitadel.com/docs/guides/integrate/serviceusers) with JWT profile, Personal Access Tokens (PAT), and Client Credentials + +Multi-Tenancy +- [Identity Brokering](https://zitadel.com/docs/guides/integrate/identity-brokering) with templates for popular identity providers +- [Delegate role management to third-parties](https://zitadel.com/docs/guides/manage/console/projects) +- Domain discovery + +Integration +- [GRPC and REST APIs](https://zitadel.com/docs/apis/introduction) +- [Actions](https://zitadel.com/docs/apis/actions/introduction) to call any API, send webhooks, adjust workflows, or customize tokens +- Role Based Access Control (RBAC) + +Self-Service +- [Self-registration](https://zitadel.com/docs/concepts/features/selfservice#registration) including verification +- [Self-service](https://zitadel.com/docs/concepts/features/selfservice) for end-users, business customers, and administrators +- [Administration UI (Console)](https://zitadel.com/docs/guides/manage/console/overview) + +Deployment - [Postgres](https://zitadel.com/docs/self-hosting/manage/database#postgres) (version >= 14) or [CockroachDB](https://zitadel.com/docs/self-hosting/manage/database#cockroach) (version >= 22.0) +- [Zero Downtime Updates](https://zitadel.com/docs/concepts/architecture/solution#zero-downtime-updates) Track upcoming features on our [roadmap](https://zitadel.com/roadmap). From 8745c87ded7e6c3c9ca3762021906274d4534d83 Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Thu, 13 Apr 2023 17:11:51 +0200 Subject: [PATCH 26/36] docs: improve idp docs (#5677) * docs: improve idp docs * warn about clear text ldap --- .../identity-providers/_activate.mdx | 3 +- .../_how_ldap_idp_works.mdx | 8 +++++ .../identity-providers/_idps_overview.mdx | 8 +++++ .../integrate/identity-providers/azure-ad.mdx | 11 ++++--- .../integrate/identity-providers/github.mdx | 10 ++++-- .../integrate/identity-providers/gitlab.mdx | 11 ++++--- .../integrate/identity-providers/google.mdx | 10 ++++-- .../integrate/identity-providers/ldap.mdx | 33 ++++++++++--------- .../integrate/identity-providers/openldap.mdx | 31 +++++++++++++---- 9 files changed, 88 insertions(+), 37 deletions(-) create mode 100644 docs/docs/guides/integrate/identity-providers/_how_ldap_idp_works.mdx create mode 100644 docs/docs/guides/integrate/identity-providers/_idps_overview.mdx diff --git a/docs/docs/guides/integrate/identity-providers/_activate.mdx b/docs/docs/guides/integrate/identity-providers/_activate.mdx index 1ddfeab8eb..689d35cb7d 100644 --- a/docs/docs/guides/integrate/identity-providers/_activate.mdx +++ b/docs/docs/guides/integrate/identity-providers/_activate.mdx @@ -1 +1,2 @@ -Once you created the IdP, you need to activate it. \ No newline at end of file +Once you created the provider, it is listed in the providers overview. +Activate it by selecting the tick with the tooltip *set as available*. \ No newline at end of file diff --git a/docs/docs/guides/integrate/identity-providers/_how_ldap_idp_works.mdx b/docs/docs/guides/integrate/identity-providers/_how_ldap_idp_works.mdx new file mode 100644 index 0000000000..dcba962dcd --- /dev/null +++ b/docs/docs/guides/integrate/identity-providers/_how_ldap_idp_works.mdx @@ -0,0 +1,8 @@ +When you use an LDAP provider in ZITADEL, this is the login process: + +1. ZITADEL tries to connect to the LDAP server with or without TLS depending on the configuration +2. If the connection fails, the next server in the list will be used to try again. +3. ZITADEL tries a bind with the BindDN and BindPassword to check if it's possible to proceed +4. ZITADEL does a SearchQuery to find the UserDN with the provided configuration of base, filters and objectClasses +5. ZITADEL tries a bind with the provided loginname and password +6. LDAP attributes get mapped to ZITADEL attributes as provided by the configuration diff --git a/docs/docs/guides/integrate/identity-providers/_idps_overview.mdx b/docs/docs/guides/integrate/identity-providers/_idps_overview.mdx new file mode 100644 index 0000000000..cee2718c13 --- /dev/null +++ b/docs/docs/guides/integrate/identity-providers/_idps_overview.mdx @@ -0,0 +1,8 @@ +Go to the settings page of your instance or organization and choose "Identity Providers". + +In the table you can see all the providers you have configured. +Also, you see all provider templates that are available. + +![Identity Provider Overview](/img/guides/zitadel_identity_provider_overview.png) + +

Select the {props.templates} Provider template.

\ No newline at end of file diff --git a/docs/docs/guides/integrate/identity-providers/azure-ad.mdx b/docs/docs/guides/integrate/identity-providers/azure-ad.mdx index c61773540f..0df5152846 100644 --- a/docs/docs/guides/integrate/identity-providers/azure-ad.mdx +++ b/docs/docs/guides/integrate/identity-providers/azure-ad.mdx @@ -6,6 +6,7 @@ sidebar_label: Azure AD import GeneralConfigDescription from './_general_config_description.mdx'; import Intro from './_intro.mdx'; import CustomLoginPolicy from './_custom_login_policy.mdx'; +import IDPsOverview from './_idps_overview.mdx'; import TestSetup from './_test_setup.mdx'; import Activate from './_activate.mdx'; @@ -64,11 +65,13 @@ To be able to get all the information that ZITADEL needs, you have to configure -### Create new Azure AD Provider +### Go to the IdP Providers Overview -Go to the settings of your ZITADEL instance or the organization where you like to add a new **Azure AD** provider. -Choose the **Microsoft** provider template. -This template has everything you need preconfigured. + + +### Create a new Azure AD Provider + +The Microsoft template has everything you need preconfigured. You only have to add the client ID and secret, you have created in the step before. You can configure the following settings if you like, a useful default will be filled if you don't change anything: diff --git a/docs/docs/guides/integrate/identity-providers/github.mdx b/docs/docs/guides/integrate/identity-providers/github.mdx index aa58ee6ddd..749bb915bb 100644 --- a/docs/docs/guides/integrate/identity-providers/github.mdx +++ b/docs/docs/guides/integrate/identity-providers/github.mdx @@ -6,6 +6,7 @@ sidebar_label: GitHub import GeneralConfigDescription from './_general_config_description.mdx'; import Intro from './_intro.mdx'; import CustomLoginPolicy from './_custom_login_policy.mdx'; +import IDPsOverview from './_idps_overview.mdx'; import Activate from './_activate.mdx'; import TestSetup from './_test_setup.mdx'; import UnlinkedOAuth from './_unlinked_oauth.mdx'; @@ -49,10 +50,13 @@ Make sure to save the secret, as you will not be able to show it again. -### Create new GitHub Provider +### Go to the IdP Providers Overview -Go to the settings of your ZITADEL instance or the organization where you like to add a new GitHub provider. -Choose the GitHub provider template. This template has everything you need preconfigured. You only have to add the client ID and secret, you have created in the step before. + + +### Create a new GitHub Provider + +The GitHub provider templates have everything you need preconfigured. You only have to add the client ID and secret, you have created in the step before. You can configure the following settings if you like, a useful default will be filled if you don't change anything: diff --git a/docs/docs/guides/integrate/identity-providers/gitlab.mdx b/docs/docs/guides/integrate/identity-providers/gitlab.mdx index 25e8e19a50..64600dbd41 100644 --- a/docs/docs/guides/integrate/identity-providers/gitlab.mdx +++ b/docs/docs/guides/integrate/identity-providers/gitlab.mdx @@ -6,6 +6,7 @@ sidebar_label: GitLab import GeneralConfigDescription from './_general_config_description.mdx'; import Intro from './_intro.mdx'; import CustomLoginPolicy from './_custom_login_policy.mdx'; +import IDPsOverview from './_idps_overview.mdx'; import Activate from './_activate.mdx'; import TestSetup from './_test_setup.mdx'; import UnlinkedOAuth from './_unlinked_oauth.mdx'; @@ -50,11 +51,13 @@ Save the ID and the Secret, you will not be able to copy the secret again, if yo -### Create new GitLab Provider +### Go to the IdP Providers Overview -Go to the settings of your ZITADEL instance or the organization where you like to add a new Gitlab provider. -Choose the GitLab provider template. -This template has everything you need preconfigured. + + +### Create a new GitLab Provider + +The GitLab provider templates have everything you need preconfigured. Add the client ID and secret you have created in the Gitlab Application. You can configure the following settings if you like, a useful default will be filled if you don't change anything: diff --git a/docs/docs/guides/integrate/identity-providers/google.mdx b/docs/docs/guides/integrate/identity-providers/google.mdx index ad8ba06258..9fd0ff7abf 100644 --- a/docs/docs/guides/integrate/identity-providers/google.mdx +++ b/docs/docs/guides/integrate/identity-providers/google.mdx @@ -6,6 +6,7 @@ sidebar_label: Google import GeneralConfigDescription from './_general_config_description.mdx'; import Intro from './_intro.mdx'; import CustomLoginPolicy from './_custom_login_policy.mdx'; +import IDPsOverview from './_idps_overview.mdx'; import Activate from './_activate.mdx'; import TestSetup from './_test_setup.mdx'; @@ -33,10 +34,13 @@ import TestSetup from './_test_setup.mdx'; -### Create new Google Provider +### Go to the IdP Providers Overview -Go to the settings of your ZITADEL instance or the organization where you want to add a new Google provider. -Choose the Google provider template. This template has everything you need preconfigured. + + +### Create a new Google Provider + +The Google provider template has everything you need preconfigured. Add the client ID and secret created before on your Google App. You can configure the following settings if you like, a useful default will be filled if you don't change anything: diff --git a/docs/docs/guides/integrate/identity-providers/ldap.mdx b/docs/docs/guides/integrate/identity-providers/ldap.mdx index 632752aab5..fb573cfcff 100644 --- a/docs/docs/guides/integrate/identity-providers/ldap.mdx +++ b/docs/docs/guides/integrate/identity-providers/ldap.mdx @@ -3,41 +3,42 @@ title: Configure LDAP as Identity Provider sidebar_label: LDAP --- -import GeneralConfigDescription from './_general_config_description.mdx'; import Intro from './_intro.mdx'; +import HowLDAPIDPWorks from './_how_ldap_idp_works.mdx' import CustomLoginPolicy from './_custom_login_policy.mdx'; +import IDPsOverview from './_idps_overview.mdx'; +import GeneralConfigDescription from './_general_config_description.mdx'; import Activate from './_activate.mdx'; import TestSetup from './_test_setup.mdx'; +## How it works + + + ## ZITADEL Configuration ### Add custom login policy -### Resulting process to connect LDAP +### Go to the IdP Providers Overview -When you wnat to use a LDAP provider in ZITADEL, the following process is followed to login: + -1. ZITADEL tries to connect to the LDAP server with or without TLS depending on the configuration -2. If the connection fails, the next server in the list will be used to try again. -3. ZITADEL tries a bind with the BindDN and BindPassword to check if it's possible to proceed -4. ZITADEL does a SearchQuery to find the UserDN with the provided configuration of base, filters and objectClasses -5. ZITADEL tries a bind with the provided loginname and password -6. LDAP attributes get mapped to ZITADEL attributes as provided by the configuration +### Create a new LDAP Provider -### Create new LDAP Provider +Fill in the following fields in the LDAP template. -Go to the settings of your ZITADEL instance or the organization where you like to add a new LDAP provider. -Choose the LDAP provider template. - -To configure the LDAP template please fill out the following fields: +:::caution +We highly recommend to use LDAPS or StartTLS enable servers. +Otherwise, your users passwords are sent in clear text through the wire. +::: **Name**: Name of the identity provider -**Servers**: List of servers in a format of "schema://host:port", as example "ldap://localhost:389", if TLS should be used then replace "ldap" with "ldaps" with the corresponding port. +**Servers**: List of servers in a format of "schema://host:port", as example "ldap://localhost:389". If possible, replace "ldap" with "ldaps" with the corresponding port. **BaseDN**: BaseDN which will be used with each request to the LDAP server @@ -51,7 +52,7 @@ To configure the LDAP template please fill out the following fields: **LDAP Attributes**: Mapping of LDAP attributes to ZITADEL attributes, the ID attributes is required, the rest depends on usage of the identity provider -**StartTLS**: If this setting is enabled after the initial connection ZITADEL tries to build a TLS connection. +**StartTLS**: If this setting is enabled after the initial connection ZITADEL tries to build a TLS connection. If your LDAP server doesn't support LDAPS, at least it should support StartTLS. **Timeout**: If this setting is set all connection run with a set timeout, if it is 0s the default timeout of 60s is used. diff --git a/docs/docs/guides/integrate/identity-providers/openldap.mdx b/docs/docs/guides/integrate/identity-providers/openldap.mdx index 95b7a7b673..3b3c045130 100644 --- a/docs/docs/guides/integrate/identity-providers/openldap.mdx +++ b/docs/docs/guides/integrate/identity-providers/openldap.mdx @@ -3,14 +3,33 @@ title: Configure local OpenLDAP as Identity Provider sidebar_label: Local OpenLDAP --- -import GeneralConfigDescription from './_general_config_description.mdx'; import Intro from './_intro.mdx'; +import HowLDAPIDPWorks from './_how_ldap_idp_works.mdx' import CustomLoginPolicy from './_custom_login_policy.mdx'; +import IDPsOverview from './_idps_overview.mdx'; +import GeneralConfigDescription from './_general_config_description.mdx'; import Activate from './_activate.mdx'; import TestSetup from './_test_setup.mdx'; +:::caution +This guide shows you how you can configure an LDAP server locally. +ZITADEL needs access to the LDAP server, so this won't work in ZITADEL Cloud. +You have to spin up your own local ZITADEL. +The easiest way to do so is [by following the Docker Compose installation guide](/docs/self-hosting/deploy/compose). +::: + +:::caution +Beware that this example configuration neighter supports LDAPS nor StartTLS. +We highly recommend to enable LDAPS or StartTLS in your production setup. +Otherwise, your users passwords are sent in clear text through the wire. +::: + +## How it works + + + ## OpenLDAP Configuration ### Basic configuration @@ -135,13 +154,13 @@ ldapadd -x -h localhost -D "cn=admin,dc=example,dc=com" -f example.ldif -w 'Pass -### Create new LDAP Provider +### Go to the IdP Providers Overview -Go to the settings of your ZITADEL instance or the organization where you like to add a new LDAP provider. -Choose the LDAP provider template. + -To get basic information on what is possible to configure, please refer to the [LDAP guide](./ldap). -To configure the LDAP template to work with the before configured OpenLDAP, please fill out the following fields: +### Create a new LDAP Provider + +Fill in the template fields with the exact values listed below. The fields are described in the [LDAP guide](./ldap#create-a-new-ldap-provider). **Name**: OpenLDAP From d140f9373aa9f0d495b70b8270545a14476ceffd Mon Sep 17 00:00:00 2001 From: Miguel Cabrerizo <30386061+doncicuto@users.noreply.github.com> Date: Fri, 14 Apr 2023 15:53:19 +0200 Subject: [PATCH 27/36] feat: Zitadel translated into Spanish (#5634) * feat: spanish translation in progress * feat: 85% of translated strings * feat: spanish translation 95% done * fix: fix some typos * fix: add missing translations for recent commits * Apply suggestions from code review Co-authored-by: Livio Spring --------- Co-authored-by: Elio Bischof Co-authored-by: Livio Spring --- console/src/app/app.component.ts | 6 +- console/src/app/app.module.ts | 3 + .../login-texts/login-texts.component.ts | 2 +- .../message-texts/message-texts.component.ts | 2 +- .../pages/org-create/org-create.component.ts | 2 +- .../user-create/user-create.component.ts | 2 +- .../auth-user-detail.component.ts | 2 +- .../user-detail/user-detail.component.ts | 2 +- .../localized-date.pipe.ts | 2 +- console/src/assets/i18n/de.json | 3 + console/src/assets/i18n/en.json | 9 +- console/src/assets/i18n/es.json | 2160 +++++++++++++++++ console/src/assets/i18n/fr.json | 3 + console/src/assets/i18n/it.json | 3 + console/src/assets/i18n/ja.json | 3 + console/src/assets/i18n/pl.json | 3 + console/src/assets/i18n/zh.json | 3 + docs/docs/guides/manage/customize/texts.md | 1 + internal/api/ui/login/static/i18n/de.yaml | 3 + internal/api/ui/login/static/i18n/en.yaml | 29 +- internal/api/ui/login/static/i18n/es.yaml | 429 ++++ internal/api/ui/login/static/i18n/fr.yaml | 3 + internal/api/ui/login/static/i18n/it.yaml | 3 + internal/api/ui/login/static/i18n/ja.yaml | 3 + internal/api/ui/login/static/i18n/pl.yaml | 3 + internal/api/ui/login/static/i18n/zh.yaml | 3 + .../templates/external_not_found_option.html | 2 + internal/notification/static/i18n/es.yaml | 49 + internal/static/i18n/en.yaml | 21 +- internal/static/i18n/es.yaml | 1170 +++++++++ 30 files changed, 3894 insertions(+), 35 deletions(-) create mode 100644 console/src/assets/i18n/es.json create mode 100644 internal/api/ui/login/static/i18n/es.yaml create mode 100644 internal/notification/static/i18n/es.yaml create mode 100644 internal/static/i18n/es.yaml diff --git a/console/src/app/app.component.ts b/console/src/app/app.component.ts index edf7d83acf..33f87560f2 100644 --- a/console/src/app/app.component.ts +++ b/console/src/app/app.component.ts @@ -267,15 +267,15 @@ export class AppComponent implements OnDestroy { } private setLanguage(): void { - this.translate.addLangs(['de', 'en', 'fr', 'it', 'ja', 'pl', 'zh']); + this.translate.addLangs(['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh']); this.translate.setDefaultLang('en'); this.authService.user.subscribe((userprofile) => { if (userprofile) { const cropped = navigator.language.split('-')[0] ?? 'en'; - const fallbackLang = cropped.match(/de|en|fr|it|ja|pl|zh/) ? cropped : 'en'; + const fallbackLang = cropped.match(/de|en|es|fr|it|ja|pl|zh/) ? cropped : 'en'; - const lang = userprofile?.human?.profile?.preferredLanguage.match(/de|en|fr|it|ja|pl|zh/) + const lang = userprofile?.human?.profile?.preferredLanguage.match(/de|en|es|fr|it|ja|pl|zh/) ? userprofile.human.profile?.preferredLanguage : fallbackLang; this.translate.use(lang); diff --git a/console/src/app/app.module.ts b/console/src/app/app.module.ts index c96f47f1a0..2adbbbd8ec 100644 --- a/console/src/app/app.module.ts +++ b/console/src/app/app.module.ts @@ -2,6 +2,7 @@ import { CommonModule, registerLocaleData } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; import localeDe from '@angular/common/locales/de'; import localeEn from '@angular/common/locales/en'; +import localeEs from '@angular/common/locales/es'; import localeFr from '@angular/common/locales/fr'; import localeIt from '@angular/common/locales/it'; import localeJa from '@angular/common/locales/ja'; @@ -64,6 +65,8 @@ registerLocaleData(localeDe); i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/de.json')); registerLocaleData(localeEn); i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/en.json')); +registerLocaleData(localeEs); +i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/es.json')); registerLocaleData(localeFr); i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/fr.json')); registerLocaleData(localeIt); diff --git a/console/src/app/modules/policies/login-texts/login-texts.component.ts b/console/src/app/modules/policies/login-texts/login-texts.component.ts index e0fd61796a..a37fb71fa9 100644 --- a/console/src/app/modules/policies/login-texts/login-texts.component.ts +++ b/console/src/app/modules/policies/login-texts/login-texts.component.ts @@ -109,7 +109,7 @@ export class LoginTextsComponent implements OnInit, OnDestroy { @Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public KeyNamesArray: string[] = KeyNamesArray; - public LOCALES: string[] = ['de', 'en', 'fr', 'it', 'ja', 'pl', 'zh']; + public LOCALES: string[] = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh']; private sub: Subscription = new Subscription(); diff --git a/console/src/app/modules/policies/message-texts/message-texts.component.ts b/console/src/app/modules/policies/message-texts/message-texts.component.ts index 4150e15865..405f3732df 100644 --- a/console/src/app/modules/policies/message-texts/message-texts.component.ts +++ b/console/src/app/modules/policies/message-texts/message-texts.component.ts @@ -441,7 +441,7 @@ export class MessageTextsComponent implements OnInit, OnDestroy { }; public locale: string = 'en'; - public LOCALES: string[] = ['de', 'en', 'fr', 'it', 'ja', 'pl', 'zh']; + public LOCALES: string[] = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh']; private sub: Subscription = new Subscription(); public canWrite$: Observable = this.authService.isAllowed([ this.serviceType === PolicyComponentServiceType.ADMIN diff --git a/console/src/app/pages/org-create/org-create.component.ts b/console/src/app/pages/org-create/org-create.component.ts index ae467f8d2c..ea5053e828 100644 --- a/console/src/app/pages/org-create/org-create.component.ts +++ b/console/src/app/pages/org-create/org-create.component.ts @@ -45,7 +45,7 @@ export class OrgCreateComponent { public pwdForm?: UntypedFormGroup; public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED]; - public languages: string[] = ['de', 'en', 'fr', 'it', 'ja', 'pl', 'zh']; + public languages: string[] = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh']; public policy?: PasswordComplexityPolicy.AsObject; public usePassword: boolean = false; diff --git a/console/src/app/pages/users/user-create/user-create.component.ts b/console/src/app/pages/users/user-create/user-create.component.ts index 65bd9d084f..c875babb4b 100644 --- a/console/src/app/pages/users/user-create/user-create.component.ts +++ b/console/src/app/pages/users/user-create/user-create.component.ts @@ -33,7 +33,7 @@ import { export class UserCreateComponent implements OnInit, OnDestroy { public user: AddHumanUserRequest.AsObject = new AddHumanUserRequest().toObject(); public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED]; - public languages: string[] = ['de', 'en', 'fr', 'it', 'ja', 'pl', 'zh']; + public languages: string[] = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh']; public selected: CountryPhoneCode | undefined; public countryPhoneCodes: CountryPhoneCode[] = []; public userForm!: UntypedFormGroup; diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts index 1c86f49584..4ee1fd759d 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts @@ -33,7 +33,7 @@ import { EditDialogComponent, EditDialogType } from './edit-dialog/edit-dialog.c export class AuthUserDetailComponent implements OnDestroy { public user?: User.AsObject; public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE]; - public languages: string[] = ['de', 'en', 'fr', 'it', 'ja', 'pl', 'zh']; + public languages: string[] = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh']; private subscription: Subscription = new Subscription(); diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts index 7ff56cbc47..db6c9d9e3a 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts @@ -44,7 +44,7 @@ export class UserDetailComponent implements OnInit { public user!: User.AsObject; public metadata: Metadata.AsObject[] = []; public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE]; - public languages: string[] = ['de', 'en', 'it', 'fr', 'ja', 'pl', 'zh']; + public languages: string[] = ['de', 'en', 'es', 'it', 'fr', 'ja', 'pl', 'zh']; public ChangeType: any = ChangeType; diff --git a/console/src/app/pipes/localized-date-pipe/localized-date.pipe.ts b/console/src/app/pipes/localized-date-pipe/localized-date.pipe.ts index 62345ba116..b0ff4f2095 100644 --- a/console/src/app/pipes/localized-date-pipe/localized-date.pipe.ts +++ b/console/src/app/pipes/localized-date-pipe/localized-date.pipe.ts @@ -22,7 +22,7 @@ export class LocalizedDatePipe implements PipeTransform { return moment(value).format(`${format}, HH:mm`); } } else { - const lang = ['de', 'en', 'fr', 'it', 'ja', 'pl', 'zh'].includes(this.translateService.currentLang) + const lang = ['de', 'en', 'es', 'fr', 'it', 'ja', 'pl', 'zh'].includes(this.translateService.currentLang) ? this.translateService.currentLang : 'en'; const datePipe: DatePipe = new DatePipe(lang); diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 11759e3830..39810d6ddd 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1019,6 +1019,7 @@ "LANGUAGE": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -1217,6 +1218,7 @@ "LOCALES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -2078,6 +2080,7 @@ "LANGUAGES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 758b8ffed6..0b8198e390 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1020,6 +1020,7 @@ "LANGUAGE": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -1149,6 +1150,10 @@ "MAXSIZEEXCEEDED": "Maximum size of 524kB exceeded.", "NOSVGSUPPORTED": "SVG are not supported!", "FONTINLOGINONLY": "The font is currently only displayed in the login interface.", + "BACKGROUNDCOLOR": "Background color", + "PRIMARYCOLOR": "Primary color", + "WARNCOLOR": "Warning color", + "FONTCOLOR": "Font color", "VIEWS": { "PREVIEW": "Preview", "CURRENT": "Current Configuration" @@ -1209,11 +1214,12 @@ "RESET_TITLE": "Restore Default Values", "RESET_DESCRIPTION": "You are about to restore all default values. All changes you have made will be permanently deleted. Do you really want to continue?", "UNSAVED_TITLE": "Continue without saving?", - "UNSAVED_DESCRIPTION": "Your have made changes without saving. Do you want to save now?", + "UNSAVED_DESCRIPTION": "You have made changes without saving. Do you want to save now?", "LOCALE": "Locale Code", "LOCALES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -2071,6 +2077,7 @@ "LANGUAGES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json new file mode 100644 index 0000000000..3514cf8132 --- /dev/null +++ b/console/src/assets/i18n/es.json @@ -0,0 +1,2160 @@ +{ + "APP_NAME": "ZITADEL", + "PAGINATOR": { + "PREVIOUS": "Anterior", + "NEXT": "Siguiente", + "COUNT": "Resultados totales", + "MORE": "Más" + }, + "FOOTER": { + "LINKS": { + "CONTACT": "Contacto", + "TOS": "Términos de servicio", + "PP": "Política de privacidad" + }, + "THEME": { + "DARK": "Oscuro", + "LIGHT": "Claro" + } + }, + "HOME": { + "WELCOME": "Comienza con ZITADEL", + "DISCLAIMER": "ZITADEL trata tus datos confidencialmente y de forma segura.", + "DISCLAIMERLINK": "Más información", + "DOCUMENTATION": { + "TITLE": "Documentación", + "DESCRIPTION": "Comienza rápidamente con ZITADEL." + }, + "GETSTARTED": { + "TITLE": "Comienza con ZITADEL", + "DESCRIPTION": "Comienza rápidamente con ZITADEL." + }, + "QUICKSTARTS": { + "LABEL": "Primeros pasos", + "TITLE": "Guías de inicio rápido", + "DESCRIPTION": "Comienza rápidamente con ZITADEL." + }, + "SHORTCUTS": { + "SHORTCUTS": "Accesos directos", + "SETTINGS": "Accesos directos disponibles", + "PROJECTS": "Proyectos", + "REORDER": "Mantén y arrastra el panel para moverlo", + "ADD": "Mantén y arrastrq el panel para añadirlo" + } + }, + "ONBOARDING": { + "DESCRIPTION": "Tu proceso de embarque", + "COMPLETED": "completado", + "MOREDESCRIPTION": "más atajos", + "DISMISS": "No gracias, soy pro.", + "CARD": { + "TITLE": "Ponte en marcha con ZITADEL", + "DESCRIPTION": "Esta lista de tareas te ayuda a configurar tu instancia y te guía por los pasos más esenciales" + }, + "EVENTS": { + "instance.policy.label.added": { + "title": "Configura tu imagen de marca", + "description": "Define el esquema de colores, da forma a tu inicio de sesión y sube tu logo y tus iconos.", + "action": "Configurar imagen" + }, + "instance.smtp.config.added": { + "title": "Establece tu configuración SMTP", + "description": "Introduce la configuración de tu propio servidor de correo.", + "action": "Configurar SMTP" + }, + "project.added": { + "title": "Crea tu primer proyecto", + "description": "Añade tu primer proyecto y define sus roles y autorizaciones.", + "action": "Crear proyecto" + }, + "project.application.added": { + "title": "Crea tu primera aplicación", + "description": "Crea una aplicación web, nativa, api o saml y configura tu flujo de autenticación.", + "action": "Crear app" + }, + "user.human.added": { + "title": "Añade usuarios", + "description": "Añade los usuarios de tu aplicación", + "action": "Añadir usuario" + }, + "user.grant.added": { + "title": "Concede permisos", + "description": "Permite a tus usuarios acceder a tu aplicación y configura sus roles.", + "action": "Conceder permisos" + } + } + }, + "MENU": { + "INSTANCE": "Instancia", + "DASHBOARD": "Inicio", + "PERSONAL_INFO": "Información personal", + "DOCUMENTATION": "Documentación", + "INSTANCEOVERVIEW": "Instancia", + "ORGS": "Organizaciones", + "VIEWS": "Vistas", + "EVENTS": "Eventos", + "FAILEDEVENTS": "Eventos fallidos", + "ORGANIZATION": "Organización", + "DOMAINS": "Dominios", + "PROJECT": "Proyectos", + "PROJECTOVERVIEW": "Resumen", + "PROJECTGRANTS": "Concesiones", + "ROLES": "Roles", + "GRANTEDPROJECT": "Proyecto concedido", + "HUMANUSERS": "Usuarios", + "MACHINEUSERS": "Usuarios del servicio", + "LOGOUT": "Cerrar sesión para todos los usuarios", + "NEWORG": "Nueva organización", + "IAMADMIN": "Eres un administrador IAM. Ten en cuenta que tienes permisos ampliados.", + "SHOWORGS": "Mostrar todas las organizaciones", + "GRANTS": "Autorizaciones", + "ACTIONS": "Acciones", + "PRIVACY": "Privacidad", + "TOS": "Términos de Servicio", + "OPENSHORTCUTSTOOLTIP": "Pulsa ? para mostrar los atajos de teclado", + "SETTINGS": "Ajustes", + "CUSTOMERPORTAL": "Portal del cliente" + }, + "ACTIONS": { + "ACTIONS": "Acciones", + "FILTER": "Filtrar", + "RENAME": "Renombrar", + "SET": "Establecer", + "COPY": "Copiar al portapapeles", + "COPIED": "Copiado al portapapeles.", + "RESET": "Restablecer", + "RESETDEFAULT": "Restablecer a valores por defecto", + "RESETTO": "Restablecer a: ", + "RESETCURRENT": "Restablecer a actual", + "SHOW": "Mostrar", + "HIDE": "Ocultar", + "SAVE": "Guardar", + "SAVENOW": "Guardar ahora", + "NEW": "Nuevo", + "ADD": "Añadir", + "CREATE": "Crear", + "CONTINUE": "Continuar", + "BACK": "Atrás", + "CLOSE": "Cerrar", + "CLEAR": "Limpiar", + "CANCEL": "Cancelar", + "INFO": "Info", + "OK": "OK", + "SELECT": "Seleccionar", + "VIEW": "Mostrar", + "SELECTIONDELETE": "Borrar selección", + "DELETE": "Borrar", + "REMOVE": "Eliminar", + "VERIFY": "Verificar", + "FINISH": "Finalizar", + "FINISHED": "Cerrar", + "CHANGE": "Cambiar", + "REACTIVATE": "Reactivar", + "ACTIVATE": "Activar", + "DEACTIVATE": "Desactivar", + "REFRESH": "Actualizar", + "LOGIN": "Iniciar sesión", + "EDIT": "Editar", + "PIN": "Marcar / Desmarcar", + "CONFIGURE": "Configurar", + "SEND": "Enviar", + "NEWVALUE": "Nuevo valor", + "RESTORE": "Restaurar", + "CONTINUEWITHOUTSAVE": "Continuar sin guardar", + "OF": "de", + "PREVIOUS": "Anterior", + "NEXT": "Siguiente", + "MORE": "más", + "STEP": "Paso", + "SETUP": "Configurar", + "COMINGSOON": "Próximamente", + "TABLE": { + "SHOWUSER": "Mostrar usuario {{value}}" + } + }, + "MEMBERROLES": { + "IAM_OWNER": "Tiene control sobre toda la instancia, incluyendo todas las organizaciones", + "IAM_OWNER_VIEWER": "Tiene permiso para revisar toda la instancia, incluyendo todas las organizaciones", + "IAM_ORG_MANAGER": "Tiene permiso para crear y gestionar organizaciones", + "IAM_USER_MANAGER": "Tiene permiso para crear y gestionar usuarios", + "ORG_OWNER": "Tiene permisos sobre toda la organización", + "ORG_USER_MANAGER": "Tiene permiso para crear y gestionar usuarios de la organización", + "ORG_OWNER_VIEWER": "TIene permiso para revisar toda la organización", + "ORG_USER_PERMISSION_EDITOR": "Tiene permiso para gestionar concesiones de usuario", + "ORG_PROJECT_PERMISSION_EDITOR": "Tiene permiso para gestionar concesiones de proyecto", + "ORG_PROJECT_CREATOR": "Tiene permiso para crear sus propios proyectos y ajustes subyacentes", + "PROJECT_OWNER": "Tiene permiso sobre todo el proyecto", + "PROJECT_OWNER_VIEWER": "Tiene permiso para revisar todo el proyecto", + "PROJECT_OWNER_GLOBAL": "Tiene permiso sobre todo el proyecto", + "PROJECT_OWNER_VIEWER_GLOBAL": "Tiene permiso para revisar todo el proyecto", + "PROJECT_GRANT_OWNER": "Tiene permiso para gestionar la concesión del proyecto", + "PROJECT_GRANT_OWNER_VIEWER": "Tiene permiso para revisar la concesión del proyecto" + }, + "OVERLAYS": { + "ORGSWITCHER": { + "TEXT": "Todos los ajustes de la organización y las tablas de la consola se basan en la organización seleccionada. Haz clic en este botón para cambiar la organización o crear una nueva." + }, + "INSTANCE": { + "TEXT": "Haz clic aquí para obtener las configuraciones de instancia. Ten en cuenta que sólo tienes acceso a este botón si tienes permisos aumentados." + }, + "PROFILE": { + "TEXT": "Aquí puedes cambiar entre tus cuentas de usuario y gestionar tus sesiones y perfiles." + }, + "NAV": { + "TEXT": "Esta navegación cambia de acuerdo a tu organización seleccionada arriba o a tu instancia" + }, + "CONTEXTCHANGED": { + "TEXT": "¡Atención! El contexto de la organización ha cambiado." + } + }, + "FILTER": { + "TITLE": "Filtrar", + "STATE": "Estado", + "DISPLAYNAME": "Nombre mostrado", + "EMAIL": "Email", + "USERNAME": "Nombre de usuario", + "ORGNAME": "Nombre de organización", + "PROJECTNAME": "Nombre de proyecto", + "RESOURCEOWNER": "Propietario del recurso", + "METHODS": { + "5": "contiene", + "7": "finaliza con", + "1": "igual" + } + }, + "KEYBOARDSHORTCUTS": { + "TITLE": "Atajos de teclado", + "UNDERORGCONTEXT": "Dentro de las páginas de la organización", + "SIDEWIDE": "Atajos para todo el sitio", + "SHORTCUTS": { + "HOME": "Go to Home", + "INSTANCE": "Go to Iinstance", + "ORG": "Go to Organization", + "ORGSETTINGS": "Go to Organization Settings", + "ORGSWITCHER": "Cambiar organización", + "ME": "Ir a tu propio perfil", + "PROJECTS": "Go to Projects", + "USERS": "Go to Users", + "USERGRANTS": "Go to Authorizations", + "ACTIONS": "Go to Actions and Flows", + "DOMAINS": "Go to Domains" + } + }, + "RESOURCEID": "Id de recurso", + "NAME": "Nombre", + "VERSION": "Versión", + "TABLE": { + "NOROWS": "No hay datos" + }, + "ERRORS": { + "REQUIRED": "Por favor rellena este campo.", + "TOKENINVALID": { + "TITLE": "Tu token de autorización token ha caducado.", + "DESCRIPTION": "Haz clic en el botón más abajo para iniciar sesión otra vez." + }, + "INVALID_FORMAT": "El formato no es valido.", + "NOTANEMAIL": "El valor proporcionado no es una dirección de email.", + "MINLENGTH": "Debe tener al menos {{requiredLength}} caracteres de longitud.", + "UPPERCASEMISSING": "Debe incluir un carácter en mayúscula.", + "LOWERCASEMISSING": "Debe incluir un carácter en minúscula.", + "SYMBOLERROR": "Debe incluir un símbolo o un signo de puntuación.", + "NUMBERERROR": "Debe incluir un dígito.", + "PWNOTEQUAL": "Las contraseñas proporcionadas no coinciden.", + "PHONE": "El número de teléfono debe comenzar con 00 o +." + }, + "USER": { + "SETTINGS": { + "TITLE": "Ajustes", + "GENERAL": "General", + "IDP": "Proveedores de identidad (IDP)", + "SECURITY": "Contraseñas y seguridad", + "KEYS": "Claves", + "PAT": "Tokens de acceso personal", + "USERGRANTS": "Autorizaciones", + "MEMBERSHIPS": "Membresías", + "METADATA": "Metadatos" + }, + "TITLE": "Información personal", + "DESCRIPTION": "Gestionar tu información y configuraciones de seguridad.", + "PAGES": { + "LIST": "Usuarios", + "TITLE": "Usuario", + "DESCRIPTION": "Crear nuevos usuarios en tu organización y gestionar el resto de organizaciones.", + "LISTMACHINE": "Usuarios del servicio", + "DESCRIPTIONMACHINE": "Crea y gestiona usuarios del servicio de tu organización", + "DETAIL": "Detalle", + "CREATE": "Crear", + "MY": "Mi información", + "LOGINNAMES": "Nombres de inicio de sesión", + "LOGINMETHODS": "Métodos de inicio de sesión", + "LOGINNAMESDESC": "Estos son tus nombres de inicio de sesión:", + "NOUSER": "No existen usuarios asociados.", + "REACTIVATE": "Reactivar", + "DEACTIVATE": "Desactivar", + "FILTER": "Filtrar", + "STATE": "Estado", + "DELETE": "Borrar usuario", + "UNLOCK": "Desbloquear usuario", + "GENERATESECRET": "Generar secreto del cliente", + "REMOVESECRET": "Borrar secreto del cliente", + "LOCKEDDESCRIPTION": "Este usuario ha sido bloqueado debido a que ha excedido el número máximo de intentos de inicio de sesión y debe desbloquearse para poder usarse otra vez.", + "DELETEACCOUNT": "Borrar cuenta", + "DELETEACCOUNT_DESC": "Si realizas esta acción, se cerrará tu sesión y no podrás volver a tener acceso a tu cuenta. Esta acción no es reversible, por favor procede con cuidado.", + "DELETEACCOUNT_BTN": "Borrar cuenta", + "DELETEACCOUNT_SUCCESS": "¡La cuenta se borró con éxito!" + }, + "DETAILS": { + "DATECREATED": "Creada", + "DATECHANGED": "Cambiada" + }, + "DIALOG": { + "DELETE_TITLE": "Borrar usuario", + "DELETE_SELF_TITLE": "Borrar cuenta", + "DELETE_DESCRIPTION": "Está a punto de borrar permanentemente un usuario. ¿Está seguro?", + "DELETE_SELF_DESCRIPTION": "Estás a punto de borrar permanentemente tu cuenta personal. Esto cerrará tu sesión y borrará tu usuario. ¡Esta acción no puede deshacerse!", + "DELETE_AUTH_DESCRIPTION": "Estás a punto de borrar permanentemente tu cuenta personal. ¿Estás seguro?", + "TYPEUSERNAME": "Escribe '{{value}}', para confirmar y borrar el usuario.", + "USERNAME": "Nombre de inicio de sesión", + "DELETE_BTN": "Borrar permanentemente" + }, + "SENDEMAILDIALOG": { + "TITLE": "Enviar notificación por email", + "DESCRIPTION": "Haz clic en el siguiente botón para enviar una notificación a la dirección de email actual o cambia la dirección de email en el campo habilitado.", + "NEWEMAIL": "Nueva dirección de email" + }, + "SECRETDIALOG": { + "CLIENTSECRET": "Secreto de cliente", + "CLIENTSECRET_DESCRIPTION": "Mantén tu secreto de cliente en un lugar seguro puesto que desaparecerá una vez que se cierre el diálogo." + }, + "TABLE": { + "DEACTIVATE": "Desactivar", + "ACTIVATE": "Activar", + "CHANGEDATE": "Última modificación", + "CREATIONDATE": "Creado el", + "TYPES": { + "HUMAN": "Usuarios", + "MACHINE": "Usuarios del servicio" + }, + "FILTER": { + "0": "Filtrar por nombre mostrado", + "1": "Filtrar por nombre de usuario", + "2": "Filtrar por nombre mostrado", + "3": "Filtrar por nombre de usuario", + "4": "filtrar por email", + "5": "filtrar for nombre mostrado", + "10": "filtrar por nombre de organización", + "12": "filtrar por nombre de proyecto" + }, + "EMPTY": "Sin entradas" + }, + "PASSWORDLESS": { + "SEND": "Enviar enlace de registro", + "TABLETYPE": "Tipo", + "TABLESTATE": "Estado", + "NAME": "Nombre", + "EMPTY": "No se han configurado dispositivos", + "TITLE": "Autenticación sin contraseña", + "DESCRIPTION": "Añadir métodos de autenticación basados en WebAuthn para iniciar sesión en ZITADEL sin contraseña.", + "MANAGE_DESCRIPTION": "Gestiona los métodos de doble factor de tus usuarios.", + "U2F": "Añadir método", + "U2F_DIALOG_TITLE": "Verificar autenticador", + "U2F_DIALOG_DESCRIPTION": "Introduce un nombre para el inicio de sesión sin contraseña utilizado", + "U2F_SUCCESS": "¡La autenticación sin contraseña se creó con éxito!", + "U2F_ERROR": "¡Se ha producido un error durante la configuración!", + "U2F_NAME": "Nombre de autenticador", + "TYPE": { + "0": "No se ha definido MFA", + "1": "One Time Password (OTP)", + "2": "Huella dactilar, claves de seguridad, Face ID y otros" + }, + "STATE": { + "0": "Sin estado", + "1": "No está listo", + "2": "Listo", + "3": "Borrado" + }, + "DIALOG": { + "DELETE_TITLE": "Eliminar método de autenticación sin contraseña", + "DELETE_DESCRIPTION": "Estás a punto de borrar un método de autenticación sin contraseña. ¿Estás seguro?", + "ADD_TITLE": "Autenticación sin contraseña", + "ADD_DESCRIPTION": "Selecciona una de las opciones disponibles para crear un método de autenticación sin contraseña.", + "SEND_DESCRIPTION": "Envíate a ti mismo un enlace de registro a tu dirección de email.", + "SEND": "Enviar enlace de registro", + "SENT": "El email fue enviado con éxito. Comprueba tu buzón para continuar con la configuración.", + "QRCODE_DESCRIPTION": "Genera un código QR para escanearlo con otro dispositivo.", + "QRCODE": "Generar código QR", + "QRCODE_SCAN": "Escanea este código QR para continuar con la configuración de tu dispositivo.", + "NEW_DESCRIPTION": "Utiliza este dispositivo para configurar la autenticación sin contraseña.", + "NEW": "Añadir nueva" + } + }, + "MFA": { + "TABLETYPE": "Tipo", + "TABLESTATE": "Estado", + "NAME": "Nombre", + "EMPTY": "No hay factores adicionales", + "TITLE": "Autenticación multifactor (MFA)", + "DESCRIPTION": "Añade un doble factor para configurar una seguridad óptima de tu cuenta.", + "MANAGE_DESCRIPTION": "Gestiona el método de doble factor para tus usuarios.", + "ADD": "Añadir factor", + "OTP": "App autenticadora para OTP (One-Time Password)", + "OTP_DIALOG_TITLE": "Añadir OTP", + "OTP_DIALOG_DESCRIPTION": "Escanea el código QR con una app autenticadora introduciendo el siguiente código para verificar y activar el método OTP.", + "U2F": "Huella dactilar, claves de seguridad, Face ID y otros", + "U2F_DIALOG_TITLE": "Verificar factor", + "U2F_DIALOG_DESCRIPTION": "Introduce un nombre para tu Multifactor universal.", + "U2F_SUCCESS": "¡Factor añadido con éxito!", + "U2F_ERROR": "¡Se produjo un error durante la configuración!", + "U2F_NAME": "Nombre del autenticador", + "TYPE": { + "0": "No se ha definido MFA", + "1": "One Time Password (OTP)", + "2": "Huella dactilar, claves de seguridad, Face ID y otros" + }, + "STATE": { + "0": "Sin estado", + "1": "No está listo", + "2": "Listo", + "3": "Borrado" + }, + "DIALOG": { + "MFA_DELETE_TITLE": "Eliminar doble factor", + "MFA_DELETE_DESCRIPTION": "Estás a punto de borrar un doble factor. ¿Estás seguro?", + "ADD_MFA_TITLE": "Añadir doble factor", + "ADD_MFA_DESCRIPTION": "Selecciona una de las siguientes opciones." + } + }, + "EXTERNALIDP": { + "TITLE": "Proveedor de identidad (IDP) externo", + "DESC": "", + "IDPCONFIGID": "IDP Config ID", + "IDPNAME": "Nombre de IDP", + "USERDISPLAYNAME": "Nombre externo", + "EXTERNALUSERID": "ID de usuario externo", + "EMPTY": "No se encontró el IDP externo", + "DIALOG": { + "DELETE_TITLE": "Eliminar IDP", + "DELETE_DESCRIPTION": "Estas a punto de borrar un proveedor de identidad (IDP) para un usuario. ¿Quieres continuar?" + } + }, + "CREATE": { + "TITLE": "Crear un nuevo usuario", + "DESCRIPTION": "Por favor, proporciona la información necesaria.", + "NAMEANDEMAILSECTION": "Nombre y email", + "GENDERLANGSECTION": "Género e idioma", + "PHONESECTION": "Números de teléfono", + "PASSWORDSECTION": "Contraseña inicial", + "ADDRESSANDPHONESECTION": "Número de teléfono", + "INITMAILDESCRIPTION": "Si ambas opciones se seleccionan, no se enviará un email para la inicialización. Si solo una de las opciones se selecciona, un email se enviará para proporcionar / verificar los datos." + }, + "CODEDIALOG": { + "TITLE": "Verificar número de teléfono", + "DESCRIPTION": "Introducir el código que has recibido por mensaje de texto para verificar tu número de teléfono.", + "CODE": "Código" + }, + "DATA": { + "STATE": "Estado", + "STATE0": "Desconocido", + "STATE1": "Activo", + "STATE2": "Inactivo", + "STATE3": "Borrado", + "STATE4": "Bloqueado", + "STATE5": "Suspendido", + "STATE6": "Inicial" + }, + "PROFILE": { + "TITLE": "Perfil", + "EMAIL": "Email", + "PHONE": "Número de teléfono", + "PHONE_HINT": "Usa 00 o el símbolo + seguido del prefijo del país o selecciona el país del menú desplegable y finalmente introduce el número de teléfono", + "USERNAME": "Nombre de usuario", + "CHANGEUSERNAME": "modificar", + "CHANGEUSERNAME_TITLE": "Cambiar nombre de usuario", + "CHANGEUSERNAME_DESC": "Introduce el nuevo nombre en el campo siguiente.", + "FIRSTNAME": "Nombre", + "LASTNAME": "Apellidos", + "NICKNAME": "Apodo", + "DISPLAYNAME": "Nombre mostrado", + "PREFERRED_LANGUAGE": "Idioma", + "GENDER": "Género", + "PASSWORD": "Contraseña", + "AVATAR": { + "UPLOADTITLE": "Subir tu imagen de perfil", + "UPLOADBTN": "Elegir fichero", + "UPLOAD": "Subir", + "CURRENT": "Imagen actual", + "PREVIEW": "Previsualizar", + "DELETESUCCESS": "¡Borrada con éxito!", + "CROPPERERROR": "Se produjo un error mientras se subía el fichero. Inténtalo con un formato o tamaño diferente si es necesario." + }, + "COUNTRY": "País" + }, + "MACHINE": { + "TITLE": "Detalles del usuario del servicio", + "USERNAME": "Nombre de usuario", + "NAME": "Nombre", + "DESCRIPTION": "Descripción", + "KEYSTITLE": "Claves", + "KEYSDESC": "Define tus claves y añade una fecha de caducidad opcional.", + "TOKENSTITLE": "Tokens de acceso personal", + "TOKENSDESC": "Los tokens de acceso personal funcionan como tokens de acceso OAuth ordinarios.", + "ID": "ID de clave", + "TYPE": "Tipo", + "EXPIRATIONDATE": "Fecha de caducidad", + "CHOOSEDATEAFTER": "Introduce una fecha de caducidad válida", + "CHOOSEEXPIRY": "Selecciona una fecha de caducidad", + "CREATIONDATE": "Fecha de creación", + "KEYDETAILS": "Detalles de la clave", + "ACCESSTOKENTYPE": "Tipo de token de acceso", + "ACCESSTOKENTYPES": { + "0": "Bearer", + "1": "JWT" + }, + "ADD": { + "TITLE": "Añadir clave", + "DESCRIPTION": "Selecciona tu tipo de clave y elige una fecha de caducidad opcional." + }, + "ADDED": { + "TITLE": "La clave fue creada", + "DESCRIPTION": "¡Descarga la clave puesto que no será visible una vez que se cierre este diálogo!" + }, + "KEYTYPES": { + "1": "JSON" + }, + "DIALOG": { + "DELETE_KEY": { + "TITLE": "Borrar clave", + "DESCRIPTION": "¿Quieres borrar la clave seleccionada? Esta acción no puede deshacerse." + } + } + }, + "PASSWORD": { + "TITLE": "Contraseña", + "LABEL": "Una contraseña segura ayuda a proteger la cuenta", + "DESCRIPTION": "Introduce la nueva contraseña de acuerdo con la siguiente política.", + "OLD": "Contraseña actual", + "NEW": "Nueva contraseña", + "CONFIRM": "Confirmar nueva contraseña", + "NEWINITIAL": "Contraseña", + "CONFIRMINITIAL": "Confirmar contraseña", + "RESET": "Restablecer contraseña actual", + "SET": "Establecer nueva contraseña", + "RESENDNOTIFICATION": "Enviar enlace para restablecer la contraseña", + "REQUIRED": "Faltan algunos campos requeridos.", + "MINLENGTHERROR": "Debe tener al menos {{value}} caracteres de longitud." + }, + "ID": "ID", + "EMAIL": "Email", + "PHONE": "Número de teléfono", + "PHONEEMPTY": "No se ha definido un número de teléfono", + "PHONEVERIFIED": "Número de teléfono verificado.", + "EMAILVERIFIED": "Email verificado", + "NOTVERIFIED": "no verificado", + "PREFERRED_LOGINNAME": "Nombre de inicio de sesión preferido", + "ISINITIAL": "El usuario no está activo, aún.", + "LOGINMETHODS": { + "TITLE": "Información de contacto", + "DESCRIPTION": "La información proporcionada se utiliza para enviarte información importante, como emails para restablecer la contraseña.", + "EMAIL": { + "TITLE": "Email", + "VALID": "validado", + "ISVERIFIED": "Email verificado", + "ISVERIFIEDDESC": "Si el email se muestra como verificado, no se realizará ninguna solicitud de verificación del email.", + "RESEND": "Reenviar correo de verificación", + "EDITTITLE": "Cambiar email", + "EDITDESC": "Introduce el nuevo email en el siguiente campo." + }, + "PHONE": { + "TITLE": "Teléfono", + "VALID": "validado", + "RESEND": "Reenviar mensaje de texto de verificación", + "EDITTITLE": "Cambiar número", + "EDITVALUE": "Número de teléfono", + "EDITDESC": "Introduce el número de teléfono en el siguiente campo.", + "DELETETITLE": "Borrar número de teléfono", + "DELETEDESC": "Estás seguro que quieres borrar el número de teléfono" + }, + "RESENDCODE": "Reenviar el código", + "ENTERCODE": "Verificar", + "ENTERCODE_DESC": "Verificar el código" + }, + "GRANTS": { + "TITLE": "Concesiones de usuario", + "DESCRIPTION": "Concede a este usuario acceso a ciertos proyectos", + "CREATE": { + "TITLE": "Crear concesión de usuario", + "DESCRIPTION": "Buscar la organización, el proyecto y los correspondientes roles de proyecto." + }, + "PROJECTNAME": "Nombre del proyecto", + "PROJECT-OWNED": "Proyecto", + "PROJECT-GRANTED": "Proyecto concedido", + "FILTER": { + "0": "filtrar por usuario", + "1": "filtrar por dominio", + "2": "filtrar por nombre de proyecto", + "3": "filtrar por rol" + } + }, + "STATE": { + "0": "Desconocido", + "1": "Activo", + "2": "Inactivo", + "3": "Borrado", + "4": "Bloqueado", + "5": "Suspendido", + "6": "Inicial" + }, + "SEARCH": { + "ADDITIONAL": "Nombre de inicio de sesión (organización actual)", + "ADDITIONAL-EXTERNAL": "Nombre de inicio de sesión (organización externa)" + }, + "TARGET": { + "SELF": "Si quieres conceder acceso a un usuario de otra organización", + "EXTERNAL": "Para conceder acceso a un usuario a tu organización", + "CLICKHERE": "hacer clic aquí" + }, + "SIGNEDOUT": "Has cerrado sesión. Haz clic en el botón \"Iniciar sesión\" para acceder otra vez.", + "SIGNEDOUT_BTN": "Iniciar sesión", + "EDITACCOUNT": "Editar cuenta", + "ADDACCOUNT": "Iniciar sesión con otra cuenta", + "RESENDINITIALEMAIL": "Reenviar correo de activación", + "RESENDEMAILNOTIFICATION": "Reenviar correo de notificación", + "TOAST": { + "CREATED": "Usuario creado con éxito.", + "SAVED": "Perfil guardado con éxito.", + "USERNAMECHANGED": "Nombre de usuario modificado.", + "EMAILSAVED": "El email se guardó con éxito.", + "INITEMAILSENT": "Enviado correo de inicialización.", + "PHONESAVED": "Teléfono guardado con éxito.", + "PHONEREMOVED": "El telefono se ha eliminado.", + "PHONEVERIFIED": "Teléfono verificado con éxito.", + "PHONEVERIFICATIONSENT": "Enviado código de verificación para el teléfono.", + "EMAILVERIFICATIONSENT": "Enviado código de verificación para el email.", + "OTPREMOVED": "OTP eliminado.", + "U2FREMOVED": "Factor eliminado.", + "PASSWORDLESSREMOVED": "Acceso sin contraseña eliminado.", + "INITIALPASSWORDSET": "Se estableció una contraseña inicial.", + "PASSWORDNOTIFICATIONSENT": "Se envió una notificación de cambio de contraseña.", + "PASSWORDCHANGED": "La contraseña se cambió con éxito.", + "REACTIVATED": "Usuario reactivado.", + "DEACTIVATED": "Usuario desactivado.", + "SELECTEDREACTIVATED": "Se reactivaron los usuarios seleccionados.", + "SELECTEDDEACTIVATED": "Se desactivaron los usuarios seleccionados.", + "SELECTEDKEYSDELETED": "Se borraron las claves seleccionadas.", + "KEYADDED": "¡Clave añadida!", + "MACHINEADDED": "¡Creado servicio de usuario!", + "DELETED": "¡El usuario se borró con éxito!", + "UNLOCKED": "¡El usuario se desbloqueó con éxito!", + "PASSWORDLESSREGISTRATIONSENT": "Enviado con éxito un enlace de registro.", + "SECRETGENERATED": "¡Secreto generado con éxito!", + "SECRETREMOVED": "¡Secreto eliminado con éxito!" + }, + "MEMBERSHIPS": { + "TITLE": "Roles de Mánager ZITADEL", + "DESCRIPTION": "Estas son todas las concesiones de membresía del usuario. Puedes modificarlas también en las páginas de organización, proyecto o detalles IAM.", + "ORGCONTEXT": "Puedes ver todas las organizaciones y proyectos que están relacionados con la organización seleccionada actualmente.", + "USERCONTEXT": "Puedes ver todas las organizaciones y proyectos para los que estás autorizado. Incluyendo otras organizaciones.", + "CREATIONDATE": "Fecha de creación", + "CHANGEDATE": "Última modificación", + "DISPLAYNAME": "Nombre mostrado", + "REMOVE": "Eliminar", + "TYPE": "Tipo", + "ORGID": "ID de organización", + "UPDATED": "La membresía fue actualizada.", + "NOPERMISSIONTOEDIT": "¡No dispones de los permisos requeridos para editar roles!", + "TYPES": { + "UNKNOWN": "Desconocido", + "ORG": "Organización", + "PROJECT": "Proyecto", + "GRANTEDPROJECT": "Proyecto concedidos" + } + }, + "PERSONALACCESSTOKEN": { + "ID": "ID", + "TOKEN": "Token", + "ADD": { + "TITLE": "Generar nuevo token de acceso personal", + "DESCRIPTION": "Define una fecha de caducidad personalizada para el token.", + "CHOOSEEXPIRY": "Selecciona una fecha de caducidad", + "CHOOSEDATEAFTER": "Introduce una fecha válida para que los tokens comiencen a caducar" + }, + "ADDED": { + "TITLE": "Token de acceso personal", + "DESCRIPTION": "Asegúrate de copiar tu token personal de acceso. ¡No podrás volver a verlo otra vez!" + }, + "DELETE": { + "TITLE": "Borrar token", + "DESCRIPTION": "Estás a punto de borrar el token de acceso personal. ¿Estás seguro?" + }, + "DELETED": "El token se borró con éxito." + } + }, + "METADATA": { + "TITLE": "Metadatos", + "DESCRIPTION": "", + "KEY": "Clave", + "VALUE": "Valor", + "ADD": "Nueva entrada", + "SAVE": "Guardar", + "EMPTY": "Sin metadatos", + "SETSUCCESS": "El elemento se guardó con éxito", + "REMOVESUCCESS": "El elemento se borró con éxito" + }, + "FLOWS": { + "TITLE": "Acciones y flujos", + "DESCRIPTION": "Define scripts que se ejecutarán ante un evento determinado.", + "ACTIONSTITLE": "Acciones", + "ACTIONSDESCRIPTION": "Estos son los scripts que puedes ejecutar en los flujos.", + "FLOWSTITLE": "Flujos", + "FLOWSDESCRIPTION": "Flujos de un cierto tipo ejecutan acciones cuando se activa un disparador.", + "ID": "ID", + "NAME": "Nombre", + "STATE": "Estado", + "STATES": { + "0": "sin estado", + "1": "inactivo", + "2": "activo" + }, + "ADDTRIGGER": "Añadir disparador", + "FLOWCHANGED": "El flujo se cambió con éxito", + "FLOWCLEARED": "El flujo se restableció con éxito", + "TIMEOUT": "Timeout", + "TIMEOUTINSEC": "Timeout en segundos", + "ALLOWEDTOFAIL": "Permitido el fallo", + "SCRIPT": "Script", + "FLOWTYPE": "Tipo de flujo", + "TRIGGERTYPE": "Tipo de disparador", + "ACTIONS": "Acciones", + "ACTIONSMAX": "Basado en tu Tier, tienes disponible un número limitado de acciones ({{value}}). Asegúrate de desactivar aquellos que no necesitas o considera mejorar tu tier.", + "DIALOG": { + "ADD": { + "TITLE": "Crear una acción" + }, + "UPDATE": { + "TITLE": "Actualizar acción" + }, + "DELETEACTION": { + "TITLE": "¿Borrar acción?", + "DESCRIPTION": "Estás a punto de borrar una acción. Esto no puede revertirse. ¿Estás seguro?", + "DELETE_SUCCESS": "La acción se borró con éxito." + }, + "CLEAR": { + "TITLE": "¿Limpiar flujo?", + "DESCRIPTION": "Estás a punto de restablecer el flujo junto con sus disparadores y acciones. Este cambio no puede revertirse. ¿Estás seguro?" + }, + "REMOVEACTIONSLIST": { + "TITLE": "¿Borrar acciones seleccionadas?", + "DESCRIPTION": "¿Estás seguro que quieres borrar las acciones seleccionadas del flujo?" + } + }, + "TOAST": { + "ACTIONSSET": "Acciones establecidas", + "ACTIONREACTIVATED": "Acciones reactivadas con éxito", + "ACTIONDEACTIVATED": "Acciones desactivadas con éxito" + } + }, + "IAM": { + "TITLE": "Instancia", + "DESCRIPTION": "Gestiona los ajustes de tu instancia y tus organizaciones", + "POLICIES": { + "TITLE": "Políticas del sistema y ajustes de acceso", + "DESCRIPTION": "Gestiona tus políticas globales y tus ajustes de gestión de accesos." + }, + "EVENTSTORE": { + "TITLE": "Administración del almacenamiento IAM", + "DESCRIPTION": "Gestionar tus vistas de ZITADEL y tus eventos fallidos." + }, + "MEMBER": { + "TITLE": "Mánagers", + "DESCRIPTION": "Estos mánagers están autorizados para hacer cambios en tu instancia." + }, + "PAGES": { + "STATE": "Estado", + "DOMAINLIST": "Dominios" + }, + "STATE": { + "0": "No especificado", + "1": "Creando", + "2": "En ejecución", + "3": "Deteniendo", + "4": "Parado" + }, + "VIEWS": { + "TITLE": "Vistas", + "DESCRIPTION": "Esta tarjeta muestra tus vistas de ZITADEL.", + "VIEWNAME": "Nombre", + "DATABASE": "Base de datos", + "SEQUENCE": "Secuencia", + "EVENTTIMESTAMP": "Marca de tiempo", + "LASTSPOOL": "Spool con éxito", + "ACTIONS": "Acciones", + "CLEAR": "Limpiar", + "CLEARED": "¡La vista se ha limpiado con éxito!", + "DIALOG": { + "VIEW_CLEAR_TITLE": "Limpiar vista", + "VIEW_CLEAR_DESCRIPTION": "Estás a punto de limpiar la vista. Limpiar una vista crea un proceso durante el cual es posible que los datos no estén disponibles para los usuarios finales. ¿Estás seguro?" + } + }, + "FAILEDEVENTS": { + "TITLE": "Eventos fallidos", + "DESCRIPTION": "Esta tarjeta muestra tus eventos fallidos.", + "VIEWNAME": "Nombre", + "DATABASE": "Base de datos", + "FAILEDSEQUENCE": "Secuencia fallida", + "FAILURECOUNT": "Número de fallos", + "LASTFAILED": "Último fallo a las", + "ERRORMESSAGE": "Mensaje de error", + "ACTIONS": "Acciones", + "DELETE": "Eliminar", + "DELETESUCCESS": "Eventos fallidos eliminados." + }, + "EVENTS": { + "TITLE": "Eventos", + "DESCRIPTION": "Esta vista muestra todos los eventos producidos.", + "EDITOR": "Editor", + "EDITORID": "ID de editor", + "AGGREGATE": "Agregado", + "AGGREGATEID": "ID de agregado", + "AGGREGATETYPE": "Tipo de agregado", + "RESOURCEOWNER": "Propietario del recurso", + "SEQUENCE": "Secuencia", + "CREATIONDATE": "Creado el", + "TYPE": "Tipo", + "PAYLOAD": "Carga útil", + "FILTERS": { + "BTN": "Filtrar", + "USER": { + "IDLABEL": "ID", + "CHECKBOX": "Filtrar por editor" + }, + "AGGREGATE": { + "TYPELABEL": "Tipo de agregado", + "IDLABEL": "ID", + "CHECKBOX": "Filtrar por agregado" + }, + "TYPE": { + "TYPELABEL": "Tipo", + "CHECKBOX": "Filtrar por tipo" + }, + "RESOURCEOWNER": { + "LABEL": "ID", + "CHECKBOX": "Filtrar por propietario de recurso" + }, + "SEQUENCE": { + "LABEL": "Secuencia", + "CHECKBOX": "Filtrar por secuencia", + "SORT": "Ordenado", + "ASC": "Ascendente", + "DESC": "Descendente" + }, + "CREATIONDATE": { + "LABEL": "Fecha de creación", + "CHECKBOX": "Filtrar por fecha de creación" + }, + "OTHER": "otro", + "OTHERS": "otros" + }, + "DIALOG": { + "TITLE": "Detalle del evento" + } + }, + "TOAST": { + "MEMBERREMOVED": "Mánager eliminado.", + "MEMBERSADDED": "Mánagers añadidos.", + "MEMBERADDED": "Mánager añadido.", + "MEMBERCHANGED": "Mánager modificado.", + "ROLEREMOVED": "Rol eliminado.", + "ROLECHANGED": "Rol cambiado.", + "REACTIVATED": "Reactivado", + "DEACTIVATED": "Desactivado" + } + }, + "ORG": { + "PAGES": { + "NAME": "Nombre", + "ID": "ID", + "CREATIONDATE": "Creado", + "DATECHANGED": "Modificado", + "FILTER": "Filtrar", + "FILTERPLACEHOLDER": "Filtrar por el nombre", + "LIST": "Organizaciones", + "LISTDESCRIPTION": "Elegir una organización.", + "ACTIVE": "Activar", + "CREATE": "Crear organización", + "DEACTIVATE": "Desactivar organización", + "REACTIVATE": "Reactivar organización", + "NOPERMISSION": "No tienes permiso para acceder a las configuraciones de la organización.", + "USERSELFACCOUNT": "Utiliza tu cuenta personal como propietaria de la organización", + "ORGDETAIL_TITLE": "Introduce el nombre y el dominio de tu nueva organización.", + "ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Introduce el nombre de tu nueva organización.", + "ORGDETAILUSER_TITLE": "Configurar propietario de la organización", + "DELETE": "Borrar organización", + "DEFAULTLABEL": "Predeterminada", + "SETASDEFAULT": "Establecer como organización predeterminada", + "DEFAULTORGSET": "Se cambió con éxito la organización predeterminada", + "RENAME": { + "ACTION": "Renombrar", + "TITLE": "Renombrar organización", + "DESCRIPTION": "Introducir el nuevo nombre de tu organización", + "BTN": "Renombrar" + }, + "ORGDOMAIN": { + "TITLE": "Verificación de la propiedad del dominio de la organización", + "VERIFICATION": "Para verificar la propiedad de tu dominio, necesitas descargar un fichero de verificación y subirlo a la URL proporcionada a continuación, o crear un registro DNS de tipo TXT que contenga la URL proporcionada. Para completar este paso, haz clic en el botón para verificarlo.", + "VERIFICATION_SKIP": "Puedes saltarte esta verificación por ahora y continuar con la creación de tu organización, pero para poder utilizar esta organización ¡deberás completar este paso!", + "VERIFICATION_VALIDATION_DESC": "Los tokens se comprueban regularmente para asegurar que todavía eres el dueño del dominio.", + "VERIFICATION_NEWTOKEN_TITLE": "Solicitar nuevo token", + "VERIFICATION_NEWTOKEN_DESC": "Si quieres solicitar un nuevo token, selecciona tu método preferido. Si quieres validar un token persistente, haz clic en el botón de arriba.", + "VERIFICATION_VALIDATION_ONGOING": "Ya se ha solicitado un token de verificación. Haz clic en el botón para lanzar una comprobación de verificación.", + "VERIFICATION_VALIDATION_ONGOING_TYPE": "Tipo de token:", + "VERIFICATION_SUCCESSFUL": "¡Dominio verificado con éxito!", + "REQUESTNEWTOKEN": "Solicitar nuevo token", + "TYPES": { + "1": "HTTP", + "2": "DNS" + } + }, + "DOWNLOAD_FILE": "Descargar fichero", + "SELECTORGTOOLTIP": "Seleccionar esta organización.", + "PRIMARYDOMAIN": "Dominio primario", + "STATE": "Estado", + "USEPASSWORD": "Establecer contraseña inicial", + "USEPASSWORDDESC": "El usuario no tiene que establecer la contraseña durante la inicialización." + }, + "LIST": { + "TITLE": "Organizaciones", + "DESCRIPTION": "Estas son las organizaciones de tu instancia" + }, + "DOMAINS": { + "NEW": "Añadir dominio", + "TITLE": "Dominios", + "DESCRIPTION": "Configura tus dominios. Este dominio puede usarse para iniciar sesión con tus usuarios.", + "SETPRIMARY": "Establecer como primario", + "DELETE": { + "TITLE": "Borrar dominio", + "DESCRIPTION": "Estás a punto de borrar uno de tus dominios. Ten en cuenta que tus usuarios no podrán usar más este dominio para iniciar sesión." + }, + "ADD": { + "TITLE": "Añadir dominio", + "DESCRIPTION": "Estás a punto de añadir un dominio para tu organización. Después de que se procese con éxito, tus usuarios serán capaces de usar el dominio para su inicio de sesión." + } + }, + "STATE": { + "0": "No definida", + "1": "Activa", + "2": "Desactivada", + "3": "Eliminada" + }, + "MEMBER": { + "TITLE": "Mánagers de la organización", + "DESCRIPTION": "Indica qué usuarios podrán cambiar las preferencias de tu organización." + }, + "TOAST": { + "UPDATED": "La organización se actualizó con éxito.", + "DEACTIVATED": "Organización desactivada.", + "REACTIVATED": "Organización reactivada.", + "DOMAINADDED": "Dominio añadido.", + "DOMAINREMOVED": "Dominio eliminado.", + "MEMBERADDED": "Mánager añadido.", + "MEMBERREMOVED": "Mánager eliminado.", + "MEMBERCHANGED": "Mánager modificado.", + "SETPRIMARY": "Dominio primario establecido.", + "DELETED": "Organización borrada con éxito", + "ORG_WAS_DELETED": "La organización ha sido borrada." + }, + "DIALOG": { + "DEACTIVATE": { + "TITLE": "Organización desactivada", + "DESCRIPTION": "Estás a punto de desactivar tu organización. Los usuarios no podrán iniciar sesión tras hacerlo. ¿Estás seguro de que quieres continuar?" + }, + "REACTIVATE": { + "TITLE": "Reactivar organización", + "DESCRIPTION": "Estás a punto de reactivar tu organización. Los usuarios serán capaces de iniciar sesión otra vez. ¿Estás seguro de que quieres continuar?" + }, + "DELETE": { + "TITLE": "Borrar organización", + "DESCRIPTION": "Estás a punto de borrar tu organización. Esto inicia un proceso donde todos los datos de las organizaciones relacionadas serán borrados. No puedes revertir esta acción.", + "TYPENAME": "Escribe '{{value}}', para borrar tu organización.", + "ORGNAME": "Nombre", + "BTN": "Borrar" + } + } + }, + "SETTINGS": { + "INSTANCE": { + "TITLE": "Ajustes de instancia", + "DESCRIPTION": "Estos ajustes se aplicará a todas tus organizaciones a menos que éstas los sobrescriban." + }, + "ORG": { + "TITLE": "Ajustes de organización", + "DESCRIPTION": "Estas configuraciones amplían y sobrescriben tus configuraciones de instancia." + }, + "LIST": { + "GENERAL": "General", + "LOGIN": "Comportamiento del inicio de sesión y de la seguridad", + "LOCKOUT": "Bloqueo", + "COMPLEXITY": "Complejidad de contraseña", + "NOTIFICATIONS": "Ajustes de notificación", + "NOTIFICATIONS_DESC": "Ajustes SMTP y SMS", + "MESSAGETEXTS": "Mensajes de texto", + "IDP": "Proveedores de identidad", + "DOMAIN": "Ajustes de dominio", + "LOGINTEXTS": "Textos de interfaz de inicio de sesión", + "BRANDING": "Imagen de marca", + "PRIVACYPOLICY": "Política de privacidad", + "OIDC": "OIDC Token lifetime and expiration", + "SECRETS": "Apariencia del secreto", + "SECURITY": "Ajustes de seguridad" + }, + "GROUPS": { + "NOTIFICATIONS": "Notificaciones", + "LOGIN": "Inicio de sesión y acceso", + "DOMAIN": "Dominio", + "TEXTS": "Textos e idiomas", + "APPEARANCE": "Apariencia", + "OTHER": "Otros" + } + }, + "SETTING": { + "DEFAULTLANGUAGE": "Idioma por defecto", + "LANGUAGE": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文" + }, + "SMTP": { + "TITLE": "Ajustes SMTP", + "SENDERADDRESS": "Dirección email del emisor", + "SENDERNAME": "Nombre del emisor", + "HOSTANDPORT": "Servidor y puerto", + "USER": "Usuario", + "PASSWORD": "Contraseña", + "SETPASSWORD": "Establecer contraseña SMTP", + "PASSWORDSET": "La contraseña SMTP se estableció con éxito.", + "TLS": "Transport Layer Security (TLS)", + "SAVED": "¡Se guardó con éxito!", + "REQUIREDWARN": "Para enviar notificaciones para tu dominio, debes introducir tus datos SMTP." + }, + "SMS": { + "TITLE": "Configuración SMS", + "PROVIDERS": "Proveedores", + "PROVIDER": "Proveedor de SMS", + "ADDPROVIDER": "Añadir proveedor SMS", + "ADDPROVIDERDESCRIPTION": "Elige uno de los siguientes proveedores e introduce los datos requeridos.", + "REMOVEPROVIDER": "Eliminar proveedor", + "REMOVEPROVIDER_DESC": "Estás a punto de borrar la configuración de un proveedor. ¿Quieres continuar?", + "SMSPROVIDERSTATE": { + "0": "No especificado", + "1": "Activo", + "2": "Inactivo" + }, + "ACTIVATED": "Proveedor activado.", + "DEACTIVATED": "Proveedor desactivado.", + "TWILIO": { + "SID": "Sid", + "TOKEN": "Token", + "SENDERNUMBER": "Número de emisor", + "ADDED": "Twilio añadido con éxito.", + "REMOVED": "Twilio eliminado", + "CHANGETOKEN": "Cambiar token", + "SETTOKEN": "Establecer token", + "TOKENSET": "Token establecido con éxito." + } + }, + "OIDC": { + "TITLE": "Ajustes OIDC", + "ACCESSTOKENLIFETIME": "Tiempo de vida del token de acceso", + "IDTOKENLIFETIME": "Tiempo de vida del Id del Token", + "REFRESHTOKENEXPIRATION": "Caducidad del token de refresco", + "REFRESHTOKENIDLEEXPIRATION": "Caducidad de token de refresco en estado idle", + "INHOURS": "horas", + "INDAYS": "días" + }, + "SECRETS": { + "TITLE": "Apariencia del secreto", + "TYPES": "Tipos de secreto", + "TYPE": { + "1": "Correo de inicialización", + "2": "Verificación de email", + "3": "Verificación de teléfono", + "4": "Restablecimiento de contraseña", + "5": "Inicialización de acceso sin contraseña", + "6": "Secreto de App" + }, + "ADDGENERATOR": "Definir apariencia del secreto", + "GENERATORTYPE": "Tipo", + "EXPIRY": "Caducidad (en horas)", + "INCLUDEDIGITS": "Incluir números", + "INCLUDESYMBOLS": "Incluir símbolos", + "INCLUDELOWERLETTERS": "Incluir letras minúsculas", + "INCLUDEUPPERLETTERS": "Incluir letras mayúsculas", + "LENGTH": "Longitud", + "UPDATED": "Ajustes actualizados." + }, + "SECURITY": { + "DESCRIPTION": "Este ajuste establece el CSP para permitir el uso de frames para un grupo de dominios permitidos. Ten en cuenta que habilitando el uso de iFrames, corres el riesgo de permitir ataques de clickjacking.", + "IFRAMEENABLED": "Permitir iFrame", + "ALLOWEDORIGINS": "URLs permitidas" + }, + "DIALOG": { + "RESET": { + "DEFAULTTITLE": "Restablecer ajuste", + "DEFAULTDESCRIPTION": "Estás a punto de restablecer tus ajustes a la configuración por defecto de tu instancia. ¿Estás seguro de que quieres continuar?", + "LOGINPOLICY_DESCRIPTION": "Aviso: Si continuas, los ajustes del proveedor de identidad se restablecerán también a los valores de la instancia." + } + } + }, + "POLICY": { + "TITLE": "Explorar ajustes", + "DESCRIPTION": "Ajustes preparados para mejorar tu seguridad.", + "APPLIEDTO": "Se aplica a", + "PWD_COMPLEXITY": { + "TITLE": "Complejidad de contraseña", + "DESCRIPTION": "Garantiza que todas las contraseñas establecidas se corresponden con un patrón específico", + "SYMBOLANDNUMBERERROR": "Debe consistir en un dígito y un signo de puntuación/símbolo.", + "SYMBOLERROR": "Debe inclur un símbolo/signo de puntuación.", + "NUMBERERROR": "Debe incluir un dígito.", + "PATTERNERROR": "La contraseña no cumple con el patrón requerido." + }, + "NOTIFICATION": { + "TITLE": "Notificación", + "DESCRIPTION": "Determina en qué cambios se enviarán notificaciones.", + "PASSWORDCHANGE": "Cambio de contraseña" + }, + "PRIVATELABELING": { + "TITLE": "Imagen de marca", + "DESCRIPTION": "Da a tu inicio de sesión tu estilo personalizado y modifica su comportamiento.", + "PREVIEW_DESCRIPTION": "Los cambios en la política serán desplegados automáticamente para previsualizar el entorno.", + "BTN": "Seleccionar fichero", + "ACTIVATEPREVIEW": "Aplicar configuración", + "DARK": "Modo oscuro", + "LIGHT": "Modo claro", + "CHANGEVIEW": "Cambiar vista", + "ACTIVATED": "Los cambios en la política ya están disponibles", + "THEME": "Tema", + "COLORS": "Colores", + "FONT": "Fuente", + "ADVANCEDBEHAVIOR": "Comportamiento avanzado", + "DROP": "Lleva aquí la imagen o", + "RELEASE": "Suéltala", + "DROPFONT": "Lleva aquí el fichero de fuente", + "RELEASEFONT": "Suéltalo", + "USEOFLOGO": "Tu logo será utilizado tanto en el inicio de sesión como en los emails, mientras que el icono se usará para elementos de la interfaz más pequeños como el menú de cambio de organizaciones", + "MAXSIZE": "El tamaño máximo está limitado a 524kB", + "EMAILNOSVG": "El formato de fichero SVG no está soportado en correos electrónicos. Por tanto, sube tu logo en PNG o en otro formato soportado.", + "MAXSIZEEXCEEDED": "Tamaño máximo de 524kB superado.", + "NOSVGSUPPORTED": "¡SVG no está soportado!", + "FONTINLOGINONLY": "La fuente se muestra actualmente solo en la interfaz de inicio de sesión.", + "BACKGROUNDCOLOR": "Color de fondo", + "PRIMARYCOLOR": "Color primario", + "WARNCOLOR": "Color de advertencia", + "FONTCOLOR": "Color de fuente", + "VIEWS": { + "PREVIEW": "Previsualizar", + "CURRENT": "Configuración actualizada" + }, + "PREVIEW": { + "TITLE": "Inicio de sesión", + "SECOND": "iniciar sesión con tu cuenta ZITADEL.", + "ERROR": "¡No se encontró el usuario!", + "PRIMARYBUTTON": "siguiente", + "SECONDARYBUTTON": "registrar" + } + }, + "PWD_AGE": { + "TITLE": "Antigüedad de la contraseña", + "DESCRIPTION": "Puedes establecer una política para la antigüedad de las contraseñas. Esta política emite un aviso después de que la antigüedad máxima se haya superado." + }, + "PWD_LOCKOUT": { + "TITLE": "Política de bloqueo", + "DESCRIPTION": "Establece un número máximo de reintentos de introducción de contraseña, después del cual las cuentas serán bloqueadas." + }, + "DOMAIN_POLICY": { + "TITLE": "Ajustes de dominio" + }, + "PRIVATELABELING_POLICY": { + "TITLE": "Imagen de marca", + "BTN": "Seleccionar fichero", + "DESCRIPTION": "Personalizar la apariencia del inicio de sesión", + "ACTIVATEPREVIEW": "Activar configuración" + }, + "LOGIN_POLICY": { + "TITLE": "Ajustes de inicio de sesión", + "DESCRIPTION": "Define cómo los usuarios pueden autenticarse y configura proveedores de identidad", + "DESCRIPTIONCREATEADMIN": "Los usuarios pueden elegir entre los siguientes proveedores de identidad disponibles.", + "DESCRIPTIONCREATEMGMT": "Los usuarios pueden elegir entre los siguientes proveedores de identidad. Nota: Puedes usar los proveedores integrados en el sistema así como sólo los proveedores configurados para tu organización.", + "ADVANCED": "Avanzado", + "LIFETIMEDURATIONS": "Tiempos de vida de los inicios de sesión", + "SAVED": "¡Guardado con éxito!" + }, + "PRIVACY_POLICY": { + "TITLE": "Política de privacidad y TDS", + "DESCRIPTION": "Establece tu Política de privacidad y los enlaces a los Términos de Servicio (TDS)", + "TOSLINK": "Enlace a Términos de Servicio", + "POLICYLINK": "Enlace a Política de privacidad", + "HELPLINK": "Enlace de ayuda", + "SUPPORTEMAIL": "Email de soporte", + "SAVED": "¡Se guardó con éxito!", + "RESET_TITLE": "Restaurar valores por defecto", + "RESET_DESCRIPTION": "Estás a punto de restaurar los enlaces por defecto para los TDS y la política de privacida. ¿Quieres continuar?" + }, + "LOGIN_TEXTS": { + "TITLE": "Textos de interfaces de inicio de sesión", + "DESCRIPTION": "Define tus textos para las interfaces de inicio de sesión. Si los textos están vacíos, se usarán los valores por defecto mostrados como sugerencias.", + "DESCRIPTION_SHORT": "Define tus textos para las interfaces de inicio de sesión.", + "NEWERVERSIONEXISTS": "Existe una nueva versión", + "CURRENTDATE": "Configuración actual", + "CHANGEDATE": "Nueva versión desde", + "KEYNAME": "Pantalla de inicio de sesión / Interfaz", + "RESET_TITLE": "Restaurar valores por defecto", + "RESET_DESCRIPTION": "Estás a punto de restaurar todos los valores por defecto. Todos los cambios que has hecho serán borrados permanentemente. ¿Estás seguro de que quieres continuar?", + "UNSAVED_TITLE": "¿Continuar sin guardar?", + "UNSAVED_DESCRIPTION": "Has hecho cambios sin guardar. ¿Quieres guardarlos ahora?", + "LOCALE": "Código de idioma", + "LOCALES": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文" + }, + "KEYS": { + "emailVerificationDoneText": "Verificación de email realizada", + "emailVerificationText": "Verificación de email", + "externalUserNotFoundText": "Usuario externo no encontrado", + "footerText": "Pie", + "initMfaDoneText": "Inicialización de MFA, hecha", + "initMfaOtpText": "Inicializar MFA", + "initMfaPromptText": "Inicializar diálogo de entrada MFA", + "initMfaU2fText": "Initializar doble factor universal", + "initPasswordDoneText": "Inicializar contraseña, hecho", + "initPasswordText": "Inicializar contraseña", + "initializeDoneText": "Inicializar usuario, hecho", + "initializeUserText": "Inicializar usuario", + "linkingUserDoneText": "Vinculación de usuario, hecho", + "loginText": "Iniciar sesión", + "logoutText": "Cerrar sesión", + "mfaProvidersText": "Proveedores MFA", + "passwordChangeDoneText": "Cambio de contraseña, hecho", + "passwordChangeText": "Cambio de contraseña", + "passwordResetDoneText": "Restablecimiento de contraseña, hecho", + "passwordText": "Contraseña", + "registrationOptionText": "Opciones de registro", + "registrationOrgText": "Registrar org", + "registrationUserText": "Registrar usuario", + "selectAccountText": "Seleccionar cuenta", + "successLoginText": "Inicio de sesión con éxito", + "usernameChangeDoneText": "Cambio de nombre de usuario, hecho", + "usernameChangeText": "Cambio de nombre de usuario", + "verifyMfaOtpText": "Verificar OTP", + "verifyMfaU2fText": "Verificar doble factor universal", + "passwordlessPromptText": "Diálogo de entrada de acceso sin contraseña", + "passwordlessRegistrationDoneText": "Registro sin contraseña, hecho", + "passwordlessRegistrationText": "Registro sin contraseña", + "passwordlessText": "Acceso sin contraseña", + "externalRegistrationUserOverviewText": "Resumen de registro de usuario externo" + } + }, + "MESSAGE_TEXTS": { + "TITLE": "Mensajes de texto", + "DESCRIPTION": "Define tus textos para tus correos de notificación.", + "TYPE": "Notificación", + "TYPES": { + "INIT": "Inicialización", + "VE": "Verificación de email", + "VP": "Verificación de teléfono", + "PR": "Restablecimiento de contraseña", + "DC": "Reclamar un dominio", + "PL": "Acceso sin contraseña", + "PC": "Cambio de contraseña" + }, + "CHIPS": { + "firstname": "Nombre", + "lastname": "Apellidos", + "code": "Código", + "preferredLoginName": "Nombre de inicio de sesión preferido", + "displayName": "Nombre mostrado", + "nickName": "Apodo", + "loginnames": "Nombres de inicio de sesión", + "domain": "Dominio", + "lastEmail": "Último email", + "lastPhone": "Último teléfono", + "verifiedEmail": "Email verificado", + "verifiedPhone": "Teléfono verificado", + "changedate": "Cambiar fecha", + "username": "Nombre de usuario", + "tempUsername": "Nombre de usuario temporal" + }, + "TOAST": { + "UPDATED": "Textos personalizados guardados." + } + }, + "DEFAULTLABEL": "Los ajustes actuales se corresponden con el estándar de tu instancia.", + "BTN_INSTALL": "Configurar", + "BTN_EDIT": "Modificar", + "DATA": { + "DESCRIPTION": "Descripción", + "MINLENGTH": "longitud mínima", + "HASNUMBER": "tiene números", + "HASSYMBOL": "tiene símbolos", + "HASLOWERCASE": "tiene minúsculas", + "HASUPPERCASE": "tiene mayúsculas", + "SHOWLOCKOUTFAILURES": "mostrar fallos de bloqueo", + "MAXATTEMPTS": "Intentos máximos", + "EXPIREWARNDAYS": "Aviso de expiración después de estos días: ", + "MAXAGEDAYS": "Antigüedad máxima en días", + "USERLOGINMUSTBEDOMAIN": "Añadir el dominio de la organización como sufijo de los nombres de inicio de sesión", + "USERLOGINMUSTBEDOMAIN_DESCRIPTION": "Si activas esta opción, todos los nombres de inicio de sesión tendrán como sufijo el dominio de esta organización. Si esta opción está desactivada, tendrás que asegurarte de que los nombres de usuario son únicos para todas las organizaciones.", + "VALIDATEORGDOMAINS": "Validar los dominios de la organización", + "SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "La dirección del remitente SMTP coincide con el dominio de la instancia", + "ALLOWUSERNAMEPASSWORD": "Nombre de usuario y contraseña permitido", + "ALLOWEXTERNALIDP": "Permitido IDP externo", + "ALLOWREGISTER": "Registro permitido", + "ALLOWUSERNAMEPASSWORD_DESC": "El inicio de sesión convencional con nombre de usuario y contraseña está permitido.", + "ALLOWEXTERNALIDP_DESC": "El inicio de sesión está permitido para los proveedores de identidad subyacentes", + "ALLOWREGISTER_DESC": "Si esta opción es seleccionada, aparece un paso adicional durante el inicio de sesión para registrar un usuario.", + "FORCEMFA": "Forzar MFA", + "FORCEMFA_DESC": "Si esta opción es seleccionada, los usuarios tendrán que configurar un doble factor para iniciar sesión.", + "HIDEPASSWORDRESET": "Ocultar restablecer contraseña", + "HIDEPASSWORDRESET_DESC": "Si esta opción es seleccionada, el usuario no podrá restablecer su contraseña en el proceso de inicio de sesión.", + "HIDELOGINNAMESUFFIX": "Ocultar sufijo del nombre de inicio de sesión", + "HIDELOGINNAMESUFFIX_DESC": "Oculta el sufijo del nombre de inicio de sesión en la interfaz de acceso", + "IGNOREUNKNOWNUSERNAMES": "Ignorar nombre de usuario desconocido", + "IGNOREUNKNOWNUSERNAMES_DESC": "Si esta opción es seleccionada, la pantalla de contraseña se mostrará durante el proceso de inicio de sesión aunque no exista el usuario. El error en la comprobación de la contraseña no revelará si fue el usuario o la contraseña el que era incorrecto.", + "ALLOWDOMAINDISCOVERY": "Permitido el descubrimiento de dominio", + "ALLOWDOMAINDISCOVERY_DESC": "Si esta opción es seleccionada, el sufijo (@dominio.com) de un nombre de usuario desconocido en la pantalla de inicio de sesión será comprobado contra los dominios de la organización y se redirigirá a la organización en cuestión en caso de éxito.", + "DISABLELOGINWITHEMAIL": "Desactivar inicio de sesión con dirección de email", + "DISABLELOGINWITHPHONE": "Desactivar inicio de sesión con número de teléfono", + "DEFAULTREDIRECTURI": "URI de redirección por defecto", + "DEFAULTREDIRECTURI_DESC": "Define a dónde se redirigirá el usuario si el inicio de sesión ha comenzado sin un contexto de aplicación (p.e desde un email)", + "ERRORMSGPOPUP": "Mostrar error en diálogo", + "DISABLEWATERMARK": "Ocultar marca de agua", + "DISABLEWATERMARK_DESC": "Ocultar la marca de agua Powered by ZITADEL en la interfaz de inicio de sesión", + "PASSWORDCHECKLIFETIME": "Tiempo de vida para comprobar la contraseña", + "EXTERNALLOGINCHECKLIFETIME": "Tiempo de vida para comprobar el inicio externo", + "MFAINITSKIPLIFETIME": "Tiempo de vida del inicio Multifactor", + "SECONDFACTORCHECKLIFETIME": "Tiempo de vida para comprobar el doble factor", + "MULTIFACTORCHECKLIFETIME": "Tiempo de vida para comprobar el Multifactor", + "INHOURS": "horas" + }, + "RESET": "Restablece los valores por defecto de la instancia", + "CREATECUSTOM": "Crear política personalizada", + "TOAST": { + "SET": "¡La política se estableció con éxito!", + "RESETSUCCESS": "¡La política se restableción con éxito!", + "UPLOADSUCCESS": "¡Subida con éxito!", + "DELETESUCCESS": "¡Borrada con éxito!", + "UPLOADFAILED": "¡Falló la subida!" + } + }, + "ORG_DETAIL": { + "TITLE": "Organización", + "DESCRIPTION": "Aquí puedes editar la configuración de tu organización y gestionar los miembros.", + "DETAIL": { + "TITLE": "Detalle", + "NAME": "Nombre", + "DOMAIN": "Dominio", + "STATE": { + "0": "No definida", + "1": "Activa", + "2": "Inactiva" + } + }, + "MEMBER": { + "TITLE": "Miembros", + "USERNAME": "Nombre de usuario", + "DISPLAYNAME": "Nombre mostrado", + "LOGINNAME": "Nombre de inicio de sesión", + "EMAIL": "Email", + "ROLES": "Roles", + "ADD": "Añadir miembro", + "ADDDESCRIPTION": "Introducir los nombres de los usuarios a añadir." + }, + "TABLE": { + "TOTAL": "Total de entradas", + "SELECTION": "Elementos seleccionados", + "DEACTIVATE": "Desactivar usuario", + "ACTIVATE": "Activar usuario", + "DELETE": "Borrar usuario", + "CLEAR": "Borrar la selección" + } + }, + "PROJECT": { + "PAGES": { + "TITLE": "Proyecto", + "DESCRIPTION": "Aquí puedes definir aplicaciones, gestionar roles y permitir a otras organizaciones utilizar tu proyecto.", + "DELETE": "Borrar proyecto", + "LIST": "Proyectos", + "LISTDESCRIPTION": "Si no puedes encontrar un proyecto, contacta con el propietario del proyecto o alguien con los permisos adecuados para obtener acceso al proyecto.", + "DETAIL": "Detalle", + "CREATE": "Crear proyecto", + "CREATE_DESC": "Inserta el nombre de tu proyecto.", + "ROLE": "Rol", + "NOITEMS": "Sin proyectos", + "ZITADELPROJECT": "Esto pertenece al proyecto ZITADEL. Cuidado: Si haces cambio puede que ZITADEL no se comporte como se espera.", + "TYPE": { + "OWNED": "Proyectos propios", + "GRANTED": "Proyectos concedidos", + "OWNED_SINGULAR": "Proyecto propio", + "GRANTED_SINGULAR": "Proyecto concedido" + }, + "PRIVATELABEL": { + "TITLE": "Ajustes de imagen de marca", + "0": { + "TITLE": "No especificado", + "DESC": "Tan pronto como el usuario se identifique, se mostrará la imagen de marca de la organización del usuario identificado, antes de que se muestre el aspecto por defecto." + }, + "1": { + "TITLE": "Utilizar configuración del proyecto", + "DESC": "Se mostrará la imagen de marca de la organización que es dueña del proyecto" + }, + "2": { + "TITLE": "Utilizar configuración de la organización del usuario", + "DESC": "Se mostrará la imagen de marca de la organización del proyecto, pero tan pronto como el usuario se identifique, se mostrará la configuración de la organización del usuario identificado." + }, + "DIALOG": { + "TITLE": "Ajustes de la imagen de marca", + "DESCRIPTION": "Selecciona el comportamiento del inicio de sesión cuando se utiliza el proyecto." + } + }, + "PINNED": "Fijado", + "ALL": "Todos", + "CREATEDON": "Creado el", + "LASTMODIFIED": "Modificado por última vez el", + "ADDNEW": "Crear nuevo proyecto", + "DIALOG": { + "REACTIVATE": { + "TITLE": "Reactivar proyecto", + "DESCRIPTION": "¿Realmente quieres reactivar tu proyecto?" + }, + "DEACTIVATE": { + "TITLE": "Desactivar proyecto", + "DESCRIPTION": "¿Realmente quieres desactivar tu proyecto?" + }, + "DELETE": { + "TITLE": "Borrar proyecto", + "DESCRIPTION": "¿Realmente quieres borrar tu proyecto?", + "TYPENAME": "Escribir el nombre del proyecto para borrarlo permanentemente." + } + } + }, + "SETTINGS": { + "TITLE": "Ajustes", + "DESCRIPTION": "" + }, + "STATE": { + "TITLE": "Estado", + "0": "No definido", + "1": "Activo", + "2": "Inactivo" + }, + "TYPE": { + "TITLE": "Tipo", + "0": "Tipo desconocido", + "1": "Propio", + "2": "Concedido" + }, + "NAME": "Nombre", + "NAMEDIALOG": { + "TITLE": "Renombrar proyecto", + "DESCRIPTION": "Introduce el nuevo nombre de tu proyecto", + "NAME": "Nuevo nombre" + }, + "MEMBER": { + "TITLE": "Mánagers", + "TITLEDESC": "Los mánagers pueden hacer cambios en este proyecto basándose en su rol.", + "DESCRIPTION": "Estos mánagers pueden editar tu proyecto.", + "USERNAME": "Nombre de usuario", + "DISPLAYNAME": "Nombre mostrado", + "LOGINNAME": "Nombre de inicio de sesión", + "EMAIL": "Email", + "ROLES": "Roles", + "USERID": "ID de usuario" + }, + "GRANT": { + "EMPTY": "No hay organizaciones concesionarias.", + "TITLE": "Concesiones de proyecto", + "DESCRIPTION": "Permitir a otra organización usar tu proyecto.", + "EDITTITLE": "Editar roles", + "CREATE": { + "TITLE": "Crear organización concesionaria", + "SEL_USERS": "Selecciona los usuarios a los que quieres conceder acceso", + "SEL_PROJECT": "Buscar un proyecto", + "SEL_ROLES": "Selecciona los roles que quieres que se añadan a la concesión", + "SEL_USER": "Seleccionar usuarios", + "SEL_ORG": "Establecer el dominio", + "SEL_ORG_DESC": "Introduce el dominio completo para especificar la organización concesionaria.", + "ORG_TITLE": "Organización", + "ORG_DESCRIPTION": "Estás a punto de conceder acceso a un usuario para la organización {{name}}.", + "ORG_DESCRIPTION_DESC": "Cambia el contexto en la cabecera superior para conceder acceso a un usuario para otra organización.", + "SEL_ORG_FORMFIELD": "Completar dominio", + "SEL_ORG_BUTTON": "Buscar organización", + "FOR_ORG": "La concesión se creó para:" + }, + "DETAIL": { + "TITLE": "Concesión de proyecto", + "DESC": "Puedes seleccionar qué roles pueden usarse por la organización especificada y elegir mánagers", + "MEMBERTITLE": "Mánagers", + "MEMBERDESC": "Estos son los mánagers de la organización concesionaria. Añade aquí los usuarios que deberían obtener acceso para editar los datos del proyecto.", + "PROJECTNAME": "Nombre del proyecto", + "GRANTEDORG": "Organización concesionaria", + "RESOURCEOWNER": "Propietario del recurso" + }, + "STATE": "Estado", + "STATES": { + "1": "Activo", + "2": "Inactivo" + }, + "ALL": "Todos", + "SHOWDETAIL": "Mostrar detalles", + "USER": "Usuario", + "MEMBERS": "Mánagers", + "ORG": "Organización", + "PROJECTNAME": "Nombre del proyecto", + "GRANTEDORG": "Organización concesionaria", + "GRANTEDORGDOMAIN": "Dominio", + "RESOURCEOWNER": "Propietario del recurso", + "GRANTEDORGNAME": "Nombre de organización", + "GRANTID": "Id de concesiones", + "CREATIONDATE": "Fecha de creación", + "CHANGEDATE": "Última modificación", + "DATES": "Fecha", + "ROLENAMESLIST": "Roles", + "NOROLES": "Sin roles", + "TYPE": "Tipo", + "TOAST": { + "PROJECTGRANTUSERGRANTADDED": "Concesión de proyecto creada.", + "PROJECTGRANTADDED": "Concesión de proyecto añadida.", + "PROJECTGRANTCHANGED": "Concesión de proyecto modificada.", + "PROJECTGRANTMEMBERADDED": "Gestor de concesiones añadido.", + "PROJECTGRANTMEMBERCHANGED": "Gestor de concesiones modificado.", + "PROJECTGRANTMEMBERREMOVED": "Gestor de concesiones eliminado.", + "PROJECTGRANTUPDATED": "Concesión de proyecto actualizada" + }, + "DIALOG": { + "DELETE_TITLE": "Borrar concesión de proyecto", + "DELETE_DESCRIPTION": "Estás a punto de borrar una concesión de proyecto. ¿Estás seguro?" + }, + "ROLES": "Roles del proyecto" + }, + "APP": { + "TITLE": "Aplicaciones", + "NAME": "Nombre", + "NAMEREQUIRED": "Se requiere un nombre." + }, + "ROLE": { + "EMPTY": "Aún no se han creado roles.", + "ADDNEWLINE": "Añadir rol adicional", + "KEY": "Clave", + "TITLE": "Roles", + "DESCRIPTION": "Define algunos roles que pueden usarse para crear concesiones sobre proyectos.", + "NAME": "Nombre", + "DISPLAY_NAME": "Nombre mostrado", + "GROUP": "Grupo", + "ACTIONS": "Acciones", + "ADDTITLE": "Crear rol", + "ADDDESCRIPTION": "Introducir los datos para el nuevo rol.", + "EDITTITLE": "Editar rol", + "EDITDESCRIPTION": "Introducir los nuevos datos para el rol.", + "DELETE": "Borrar rol", + "CREATIONDATE": "Creado", + "CHANGEDATE": "Última modificación", + "SELECTGROUPTOOLTIP": "Selecciona todos los roles del grupo {{group}}.", + "OPTIONS": "Opciones", + "ASSERTION": "Comprobar roles en la autenticación", + "ASSERTION_DESCRIPTION": "La información del rol es enviada desde el endpoint de Userinfo y, dependiendo de la configuración de la aplicación, en tokens y otros tipos.", + "CHECK": "Comprobar autorización en la autenticación", + "CHECK_DESCRIPTION": "Si se establece, a los usuarios solo se les permite autenticarse si se ha asignado algún rol a su cuenta.", + "DIALOG": { + "DELETE_TITLE": "Borrar rol", + "DELETE_DESCRIPTION": "Estás a punto de borrar un rol de proyecto. ¿Estás seguro?" + } + }, + "HAS_PROJECT": "Comprobar proyecto en la autenticación", + "HAS_PROJECT_DESCRIPTION": "Se comprueba si la organización del usuario tiene este proyecto. De no tenerlo, el usuario no puede ser autenticado.", + "TABLE": { + "TOTAL": "Total de entradas:", + "SELECTION": "Elementos seleccionados", + "DEACTIVATE": "Desactivar proyecto", + "ACTIVATE": "Activar proyecto", + "DELETE": "Borrar proyecto", + "ORGNAME": "Nombre de organización", + "ORGDOMAIN": "Dominio de organización", + "STATE": "Estado", + "TYPE": "Tipo", + "CREATIONDATE": "Creado el", + "CHANGEDATE": "Última modificación", + "RESOURCEOWNER": "Propietario", + "SHOWTABLE": "Mostrar tabla", + "SHOWGRID": "Mostrar cuadrícula", + "EMPTY": "No se encontraron proyectos" + }, + "TOAST": { + "MEMBERREMOVED": "Mánager eliminado.", + "MEMBERSADDED": "Mánagers añadidos.", + "MEMBERADDED": "Mánager añadido.", + "MEMBERCHANGED": "Mánager modificado.", + "ROLESCREATED": "Roles creados.", + "ROLEREMOVED": "Rol eliminado.", + "ROLECHANGED": "Rol modificado.", + "REACTIVATED": "Reactivado.", + "DEACTIVATED": "Desactivado.", + "CREATED": "Proyecto creado.", + "UPDATED": "Proyecto modificado.", + "GRANTUPDATED": "Concesión modificada.", + "DELETED": "Proyecto borrado." + } + }, + "ROLES": { + "DIALOG": { + "DELETE_TITLE": "Borrar rol", + "DELETE_DESCRIPTION": "Estás a punto de borrar un rol. ¿Estás seguro?" + } + }, + "NEXTSTEPS": { + "TITLE": "Próximos pasos" + }, + "IDP": { + "LIST": { + "TITLE": "Proveedores de identidad", + "DESCRIPTION": "Gestiona la configuración de tu proveedor de identidad, que puede activarse en los ajustes de inicio de sesión.", + "ACTIVETITLE": "Activar proveedores de identidad" + }, + "CREATE": { + "TITLE": "Añadir proveedor", + "DESCRIPTION": "Selecciona uno o más de los siguientes proveedores.", + "STEPPERTITLE": "Crear proveedor", + "OIDC": { + "TITLE": "Proveedor OIDC", + "DESCRIPTION": "Introduce los datos requeridos por tu proveedor OIDC." + }, + "OAUTH": { + "TITLE": "Proveedor OAuth", + "DESCRIPTION": "Introduce los datos requeridos por tu proveedor OAuth." + }, + "JWT": { + "TITLE": "Proveedor JWT", + "DESCRIPTION": "Introduce los datos requeridos por tu proveedor JWT." + }, + "GOOGLE": { + "TITLE": "Proveedor Google", + "DESCRIPTION": "Introduce los datos requeridos por tu proveedor de identidad Google" + }, + "GITLAB": { + "TITLE": "Proveedor Gitlab", + "DESCRIPTION": "Introduce los datos requeridos por tu proveedor de identidad Gitlab" + }, + "GITLABSELFHOSTED": { + "TITLE": "Proveedor Gitlab Self Hosted", + "DESCRIPTION": "Introduce las credenciales para tu proveedor de identidad Gitlab Self Hosted" + }, + "GITHUBES": { + "TITLE": "Proveedor GitHub Enterprise Server", + "DESCRIPTION": "Introduce las credenciales para tu proveedor de identidad GitHub Enterprise Server" + }, + "GITHUB": { + "TITLE": "Proveedor Github", + "DESCRIPTION": "Introduce las credenciales para tu proveedor de identidad Github" + }, + "AZUREAD": { + "TITLE": "Proveedor Microsoft", + "DESCRIPTION": "Introduce las credenciales para tu proveedor de identidad Microsoft" + }, + "LDAP": { + "TITLE": "Active Directory / LDAP", + "DESCRIPTION": "Introduce las credenciales para tu proveedor LDAP" + } + }, + "DETAIL": { + "TITLE": "Proveedor de identidad (IDP)", + "DESCRIPTION": "Actualiza la configuración de tu proveedor", + "DATECREATED": "Creado", + "DATECHANGED": "Cambiado" + }, + "OPTIONS": { + "ISAUTOCREATION": "Creación automática", + "ISAUTOCREATION_DESC": "Si se selecciona, una cuenta se creará si aún no existiera.", + "ISAUTOUPDATE": "Actualización automática", + "ISAUTOUPDATE_DESC": "Si se selecciona, las cuentas se actualizarán en la reautenticación.", + "ISCREATIONALLOWED": "Creación de cuentas permitida", + "ISCREATIONALLOWED_DESC": "Determina si se pueden crear cuentas.", + "ISLINKINGALLOWED": "Permitida la vinculación de cuentas", + "ISLINKINGALLOWED_DESC": "Determina si una identidad puede vincularse a una cuenta existente." + }, + "OWNERTYPES": { + "0": "desconocido", + "1": "Instancia", + "2": "Organización" + }, + "STATES": { + "1": "activo", + "2": "inactivo" + }, + "AZUREADTENANTTYPES": { + "0": "Común", + "1": "Organizaciones", + "2": "Clientes" + }, + "AZUREADTENANTTYPE": "Tipo de Tenant", + "AZUREADTENANTID": "ID de Tenant", + "EMAILVERIFIED": "Correo verificado", + "NAMEHINT": "Si se especifica se mostrará en la interfaz de inicio de sesión.", + "OPTIONAL": "opcional", + "LDAPATTRIBUTES": "Atributos LDAP", + "UPDATEBINDPASSWORD": "actualizar contraseña para Bind", + "UPDATECLIENTSECRET": "actualizar secreto del cliente", + "ADD": "Añadir proveedor de identidad", + "TYPE": "Tipo", + "OWNER": "Propietario", + "ID": "ID", + "NAME": "Nombre", + "AUTHORIZATIONENDPOINT": "Endpoint de autorización", + "TOKENENDPOINT": "Endpoint de Tokens", + "USERENDPOINT": "Endpoint de usuarios", + "IDATTRIBUTE": "Atributo ID", + "AVAILABILITY": "Disponibilidad", + "AVAILABLE": "disponible", + "AVAILABLEBUTINACTIVE": "disponible pero inactivo", + "SETAVAILABLE": "configurar como disponible", + "SETUNAVAILABLE": "configurar como no disponible", + "CONFIG": "Configuración", + "STATE": "Estado", + "ISSUER": "Emisor", + "SCOPESLIST": "Lista de scopes", + "CLIENTID": "ID del cliente", + "CLIENTSECRET": "Secreto del cliente", + "LDAPCONNECTION": "Conexión", + "LDAPUSERBINDING": "User binding", + "BASEDN": "BaseDn", + "BINDDN": "BindDn", + "BINDPASSWORD": "Contraseña Bind", + "SERVERS": "Servidores", + "STARTTLS": "Start TLS", + "TIMEOUT": "Timeout en segundos", + "USERBASE": "Userbase", + "USERFILTERS": "Filtros de usuario", + "USEROBJECTCLASSES": "User Object Classes", + "REQUIRED": "requerido", + "LDAPIDATTRIBUTE": "Atributo ID", + "AVATARURLATTRIBUTE": "Atributo de Url de avatar", + "DISPLAYNAMEATTRIBUTE": "Atributo Displayname", + "EMAILATTRIBUTEATTRIBUTE": "Atributo Email", + "EMAILVERIFIEDATTRIBUTE": "Atributo Email verificado", + "FIRSTNAMEATTRIBUTE": "Atributo Firstname", + "LASTNAMEATTRIBUTE": "Atributo Lastname", + "NICKNAMEATTRIBUTE": "Atributo Nickname", + "PHONEATTRIBUTE": "Atributo Phone", + "PHONEVERIFIEDATTRIBUTE": "Atributo phone verificado", + "PREFERREDLANGUAGEATTRIBUTE": "Atributo Preferred language", + "PREFERREDUSERNAMEATTRIBUTE": "Atributo Preferred username", + "PROFILEATTRIBUTE": "Atributo Profile", + "IDPDISPLAYNAMMAPPING": "Mapeado de nombres mostrados IDP", + "USERNAMEMAPPING": "Maperado de nombres de usuario", + "DATES": "Fechas", + "CREATIONDATE": "Creado el", + "CHANGEDATE": "Última modificación", + "DEACTIVATE": "Desactivar", + "ACTIVATE": "Activar", + "DELETE": "Borrar", + "DELETE_TITLE": "Borrar IDP", + "DELETE_DESCRIPTION": "Estás a punto de borrar un proveedor de identidad. Los cambios son irrevocables. ¿Estás seguro de que quieres hacer esto?", + "DELETE_SELECTION_TITLE": "Borrar IDP", + "DELETE_SELECTION_DESCRIPTION": "Estás a punto de borrar un proveedor de identidad. Los cambios resultantes son irrevocables. ¿Estás seguro de que quieres hacer esto?", + "EMPTY": "No hay IDP disponible", + "OIDC": { + "GENERAL": "Información general", + "TITLE": "Configuración OIDC", + "DESCRIPTION": "Introduce los datos del proveedor de identidad OIDC." + }, + "JWT": { + "TITLE": "Configuración JWT", + "DESCRIPTION": "Introduce los datos para el proveedor de identidad JWT.", + "HEADERNAME": "Nombre de cabecera", + "JWTENDPOINT": "JWT Endpoint", + "JWTKEYSENDPOINT": "JWT Keys Endpoint" + }, + "TOAST": { + "SAVED": "Guardado con éxito.", + "REACTIVATED": "IDP reactivado.", + "DEACTIVATED": "IDP desactivado.", + "SELECTEDREACTIVATED": "Los IDPs seleccionados se reactivaron.", + "SELECTEDDEACTIVATED": "Los IDPs seleccionados se han desactivado.", + "SELECTEDKEYSDELETED": "Los IDPs seleccionados se han borrado.", + "DELETED": "¡IDP eliminado con éxito!", + "ADDED": "Añadido con éxito.", + "REMOVED": "Eliminado con éxito." + } + }, + "MFA": { + "LIST": { + "MULTIFACTORTITLE": "Acceso sin contraseña", + "MULTIFACTORDESCRIPTION": "Define tus multifactores para tu autenticación sin contraseña aquí.", + "SECONDFACTORTITLE": "Autenticación multifactor", + "SECONDFACTORDESCRIPTION": "Define otros factores posibles con los que puedes asegurar tu autenticación con contraseña." + }, + "CREATE": { + "TITLE": "Nuevo factor", + "DESCRIPTION": "Selecciona tu nuevo tipo de factor." + }, + "DELETE": { + "TITLE": "Borrar factor", + "DESCRIPTION": "Está a punto de borrar un factor de las configuraciones de inicio de sesión. ¿Estás seguro?" + }, + "TOAST": { + "ADDED": "Añadido con éxito.", + "SAVED": "Guardado con éxito.", + "DELETED": "Eliminado con éxito." + }, + "TYPE": "Tipo", + "MULTIFACTORTYPES": { + "0": "Desconocido", + "1": "Huella dactilar, claves de seguridad, Face ID y otros" + }, + "SECONDFACTORTYPES": { + "0": "Desconocido", + "1": "One Time Password (OTP)", + "2": "Huella dactilar, claves de seguridad, Face ID y otros" + } + }, + "LOGINPOLICY": { + "CREATE": { + "TITLE": "Ajustes de inicio de sesión", + "DESCRIPTION": "Define cómo tus usuarios pueden autenticarse en tu organización." + }, + "IDPS": "Proveedores de identidad (IDPs)", + "ADDIDP": { + "TITLE": "Añadir proveedor de identidad (IDP)", + "DESCRIPTION": "Puedes seleccionar proveedores predefinidos o creados por uno mismo para la autenticación.", + "SELECTIDPS": "Proveedores de identidad (IDP)" + }, + "PASSWORDLESS": "Inicio de sesión sin contraseña", + "PASSWORDLESSTYPE": { + "0": "No permitido", + "1": "Permitido" + } + }, + "APP": { + "LIST": "Aplicaciones", + "COMPLIANCE": "Cumplimiento OIDC", + "URLS": "URLs", + "CONFIGURATION": "Configuración", + "TOKEN": "Ajustes de Token", + "PAGES": { + "TITLE": "Aplicación", + "ID": "ID", + "DESCRIPTION": "Aquí puedes editar los datos de tu aplicación y su configuración.", + "CREATE": "Crear aplicación", + "CREATE_SELECT_PROJECT": "Selecciona primero tu proyecto", + "CREATE_NEW_PROJECT": "o crea uno nuevo
aquí.", + "CREATE_DESC_TITLE": "Introduce los detalles de tu aplicación paso a paso", + "CREATE_DESC_SUB": "Se generará automáticamente una configuración recomendada.", + "STATE": "Estado", + "DATECREATED": "Creada", + "DATECHANGED": "Cambiada", + "URLS": "URLs", + "DELETE": "Borrar App", + "DETAIL": { + "TITLE": "Detalle", + "STATE": { + "0": "No definida", + "1": "Activa", + "2": "Inactiva" + } + }, + "DIALOG": { + "CONFIG": { + "TITLE": "Cambiar configuración OIDC" + }, + "DELETE": { + "TITLE": "Borrar App", + "DESCRIPTION": "¿Realmente quieres borrar esta aplicación?" + } + }, + "NEXTSTEPS": { + "TITLE": "Pasos siguientes", + "0": { + "TITLE": "Añadir roles", + "DESC": "Introduce tus roles de proyecto" + }, + "1": { + "TITLE": "Añadir usuarios", + "DESC": "Aañadir nuevos usuarios de tu organización" + }, + "2": { + "TITLE": "Ayuda & Soporte", + "DESC": "Lee nuestra documentación acerca de crear aplicaciones o contactar con nuestro soporte" + } + } + }, + "NAMEDIALOG": { + "TITLE": "Renombrar App", + "DESCRIPTION": "Introducir el nuevo nombre de tu app", + "NAME": "Nuevo nombre" + }, + "NAME": "Nombre", + "TYPE": "Tipo de aplicación", + "AUTHMETHOD": "Método de autenticación", + "AUTHMETHODSECTION": "Método de autenticación", + "GRANT": "Tipos de concesión", + "ADDITIONALORIGINS": "Orígenes adicionales", + "ADDITIONALORIGINSDESC": "Si quieres añadir orígenes adicionales a tu aplicación que no se usan como redirección puedes hacerlo aquí.", + "ORIGINS": "Orígenes", + "NOTANORIGIN": "El valor introducido no es un orígen", + "PROSWITCH": "Soy pro. Saltar este asistente.", + "NAMEANDTYPESECTION": "Nombre y tipo", + "TITLEFIRST": "Nombre de la aplicación", + "TYPETITLE": "Tipo de aplicación", + "OIDC": { + "WELLKNOWN": "Más enlaces pueden obtenerse del endpoint de descubrimiento.", + "INFO": { + "ISSUER": "Emisor", + "CLIENTID": "Id de cliente" + }, + "CURRENT": "Configuración actual", + "TOKENSECTIONTITLE": "Opciones de AuthToken", + "REDIRECTSECTIONTITLE": "Ajustes de redirección", + "REDIRECTTITLE": "Especifica las URIs a las que redirigirá el inicio de sesión.", + "POSTREDIRECTTITLE": "Esta es la URI a la que se redirigirá tras cerrar sesión.", + "REDIRECTDESCRIPTIONWEB": "Las URIs de redirección deben comenzar con https://. http:// solo es válida con el modo de desarrollo activado.", + "REDIRECTDESCRIPTIONNATIVE": "Las URIs de redirección deben comenzar con tu propio protocolo, http://127.0.0.1, http://[::1] o http://localhost.", + "REDIRECTNOTVALID": "Esta URI de redirección no es válida.", + "COMMAORENTERSEPERATION": "separado con ↵", + "TYPEREQUIRED": "Se requiere el tipo.", + "TITLE": "Configuración OIDC", + "CLIENTID": "ID de cliente", + "CLIENTSECRET": "Secreto de cliente", + "CLIENTSECRET_NOSECRET": "Con tu flujo de autenticación elegido, no se requiere ningún secreto y por tanto no está disponible.", + "CLIENTSECRET_DESCRIPTION": "Mantén tu secreto de cliente en un lugar seguro puesto que desaparecerá una vez que se cierre el diálogo.", + "REGENERATESECRET": "Regenerar secreto de cliente", + "DEVMODE": "Modo Desarrollo", + "DEVMODEDESC": "Cuidado: Si el modo de desarrollo está activado las URIs de redirección no serán validadas.", + "SKIPNATIVEAPPSUCCESSPAGE": "Saltar página de inicio de sesión con éxito", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "Sáltate la página de éxito después de iniciar sesión en esta app nativa.", + "REDIRECT": "URIs de redirección", + "REDIRECTSECTION": "URIs de redirección", + "POSTLOGOUTREDIRECT": "URIs a usar tras cerrar sesión", + "RESPONSESECTION": "Tipos de respuestas", + "GRANTSECTION": "Tipos de concesiones", + "GRANTTITLE": "Selecciona tus tipos de concesiones. Nota: Implícito solo está disponibles para aplicaciones de navegador.", + "APPTYPE": { + "0": "Web", + "1": "User Agent", + "2": "Nativo" + }, + "RESPONSETYPE": "Tipos de respuestas", + "RESPONSE": { + "0": "Código", + "1": "ID Token", + "2": "Token-ID Token" + }, + "REFRESHTOKEN": "Token de refresco", + "GRANTTYPE": "Tipos de concesiones", + "GRANT": { + "0": "Código de autorización", + "1": "Implícito", + "2": "Token de refresco" + }, + "AUTHMETHOD": { + "0": "Básico", + "1": "Post", + "2": "Ninguno", + "3": "Clave privada JWT" + }, + "TOKENTYPE": "Auth Token Type", + "TOKENTYPE0": "Bearer Token", + "TOKENTYPE1": "JWT", + "UNSECUREREDIRECT": "Espero que sepas lo que estás haciendo.", + "OVERVIEWSECTION": "Resumen", + "OVERVIEWTITLE": "Has terminado. Revisa tu configuración.", + "ACCESSTOKENROLEASSERTION": "Añadir roles de usuario para el token de acceso", + "ACCESSTOKENROLEASSERTION_DESCRIPTION": "Si se selecciona, los roles solicitados para el usuario autenticado se añaden al token de acceso.", + "IDTOKENROLEASSERTION": "Roles de usuario dentro del Token de ID", + "IDTOKENROLEASSERTION_DESCRIPTION": "Si se selecciona, los roles solicitados para el usuario autenticado se añaden al token de ID.", + "IDTOKENUSERINFOASSERTION": "Información del usuario dentro del Token de ID", + "IDTOKENUSERINFOASSERTION_DESCRIPTION": "Permite a los clientes obtener los claims de perfil, email, teléfono y dirección del token de ID.", + "CLOCKSKEW": "Permite a los clientes manejar el sesgo de reloj de OP y el cliente. La duración (0-5 s) se agregará al claim exp y se restará de iats, auth_time y nbf.", + "RECOMMENDED": "recomendado", + "NOTRECOMMENDED": "no recomendado", + "SELECTION": { + "APPTYPE": { + "WEB": { + "TITLE": "Web", + "DESCRIPTION": "Aplicaciones web habituales como .net, PHP, Node.js, Java, etc." + }, + "NATIVE": { + "TITLE": "Nativo", + "DESCRIPTION": "Apps móviles, de escritorio, dispositivos inteligentes, etc." + }, + "USERAGENT": { + "TITLE": "User Agent", + "DESCRIPTION": "Single Page Applications (SPA) y en general todos los frameworks de JS ejecutados en navegadores" + } + } + } + }, + "API": { + "INFO": { + "CLIENTID": "Id de cliente" + }, + "REGENERATESECRET": "Regenerar secreto del cliente", + "SELECTION": { + "TITLE": "API", + "DESCRIPTION": "APIs en general" + }, + "AUTHMETHOD": { + "0": "Básica", + "1": "Clave privada JWT" + } + }, + "SAML": { + "SELECTION": { + "TITLE": "SAML", + "DESCRIPTION": "Aplicaciones SAML" + }, + "CONFIGSECTION": "Configuración SAML", + "URL": "URL donde está ubicado el fichero de metadatos", + "OR": "o", + "XML": "Sube un fichero XML de metadatos", + "METADATA": "Metadatos", + "METADATAFROMFILE": "Metadatos desde un fichero" + }, + "AUTHMETHODS": { + "CODE": { + "TITLE": "Código", + "DESCRIPTION": "Intercambia el código de autorización por tokens" + }, + "PKCE": { + "TITLE": "PKCE", + "DESCRIPTION": "Usar un hash aleatorio en lugar de un secreto de cliente estático para más seguridad" + }, + "POST": { + "TITLE": "POST", + "DESCRIPTION": "Enviar client_id y client_secret como parte del formulario" + }, + "PK_JWT": { + "TITLE": "Clave privada JWT", + "DESCRIPTION": "Usar una clave privada para autorizar la aplicación" + }, + "BASIC": { + "TITLE": "Básica", + "DESCRIPTION": "Autenticación con nombre de usuario y contraseña" + }, + "IMPLICIT": { + "TITLE": "Implícita", + "DESCRIPTION": "Obtén los tokens directamente del endpoint de autorización" + }, + "CUSTOM": { + "TITLE": "Personalizada", + "DESCRIPTION": "Tu configuración no se corresponde con alguna de las otras opciones." + } + }, + "TOAST": { + "REACTIVATED": "Aplicación reactivada.", + "DEACTIVATED": "Aplicación desactivada.", + "OIDCUPDATED": "App actualizada.", + "APIUPDATED": "App actualizada", + "UPDATED": "App actualizada.", + "CREATED": "App creada.", + "CLIENTSECRETREGENERATED": "secreto del cliente generado.", + "DELETED": "App borrada.", + "CONFIGCHANGED": "¡Cambios detectados!" + } + }, + "GENDERS": { + "0": "Desconocido", + "1": "Mujer", + "2": "Hombre", + "3": "Otro" + }, + "LANGUAGES": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文" + }, + "MEMBER": { + "ADD": "Añadir un Mánager", + "CREATIONTYPE": "Tipo de creación", + "CREATIONTYPES": { + "3": "IAM", + "2": "Organización", + "0": "Proyecto propio", + "1": "Proyecto con acceso concedido", + "4": "Proyecto" + }, + "EDITROLE": "Editar roles", + "EDITFOR": "Editar los roles para el usuario: {{value}}", + "DIALOG": { + "DELETE_TITLE": "Eliminar Mánager", + "DELETE_DESCRIPTION": "Estás a punto de eliminar un mánager. ¿Estás seguro?" + } + }, + "ROLESLABEL": "Roles", + "GRANTS": { + "TITLE": "Autorizaciones", + "DESC": "Estas son todas las autorizaciones de tu organización.", + "DELETE": "Borrar autorización", + "EMPTY": "No se encontraron autorizaciones", + "ADD": "Crear autorización", + "ADD_BTN": "Nueva", + "PROJECT": { + "TITLE": "Autorización", + "DESCRIPTION": "Define las autorizaciones para el proyecto especificado. Ten en cuenta que solo puedes ver entradas de los proyectos y usuarios para los que tienes permisos." + }, + "USER": { + "TITLE": "Autorización", + "DESCRIPTION": "Define las autorizaciones para el usuario especificado. Ten en cuenta que solo puedes ver entradas de los proyectos y usuarios para los que tienes permisos." + }, + "CREATE": { + "TITLE": "Crear autorización", + "DESCRIPTION": "Buscar la organización, el proyecto, y los roles correspondientes." + }, + "EDIT": { + "TITLE": "Cambiar autorización" + }, + "DETAIL": { + "TITLE": "Detalles de autorización", + "DESCRIPTION": "Aquí puedes ver todos los detalles de la autorización." + }, + "TOAST": { + "UPDATED": "Autorización actualizada.", + "REMOVED": "Autorización eliminada", + "BULKREMOVED": "Autorizaciones eliminadas." + }, + "DIALOG": { + "DELETE_TITLE": "Borrar autorización", + "DELETE_DESCRIPTION": "Estás a punto de borrar una autorización. ¿Quieres continuar?", + "BULK_DELETE_TITLE": "Borrar autorizaciones", + "BULK_DELETE_DESCRIPTION": "Estás a punto de borrar múltiples autorizaciones. ¿Quieres continuar?" + } + }, + "CHANGES": { + "LISTTITLE": "Últimos cambios", + "BOTTOM": "Has llegado al final de la lista.", + "LOADMORE": "Cargar más", + "ORG": { + "TITLE": "Actividad", + "DESCRIPTION": "Aquí puedes ver los últimos eventos que han generado un cambio de organización." + }, + "PROJECT": { + "TITLE": "Actividad", + "DESCRIPTION": "Aquí puedes ver los últimos eventos que han generado un cambio de proyecto." + }, + "USER": { + "TITLE": "Actividad", + "DESCRIPTION": "Aquí puedes ver los últimos eventos que han generado un cambio de usuario." + } + } +} diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 25a521b55d..2a0e3292b8 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -1019,6 +1019,7 @@ "LANGUAGE": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -1217,6 +1218,7 @@ "LOCALES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -2067,6 +2069,7 @@ "LANGUAGES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 4776e30904..8a1e3dc07b 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -1020,6 +1020,7 @@ "LANGUAGE": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -1218,6 +1219,7 @@ "LOCALES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -2080,6 +2082,7 @@ "LANGUAGES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index f6a262a3ac..4821833892 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -1020,6 +1020,7 @@ "LANGUAGE": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -1213,6 +1214,7 @@ "LOCALES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -2070,6 +2072,7 @@ "LANGUAGES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 9c93e1688f..3884b850c8 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -1019,6 +1019,7 @@ "LANGUAGE": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -1217,6 +1218,7 @@ "LOCALES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -2079,6 +2081,7 @@ "LANGUAGES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index b40de11ce1..27bb27d810 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -1019,6 +1019,7 @@ "LANGUAGE": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -1216,6 +1217,7 @@ "LOCALES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", @@ -2066,6 +2068,7 @@ "LANGUAGES": { "de": "Deutsch", "en": "English", + "es": "Español", "fr": "Français", "it": "Italiano", "ja": "日本語", diff --git a/docs/docs/guides/manage/customize/texts.md b/docs/docs/guides/manage/customize/texts.md index 4f1d1de8c5..1609e2a93e 100644 --- a/docs/docs/guides/manage/customize/texts.md +++ b/docs/docs/guides/manage/customize/texts.md @@ -35,6 +35,7 @@ ZITADEL is available in the following languages - German (de) - English (en) +- Spanish (es) - French (fr) - Italian (it) - 日本語 (ja) diff --git a/internal/api/ui/login/static/i18n/de.yaml b/internal/api/ui/login/static/i18n/de.yaml index b634fff2d5..5b12002119 100644 --- a/internal/api/ui/login/static/i18n/de.yaml +++ b/internal/api/ui/login/static/i18n/de.yaml @@ -221,6 +221,7 @@ RegistrationUser: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español GenderLabel: Geschlecht Female: weiblich Male: männlich @@ -253,6 +254,7 @@ ExternalRegistrationUserOverview: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español TosAndPrivacyLabel: Allgemeine Geschäftsbedingungen und Datenschutz TosConfirm: Ich akzeptiere die TosLinkText: AGBs @@ -313,6 +315,7 @@ ExternalNotFound: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español Footer: PoweredBy: Powered By diff --git a/internal/api/ui/login/static/i18n/en.yaml b/internal/api/ui/login/static/i18n/en.yaml index cf7de142fb..14b0d90011 100644 --- a/internal/api/ui/login/static/i18n/en.yaml +++ b/internal/api/ui/login/static/i18n/en.yaml @@ -7,7 +7,7 @@ Login: UsernamePlaceHolder: username LoginnamePlaceHolder: username@domain ExternalUserDescription: Login with an external user. - MustBeMemberOfOrg: The user must be member of the {{.OrgName}} organisation. + MustBeMemberOfOrg: The user must be member of the {{.OrgName}} organization. RegisterButtonText: register NextButtonText: next @@ -26,7 +26,7 @@ SelectAccount: OtherUser: Other User SessionState0: active SessionState1: inactive - MustBeMemberOfOrg: The user must be member of the {{.OrgName}} organisation. + MustBeMemberOfOrg: The user must be member of the {{.OrgName}} organization. Password: Title: Password @@ -118,7 +118,7 @@ InitMFADone: MFAProvider: Provider0: Authenticator App (e.g Google/Microsoft Authenticator, Authy) Provider1: Device dependent (e.g FaceID, Windows Hello, Fingerprint) - ChooseOther: or choose an other option + ChooseOther: or choose another option VerifyMFAOTP: Title: Verify 2-Factor @@ -143,7 +143,7 @@ Passwordless: PasswordlessPrompt: Title: Passwordless setup - Description: Would you like to setup passwordless login? (Authenticationmethods of your device like FaceID, Windows Hello or Fingerprint) + Description: Would you like to setup passwordless login? (Authentication methods of your device like FaceID, Windows Hello or Fingerprint) DescriptionInit: You need to set up passwordless login. Use the link you were given to register your device. PasswordlessButtonText: Go passwordless NextButtonText: next @@ -221,6 +221,7 @@ RegistrationUser: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español GenderLabel: Gender Female: Female Male: Male @@ -253,6 +254,7 @@ ExternalRegistrationUserOverview: Chinese: 简体中文 Japanese: 日本語 Polish: Polski + Spanish: Español TosAndPrivacyLabel: Terms and conditions TosConfirm: I accept the TosLinkText: TOS @@ -263,9 +265,9 @@ ExternalRegistrationUserOverview: NextButtonText: save RegistrationOrg: - Title: Organisation Registration - Description: Enter your organisationname and userdata. - OrgNameLabel: Organisationname + Title: Organization Registration + Description: Enter your organization name and userdata. + OrgNameLabel: Organization name EmailLabel: E-Mail UsernameLabel: Username FirstnameLabel: First name @@ -313,6 +315,7 @@ ExternalNotFound: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español Footer: PoweredBy: Powered By @@ -334,8 +337,8 @@ Errors: NotFound: User could not be found AlreadyExists: User already exists Inactive: User is inactive - NotFoundOnOrg: User could not be found on chosen organisation - NotAllowedOrg: User is no member of the required organisation + NotFoundOnOrg: User could not be found on chosen organization + NotAllowedOrg: User is no member of the required organization NotMatchingUserID: User and user in authrequest don't match UserIDMissing: UserID is empty Invalid: Invalid userdata @@ -365,11 +368,11 @@ Errors: NotFound: Address not found NotChanged: Address not changed Username: - AlreadyExists: Username already taken + AlreadyExists: Username already taken Reserved: Username is already taken Empty: Username is empty Password: - ConfirmationWrong: Passwordconfirmation is wrong + ConfirmationWrong: Password confirmation is wrong Empty: Password is empty Invalid: Password is invalid InvalidAndLocked: Password is invalid and user is locked, contact your administrator. @@ -377,7 +380,7 @@ Errors: Invalid: Username or Password is invalid PasswordComplexityPolicy: NotFound: Password policy not found - MinLength: Password is to short + MinLength: Password is too short HasLower: Password must contain lower letter HasUpper: Password must contain upper letter HasNumber: Password must contain number @@ -413,7 +416,7 @@ Errors: CreationNotAllowed: Creation of a new user is not allowed on this Provider LinkingNotAllowed: Linking of a user is not allowed on this Provider GrantRequired: Login not possible. The user is required to have at least one grant on the application. Please contact your administrator. - ProjectRequired: Login not possible. The organisation of the user must be granted to the project. Please contact your administrator. + ProjectRequired: Login not possible. The organization of the user must be granted to the project. Please contact your administrator. IdentityProvider: InvalidConfig: Identity Provider configuration is invalid IAM: diff --git a/internal/api/ui/login/static/i18n/es.yaml b/internal/api/ui/login/static/i18n/es.yaml new file mode 100644 index 0000000000..a9ab5e9f40 --- /dev/null +++ b/internal/api/ui/login/static/i18n/es.yaml @@ -0,0 +1,429 @@ +Login: + Title: ¡Hola de nuevo! + Description: Introduce tus datos de inicio de sesión. + TitleLinking: Inicio de sesión para vincular un usuario + DescriptionLinking: Introduce tus datos de inicio de sesión para vincular tu usuario externo con un usuario ZITADEL. + LoginNameLabel: Nombre de inicio de sesión + UsernamePlaceHolder: username + LoginnamePlaceHolder: username@dominio + ExternalUserDescription: Inicia sesión con un usuario externo. + MustBeMemberOfOrg: El usuario debe ser miembro de la organización {{.OrgName}}. + RegisterButtonText: registrar + NextButtonText: siguiente + +LDAP: + Title: Inicio de sesión + Description: Introduce tus datos de inicio de sesión. + LoginNameLabel: Nombre de inicio de sesión + PasswordLabel: Contraseña + NextButtonText: siguiente + +SelectAccount: + Title: Seleccionar cuenta + Description: Utiliza tu cuenta ZITADEL + TitleLinking: Selecciona tu cuenta para vincular el usuario + DescriptionLinking: Selecciona tu cuenta para vincular con tu usuario externo. + OtherUser: Otro Usuario + SessionState0: activo + SessionState1: inactivo + MustBeMemberOfOrg: El usuario debe ser miembro de la organización {{.OrgName}}. + +Password: + Title: Contraseña + Description: Introduce tus datos de inicio de sesión. + PasswordLabel: Contraseña + MinLength: Longitud mínima + HasUppercase: Una letra mayúscula + HasLowercase: Una letra minúscula + HasNumber: Número + HasSymbol: Símbolo + Confirmation: Las contraseñas coinciden + ResetLinkText: restablecer contraseña + BackButtonText: atrás + NextButtonText: siguiente + +UsernameChange: + Title: Cambiar nombre de usuario + Description: Introduce tu nuevo nombre de usuario + UsernameLabel: Nombre de usuario + CancelButtonText: cancelar + NextButtonText: siguiente + +UsernameChangeDone: + Title: Nombre de usuario cambiado + Description: Tu nombre de usuario se cambió correctamente. + NextButtonText: siguiente + +InitPassword: + Title: Establecer contraseña + Description: Habrás recibido un código, que tendrás que introducir en el siguiente campo, para establecer tu nueva contraseña. + CodeLabel: Código + NewPasswordLabel: Nueva contraseña + NewPasswordConfirmLabel: Confirmar contraseña + ResendButtonText: reenviar código + NextButtonText: siguiente + +InitPasswordDone: + Title: Contraseña establecida + Description: La contraseña se estableció correctamente + NextButtonText: siguiente + CancelButtonText: cancelar + +InitUser: + Title: Activar usuario + Description: Verifica tu email con el siguiente código y establece tu contraseña. + CodeLabel: Código + NewPasswordLabel: Nueva contraseña + NewPasswordConfirm: Confirmar contraseña + NextButtonText: siguiente + ResendButtonText: reenviar código + +InitUserDone: + Title: Usuario activado + Description: Email verificado y contraseña establecida correctamente + NextButtonText: siguiente + CancelButtonText: cancelar + +InitMFAPrompt: + Title: Configuración de doble factor + Description: La autenticación de doble factor te proporciona seguridad adicional para tu cuenta de usuario. Ésta asegura que solo tú tienes acceso a tu cuenta. + Provider0: App autenticadora (p.e Google/Microsoft Authenticator, Authy) + Provider1: Dependiente de un dispositivo (p.e FaceID, Windows Hello, Huella dactilar) + NextButtonText: siguiente + SkipButtonText: saltar + +InitMFAOTP: + Title: Verificación de doble factor + Description: Crea tu doble factor de autenticación. Descarga una aplicación autenticadora si todavía no tienes una. + OTPDescription: Escanea el código con tu app autenticadora (p.e Google/Microsoft Authenticator, Authy) o copia el secreto e inserta el código generado más abajo. + SecretLabel: Secreto + CodeLabel: Código + NextButtonText: siguiente + CancelButtonText: cancelar + +InitMFAU2F: + Title: Añadir clave de seguridad + Description: Una clave de seguridad es un método de verificación que puede integrarse en tu teléfono móvil, con Bluetooth, o conectándolo directamente en el puerto USB de tu ordenador. + TokenNameLabel: Nombre de la clave de seguridad / dispositivo + NotSupported: WebAuthN no está soportado por tu navegador. Por favor asegúrate de que está actualizado o utiliza uno diferente (p.e. Chrome, Safari, Firefox) + RegisterTokenButtonText: Añadir clave de seguridad + ErrorRetry: Reintentar, crear un nuevo challenge o elegir un método diferente. + +InitMFADone: + Title: Clave de seguridad verificada + Description: ¡Genial! Acabas de configurar satisfactoriamente tu doble factor y has hecho que tu cuenta sea más segura. El doble factor tendrá que introducirse en cada inicio de sesión. + NextButtonText: siguiente + CancelButtonText: cancelar + +MFAProvider: + Provider0: App autenticadora (p.e Google/Microsoft Authenticator, Authy) + Provider1: Dependiente de un dispositivo (p.e FaceID, Windows Hello, Huella dactilar) + ChooseOther: o elige otra opción + +VerifyMFAOTP: + Title: Verificar doble factor + Description: Verifica tu doble factor + CodeLabel: Código + NextButtonText: siguiente + +VerifyMFAU2F: + Title: Verificación de doble factor + Description: Verifica tu doble factor de autenticación con el dispositivo registrado (p.e FaceID, Windows Hello, Huella dactilar) + NotSupported: WebAuthN no está soportado por tu navegador. Por favor asegúrate de que está actualizado o utiliza uno diferente (p.e. Chrome, Safari, Firefox) + ErrorRetry: Inténtalo nuevamente, crea una nueva petición o elige otro método. + ValidateTokenButtonText: Verificar doble factor + +Passwordless: + Title: Inicio de sesión sin contraseña + Description: Iniciar sesión con métodos de autenticación proporcionados por tu dispositivo como FaceID, Windows Hello o tu huella dactilar. + NotSupported: WebAuthN no está soportado por tu navegador. Por favor asegúrate de que está actualizado o utiliza uno diferente (p.e. Chrome, Safari, Firefox) + ErrorRetry: Inténtalo nuevamente, crea un nuevo reto (challenge) o elige un método diferente. + LoginWithPwButtonText: Inicio de sesión con contraseña + ValidateTokenButtonText: Inicio de sesión sin contraseña + +PasswordlessPrompt: + Title: Configuración de acceso sin contraseña + Description: ¿Te gustaría configurar tu inicio de sesión sin contraseña? (métodos de autenticación de tu dispositivo como FaceID, Windows Hello o tu huella dactilar) + DescriptionInit: Necesitas configurar tu inicio de sesión sin contraseña. Utiliza el enlace que se te ha proporcionado para registrar tu dispositivo. + PasswordlessButtonText: Adelante con el inicio sin contraseñas + NextButtonText: siguiente + SkipButtonText: saltar + +PasswordlessRegistration: + Title: Configuración de acceso sin contraseña + Description: Añade tu medio de autenticación proporcionando un nombre (p.e MyMobilePhone, MacBook, etc) y después haz clic en el botón 'Registrar acceso sin contraseña'. + TokenNameLabel: Nombre del dispositivo + NotSupported: WebAuthN no está soportado por tu navegador. Por favor asegúrate de que está actualizado o utiliza uno diferente (p.e. Chrome, Safari, Firefox) + RegisterTokenButtonText: Registrar acceso sin contraseña + ErrorRetry: Inténtalo nuevamente, crea un nuevo reto (challenge) o elige un método diferente. + +PasswordlessRegistrationDone: + Title: Configuración de acceso sin contraseña + Description: Se añadió con éxito el dispositivo para iniciar sesión sin contraseña. + DescriptionClose: Ya puedes cerrar esta ventana. + NextButtonText: siguiente + CancelButtonText: cancelar + +PasswordChange: + Title: Cambiar contraseña + Description: Cambia tu contraseña. Introduce tu contraseña anterior y la nueva. + OldPasswordLabel: Contraseña anterior + NewPasswordLabel: Nueva contraseña + NewPasswordConfirmLabel: Confirmación de contraseña + CancelButtonText: cancelar + NextButtonText: siguiente + Footer: Pie + +PasswordChangeDone: + Title: Cambiar contraseña + Description: Tu contraseña se cambió correctamente. + NextButtonText: siguiente + +PasswordResetDone: + Title: Se ha enviado un enlace para restablecer la contraseña + Description: Comprueba tu email para restablecer la contraseña. + NextButtonText: siguiente + +EmailVerification: + Title: Verificación de email + Description: Te hemos enviado un email para verificar tu dirección. Por favor introduce el código en el siguiente campo. + CodeLabel: Código + NextButtonText: siguiente + ResendButtonText: reenviar código + +EmailVerificationDone: + Title: Verificación de email + Description: Tu dirección de email se ha verificado correctamente. + NextButtonText: siguiente + CancelButtonText: cancelar + LoginButtonText: iniciar sesión + +RegisterOption: + Title: Opciones de registro + Description: Elige cómo te gustaría registrarte + RegisterUsernamePasswordButtonText: Con nombre de usuario y contraseña + ExternalLoginDescription: o regístrate con un usuario externo + LoginButtonText: iniciar sesión + +RegistrationUser: + Title: Registro + Description: Introduce tus datos de usuario. Tu email se utilizará como nombre de inicio de sesión. + DescriptionOrgRegister: Introduce tus datos de usuario. + EmailLabel: Email + UsernameLabel: Nombre de usuario + FirstnameLabel: Nombre + LastnameLabel: Apellidos + LanguageLabel: Idioma + German: Deutsch + English: English + Italian: Italiano + French: Français + Chinese: 简体中文 + Polish: Polski + Japanese: 日本語 + Spanish: Español + GenderLabel: Género + Female: Mujer + Male: Hombre + Diverse: Diverso / X + PasswordLabel: Contraseña + PasswordConfirmLabel: Confirmación de contraseña + TosAndPrivacyLabel: Términos y condiciones + TosConfirm: Acepto los + TosLinkText: TDS + PrivacyConfirm: Acepto la + PrivacyLinkText: política de privacidad + ExternalLogin: o regístrate con un usuario externo + BackButtonText: inicio de sesión + NextButtonText: siguiente + +ExternalRegistrationUserOverview: + Title: Registro de usuarios externos + Description: Hemos tomado los detalles de tu usuario del proveedor seleccionado. Ahora puedes cambiarlos o completarlos. + EmailLabel: Email + UsernameLabel: Nombre de usuario + FirstnameLabel: Nombre + LastnameLabel: Apellidos + NicknameLabel: Apodo + PhoneLabel: Número de teléfono + LanguageLabel: Idioma + German: Deutsch + English: English + Italian: Italiano + French: Français + Chinese: 简体中文 + Japanese: 日本語 + Polish: Polski + Spanish: Español + TosAndPrivacyLabel: Términos y condiciones + TosConfirm: Acepto los + TosLinkText: TDS + PrivacyConfirm: Acepto la + PrivacyLinkText: política de privacidad + ExternalLogin: o regístrate con un usuario externo + BackButtonText: atrás + NextButtonText: guardar + +RegistrationOrg: + Title: Registro de organización + Description: Introduce el nombre de tu organización y tus datos de usuario. + OrgNameLabel: Nombre de organización + EmailLabel: Email + UsernameLabel: Nombre de usuario + FirstnameLabel: Nombre + LastnameLabel: Apellidos + PasswordLabel: Contraseña + PasswordConfirmLabel: Confirmación de contraseña + TosAndPrivacyLabel: Términos y condiciones + TosConfirm: Acepto los + TosLinkText: TDS + PrivacyConfirm: Acepto la + PrivacyLinkText: política de privacidad + SaveButtonText: Crear organización + +LoginSuccess: + Title: Se inició sesión con éxito + AutoRedirectDescription: Se te redirigirá a tu aplicación automáticamente. Si no fuera así, haz clic en el botón siguiente. Puedes cerrar esta ventana posteriormente. + RedirectedDescription: Ya puedes cerrar esta ventana. + NextButtonText: siguiente + +LogoutDone: + Title: Cerraste sesión + Description: Cerraste la sesión con éxito. + LoginButtonText: iniciar sesión + +LinkingUsersDone: + Title: Vinculación de usuario + Description: usuario vinculado con éxito. + CancelButtonText: cancelar + NextButtonText: siguiente + +ExternalNotFound: + Title: Usuario externo no encontrado + Description: Usuario externo no encontrado. ¿Quieres vincular tu usuario o autoregistrar uno nuevo? + LinkButtonText: Vincular + AutoRegisterButtonText: registrar + TosAndPrivacyLabel: Términos y condiciones + TosConfirm: Acepto los + TosLinkText: TDS + PrivacyConfirm: Acepto la + PrivacyLinkText: política de privacidad + German: Deutsch + English: English + Italian: Italiano + French: Français + Chinese: 简体中文 + Polish: Polski + Japanese: 日本語 + Spanish: Español + +Footer: + PoweredBy: Powered By + Tos: TDS + PrivacyPolicy: Política de privacidad + Help: Ayuda + SupportEmail: Email de soporte + +Errors: + Internal: Se produjo un error interno + AuthRequest: + NotFound: No pude encontrar la petición de autenticación (authrequest) + UserAgentNotCorresponding: El User Agent no se corresponde + UserAgentNotFound: No se encontró el ID del User Agent + TokenNotFound: No se encontró el Token + RequestTypeNotSupported: El tipo de petición no está soportado + MissingParameters: Faltan parámetros requeridos + User: + NotFound: El usuario no pudo ser encontrado + AlreadyExists: El usuario ya existe + Inactive: El usuario está inactivo + NotFoundOnOrg: El usuario no pudo encontrarse en la organización elegida + NotAllowedOrg: El usuario no es miembro de la organización requerida + NotMatchingUserID: El usuario y el usuario contenido en la petición de autenticación (authrequest) no coinciden + UserIDMissing: El ID de usuario está vacío + Invalid: Datos de usuario no válidos + DomainNotAllowedAsUsername: El dominio ya está reservado y no puede usarse + NotAllowedToLink: El usuario no está autorizado para vincularse con un proveedor de inicio de sesión externo + Profile: + NotFound: Perfil no encontrado + NotChanged: El perfil no ha cambiado + Empty: El perfil está vacío + FirstNameEmpty: El nombre del perfil está vacío + LastNameEmpty: Los apellidos del perfil están vacíos + IDMissing: Falta el ID del perfil + Email: + NotFound: Email no encontrado + Invalid: El email no es válido + AlreadyVerified: El email ya ha sido verificado + NotChanged: El email no ha cambiado + Empty: El email está vacío + IDMissing: Falta el ID del email + Phone: + NotFound: Teléfono no encontrado + Invalid: El teléfono no es válido + AlreadyVerified: El teléfono ya ha sido verificado + Empty: El teléfono está vacío + NotChanged: El teléfono no ha cambiado + Address: + NotFound: Dirección no encontrada + NotChanged: La dirección no cambió + Username: + AlreadyExists: El nombre de usuario ya está cogido + Reserved: El nombre de usuario ya está cogido + Empty: El nombre de usuario está vacío + Password: + ConfirmationWrong: La confirmación de la contraseña es incorrecta + Empty: La contraseña está vacía + Invalid: La contraseña no es válida + InvalidAndLocked: La contraseña no es válida y el usuario está bloqueado, contacta con tu administrador. + UsernameOrPassword: + Invalid: El nombre de usuario o la contraseña no son válidos + PasswordComplexityPolicy: + NotFound: No se encontró una política de contraseñas + MinLength: La contraseña es demasiada corta + HasLower: La contraseña debe contener una letra minúscula + HasUpper: La contraseña debe contener una letra mayúscula + HasNumber: La contraseña debe contener un número + HasSymbol: La contraseña debe contener un símbolo + Code: + Expired: El código ha caducado + Invalid: El código no es válido + Empty: El código está vacío + CryptoCodeNil: El código criptográfico es nulo + NotFound: No pude encontrar el código + GeneratorAlgNotSupported: Algoritmo de generación no soportado + EmailVerify: + UserIDEmpty: El ID de usuario está vacío + ExternalData: + CouldNotRead: Los datos externos no pudieron leerse correctamente + MFA: + NoProviders: No hay proveedores multifactor disponibles + OTP: + AlreadyReady: El multifactor OTP (OneTimePassword) ya está configurado + NotExisting: El multifactor OTP (OneTimePassword) no existe + InvalidCode: Código no válido + NotReady: El multifactor OTP (OneTimePassword) no está listo + Locked: El usuario está bloqueado + SomethingWentWrong: Algo fue mal + NotActive: El usuario no está activo + ExternalIDP: + IDPTypeNotImplemented: El tipo de IDP no está implementado + NotAllowed: El proveedor de inicio de sesión externo no está permitido + IDPConfigIDEmpty: El ID del proveedor de identidad está vacío + ExternalUserIDEmpty: El ID de usuario externo está vacío + UserDisplayNameEmpty: El nombre mostrado del usuario está vacío + NoExternalUserData: No se recibieron datos del usuario externo + CreationNotAllowed: La creación de un nuevo usuario no está permitida para este proveedor + LinkingNotAllowed: La vinculación de un usuario no está permitida para este proveedor + GrantRequired: El inicio de sesión no es posible. Se requiere que el usuario tenga al menos una concesión sobre la aplicación. Por favor contacta con tu administrador. + ProjectRequired: El inicio de sesión no es posible. La organización del usuario debe tener el acceso concedido para el proyecto. Por favor contacta con tu administrador. + IdentityProvider: + InvalidConfig: La configuración del proveedor de identidades no es válida + IAM: + LockoutPolicy: + NotExisting: No existe política de bloqueo + Org: + LoginPolicy: + RegistrationNotAllowed: El registro no está permitido + +optional: (opcional) diff --git a/internal/api/ui/login/static/i18n/fr.yaml b/internal/api/ui/login/static/i18n/fr.yaml index f1eae1f15c..be50b4327e 100644 --- a/internal/api/ui/login/static/i18n/fr.yaml +++ b/internal/api/ui/login/static/i18n/fr.yaml @@ -221,6 +221,7 @@ RegistrationUser: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español GenderLabel: Genre Female: Femme Male: Homme @@ -253,6 +254,7 @@ ExternalRegistrationUserOverview: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español TosAndPrivacyLabel: Termes et conditions TosConfirm: J'accepte les TosLinkText: TOS @@ -313,6 +315,7 @@ ExternalNotFound: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español Footer: PoweredBy: Promulgué par diff --git a/internal/api/ui/login/static/i18n/it.yaml b/internal/api/ui/login/static/i18n/it.yaml index 7231632c6e..b27e27b072 100644 --- a/internal/api/ui/login/static/i18n/it.yaml +++ b/internal/api/ui/login/static/i18n/it.yaml @@ -221,6 +221,7 @@ RegistrationUser: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español GenderLabel: Genere Female: Femminile Male: Maschile @@ -253,6 +254,7 @@ ExternalRegistrationUserOverview: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español TosAndPrivacyLabel: Termini di servizio TosConfirm: Accetto i TosLinkText: Termini di servizio @@ -313,6 +315,7 @@ ExternalNotFound: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español Footer: PoweredBy: Alimentato da diff --git a/internal/api/ui/login/static/i18n/ja.yaml b/internal/api/ui/login/static/i18n/ja.yaml index a27a728b12..bdb9ec09d7 100644 --- a/internal/api/ui/login/static/i18n/ja.yaml +++ b/internal/api/ui/login/static/i18n/ja.yaml @@ -213,6 +213,7 @@ RegistrationUser: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español GenderLabel: 性別 Female: 女性 Male: 男性 @@ -245,6 +246,7 @@ ExternalRegistrationUserOverview: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español TosAndPrivacyLabel: 利用規約 TosConfirm: 私は利用規約を承諾します。 TosLinkText: TOS @@ -305,6 +307,7 @@ ExternalNotFound: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español Footer: PoweredBy: Powered By diff --git a/internal/api/ui/login/static/i18n/pl.yaml b/internal/api/ui/login/static/i18n/pl.yaml index bdc4e33760..ca038b2561 100644 --- a/internal/api/ui/login/static/i18n/pl.yaml +++ b/internal/api/ui/login/static/i18n/pl.yaml @@ -221,6 +221,7 @@ RegistrationUser: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español GenderLabel: Płeć Female: Kobieta Male: Mężczyzna @@ -253,6 +254,7 @@ ExternalRegistrationUserOverview: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español TosAndPrivacyLabel: Warunki i zasady TosConfirm: Akceptuję TosLinkText: Warunki korzystania @@ -313,6 +315,7 @@ ExternalNotFound: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español Footer: PoweredBy: Obsługiwane przez diff --git a/internal/api/ui/login/static/i18n/zh.yaml b/internal/api/ui/login/static/i18n/zh.yaml index 80f9e4f0a9..830476299f 100644 --- a/internal/api/ui/login/static/i18n/zh.yaml +++ b/internal/api/ui/login/static/i18n/zh.yaml @@ -221,6 +221,7 @@ RegistrationUser: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español GenderLabel: 性别 Female: 女性 Male: 男性 @@ -253,6 +254,7 @@ ExternalRegistrationUserOverview: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español TosAndPrivacyLabel: 条款和条款 TosConfirm: 我接受 TosLinkText: 服务条款 @@ -313,6 +315,7 @@ ExternalNotFound: Chinese: 简体中文 Polish: Polski Japanese: 日本語 + Spanish: Español Footer: PoweredBy: Powered By diff --git a/internal/api/ui/login/static/templates/external_not_found_option.html b/internal/api/ui/login/static/templates/external_not_found_option.html index 96a1197e0a..253962588a 100644 --- a/internal/api/ui/login/static/templates/external_not_found_option.html +++ b/internal/api/ui/login/static/templates/external_not_found_option.html @@ -64,6 +64,8 @@ +
diff --git a/console/src/app/modules/filter-events/filter-events.component.html b/console/src/app/modules/filter-events/filter-events.component.html index 07deb4d0bc..7e76d422e6 100644 --- a/console/src/app/modules/filter-events/filter-events.component.html +++ b/console/src/app/modules/filter-events/filter-events.component.html @@ -16,8 +16,6 @@ , protected _platform: Platform, /** @docs-private */ - @Optional() @Self() public ngControl: NgControl, + @Optional() @Self() public override ngControl: NgControl, @Optional() _parentForm: NgForm, @Optional() _parentFormGroup: FormGroupDirective, _defaultErrorStateMatcher: ErrorStateMatcher, diff --git a/console/src/app/modules/metadata/metadata/metadata.component.ts b/console/src/app/modules/metadata/metadata/metadata.component.ts index 45eb43a34a..22ff727a8a 100644 --- a/console/src/app/modules/metadata/metadata/metadata.component.ts +++ b/console/src/app/modules/metadata/metadata/metadata.component.ts @@ -27,8 +27,8 @@ export class MetadataComponent implements OnChanges { constructor() {} ngOnChanges(changes: SimpleChanges): void { - if (changes.metadata?.currentValue) { - this.dataSource = new MatTableDataSource(changes.metadata.currentValue); + if (changes['metadata']?.currentValue) { + this.dataSource = new MatTableDataSource(changes['metadata'].currentValue); } } } diff --git a/console/src/app/modules/nav/nav.component.html b/console/src/app/modules/nav/nav.component.html index e0b0ee0ec1..38e77ed8ac 100644 --- a/console/src/app/modules/nav/nav.component.html +++ b/console/src/app/modules/nav/nav.component.html @@ -111,7 +111,7 @@ {{ 'MENU.DASHBOARD' | translate }} - +
- - + + {{ 'ERRORS.SYMBOLERROR' | translate }}
- - + + {{ 'ERRORS.NUMBERERROR' | translate }}
- - + + {{ 'ERRORS.UPPERCASEMISSING' | translate }}
- - + + {{ 'ERRORS.LOWERCASEMISSING' | translate }}
diff --git a/console/src/app/modules/policies/message-texts/message-texts.component.html b/console/src/app/modules/policies/message-texts/message-texts.component.html index bdeabc1533..105371fcf3 100644 --- a/console/src/app/modules/policies/message-texts/message-texts.component.html +++ b/console/src/app/modules/policies/message-texts/message-texts.component.html @@ -46,7 +46,7 @@
@@ -117,7 +123,7 @@ color="primary" mat-raised-button class="continue-button" - [disabled]="(form.invalid && attributes.toObject().idAttribute !== '') || form.disabled" + [disabled]="form.invalid || attributes.toObject().idAttribute === '' || form.disabled" type="submit" > {{ 'ACTIONS.SAVE' | translate }} diff --git a/console/src/app/modules/providers/provider-ldap/provider-ldap.component.ts b/console/src/app/modules/providers/provider-ldap/provider-ldap.component.ts index 98df95ad99..abf3938707 100644 --- a/console/src/app/modules/providers/provider-ldap/provider-ldap.component.ts +++ b/console/src/app/modules/providers/provider-ldap/provider-ldap.component.ts @@ -83,7 +83,7 @@ export class ProviderLDAPComponent { }); this.route.data.pipe(take(1)).subscribe((data) => { - this.serviceType = data.serviceType; + this.serviceType = data['serviceType']; switch (this.serviceType) { case PolicyComponentServiceType.MGMT: diff --git a/console/src/app/modules/providers/provider-oauth/provider-oauth.component.ts b/console/src/app/modules/providers/provider-oauth/provider-oauth.component.ts index 3f68bf83a6..c838143e6f 100644 --- a/console/src/app/modules/providers/provider-oauth/provider-oauth.component.ts +++ b/console/src/app/modules/providers/provider-oauth/provider-oauth.component.ts @@ -81,7 +81,7 @@ export class ProviderOAuthComponent { }); this.route.data.pipe(take(1)).subscribe((data) => { - this.serviceType = data.serviceType; + this.serviceType = data['serviceType']; switch (this.serviceType) { case PolicyComponentServiceType.MGMT: diff --git a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts index b26d2e9f92..02d6652ec5 100644 --- a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts @@ -59,7 +59,7 @@ export class ProviderOIDCComponent { }); this.route.data.pipe(take(1)).subscribe((data) => { - this.serviceType = data.serviceType; + this.serviceType = data['serviceType']; switch (this.serviceType) { case PolicyComponentServiceType.MGMT: diff --git a/console/src/app/modules/settings-list/settings-list.component.ts b/console/src/app/modules/settings-list/settings-list.component.ts index d7f4181b49..5c97d1c7ec 100644 --- a/console/src/app/modules/settings-list/settings-list.component.ts +++ b/console/src/app/modules/settings-list/settings-list.component.ts @@ -19,10 +19,10 @@ export class SettingsListComponent implements OnChanges { constructor() {} ngOnChanges(changes: SimpleChanges): void { - if (changes.selectedId?.currentValue) { + if (changes['selectedId']?.currentValue) { this.currentSetting = - this.settingsList && this.settingsList.find((l) => l.id === changes.selectedId.currentValue) - ? changes.selectedId.currentValue + this.settingsList && this.settingsList.find((l) => l.id === changes['selectedId'].currentValue) + ? changes['selectedId'].currentValue : ''; } else { this.currentSetting = this.settingsList ? this.settingsList[0].id : ''; diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index f910e129d3..0d69f370d0 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -398,10 +398,10 @@ - +
diff --git a/console/src/app/pages/projects/owned-projects/project-grant-create/project-grant-create.component.ts b/console/src/app/pages/projects/owned-projects/project-grant-create/project-grant-create.component.ts index 29cf44d169..6cb71c63a6 100644 --- a/console/src/app/pages/projects/owned-projects/project-grant-create/project-grant-create.component.ts +++ b/console/src/app/pages/projects/owned-projects/project-grant-create/project-grant-create.component.ts @@ -34,7 +34,7 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy { public ngOnInit(): void { this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => { - this.projectId = params.projectid; + this.projectId = params['projectid']; const breadcrumbs = [ new Breadcrumb({ diff --git a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts index bb8be7939d..3532f5e43f 100644 --- a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts +++ b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts @@ -49,19 +49,19 @@ export class ProjectGrantDetailComponent { private breadcrumbService: BreadcrumbService, ) { this.route.params.subscribe((params) => { - this.projectid = params.projectid; - this.grantid = params.grantid; + this.projectid = params['projectid']; + this.grantid = params['grantid']; this.dataSource = new ProjectGrantMembersDataSource(this.mgmtService); - this.dataSource.loadMembers(params.projectid, params.grantid, 0, this.INITIALPAGESIZE); + this.dataSource.loadMembers(params['projectid'], params['grantid'], 0, this.INITIALPAGESIZE); - this.getRoleOptions(params.projectid); + this.getRoleOptions(params['projectid']); this.getMemberRoleOptions(); this.changePageFactory = (event?: PageEvent) => { return this.dataSource.loadMembers( - params.projectid, - params.grantid, + params['projectid'], + params['grantid'], event?.pageIndex ?? 0, event?.pageSize ?? this.INITIALPAGESIZE, ); diff --git a/console/src/app/pages/projects/projects.component.ts b/console/src/app/pages/projects/projects.component.ts index add43ca66f..4d1b55455a 100644 --- a/console/src/app/pages/projects/projects.component.ts +++ b/console/src/app/pages/projects/projects.component.ts @@ -24,7 +24,7 @@ export class ProjectsComponent { breadcrumbService: BreadcrumbService, ) { this.activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => { - const type = params.type; + const type = params['type']; if (type && type === 'owned') { this.setType(ProjectType.PROJECTTYPE_OWNED); } else if (type && type === 'granted') { diff --git a/console/src/app/pages/users/user-detail/phone-detail/phone-detail.component.ts b/console/src/app/pages/users/user-detail/phone-detail/phone-detail.component.ts index db0287300d..c69980b0e2 100644 --- a/console/src/app/pages/users/user-detail/phone-detail/phone-detail.component.ts +++ b/console/src/app/pages/users/user-detail/phone-detail/phone-detail.component.ts @@ -11,8 +11,8 @@ export class PhoneDetailComponent implements OnChanges { public country: string | undefined; ngOnChanges(changes: SimpleChanges): void { - if (changes.phone.currentValue) { - const phoneNumber = formatPhone(changes.phone.currentValue); + if (changes['phone'].currentValue) { + const phoneNumber = formatPhone(changes['phone'].currentValue); if (this.phone !== phoneNumber.phone) { this.phone = phoneNumber.phone; this.country = phoneNumber.country; diff --git a/console/src/app/pages/users/user-list/user-table/user-table.component.ts b/console/src/app/pages/users/user-list/user-table/user-table.component.ts index 777fa5ad81..415fd6110e 100644 --- a/console/src/app/pages/users/user-list/user-table/user-table.component.ts +++ b/console/src/app/pages/users/user-list/user-table/user-table.component.ts @@ -96,7 +96,7 @@ export class UserTableComponent implements OnInit { ngOnInit(): void { this.route.queryParams.pipe(take(1)).subscribe((params) => { this.getData(this.INITIAL_PAGE_SIZE, 0, this.type); - if (params.deferredReload) { + if (params['deferredReload']) { setTimeout(() => { this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize, this.type); }, 2000); diff --git a/console/src/app/services/seo.service.ts b/console/src/app/services/seo.service.ts index d5a28999e2..6db0936d29 100644 --- a/console/src/app/services/seo.service.ts +++ b/console/src/app/services/seo.service.ts @@ -1,8 +1,6 @@ import { Injectable } from '@angular/core'; import { Meta } from '@angular/platform-browser'; -import { environment } from '../../environments/environment'; - @Injectable({ providedIn: 'root', }) @@ -14,7 +12,7 @@ export class SeoService { config = { title: 'ZITADEL Console', description: 'Managementplatform for ZITADEL', - image: 'https://www.zitadel.ch/zitadel-social-preview25.png', + image: 'https://www.zitadel.com/images/preview.png', slug: '', ...config, }; @@ -27,15 +25,11 @@ export class SeoService { if (config.image) { this.meta.updateTag({ property: 'og:image', content: config.image }); } - this.meta.updateTag({ - property: 'og:url', - content: `https://${environment.production ? 'console.zitadel.ch' : 'console.zitadel.dev'}/${config.slug}`, - }); this.meta.updateTag({ property: 'twitter:card', content: 'summary' }); this.meta.updateTag({ property: 'og:site', content: '@zitadel_ch' }); this.meta.updateTag({ property: 'og:title', content: config.title }); - this.meta.updateTag({ property: 'og:image', content: 'https://www.zitadel.ch/zitadel-social-preview25.png' }); + this.meta.updateTag({ property: 'og:image', content: 'https://www.zitadel.com/images/preview.png' }); this.meta.updateTag({ property: 'og:description', content: config.description }); } } diff --git a/console/src/environments/environment.prod.ts b/console/src/environments/environment.prod.ts deleted file mode 100644 index c9669790be..0000000000 --- a/console/src/environments/environment.prod.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const environment = { - production: true, -}; diff --git a/console/src/environments/environment.ts b/console/src/environments/environment.ts deleted file mode 100644 index 31cb7855f1..0000000000 --- a/console/src/environments/environment.ts +++ /dev/null @@ -1,16 +0,0 @@ -// This file can be replaced during build by using the `fileReplacements` array. -// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. -// The list of file replacements can be found in `angular.json`. - -export const environment = { - production: false, -}; - -/* - * For easier debugging in development mode, you can import the following file - * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. - * - * This import should be commented out in production mode because it will have a negative impact - * on performance if an error is thrown. - */ -// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/console/src/main.ts b/console/src/main.ts index 243696d657..17a5cd4e0a 100644 --- a/console/src/main.ts +++ b/console/src/main.ts @@ -1,15 +1,6 @@ -import 'codemirror/mode/javascript/javascript'; -import 'codemirror/mode/xml/xml'; - -import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} platformBrowserDynamic() .bootstrapModule(AppModule) diff --git a/console/tsconfig.json b/console/tsconfig.json index 4cf6388081..1301bf238f 100644 --- a/console/tsconfig.json +++ b/console/tsconfig.json @@ -6,6 +6,8 @@ "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "sourceMap": true, @@ -15,11 +17,12 @@ "moduleResolution": "node", "importHelpers": true, "target": "ES2022", - "module": "es2022", - "lib": ["ES2022", "dom"], - "useDefineForClassFields": false + "module": "ES2022", + "useDefineForClassFields": false, + "lib": ["ES2022", "dom"] }, "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, "strictTemplates": true diff --git a/console/tsconfig.spec.json b/console/tsconfig.spec.json index f140ce15bf..47e3dd7551 100644 --- a/console/tsconfig.spec.json +++ b/console/tsconfig.spec.json @@ -5,6 +5,5 @@ "outDir": "./out-tsc/spec", "types": ["jasmine"] }, - "files": ["src/test.ts"], "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] } From c420de1533090b11e1406f47131e32964d8f0e5f Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 18 Apr 2023 17:37:26 +0200 Subject: [PATCH 33/36] fix(console): LDAP UI optimization for better required field recognition, improve onboarding all done visibility (#5659) * fix: onboarding all done styles * ldap UI change * rm log * set required marker to formfield, max width of string-list comp * seperate formfields * formarray * clear action * validator * hide pwd field * rm dead code * lint --- .../field/form-field.component.html | 11 +- .../form-field/field/form-field.component.ts | 2 + .../form-field/validators/validators.ts | 12 ++ .../app/modules/label/label.component.scss | 16 ++- .../onboarding/onboarding.component.scss | 6 + .../ldap-attributes.component.html | 111 ++++++++++-------- .../ldap-attributes.component.scss | 5 + .../ldap-attributes.component.ts | 1 + .../provider-ldap.component.html | 36 +++--- .../provider-ldap/provider-ldap.component.ts | 35 ++++-- .../src/app/modules/providers/providers.scss | 15 +++ .../string-list/string-list.component.html | 67 ++++++----- .../string-list/string-list.component.scss | 70 +++++------ .../string-list/string-list.component.ts | 65 +++++----- .../modules/string-list/string-list.module.ts | 2 + .../user-create-machine.component.html | 4 +- .../user-create/user-create.component.html | 8 +- console/src/assets/i18n/de.json | 1 + console/src/assets/i18n/en.json | 1 + console/src/assets/i18n/es.json | 1 + console/src/assets/i18n/fr.json | 1 + console/src/assets/i18n/it.json | 1 + console/src/assets/i18n/ja.json | 1 + console/src/assets/i18n/pl.json | 1 + console/src/assets/i18n/zh.json | 1 + 25 files changed, 277 insertions(+), 197 deletions(-) diff --git a/console/src/app/modules/form-field/field/form-field.component.html b/console/src/app/modules/form-field/field/form-field.component.html index 376dbc229a..245bc57f80 100644 --- a/console/src/app/modules/form-field/field/form-field.component.html +++ b/console/src/app/modules/form-field/field/form-field.component.html @@ -1,5 +1,14 @@ + + + +
- +
diff --git a/console/src/app/modules/form-field/field/form-field.component.ts b/console/src/app/modules/form-field/field/form-field.component.ts index fd8e4a23af..804b44fb61 100644 --- a/console/src/app/modules/form-field/field/form-field.component.ts +++ b/console/src/app/modules/form-field/field/form-field.component.ts @@ -51,6 +51,7 @@ interface ValidationError { '[class.ng-valid]': '_shouldForward("valid")', '[class.ng-invalid]': '_shouldForward("invalid")', '[class.ng-pending]': '_shouldForward("pending")', + '[class.ng-required]': '_control.required', '[class.cnsl-form-field-disabled]': '_control.disabled', '[class.cnsl-form-field-autofilled]': '_control.autofilled', '[class.cnsl-focused]': '_control.focused', @@ -69,6 +70,7 @@ export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestr @ContentChild(MatFormFieldControl) _controlNonStatic!: MatFormFieldControl; @ContentChild(MatFormFieldControl, { static: true }) _controlStatic!: MatFormFieldControl; @Input() public disableValidationErrors = false; + @Input() public hideRequiredMarker = false; get _control(): MatFormFieldControl { return this._explicitFormFieldControl || this._controlNonStatic || this._controlStatic; diff --git a/console/src/app/modules/form-field/validators/validators.ts b/console/src/app/modules/form-field/validators/validators.ts index 9c89699f42..d3661d0dc6 100644 --- a/console/src/app/modules/form-field/validators/validators.ts +++ b/console/src/app/modules/form-field/validators/validators.ts @@ -24,6 +24,12 @@ export function requiredValidator(c: AbstractControl): ValidationErrors | null { return i18nErr(Validators.required(c), 'ERRORS.REQUIRED'); } +export function minArrayLengthValidator(minArrLength: number): ValidatorFn { + return (c: AbstractControl): ValidationErrors | null => { + return arrayLengthValidator(c, minArrLength, 'ERRORS.ATLEASTONE'); + }; +} + export function emailValidator(c: AbstractControl): ValidationErrors | null { return i18nErr(Validators.email(c), 'ERRORS.NOTANEMAIL'); } @@ -56,6 +62,12 @@ function regexpValidator(c: AbstractControl, regexp: RegExp, i18nKey: string): V return !c.value || regexp.test(c.value) ? null : i18nErr({ invalid: true }, i18nKey, { regexp: regexp }); } +function arrayLengthValidator(c: AbstractControl, length: number, i18nKey: string): ValidationErrors | null { + const arr: string[] = c.value; + const invalidStrings: string[] = arr.filter((val: string) => val.trim() === ''); + return arr && invalidStrings.length === 0 && arr.length >= length ? null : i18nErr({ invalid: true }, i18nKey); +} + function i18nErr(err: ValidationErrors | null | undefined, i18nKey: string, params?: any): ValidationErrors | null { if (err === null) { return null; diff --git a/console/src/app/modules/label/label.component.scss b/console/src/app/modules/label/label.component.scss index 684b7b217f..f4046d4866 100644 --- a/console/src/app/modules/label/label.component.scss +++ b/console/src/app/modules/label/label.component.scss @@ -9,23 +9,31 @@ $foreground: map-get($theme, foreground); $secondary-text: map-get($foreground, secondary-text); - .cnsl-label { - display: block; + .cnsl-label-wrapper { + display: flex; font-size: 12px; color: $secondary-text; transition: color 0.2s ease; margin-bottom: 4px; font-weight: 400; + + .cnsl-label { + display: block; + } + + .cnsl-form-field-required-marker { + margin-left: 1px; + } } .cnsl-form-field-disabled { - .cnsl-label { + .cnsl-label-wrapper { color: if($is-dark-theme, #ffffff80, #00000061); } } .cnsl-form-field-invalid { - .cnsl-label { + .cnsl-label-wrapper { color: $warn-color; } } diff --git a/console/src/app/modules/onboarding/onboarding.component.scss b/console/src/app/modules/onboarding/onboarding.component.scss index 23872223d5..09a692ed6f 100644 --- a/console/src/app/modules/onboarding/onboarding.component.scss +++ b/console/src/app/modules/onboarding/onboarding.component.scss @@ -198,6 +198,12 @@ .state-circle { display: none; } + + .action-card { + .action-content { + opacity: 1; + } + } } } } diff --git a/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.html b/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.html index c119460860..5333825709 100644 --- a/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.html +++ b/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.html @@ -1,54 +1,65 @@ - {{ 'IDP.LDAPIDATTRIBUTE' | translate }}* - - - - {{ 'IDP.AVATARURLATTRIBUTE' | translate }} - - - - {{ 'IDP.DISPLAYNAMEATTRIBUTE' | translate }} - - - - {{ 'IDP.EMAILATTRIBUTEATTRIBUTE' | translate }} - - - - {{ 'IDP.EMAILVERIFIEDATTRIBUTE' | translate }} - - - - {{ 'IDP.FIRSTNAMEATTRIBUTE' | translate }} - - - - {{ 'IDP.LASTNAMEATTRIBUTE' | translate }} - - - - {{ 'IDP.NICKNAMEATTRIBUTE' | translate }} - - - - {{ 'IDP.PHONEATTRIBUTE' | translate }} - - - - {{ 'IDP.PHONEVERIFIEDATTRIBUTE' | translate }} - - - - {{ 'IDP.PREFERREDLANGUAGEATTRIBUTE' | translate }} - - - - {{ 'IDP.PREFERREDUSERNAMEATTRIBUTE' | translate }} - - - - {{ 'IDP.PROFILEATTRIBUTE' | translate }} - + {{ 'IDP.LDAPIDATTRIBUTE' | translate }} + + +
+ {{ 'ACTIONS.MORE' | translate }} + +
+ + + + {{ 'IDP.AVATARURLATTRIBUTE' | translate }} + + + + {{ 'IDP.DISPLAYNAMEATTRIBUTE' | translate }} + + + + {{ 'IDP.EMAILATTRIBUTEATTRIBUTE' | translate }} + + + + {{ 'IDP.EMAILVERIFIEDATTRIBUTE' | translate }} + + + + {{ 'IDP.FIRSTNAMEATTRIBUTE' | translate }} + + + + {{ 'IDP.LASTNAMEATTRIBUTE' | translate }} + + + + {{ 'IDP.NICKNAMEATTRIBUTE' | translate }} + + + + {{ 'IDP.PHONEATTRIBUTE' | translate }} + + + + {{ 'IDP.PHONEVERIFIEDATTRIBUTE' | translate }} + + + + {{ 'IDP.PREFERREDLANGUAGEATTRIBUTE' | translate }} + + + + {{ 'IDP.PREFERREDUSERNAMEATTRIBUTE' | translate }} + + + + {{ 'IDP.PROFILEATTRIBUTE' | translate }} + + + diff --git a/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.scss b/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.scss index 31060727c2..7d02387d8e 100644 --- a/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.scss +++ b/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.scss @@ -4,3 +4,8 @@ max-width: 400px; padding-bottom: 1rem; } + +.attribute-more-row { + display: flex; + align-items: center; +} diff --git a/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.ts b/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.ts index 3ccf153de6..283a9940f0 100644 --- a/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.ts +++ b/console/src/app/modules/providers/ldap-attributes/ldap-attributes.component.ts @@ -29,6 +29,7 @@ export class LDAPAttributesComponent implements OnChanges, OnDestroy { profileAttribute: new FormControl('', []), }); + public showMore: boolean = false; constructor() { this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => { if (value) { diff --git a/console/src/app/modules/providers/provider-ldap/provider-ldap.component.html b/console/src/app/modules/providers/provider-ldap/provider-ldap.component.html index 75e2391b5c..21bec37703 100644 --- a/console/src/app/modules/providers/provider-ldap/provider-ldap.component.html +++ b/console/src/app/modules/providers/provider-ldap/provider-ldap.component.html @@ -17,12 +17,13 @@
{{ 'IDP.NAME' | translate }} - +

{{ 'IDP.LDAPCONNECTION' | translate }}

{{ 'IDP.BASEDN' | translate }} - +
{{ 'IDP.BINDDN' | translate }} - + {{ 'IDP.UPDATEBINDPASSWORD' | translate }} - + {{ 'IDP.BINDPASSWORD' | translate }}
@@ -62,16 +63,18 @@ {{ 'IDP.USERBASE' | translate }} - +

{{ 'IDP.LDAPATTRIBUTES' | translate }}

- - - - {{ - 'IDP.REQUIRED' | translate - }} -
-
-
+

{{ 'IDP.OPTIONAL' | translate }}

@@ -123,7 +115,7 @@ color="primary" mat-raised-button class="continue-button" - [disabled]="form.invalid || attributes.toObject().idAttribute === '' || form.disabled" + [disabled]="!form.valid || !attributes.toObject().idAttribute || form.disabled" type="submit" > {{ 'ACTIONS.SAVE' | translate }} diff --git a/console/src/app/modules/providers/provider-ldap/provider-ldap.component.ts b/console/src/app/modules/providers/provider-ldap/provider-ldap.component.ts index abf3938707..db54dbe6b5 100644 --- a/console/src/app/modules/providers/provider-ldap/provider-ldap.component.ts +++ b/console/src/app/modules/providers/provider-ldap/provider-ldap.component.ts @@ -20,7 +20,7 @@ import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/ import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; -import { requiredValidator } from '../../form-field/validators/validators'; +import { minArrayLengthValidator, requiredValidator } from '../../form-field/validators/validators'; import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; @@ -30,7 +30,6 @@ import { PolicyComponentServiceType } from '../../policies/policy-component-type }) export class ProviderLDAPComponent { public updateBindPassword: boolean = false; - public showAttributes: boolean = false; public showOptional: boolean = false; public options: Options = new Options().setIsCreationAllowed(true).setIsLinkingAllowed(true); public attributes: LDAPAttributes = new LDAPAttributes(); @@ -54,15 +53,15 @@ export class ProviderLDAPComponent { ) { this.form = new FormGroup({ name: new FormControl('', [requiredValidator]), - serversList: new FormControl('', [requiredValidator]), + serversList: new FormControl([''], [minArrayLengthValidator(1)]), baseDn: new FormControl('', [requiredValidator]), bindDn: new FormControl('', [requiredValidator]), bindPassword: new FormControl('', [requiredValidator]), userBase: new FormControl('', [requiredValidator]), - userFiltersList: new FormControl('', [requiredValidator]), - userObjectClassesList: new FormControl('', [requiredValidator]), + userFiltersList: new FormControl([''], [minArrayLengthValidator(1)]), + userObjectClassesList: new FormControl([''], [minArrayLengthValidator(1)]), timeout: new FormControl(0), - startTls: new FormControl(false), + startTls: new FormControl(false), }); this.authService @@ -112,6 +111,7 @@ export class ProviderLDAPComponent { if (this.id) { this.getData(this.id); this.bindPassword?.setValidators([]); + this.bindPassword?.updateValueAndValidity(); } }); } @@ -125,12 +125,25 @@ export class ProviderLDAPComponent { this.service .getProviderByID(req) .then((resp) => { - this.provider = resp.idp; - this.loading = false; - if (this.provider?.config?.ldap) { - this.form.patchValue(this.provider.config.ldap); + if (resp.idp) { + this.provider = resp.idp; + this.loading = false; + this.name?.setValue(this.provider.name); - this.timeout?.setValue(this.provider.config.ldap.timeout?.seconds); + + const config = this.provider?.config?.ldap; + if (config) { + this.serversList?.setValue(config.serversList); + this.startTls?.setValue(config.startTls); + this.baseDn?.setValue(config.baseDn); + this.bindDn?.setValue(config.bindDn); + this.userBase?.setValue(config.userBase); + this.userObjectClassesList?.setValue(config.userObjectClassesList); + this.userFiltersList?.setValue(config.userFiltersList); + if (this.provider?.config?.ldap?.timeout?.seconds) { + this.timeout?.setValue(this.provider?.config?.ldap?.timeout?.seconds); + } + } } }) .catch((error) => { diff --git a/console/src/app/modules/providers/providers.scss b/console/src/app/modules/providers/providers.scss index 4241609431..4eb7702c6e 100644 --- a/console/src/app/modules/providers/providers.scss +++ b/console/src/app/modules/providers/providers.scss @@ -1,5 +1,8 @@ +@use '@angular/material' as mat; + @mixin identity-provider-theme($theme) { $is-dark-theme: map-get($theme, is-dark); + $background: map-get($theme, background); .identity-provider-desc { font-size: 14px; @@ -44,6 +47,14 @@ display: block; max-width: 400px; + &.pwd { + display: none; + } + + &.pwd.show { + display: block; + } + .name-hint { font-size: 12px; } @@ -63,6 +74,10 @@ } } + .string-list-component-wrapper { + max-width: 400px; + } + .identity-provider-content { display: flex; flex-direction: column; diff --git a/console/src/app/modules/string-list/string-list.component.html b/console/src/app/modules/string-list/string-list.component.html index 936bc9159f..1ff6a41e34 100644 --- a/console/src/app/modules/string-list/string-list.component.html +++ b/console/src/app/modules/string-list/string-list.component.html @@ -1,30 +1,43 @@ -
- - {{ title }} - - - -
+
+
+
+

{{ title }}*

+ +
+ +
+ + + -
-
- {{ str }} - - + +
+ + {{ + control.errors['errorsatleastone'].i18nKey | translate + }}
diff --git a/console/src/app/modules/string-list/string-list.component.scss b/console/src/app/modules/string-list/string-list.component.scss index 2d1779eb72..d44143914a 100644 --- a/console/src/app/modules/string-list/string-list.component.scss +++ b/console/src/app/modules/string-list/string-list.component.scss @@ -5,56 +5,50 @@ $background: map-get($theme, background); $is-dark-theme: map-get($theme, is-dark); $warn: map-get($theme, warn); - $warn-color: map-get($warn, 500); - $button-text-color: map-get($foreground, text); - $button-disabled-text-color: map-get($foreground, disabled-button); - $divider-color: map-get($foreground, dividers); - $secondary-text: map-get($foreground, secondary-text); + $warncolor: map-get($warn, 500); - .string-list { - width: 100%; + .form-array-list { + display: flex; + flex-direction: row; max-width: 400px; + background: if($is-dark-theme, #00000020, mat.get-color-from-palette($background, cards)); + margin-left: -1rem; + margin-right: -1rem; + padding: 0 1rem 0.5rem 1rem; + margin-top: 0.5rem; - .value-line { + .list-header-wrapper { display: flex; align-items: center; - margin: 0.5rem 0; - padding: 0 0 0 0.75rem; - border-radius: 4px; - background: map-get($background, infosection); + margin: 0.5rem -0.5rem 0 0; - .fill-space { - flex: 1; + .list-header { + font-size: 12px; } + } - .icon-button { - height: 30px; - line-height: 30px; + .form-field-list { + flex: 1; + display: flex; + flex-direction: column; - .icon { - font-size: 1rem; - margin-bottom: 3px; - } + .element-row { + display: flex; + align-items: center; - &:not(:hover) { - color: $secondary-text; + .formfield { + flex: 1; } } + + .control-error { + font-size: 12px; + color: $warncolor; + } + } + + .add-element-btn { + margin-bottom: 0rem; } } } - -.string-list-form { - display: flex; - align-items: flex-end; - min-width: 320px; - - .formfield { - width: 500px; - } - - button { - margin-bottom: 0.9rem; - margin-right: -0.5rem; - } -} diff --git a/console/src/app/modules/string-list/string-list.component.ts b/console/src/app/modules/string-list/string-list.component.ts index 3afa966aa1..c0380ad937 100644 --- a/console/src/app/modules/string-list/string-list.component.ts +++ b/console/src/app/modules/string-list/string-list.component.ts @@ -1,7 +1,7 @@ -import { Component, forwardRef, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; -import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable, Subject, takeUntil } from 'rxjs'; -import { requiredValidator } from '../form-field/validators/validators'; +import { Component, forwardRef, Input, OnDestroy, ViewChildren, ViewEncapsulation } from '@angular/core'; +import { ControlValueAccessor, FormArray, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { distinctUntilChanged, Subject, takeUntil } from 'rxjs'; +import { minArrayLengthValidator, requiredValidator } from '../form-field/validators/validators'; @Component({ selector: 'cnsl-string-list', @@ -16,22 +16,23 @@ import { requiredValidator } from '../form-field/validators/validators'; }, ], }) -export class StringListComponent implements ControlValueAccessor, OnInit, OnDestroy { +export class StringListComponent implements ControlValueAccessor, OnDestroy { @Input() title: string = ''; @Input() required: boolean = false; - @Input() public getValues: Observable = new Observable(); // adds formfieldinput to array on emission - @Input() public control: FormControl = new FormControl({ value: '', disabled: true }); + @Input() public control: FormControl = new FormControl({ value: [], disabled: true }); + private destroy$: Subject = new Subject(); - @ViewChild('redInput') input!: any; - private val: string[] = []; + @ViewChildren('stringInput') input!: any[]; + public val: string[] = []; - ngOnInit(): void { - this.getValues.pipe(takeUntil(this.destroy$)).subscribe(() => { - this.add(this.input.nativeElement); + public formArray: FormArray = new FormArray([new FormControl('', [requiredValidator])]); + + constructor() { + this.control.setValidators([minArrayLengthValidator(1)]); + this.formArray.valueChanges.pipe(takeUntil(this.destroy$), distinctUntilChanged()).subscribe((value) => { + this.value = value; }); - - this.required ? this.control.setValidators([requiredValidator]) : this.control.setValidators([]); } ngOnDestroy(): void { @@ -50,12 +51,24 @@ export class StringListComponent implements ControlValueAccessor, OnInit, OnDest } } + addArrayEntry() { + this.formArray.push(new FormControl('', [requiredValidator])); + } + + removeEntryAtIndex(index: number) { + this.formArray.removeAt(index); + } + + clearEntryAtIndex(index: number) { + this.formArray.controls[index].setValue(''); + } get value() { return this.val; } writeValue(value: string[]) { this.value = value; + value.map((v, i) => this.formArray.setControl(i, new FormControl(v, [requiredValidator]))); } registerOnChange(fn: any) { @@ -73,28 +86,4 @@ export class StringListComponent implements ControlValueAccessor, OnInit, OnDest this.control.enable(); } } - - public add(input: any): void { - if (this.control.valid) { - const trimmed = input.value.trim(); - if (trimmed) { - this.val ? this.val.push(input.value) : (this.val = [input.value]); - this.onChange(this.val); - this.onTouch(this.val); - } - if (input) { - input.value = ''; - } - } - } - - public remove(str: string): void { - const index = this.value.indexOf(str); - - if (index >= 0) { - this.value.splice(index, 1); - this.onChange(this.value); - this.onTouch(this.value); - } - } } diff --git a/console/src/app/modules/string-list/string-list.module.ts b/console/src/app/modules/string-list/string-list.module.ts index fe72b2adaa..db9322641b 100644 --- a/console/src/app/modules/string-list/string-list.module.ts +++ b/console/src/app/modules/string-list/string-list.module.ts @@ -3,6 +3,7 @@ import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatIconModule } from '@angular/material/icon'; import { MatLegacyButtonModule } from '@angular/material/legacy-button'; +import { MatLegacyChipsModule } from '@angular/material/legacy-chips'; import { MatLegacyTooltipModule } from '@angular/material/legacy-tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { InputModule } from '../input/input.module'; @@ -15,6 +16,7 @@ import { StringListComponent } from './string-list.component'; InputModule, FormsModule, ReactiveFormsModule, + MatLegacyChipsModule, TranslateModule, MatIconModule, MatLegacyTooltipModule, diff --git a/console/src/app/pages/users/user-create-machine/user-create-machine.component.html b/console/src/app/pages/users/user-create-machine/user-create-machine.component.html index 9685f90665..2e273a88d6 100644 --- a/console/src/app/pages/users/user-create-machine/user-create-machine.component.html +++ b/console/src/app/pages/users/user-create-machine/user-create-machine.component.html @@ -10,11 +10,11 @@
- {{ 'USER.MACHINE.USERNAME' | translate }}* + {{ 'USER.MACHINE.USERNAME' | translate }} - {{ 'USER.MACHINE.NAME' | translate }}* + {{ 'USER.MACHINE.NAME' | translate }} diff --git a/console/src/app/pages/users/user-create/user-create.component.html b/console/src/app/pages/users/user-create/user-create.component.html index acd3a8e103..e74020ea9e 100644 --- a/console/src/app/pages/users/user-create/user-create.component.html +++ b/console/src/app/pages/users/user-create/user-create.component.html @@ -13,11 +13,11 @@
- {{ 'USER.PROFILE.EMAIL' | translate }}* + {{ 'USER.PROFILE.EMAIL' | translate }} - {{ 'USER.PROFILE.USERNAME' | translate }}* + {{ 'USER.PROFILE.USERNAME' | translate }} - {{ 'USER.PROFILE.FIRSTNAME' | translate }}* + {{ 'USER.PROFILE.FIRSTNAME' | translate }} - {{ 'USER.PROFILE.LASTNAME' | translate }}* + {{ 'USER.PROFILE.LASTNAME' | translate }} diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index d54974b5b2..eabe26ba69 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -247,6 +247,7 @@ }, "ERRORS": { "REQUIRED": "Bitte fülle dieses Feld aus.", + "ATLEASTONE": "Geben Sie mindestens einen Wert an.", "TOKENINVALID": { "TITLE": "Du bist abgemeldet", "DESCRIPTION": "Klicke auf \"Einloggen\", um Dich erneut anzumelden." diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 8aabe4d58e..d85876e19f 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -248,6 +248,7 @@ }, "ERRORS": { "REQUIRED": "Please fill in this field.", + "ATLEASTONE": "Provide at least one value.", "TOKENINVALID": { "TITLE": "Your authorization token has expired.", "DESCRIPTION": "Click the button below to log in again." diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json index c9c1a26829..ac8799d411 100644 --- a/console/src/assets/i18n/es.json +++ b/console/src/assets/i18n/es.json @@ -248,6 +248,7 @@ }, "ERRORS": { "REQUIRED": "Por favor rellena este campo.", + "ATLEASTONE": "Proporcione al menos un valor.", "TOKENINVALID": { "TITLE": "Tu token de autorización token ha caducado.", "DESCRIPTION": "Haz clic en el botón más abajo para iniciar sesión otra vez." diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 6c11c83522..adb8b36f22 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -247,6 +247,7 @@ }, "ERRORS": { "REQUIRED": "Remplis ce champ s'il te plaît.", + "ATLEASTONE": "Indiquez au moins une valeur.", "TOKENINVALID": { "TITLE": "Votre jeton d'autorisation a expiré.", "DESCRIPTION": "Cliquez sur le bouton ci-dessous pour vous reconnecter." diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 4b9c88bae4..9022045573 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -247,6 +247,7 @@ }, "ERRORS": { "REQUIRED": "Compilare questo campo.", + "ATLEASTONE": "Inserisci almeno un valore.", "TOKENINVALID": { "TITLE": "Il tuo Access Token \u00e8 scaduto.", "DESCRIPTION": "Clicca il pulsante per richiedere una nuova sessione." diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 5127adf698..de94a152bc 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -248,6 +248,7 @@ }, "ERRORS": { "REQUIRED": "一部の必須項目が不足しています。", + "ATLEASTONE": "少なくとも 1 つの値を指定してください。", "TOKENINVALID": { "TITLE": "トークンが期限切れになりました。", "DESCRIPTION": "下のボタンをクリックして、もう一度ログインする。" diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 594e3654b2..74c5d81531 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -247,6 +247,7 @@ }, "ERRORS": { "REQUIRED": "Proszę wypełnić to pole.", + "ATLEASTONE": "Podaj co najmniej jedną wartość.", "TOKENINVALID": { "TITLE": "Twój token autoryzacji wygasł.", "DESCRIPTION": "Kliknij przycisk poniżej, aby ponownie się zalogować." diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 0c0b953e38..b560132685 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -247,6 +247,7 @@ }, "ERRORS": { "REQUIRED": "请填写此栏", + "ATLEASTONE": "P至少提供一个值。", "TOKENINVALID": { "TITLE": "您的授权令牌已过期。", "DESCRIPTION": "点击下方按钮再次登录。" From 8da8fbe6ce9b1aae077d73d671d5dd34db887091 Mon Sep 17 00:00:00 2001 From: Silvan Date: Tue, 18 Apr 2023 19:29:04 +0200 Subject: [PATCH 34/36] fix(eventstore): correct creation date of events (#5683) * fix: add setup step to correct creation dates * fix(eventstore): replace now with statement ts * fix(step10): correct number * fix: handle wrong instance domain removed events --- .releaserc.js | 3 +- cmd/setup/10.go | 53 ++++++++++++++++++++++ cmd/setup/10.sql | 28 ++++++++++++ cmd/setup/config.go | 19 ++++---- cmd/setup/setup.go | 3 ++ internal/command/system_model.go | 14 ++++-- internal/eventstore/repository/sql/crdb.go | 2 +- 7 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 cmd/setup/10.go create mode 100644 cmd/setup/10.sql diff --git a/.releaserc.js b/.releaserc.js index 250d6d71db..16cb8f56b1 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -1,7 +1,8 @@ module.exports = { branches: [ {name: 'main'}, - {name: '1.87.x', range: '1.87.x', channel: '1.87.x'} + {name: '1.87.x', range: '1.87.x', channel: '1.87.x'}, + {name: 'next'} ], plugins: [ "@semantic-release/commit-analyzer" diff --git a/cmd/setup/10.go b/cmd/setup/10.go new file mode 100644 index 0000000000..77db7c6c5e --- /dev/null +++ b/cmd/setup/10.go @@ -0,0 +1,53 @@ +package setup + +import ( + "context" + _ "embed" + + "github.com/zitadel/logging" + + "github.com/zitadel/zitadel/internal/database" +) + +var ( + //go:embed 10.sql + correctCreationDate10 string +) + +type CorrectCreationDate struct { + dbClient *database.DB +} + +func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) { + tx, err := mig.dbClient.Begin() + if err != nil { + return err + } + if mig.dbClient.Type() == "cockroach" { + if _, err := tx.Exec("SET experimental_enable_temp_tables=on"); err != nil { + return err + } + } + defer func() { + if err != nil { + logging.OnError(tx.Rollback()).Debug("rollback failed") + return + } + err = tx.Commit() + }() + for { + res, err := tx.ExecContext(ctx, correctCreationDate10) + if err != nil { + return err + } + affected, _ := res.RowsAffected() + logging.WithFields("count", affected).Info("creation dates changed") + if affected == 0 { + return nil + } + } +} + +func (mig *CorrectCreationDate) String() string { + return "10_correct_creation_date" +} diff --git a/cmd/setup/10.sql b/cmd/setup/10.sql new file mode 100644 index 0000000000..ae2af741dc --- /dev/null +++ b/cmd/setup/10.sql @@ -0,0 +1,28 @@ +CREATE temporary TABLE IF NOT EXISTS wrong_events ( + instance_id STRING + , event_sequence INT8 + , current_cd TIMESTAMPTZ + , next_cd TIMESTAMPTZ +); + +TRUNCATE wrong_events; + +INSERT INTO wrong_events ( + SELECT * FROM ( + SELECT + instance_id + , event_sequence + , creation_date AS current_cd + , lead(creation_date) OVER ( + PARTITION BY instance_id + ORDER BY event_sequence DESC + ) AS next_cd + FROM + eventstore.events + ) WHERE + current_cd < next_cd + ORDER BY + event_sequence DESC +); + +UPDATE eventstore.events e SET creation_date = we.next_cd FROM wrong_events we WHERE e.event_sequence = we.event_sequence and e.instance_id = we.instance_id; \ No newline at end of file diff --git a/cmd/setup/config.go b/cmd/setup/config.go index e557676047..eb55ba492a 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -56,15 +56,16 @@ func MustNewConfig(v *viper.Viper) *Config { } type Steps struct { - s1ProjectionTable *ProjectionTable - s2AssetsTable *AssetTable - FirstInstance *FirstInstance - s4EventstoreIndexes *EventstoreIndexesNew - s5LastFailed *LastFailed - s6OwnerRemoveColumns *OwnerRemoveColumns - s7LogstoreTables *LogstoreTables - s8AuthTokens *AuthTokenIndexes - s9EventstoreIndexes2 *EventstoreIndexesNew + s1ProjectionTable *ProjectionTable + s2AssetsTable *AssetTable + FirstInstance *FirstInstance + s4EventstoreIndexes *EventstoreIndexesNew + s5LastFailed *LastFailed + s6OwnerRemoveColumns *OwnerRemoveColumns + s7LogstoreTables *LogstoreTables + s8AuthTokens *AuthTokenIndexes + s9EventstoreIndexes2 *EventstoreIndexesNew + s10EventstoreCreationDate *CorrectCreationDate } type encryptionKeyConfig struct { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index bc37decb58..a7deef0071 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -88,6 +88,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.s7LogstoreTables = &LogstoreTables{dbClient: dbClient.DB, username: config.Database.Username(), dbType: config.Database.Type()} steps.s8AuthTokens = &AuthTokenIndexes{dbClient: dbClient} steps.s9EventstoreIndexes2 = New09(dbClient) + steps.s10EventstoreCreationDate = &CorrectCreationDate{dbClient: dbClient} err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -123,6 +124,8 @@ func Setup(config *Config, steps *Steps, masterKey string) { logging.OnError(err).Fatal("unable to migrate step 8") err = migration.Migrate(ctx, eventstoreClient, steps.s9EventstoreIndexes2) logging.OnError(err).Fatal("unable to migrate step 9") + err = migration.Migrate(ctx, eventstoreClient, steps.s10EventstoreCreationDate) + logging.OnError(err).Fatal("unable to migrate step 10") for _, repeatableStep := range repeatableSteps { err = migration.Migrate(ctx, eventstoreClient, repeatableStep) diff --git a/internal/command/system_model.go b/internal/command/system_model.go index 9ed0684f6b..40847cfab7 100644 --- a/internal/command/system_model.go +++ b/internal/command/system_model.go @@ -59,12 +59,16 @@ func (wm *SystemConfigWriteModel) Reduce() error { } wm.Instances[e.Aggregate().InstanceID].Domains = append(wm.Instances[e.Aggregate().InstanceID].Domains, e.Domain) case *instance.DomainRemovedEvent: - domains := wm.Instances[e.Aggregate().InstanceID].Domains - for i, domain := range domains { + instance, ok := wm.Instances[e.Aggregate().InstanceID] + if !ok { + continue + } + + for i, domain := range instance.Domains { if domain == e.Domain { - domains[i] = domains[len(domains)-1] - domains[len(domains)-1] = "" - wm.Instances[e.Aggregate().InstanceID].Domains = domains[:len(domains)-1] + instance.Domains[i] = instance.Domains[len(instance.Domains)-1] + instance.Domains[len(instance.Domains)-1] = "" + wm.Instances[e.Aggregate().InstanceID].Domains = instance.Domains[:len(instance.Domains)-1] break } } diff --git a/internal/eventstore/repository/sql/crdb.go b/internal/eventstore/repository/sql/crdb.go index 8cbd1046de..6400776251 100644 --- a/internal/eventstore/repository/sql/crdb.go +++ b/internal/eventstore/repository/sql/crdb.go @@ -67,7 +67,7 @@ const ( " $2::VARCHAR AS aggregate_type," + " $3::VARCHAR AS aggregate_id," + " $4::VARCHAR AS aggregate_version," + - " NOW() AS creation_date," + + " statement_timestamp() AS creation_date," + " $5::JSONB AS event_data," + " $6::VARCHAR AS editor_user," + " $7::VARCHAR AS editor_service," + From 85054e8d2b65b27f7ad0327f79c8b6f7d595f394 Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 19 Apr 2023 09:41:33 +0200 Subject: [PATCH 35/36] chore: next as pre release --- .releaserc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.releaserc.js b/.releaserc.js index 16cb8f56b1..0bcd219e10 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -2,7 +2,7 @@ module.exports = { branches: [ {name: 'main'}, {name: '1.87.x', range: '1.87.x', channel: '1.87.x'}, - {name: 'next'} + {name: 'next', prerelease: true} ], plugins: [ "@semantic-release/commit-analyzer" From 3cd2cecfdf481047b8b1dc1f7d9590be2bec1b15 Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 19 Apr 2023 10:01:27 +0200 Subject: [PATCH 36/36] chore: releaserc --- .releaserc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.releaserc.js b/.releaserc.js index 0bcd219e10..177956a628 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -1,6 +1,6 @@ module.exports = { branches: [ - {name: 'main'}, + {name: 'main', channel: 'next'}, {name: '1.87.x', range: '1.87.x', channel: '1.87.x'}, {name: 'next', prerelease: true} ],