mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-22 18:28:25 +00:00
Merge branch 'main' into next
This commit is contained in:
commit
7b8be37fd6
5
.github/workflows/issues.yml
vendored
5
.github/workflows/issues.yml
vendored
@ -23,19 +23,20 @@ jobs:
|
||||
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
||||
- uses: tspascoal/get-user-teams-membership@v3
|
||||
id: checkUserMember
|
||||
if: github.actor != 'dependabot[bot]'
|
||||
with:
|
||||
username: ${{ github.actor }}
|
||||
GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
||||
- name: add pr
|
||||
uses: actions/add-to-project@v0.5.0
|
||||
if: ${{ github.event_name == 'pull_request_target' && !contains(steps.checkUserMember.outputs.teams, 'engineers') && github.actor != 'app/dependabot'}}
|
||||
if: ${{ github.event_name == 'pull_request_target' && github.actor != 'dependabot[bot]' && !contains(steps.checkUserMember.outputs.teams, 'engineers')}}
|
||||
with:
|
||||
# You can target a repository in a different organization
|
||||
# to the issue
|
||||
project-url: https://github.com/orgs/zitadel/projects/2
|
||||
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
||||
- uses: actions-ecosystem/action-add-labels@v1.1.0
|
||||
if: ${{ github.event_name == 'pull_request_target' && !contains(steps.checkUserMember.outputs.teams, 'staff') && github.actor != 'app/dependabot'}}
|
||||
if: ${{ github.event_name == 'pull_request_target' && github.actor != 'dependabot[bot]' && !contains(steps.checkUserMember.outputs.teams, 'staff')}}
|
||||
with:
|
||||
github_token: ${{ secrets.ADD_TO_PROJECT_PAT }}
|
||||
labels: |
|
||||
|
@ -543,6 +543,11 @@ Eventstore:
|
||||
# Maximum amount of push retries in case of primary key violation on the sequence
|
||||
MaxRetries: 5 #ZITADEL_EVENTSTORE_MAXRETRIES
|
||||
|
||||
# The DefaultInstance section defines the default values for each new virtual instance that is created.
|
||||
# Check out https://zitadel.com/docs/concepts/structure/instance#multiple-virtual-instances for more information about virtual instances.
|
||||
# For the initial setup, the default values are used to create the first instance.
|
||||
# However, you might want to have your first instance created by the setup job to have a different configuration.
|
||||
# To overwrite the default values for the initial setup, configure the FirstInstance yaml section and pass it using the --steps flag.
|
||||
DefaultInstance:
|
||||
InstanceName: ZITADEL # ZITADEL_DEFAULTINSTANCE_INSTANCENAME
|
||||
DefaultLanguage: en # ZITADEL_DEFAULTINSTANCE_DEFAULTLANGUAGE
|
||||
|
@ -1,3 +1,4 @@
|
||||
# By using the FirstInstance section, you can overwrite the DefaultInstance configuration for the first instance created by zitadel setup.
|
||||
FirstInstance:
|
||||
# The machine key from the section FirstInstance.Org.Machine.MachineKey is written to the MachineKeyPath.
|
||||
MachineKeyPath: # ZITADEL_FIRSTINSTANCE_MACHINEKEYPATH
|
||||
|
@ -10,4 +10,4 @@ By configuring a custom domain within ZITADEL, organizations can replace the def
|
||||
|
||||
This not only enhances the overall user experience but also reinforces the organization's brand presence. Additionally, custom domains can contribute to trust and credibility, as users are more likely to recognize and trust URLs associated with the organization rather than generic domains. Overall, ZITADEL's custom domain feature empowers organizations to tailor the authentication process to align with their brand identity and user expectations.
|
||||
|
||||
Learn how to [configure a custom domain in ZITADEL Cloud](http://localhost:3000/docs/guides/manage/cloud/instances#add-custom-domain) or how to configure [custom domain when self-hosting](http://localhost:3000/docs/self-hosting/manage/custom-domain).
|
||||
Learn how to [configure a custom domain in ZITADEL Cloud](/guides/manage/cloud/instances#add-custom-domain) or how to configure [custom domain when self-hosting](/self-hosting/manage/custom-domain).
|
@ -210,7 +210,7 @@ The user experience depends mainly on the operating system and browser.
|
||||
## Build a custom Login UI to authenticate users
|
||||
|
||||
In certain cases, you want to build your own login UI to optimize your user experience.
|
||||
We have dedicated guides on [how to build your custom login UI](http://localhost:3000/docs/guides/integrate/login-ui) with ZITADEL.
|
||||
We have dedicated guides on [how to build your custom login UI](../login-ui) with ZITADEL.
|
||||
|
||||
When building your own login UI, you will leverage the [Session API](#zitadels-session-api) to authenticate users and manage user sessions.
|
||||
|
||||
|
@ -352,7 +352,7 @@ The provided config extends the `UserManagerSettings` of the `oidc-client-ts` li
|
||||
- post_logout_redirect_uri (the URL to redirect to after the user logs out)
|
||||
- scope (the permissions requested from the user)
|
||||
- project_resource_id (To add a ZITADEL project scope. `urn:zitadel:iam:org:project:id:[projectId]:aud` and `urn:zitadel:iam:org:projects:roles` [scopes](https://zitadel.com/docs/apis/openidoauth/scopes#reserved-scopes).)
|
||||
- prompt ([the OIDC prompt parameter](http://localhost:3000/docs/apis/openidoauth/endpoints#additional-parameters))
|
||||
- prompt ([the OIDC prompt parameter](/apis/openidoauth/endpoints#additional-parameters))
|
||||
|
||||
2. Create a folder named components in the src directory. Create two files named Login.js and Callback.js.
|
||||
|
||||
|
@ -21,6 +21,11 @@ This guide assumes you are familiar with [running ZITADEL using the least amount
|
||||
You can configure the runtime using the `--config` flag of the `zitadel` binary.
|
||||
Also, you can use the environment variables listed in the defaults.yaml.
|
||||
|
||||
:::tip
|
||||
For overwriting the default configuration for the first instance created by zitadel setup, use the FirstInstance section in the [database initialization file](#database-initialization-file).
|
||||
:::
|
||||
|
||||
|
||||
<details>
|
||||
<summary>defaults.yaml</summary>
|
||||
<CodeBlock language="yaml">{DefaultsYamlSource}</CodeBlock>
|
||||
@ -32,6 +37,10 @@ ZITADEL uses a [different configuration file](https://github.com/zitadel/zitadel
|
||||
Use the `--steps` flag of the `zitadel` binary to provide this configuration file.
|
||||
Also, you can use the environment variables listed in the steps.yaml.
|
||||
|
||||
:::tip
|
||||
By using the FirstInstance section, you can overwrite the [DefaultInstance configuration](#runtime-configuration-file) for the first instance created by zitadel setup.
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary>steps.yaml</summary>
|
||||
<CodeBlock language="yaml">{StepsYamlSource}</CodeBlock>
|
||||
|
@ -2,6 +2,7 @@ package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
@ -60,14 +61,21 @@ func eventRequestToFilter(ctx context.Context, req *admin_pb.ListEventsRequest)
|
||||
for i, eventType := range req.EventTypes {
|
||||
eventTypes[i] = eventstore.EventType(eventType)
|
||||
}
|
||||
|
||||
aggregateIDs := make([]string, 0, 1)
|
||||
if req.AggregateId != "" {
|
||||
aggregateIDs = append(aggregateIDs, req.AggregateId)
|
||||
}
|
||||
|
||||
aggregateTypes := make([]eventstore.AggregateType, len(req.AggregateTypes))
|
||||
for i, aggregateType := range req.AggregateTypes {
|
||||
aggregateTypes[i] = eventstore.AggregateType(aggregateType)
|
||||
}
|
||||
if len(aggregateTypes) == 0 {
|
||||
aggregateTypes = aggregateTypesFromEventTypes(eventTypes)
|
||||
}
|
||||
aggregateTypes = slices.Compact(aggregateTypes)
|
||||
|
||||
limit := uint64(req.Limit)
|
||||
if limit == 0 || limit > maxLimit {
|
||||
limit = maxLimit
|
||||
@ -100,3 +108,13 @@ func eventRequestToFilter(ctx context.Context, req *admin_pb.ListEventsRequest)
|
||||
}
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
func aggregateTypesFromEventTypes(eventTypes []eventstore.EventType) []eventstore.AggregateType {
|
||||
aggregateTypes := make([]eventstore.AggregateType, 0, len(eventTypes))
|
||||
|
||||
for _, eventType := range eventTypes {
|
||||
aggregateTypes = append(aggregateTypes, eventstore.AggregateTypeFromEventType(eventType))
|
||||
}
|
||||
|
||||
return aggregateTypes
|
||||
}
|
||||
|
58
internal/api/grpc/admin/event_test.go
Normal file
58
internal/api/grpc/admin/event_test.go
Normal file
@ -0,0 +1,58 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/deviceauth"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
func Test_aggregateTypesFromEventTypes(t *testing.T) {
|
||||
type args struct {
|
||||
eventTypes []eventstore.EventType
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []eventstore.AggregateType
|
||||
}{
|
||||
{
|
||||
name: "no event types",
|
||||
args: args{
|
||||
eventTypes: []eventstore.EventType{},
|
||||
},
|
||||
want: []eventstore.AggregateType{},
|
||||
},
|
||||
{
|
||||
name: "only by prefix",
|
||||
args: args{
|
||||
eventTypes: []eventstore.EventType{user.MachineAddedEventType, org.OrgAddedEventType},
|
||||
},
|
||||
want: []eventstore.AggregateType{user.AggregateType, org.AggregateType},
|
||||
},
|
||||
{
|
||||
name: "with special",
|
||||
args: args{
|
||||
eventTypes: []eventstore.EventType{deviceauth.ApprovedEventType, org.OrgAddedEventType},
|
||||
},
|
||||
want: []eventstore.AggregateType{deviceauth.AggregateType, org.AggregateType},
|
||||
},
|
||||
{
|
||||
name: "duplicates",
|
||||
args: args{
|
||||
eventTypes: []eventstore.EventType{org.OrgAddedEventType, org.OrgChangedEventType},
|
||||
},
|
||||
want: []eventstore.AggregateType{org.AggregateType, org.AggregateType},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := aggregateTypesFromEventTypes(tt.args.eventTypes); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("aggregateTypesFromEventTypes() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -48,7 +48,6 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
AwaitOpenTransactions().
|
||||
AddQuery().
|
||||
AggregateTypes(feature_v2.AggregateType).
|
||||
AggregateIDs(m.AggregateID).
|
||||
EventTypes(
|
||||
feature_v1.DefaultLoginInstanceEventType,
|
||||
feature_v2.InstanceResetEventType,
|
||||
|
@ -31,6 +31,7 @@ var (
|
||||
eventInterceptors map[EventType]eventTypeInterceptors
|
||||
eventTypes []string
|
||||
aggregateTypes []string
|
||||
eventTypeMapping = map[EventType]AggregateType{}
|
||||
)
|
||||
|
||||
// RegisterFilterEventMapper registers a function for mapping an eventstore event to an event
|
||||
@ -45,9 +46,11 @@ func RegisterFilterEventMapper(aggregateType AggregateType, eventType EventType,
|
||||
if eventInterceptors == nil {
|
||||
eventInterceptors = make(map[EventType]eventTypeInterceptors)
|
||||
}
|
||||
|
||||
interceptor := eventInterceptors[eventType]
|
||||
interceptor.eventMapper = mapper
|
||||
eventInterceptors[eventType] = interceptor
|
||||
eventTypeMapping[eventType] = aggregateType
|
||||
}
|
||||
|
||||
type eventTypeInterceptors struct {
|
||||
@ -112,6 +115,10 @@ retry:
|
||||
return mappedEvents, nil
|
||||
}
|
||||
|
||||
func AggregateTypeFromEventType(typ EventType) AggregateType {
|
||||
return eventTypeMapping[typ]
|
||||
}
|
||||
|
||||
func (es *Eventstore) EventTypes() []string {
|
||||
return eventTypes
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ func eventsScanner(useV1 bool) func(scanner scan, dest interface{}) (err error)
|
||||
}
|
||||
|
||||
func prepareConditions(criteria querier, query *repository.SearchQuery, useV1 bool) (string, []any) {
|
||||
clauses, args := prepareQuery(criteria, useV1, query.InstanceID, query.ExcludedInstances)
|
||||
clauses, args := prepareQuery(criteria, useV1, query.InstanceID, query.InstanceIDs, query.ExcludedInstances)
|
||||
if clauses != "" && len(query.SubQueries) > 0 {
|
||||
clauses += " AND "
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ with domain as (
|
||||
) features
|
||||
from domain d
|
||||
cross join projections.system_features s
|
||||
full outer join projections.instance_features i using (key, instance_id)
|
||||
full outer join projections.instance_features2 i using (key, instance_id)
|
||||
group by instance_id
|
||||
)
|
||||
select
|
||||
|
@ -5,7 +5,7 @@ with features as (
|
||||
) features
|
||||
from (select $1::text instance_id) x
|
||||
cross join projections.system_features s
|
||||
full outer join projections.instance_features i using (key, instance_id)
|
||||
full outer join projections.instance_features2 i using (key, instance_id)
|
||||
group by instance_id
|
||||
)
|
||||
select
|
||||
|
@ -54,7 +54,6 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
AwaitOpenTransactions().
|
||||
AddQuery().
|
||||
AggregateTypes(feature_v2.AggregateType).
|
||||
AggregateIDs(m.AggregateID).
|
||||
EventTypes(
|
||||
feature_v1.DefaultLoginInstanceEventType,
|
||||
feature_v2.InstanceResetEventType,
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
InstanceFeatureTable = "projections.instance_features"
|
||||
InstanceFeatureTable = "projections.instance_features2"
|
||||
|
||||
InstanceFeatureInstanceIDCol = "instance_id"
|
||||
InstanceFeatureKeyCol = "key"
|
||||
|
@ -38,7 +38,7 @@ func TestInstanceFeaturesProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.instance_features (instance_id, key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (instance_id, key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.instance_features.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
|
||||
expectedStmt: "INSERT INTO projections.instance_features2 (instance_id, key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (instance_id, key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.instance_features2.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"legacy_introspection",
|
||||
@ -69,9 +69,9 @@ func TestInstanceFeaturesProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.instance_features (instance_id, key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (instance_id, key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.instance_features.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
|
||||
expectedStmt: "INSERT INTO projections.instance_features2 (instance_id, key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (instance_id, key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.instance_features2.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
"login_default_org",
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@ -100,7 +100,7 @@ func TestInstanceFeaturesProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.instance_features WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.instance_features2 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
@ -126,7 +126,7 @@ func TestInstanceFeaturesProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.instance_features WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.instance_features2 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
|
@ -22,6 +22,10 @@ func DefaultLoginInstanceEventToV2(e *SetEvent[Boolean]) *feature_v2.SetEvent[bo
|
||||
BaseEvent: e.BaseEvent,
|
||||
Value: e.Value.Boolean,
|
||||
}
|
||||
|
||||
// v1 used a random aggregate ID.
|
||||
// v2 uses the instance ID as aggregate ID.
|
||||
v2e.BaseEvent.Agg.ID = e.Agg.InstanceID
|
||||
v2e.BaseEvent.EventType = feature_v2.InstanceLoginDefaultOrgEventType
|
||||
return v2e
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user