mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 23:27:23 +00:00
feat: Instance domains (#3444)
* feat: add domain list * feat: domain tests * feat: add redirect url on adding instance domain * Update internal/command/instance_domain.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * feat: remove unused code * fix Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
parent
820a21dce3
commit
c25d853820
@ -56,6 +56,18 @@ Set the default language
|
||||
GET: /languages/default
|
||||
|
||||
|
||||
### ListInstanceDomains
|
||||
|
||||
> **rpc** ListInstanceDomains([ListInstanceDomainsRequest](#listinstancedomainsrequest))
|
||||
[ListInstanceDomainsResponse](#listinstancedomainsresponse)
|
||||
|
||||
Returns the domains of an instance
|
||||
|
||||
|
||||
|
||||
GET: /domains
|
||||
|
||||
|
||||
### ListSecretGenerators
|
||||
|
||||
> **rpc** ListSecretGenerators([ListSecretGeneratorsRequest](#listsecretgeneratorsrequest))
|
||||
@ -2629,6 +2641,32 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### ListInstanceDomainsRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| query | zitadel.v1.ListQuery | - | |
|
||||
| sorting_column | zitadel.instance.v1.DomainFieldName | the field the result is sorted | |
|
||||
| queries | repeated zitadel.instance.v1.DomainSearchQuery | criterias the client is looking for | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ListInstanceDomainsResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ListDetails | - | |
|
||||
| sorting_column | zitadel.instance.v1.DomainFieldName | - | |
|
||||
| result | repeated zitadel.instance.v1.Domain | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ListLoginPolicyIDPsRequest
|
||||
|
||||
|
||||
|
@ -9,14 +9,63 @@ title: zitadel/instance.proto
|
||||
## Messages
|
||||
|
||||
|
||||
### DomainsQuery
|
||||
### Domain
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| domains | repeated string | - | string.max_len: 200<br /> |
|
||||
| method | zitadel.v1.ListQueryMethod | - | enum.defined_only: true<br /> |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
| domain | string | - | |
|
||||
| primary | bool | - | |
|
||||
| generated | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DomainGeneratedQuery
|
||||
DomainGeneratedQuery is always equals
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| generated | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DomainPrimaryQuery
|
||||
DomainPrimaryQuery is always equals
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| primary | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### DomainQuery
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| domain | string | - | string.max_len: 200<br /> |
|
||||
| method | zitadel.v1.TextQueryMethod | - | enum.defined_only: true<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### DomainSearchQuery
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.domain_query | DomainQuery | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.generated_query | DomainGeneratedQuery | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.primary_query | DomainPrimaryQuery | - | |
|
||||
|
||||
|
||||
|
||||
@ -41,8 +90,6 @@ IdQuery is always equals
|
||||
| id | string | - | |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
| state | State | - | |
|
||||
| generated_domain | string | - | |
|
||||
| custom_domains | repeated string | - | |
|
||||
| name | string | - | |
|
||||
| version | string | - | |
|
||||
|
||||
@ -56,7 +103,6 @@ IdQuery is always equals
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.id_query | IdQuery | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.domains_query | DomainsQuery | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.state_query | StateQuery | - | |
|
||||
|
||||
|
||||
@ -78,6 +124,20 @@ StateQuery is always equals
|
||||
## Enums
|
||||
|
||||
|
||||
### DomainFieldName {#domainfieldname}
|
||||
|
||||
|
||||
| Name | Number | Description |
|
||||
| ---- | ------ | ----------- |
|
||||
| DOMAIN_FIELD_NAME_UNSPECIFIED | 0 | - |
|
||||
| DOMAIN_FIELD_NAME_DOMAIN | 1 | - |
|
||||
| DOMAIN_FIELD_NAME_PRIMARY | 2 | - |
|
||||
| DOMAIN_FIELD_NAME_GENERATED | 3 | - |
|
||||
| DOMAIN_FIELD_NAME_CREATION_DATE | 4 | - |
|
||||
|
||||
|
||||
|
||||
|
||||
### FieldName {#fieldname}
|
||||
|
||||
|
||||
@ -85,9 +145,8 @@ StateQuery is always equals
|
||||
| ---- | ------ | ----------- |
|
||||
| FIELD_NAME_UNSPECIFIED | 0 | - |
|
||||
| FIELD_NAME_ID | 1 | - |
|
||||
| FIELD_NAME_GENERATED_DOMAIN | 2 | - |
|
||||
| FIELD_NAME_NAME | 3 | - |
|
||||
| FIELD_NAME_CREATION_DATE | 4 | - |
|
||||
| FIELD_NAME_NAME | 2 | - |
|
||||
| FIELD_NAME_CREATION_DATE | 3 | - |
|
||||
|
||||
|
||||
|
||||
|
@ -82,40 +82,52 @@ Returns the usage metrics of an instance
|
||||
GET: /instances/{id}/usage
|
||||
|
||||
|
||||
### GetGeneratedDomain
|
||||
### ListDomains
|
||||
|
||||
> **rpc** GetGeneratedDomain([GetGeneratedDomainRequest](#getgenerateddomainrequest))
|
||||
[GetGeneratedDomainResponse](#getgenerateddomainresponse)
|
||||
|
||||
Returns the domain of an instance
|
||||
|
||||
|
||||
|
||||
GET: /instances/{id}/domains/generated
|
||||
|
||||
|
||||
### GetCustomDomains
|
||||
|
||||
> **rpc** GetCustomDomains([GetCustomDomainsRequest](#getcustomdomainsrequest))
|
||||
[GetCustomDomainsResponse](#getcustomdomainsresponse)
|
||||
> **rpc** ListDomains([ListDomainsRequest](#listdomainsrequest))
|
||||
[ListDomainsResponse](#listdomainsresponse)
|
||||
|
||||
Returns the custom domains of an instance
|
||||
|
||||
|
||||
|
||||
GET: /instances/{id}/domains/custom
|
||||
GET: /instances/{id}/domains
|
||||
|
||||
|
||||
### AddCustomDomain
|
||||
### AddDomain
|
||||
|
||||
> **rpc** AddCustomDomain([AddCustomDomainRequest](#addcustomdomainrequest))
|
||||
[AddCustomDomainResponse](#addcustomdomainresponse)
|
||||
> **rpc** AddDomain([AddDomainRequest](#adddomainrequest))
|
||||
[AddDomainResponse](#adddomainresponse)
|
||||
|
||||
Returns the domain of an instance
|
||||
|
||||
|
||||
|
||||
POST: /instances/{id}/domains/custom
|
||||
POST: /instances/{id}/domains
|
||||
|
||||
|
||||
### RemoveDomain
|
||||
|
||||
> **rpc** RemoveDomain([RemoveDomainRequest](#removedomainrequest))
|
||||
[RemoveDomainResponse](#removedomainresponse)
|
||||
|
||||
Returns the domain of an instance
|
||||
|
||||
|
||||
|
||||
DELETE: /instances/{id}/domains/{domain}
|
||||
|
||||
|
||||
### SetPrimaryDomain
|
||||
|
||||
> **rpc** SetPrimaryDomain([SetPrimaryDomainRequest](#setprimarydomainrequest))
|
||||
[SetPrimaryDomainResponse](#setprimarydomainresponse)
|
||||
|
||||
Returns the domain of an instance
|
||||
|
||||
|
||||
|
||||
POST: /instances/{id}/domains/_set_primary
|
||||
|
||||
|
||||
### ListViews
|
||||
@ -185,19 +197,19 @@ failed event. You can find out if it worked on the `failure_count`
|
||||
## Messages
|
||||
|
||||
|
||||
### AddCustomDomainRequest
|
||||
### AddDomainRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| custom_domain | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| domain | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### AddCustomDomainResponse
|
||||
### AddDomainResponse
|
||||
|
||||
|
||||
|
||||
@ -297,52 +309,6 @@ This is an empty response
|
||||
|
||||
|
||||
|
||||
### GetCustomDomainsRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetCustomDomainsResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
| domains | repeated string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetGeneratedDomainRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetGeneratedDomainResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
| domain | string | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetInstanceRequest
|
||||
|
||||
|
||||
@ -401,6 +367,33 @@ This is an empty response
|
||||
|
||||
|
||||
|
||||
### ListDomainsRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| id | string | list limitations and ordering | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| query | zitadel.v1.ListQuery | - | |
|
||||
| sorting_column | zitadel.instance.v1.DomainFieldName | the field the result is sorted | |
|
||||
| queries | repeated zitadel.instance.v1.DomainSearchQuery | criterias the client is looking for | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ListDomainsResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ListDetails | - | |
|
||||
| sorting_column | zitadel.instance.v1.DomainFieldName | - | |
|
||||
| result | repeated zitadel.instance.v1.Domain | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### ListFailedEventsRequest
|
||||
This is an empty request
|
||||
|
||||
@ -461,6 +454,29 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### RemoveDomainRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| domain | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveDomainResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveFailedEventRequest
|
||||
|
||||
|
||||
@ -502,6 +518,29 @@ This is an empty response
|
||||
|
||||
|
||||
|
||||
### SetPrimaryDomainRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
| domain | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### SetPrimaryDomainResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### View
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ type Instance interface {
|
||||
InstanceID() string
|
||||
ProjectID() string
|
||||
ConsoleClientID() string
|
||||
ConsoleApplicationID() string
|
||||
RequestedDomain() string
|
||||
}
|
||||
|
||||
@ -36,6 +37,10 @@ func (i *instance) ConsoleClientID() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (i *instance) ConsoleApplicationID() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (i *instance) RequestedDomain() string {
|
||||
return i.Domain
|
||||
}
|
||||
|
@ -79,6 +79,10 @@ func (m *mockInstance) ConsoleClientID() string {
|
||||
return "consoleID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) ConsoleApplicationID() string {
|
||||
return "appID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) RequestedDomain() string {
|
||||
return "zitadel.cloud"
|
||||
}
|
||||
|
29
internal/api/grpc/admin/instance.go
Normal file
29
internal/api/grpc/admin/instance.go
Normal file
@ -0,0 +1,29 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
instance_grpc "github.com/caos/zitadel/internal/api/grpc/instance"
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
|
||||
)
|
||||
|
||||
func (s *Server) GetInstanceDomains(ctx context.Context, req *admin_pb.ListInstanceDomainsRequest) (*admin_pb.ListInstanceDomainsResponse, error) {
|
||||
queries, err := ListInstanceDomainsRequestToModel(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains, err := s.query.SearchInstanceDomains(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.ListInstanceDomainsResponse{
|
||||
Result: instance_grpc.DomainsToPb(domains.Domains),
|
||||
Details: object.ToListDetails(
|
||||
domains.Count,
|
||||
domains.Sequence,
|
||||
domains.Timestamp,
|
||||
),
|
||||
}, nil
|
||||
}
|
25
internal/api/grpc/admin/instance_converter.go
Normal file
25
internal/api/grpc/admin/instance_converter.go
Normal file
@ -0,0 +1,25 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
instance_grpc "github.com/caos/zitadel/internal/api/grpc/instance"
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
|
||||
)
|
||||
|
||||
func ListInstanceDomainsRequestToModel(req *admin_pb.ListInstanceDomainsRequest) (*query.InstanceDomainSearchQueries, error) {
|
||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||
queries, err := instance_grpc.DomainQueriesToModel(req.Queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.InstanceDomainSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
},
|
||||
//SortingColumn: //TODO: sorting
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
54
internal/api/grpc/instance/converter.go
Normal file
54
internal/api/grpc/instance/converter.go
Normal file
@ -0,0 +1,54 @@
|
||||
package org
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
instance_pb "github.com/caos/zitadel/pkg/grpc/instance"
|
||||
)
|
||||
|
||||
func DomainQueriesToModel(queries []*instance_pb.DomainSearchQuery) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i], err = DomainQueryToModel(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func DomainQueryToModel(searchQuery *instance_pb.DomainSearchQuery) (query.SearchQuery, error) {
|
||||
switch q := searchQuery.Query.(type) {
|
||||
case *instance_pb.DomainSearchQuery_DomainQuery:
|
||||
return query.NewInstanceDomainDomainSearchQuery(object.TextMethodToQuery(q.DomainQuery.Method), q.DomainQuery.Domain)
|
||||
case *instance_pb.DomainSearchQuery_GeneratedQuery:
|
||||
return query.NewInstanceDomainGeneratedSearchQuery(q.GeneratedQuery.Generated)
|
||||
case *instance_pb.DomainSearchQuery_PrimaryQuery:
|
||||
return query.NewInstanceDomainPrimarySearchQuery(q.PrimaryQuery.Primary)
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-Ags42", "List.Query.Invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func DomainsToPb(domains []*query.InstanceDomain) []*instance_pb.Domain {
|
||||
d := make([]*instance_pb.Domain, len(domains))
|
||||
for i, domain := range domains {
|
||||
d[i] = DomainToPb(domain)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func DomainToPb(d *query.InstanceDomain) *instance_pb.Domain {
|
||||
return &instance_pb.Domain{
|
||||
Domain: d.Domain,
|
||||
Primary: d.IsPrimary,
|
||||
Generated: d.IsGenerated,
|
||||
Details: object.ToViewDetailsPb(
|
||||
d.Sequence,
|
||||
d.CreationDate,
|
||||
d.ChangeDate,
|
||||
d.InstanceID,
|
||||
),
|
||||
}
|
||||
}
|
@ -173,6 +173,10 @@ func (m *mockInstance) ConsoleClientID() string {
|
||||
return "consoleClientID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) ConsoleApplicationID() string {
|
||||
return "consoleApplicationID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) RequestedDomain() string {
|
||||
return "localhost"
|
||||
}
|
||||
|
@ -257,6 +257,10 @@ func (m *mockInstance) ConsoleClientID() string {
|
||||
return "consoleClientID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) ConsoleApplicationID() string {
|
||||
return "consoleApplicationID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) RequestedDomain() string {
|
||||
return "zitadel.cloud"
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ func (c *commandNew) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*
|
||||
),
|
||||
|
||||
AddOIDCAppCommand(console, nil),
|
||||
SetIAMConsoleID(instanceAgg, &console.ClientID),
|
||||
SetIAMConsoleID(instanceAgg, &console.ClientID, &setup.Zitadel.consoleAppID),
|
||||
)
|
||||
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.es.Filter, validations...)
|
||||
@ -387,11 +387,11 @@ func SetIAMProject(a *instance.Aggregate, projectID string) preparation.Validati
|
||||
}
|
||||
|
||||
//SetIAMConsoleID defines the command to set the clientID of the Console App onto the instance
|
||||
func SetIAMConsoleID(a *instance.Aggregate, clientID *string) preparation.Validation {
|
||||
func SetIAMConsoleID(a *instance.Aggregate, clientID, appID *string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{
|
||||
instance.NewIAMConsoleSetEvent(ctx, &a.Aggregate, clientID),
|
||||
instance.NewIAMConsoleSetEvent(ctx, &a.Aggregate, clientID, appID),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
)
|
||||
|
||||
func (c *Commands) AddInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) {
|
||||
@ -30,6 +31,24 @@ func (c *Commands) AddInstanceDomain(ctx context.Context, instanceDomain string)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Commands) SetPrimaryInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) {
|
||||
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
|
||||
validation := c.setPrimaryInstanceDomain(instanceAgg, instanceDomain)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &domain.ObjectDetails{
|
||||
Sequence: events[len(events)-1].Sequence(),
|
||||
EventDate: events[len(events)-1].CreationDate(),
|
||||
ResourceOwner: events[len(events)-1].Aggregate().InstanceID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) {
|
||||
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
|
||||
validation := c.removeInstanceDomain(instanceAgg, instanceDomain)
|
||||
@ -61,7 +80,46 @@ func (c *Commands) addInstanceDomain(a *instance.Aggregate, instanceDomain strin
|
||||
if domainWriteModel.State == domain.InstanceDomainStateActive {
|
||||
return nil, errors.ThrowAlreadyExists(nil, "INST-i2nl", "Errors.Instance.Domain.AlreadyExists")
|
||||
}
|
||||
return []eventstore.Command{instance.NewDomainAddedEvent(ctx, &a.Aggregate, instanceDomain, generated)}, nil
|
||||
appWriteModel, err := c.getOIDCAppWriteModel(ctx, authz.GetInstance(ctx).ProjectID(), authz.GetInstance(ctx).ConsoleApplicationID(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
redirectUrls := append(appWriteModel.RedirectUris, instanceDomain+consoleRedirectPath)
|
||||
logoutUrls := append(appWriteModel.PostLogoutRedirectUris, instanceDomain+consolePostLogoutPath)
|
||||
consoleChangeEvent, err := project.NewOIDCConfigChangedEvent(
|
||||
ctx,
|
||||
ProjectAggregateFromWriteModel(&appWriteModel.WriteModel),
|
||||
appWriteModel.AppID,
|
||||
[]project.OIDCConfigChanges{
|
||||
project.ChangeRedirectURIs(redirectUrls),
|
||||
project.ChangePostLogoutRedirectURIs(logoutUrls),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{
|
||||
instance.NewDomainAddedEvent(ctx, &a.Aggregate, instanceDomain, generated),
|
||||
consoleChangeEvent,
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) setPrimaryInstanceDomain(a *instance.Aggregate, instanceDomain string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if instanceDomain = strings.TrimSpace(instanceDomain); instanceDomain == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "INST-9mWjf", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
domainWriteModel, err := c.getInstanceDomainWriteModel(ctx, instanceDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !domainWriteModel.State.Exists() {
|
||||
return nil, errors.ThrowNotFound(nil, "INSTANCE-9nkWf", "Errors.Instance.Domain.NotFound")
|
||||
}
|
||||
return []eventstore.Command{instance.NewDomainPrimarySetEvent(ctx, &a.Aggregate, instanceDomain)}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,11 @@ package command
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
@ -77,6 +80,43 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) {
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"consoleApplicationID",
|
||||
"app",
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
project.NewOIDCConfigAddedEvent(context.Background(),
|
||||
&project.NewAggregate("projectID", "org1").Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"consoleApplicationID",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypePost,
|
||||
[]string{"https://test.ch/logout"},
|
||||
true,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
time.Second*1,
|
||||
[]string{"https://sub.test.ch"}),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
@ -86,11 +126,121 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) {
|
||||
"domain.ch",
|
||||
false,
|
||||
)),
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
newOIDCAppChangedEventInstanceDomain(context.Background(), "consoleApplicationID", "projectID", "org1"),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", instance.NewAddInstanceDomainUniqueConstraint("domain.ch")),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstance(context.Background(), new(mockInstance)),
|
||||
domain: "domain.ch",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "INSTANCE",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddInstanceDomain(tt.args.ctx, tt.args.domain)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_SetPrimaryInstanceDomain(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
domain string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "invalid domain, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
domain: "",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "domain not exists, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
domain: "domain.ch",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "set primary domain, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
instance.NewDomainAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"domain.ch",
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
instance.NewDomainPrimarySetEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"domain.ch",
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
domain: "domain.ch",
|
||||
@ -107,7 +257,7 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddInstanceDomain(tt.args.ctx, tt.args.domain)
|
||||
got, err := r.SetPrimaryInstanceDomain(tt.args.ctx, tt.args.domain)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -251,3 +401,16 @@ func TestCommandSide_RemoveInstanceDomain(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newOIDCAppChangedEventInstanceDomain(ctx context.Context, appID, projectID, resourceOwner string) *project.OIDCConfigChangedEvent {
|
||||
changes := []project.OIDCConfigChanges{
|
||||
project.ChangeRedirectURIs([]string{"https://test.ch", "domain.ch/ui/console/auth/callback"}),
|
||||
project.ChangePostLogoutRedirectURIs([]string{"https://test.ch/logout", "domain.ch/ui/console/signedout"}),
|
||||
}
|
||||
event, _ := project.NewOIDCConfigChangedEvent(ctx,
|
||||
&project.NewAggregate(projectID, resourceOwner).Aggregate,
|
||||
appID,
|
||||
changes,
|
||||
)
|
||||
return event
|
||||
}
|
||||
|
@ -256,3 +256,25 @@ func (v *testVerifier) CheckOrgFeatures(ctx context.Context, orgID string, requi
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockInstance struct{}
|
||||
|
||||
func (m *mockInstance) InstanceID() string {
|
||||
return "INSTANCE"
|
||||
}
|
||||
|
||||
func (m *mockInstance) ProjectID() string {
|
||||
return "projectID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) ConsoleClientID() string {
|
||||
return "consoleID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) ConsoleApplicationID() string {
|
||||
return "consoleApplicationID"
|
||||
}
|
||||
|
||||
func (m *mockInstance) RequestedDomain() string {
|
||||
return "zitadel.cloud"
|
||||
}
|
||||
|
@ -309,7 +309,7 @@ func (c *Commands) VerifyOIDCClientSecret(ctx context.Context, projectID, appID,
|
||||
return err
|
||||
}
|
||||
_, err = c.eventstore.Push(ctx, project_repo.NewOIDCConfigSecretCheckFailedEvent(ctx, projectAgg, app.AppID))
|
||||
logging.Log("COMMAND-ADfhz").OnError(err).Error("could not push event OIDCClientSecretCheckFailed")
|
||||
logging.New().OnError(err).Error("could not push event OIDCClientSecretCheckFailed")
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-Bz542", "Errors.Project.App.ClientSecretInvalid")
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,10 @@ func (f InstanceDomainState) Valid() bool {
|
||||
return f >= 0 && f < instanceDomainStateCount
|
||||
}
|
||||
|
||||
func (f InstanceDomainState) Exists() bool {
|
||||
return f == InstanceDomainStateActive
|
||||
}
|
||||
|
||||
func NewGeneratedInstanceDomain(instanceName, iamDomain string) string {
|
||||
return strings.ToLower(strings.ReplaceAll(instanceName, " ", "-") + "." + iamDomain)
|
||||
}
|
||||
|
@ -43,6 +43,10 @@ var (
|
||||
name: projection.InstanceColumnConsoleID,
|
||||
table: instanceTable,
|
||||
}
|
||||
InstanceColumnConsoleAppID = Column{
|
||||
name: projection.InstanceColumnConsoleAppID,
|
||||
table: instanceTable,
|
||||
}
|
||||
InstanceColumnSetupStarted = Column{
|
||||
name: projection.InstanceColumnSetUpStarted,
|
||||
table: instanceTable,
|
||||
@ -65,6 +69,7 @@ type Instance struct {
|
||||
GlobalOrgID string
|
||||
IAMProjectID string
|
||||
ConsoleID string
|
||||
ConsoleAppID string
|
||||
DefaultLanguage language.Tag
|
||||
SetupStarted domain.Step
|
||||
SetupDone domain.Step
|
||||
@ -83,6 +88,10 @@ func (i *Instance) ConsoleClientID() string {
|
||||
return i.ConsoleID
|
||||
}
|
||||
|
||||
func (i *Instance) ConsoleApplicationID() string {
|
||||
return i.ConsoleAppID
|
||||
}
|
||||
|
||||
func (i *Instance) RequestedDomain() string {
|
||||
return i.Host
|
||||
}
|
||||
@ -142,6 +151,7 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta
|
||||
InstanceColumnGlobalOrgID.identifier(),
|
||||
InstanceColumnProjectID.identifier(),
|
||||
InstanceColumnConsoleID.identifier(),
|
||||
InstanceColumnConsoleAppID.identifier(),
|
||||
InstanceColumnSetupStarted.identifier(),
|
||||
InstanceColumnSetupDone.identifier(),
|
||||
InstanceColumnDefaultLanguage.identifier(),
|
||||
@ -157,6 +167,7 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta
|
||||
&instance.GlobalOrgID,
|
||||
&instance.IAMProjectID,
|
||||
&instance.ConsoleID,
|
||||
&instance.ConsoleAppID,
|
||||
&instance.SetupStarted,
|
||||
&instance.SetupDone,
|
||||
&lang,
|
||||
|
@ -19,6 +19,7 @@ type InstanceDomain struct {
|
||||
Domain string
|
||||
InstanceID string
|
||||
IsGenerated bool
|
||||
IsPrimary bool
|
||||
}
|
||||
|
||||
type InstanceDomains struct {
|
||||
@ -47,8 +48,12 @@ func NewInstanceDomainInstanceIDSearchQuery(value string) (SearchQuery, error) {
|
||||
return NewTextQuery(InstanceDomainInstanceIDCol, value, TextEquals)
|
||||
}
|
||||
|
||||
func NewInstanceDomainGeneratedSearchQuery(verified bool) (SearchQuery, error) {
|
||||
return NewBoolQuery(InstanceDomainIsGeneratedCol, verified)
|
||||
func NewInstanceDomainGeneratedSearchQuery(generated bool) (SearchQuery, error) {
|
||||
return NewBoolQuery(InstanceDomainIsGeneratedCol, generated)
|
||||
}
|
||||
|
||||
func NewInstanceDomainPrimarySearchQuery(primary bool) (SearchQuery, error) {
|
||||
return NewBoolQuery(InstanceDomainIsPrimaryCol, primary)
|
||||
}
|
||||
|
||||
func (q *Queries) SearchInstanceDomains(ctx context.Context, queries *InstanceDomainSearchQueries) (domains *InstanceDomains, err error) {
|
||||
@ -81,6 +86,7 @@ func prepareInstanceDomainsQuery() (sq.SelectBuilder, func(*sql.Rows) (*Instance
|
||||
InstanceDomainDomainCol.identifier(),
|
||||
InstanceDomainInstanceIDCol.identifier(),
|
||||
InstanceDomainIsGeneratedCol.identifier(),
|
||||
InstanceDomainIsPrimaryCol.identifier(),
|
||||
countColumn.identifier(),
|
||||
).From(instanceDomainsTable.identifier()).PlaceholderFormat(sq.Dollar),
|
||||
func(rows *sql.Rows) (*InstanceDomains, error) {
|
||||
@ -95,6 +101,7 @@ func prepareInstanceDomainsQuery() (sq.SelectBuilder, func(*sql.Rows) (*Instance
|
||||
&domain.Domain,
|
||||
&domain.InstanceID,
|
||||
&domain.IsGenerated,
|
||||
&domain.IsPrimary,
|
||||
&count,
|
||||
)
|
||||
if err != nil {
|
||||
@ -145,4 +152,8 @@ var (
|
||||
name: projection.InstanceDomainIsGeneratedCol,
|
||||
table: instanceDomainsTable,
|
||||
}
|
||||
InstanceDomainIsPrimaryCol = Column{
|
||||
name: projection.InstanceDomainIsPrimaryCol,
|
||||
table: instanceDomainsTable,
|
||||
}
|
||||
)
|
||||
|
@ -31,6 +31,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
` projections.instance_domains.domain,`+
|
||||
` projections.instance_domains.instance_id,`+
|
||||
` projections.instance_domains.is_generated,`+
|
||||
` projections.instance_domains.is_primary,`+
|
||||
` COUNT(*) OVER ()`+
|
||||
` FROM projections.instance_domains`),
|
||||
nil,
|
||||
@ -50,6 +51,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
` projections.instance_domains.domain,`+
|
||||
` projections.instance_domains.instance_id,`+
|
||||
` projections.instance_domains.is_generated,`+
|
||||
` projections.instance_domains.is_primary,`+
|
||||
` COUNT(*) OVER ()`+
|
||||
` FROM projections.instance_domains`),
|
||||
[]string{
|
||||
@ -59,6 +61,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
"domain",
|
||||
"instance_id",
|
||||
"is_generated",
|
||||
"is_primary",
|
||||
"count",
|
||||
},
|
||||
[][]driver.Value{
|
||||
@ -69,6 +72,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
"zitadel.ch",
|
||||
"inst-id",
|
||||
true,
|
||||
true,
|
||||
},
|
||||
},
|
||||
),
|
||||
@ -85,6 +89,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
Domain: "zitadel.ch",
|
||||
InstanceID: "inst-id",
|
||||
IsGenerated: true,
|
||||
IsPrimary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -100,6 +105,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
` projections.instance_domains.domain,`+
|
||||
` projections.instance_domains.instance_id,`+
|
||||
` projections.instance_domains.is_generated,`+
|
||||
` projections.instance_domains.is_primary,`+
|
||||
` COUNT(*) OVER ()`+
|
||||
` FROM projections.instance_domains`),
|
||||
[]string{
|
||||
@ -109,6 +115,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
"domain",
|
||||
"instance_id",
|
||||
"is_generated",
|
||||
"is_primary",
|
||||
"count",
|
||||
},
|
||||
[][]driver.Value{
|
||||
@ -119,6 +126,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
"zitadel.ch",
|
||||
"inst-id",
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
testNow,
|
||||
@ -127,6 +135,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
"zitadel.com",
|
||||
"inst-id",
|
||||
false,
|
||||
false,
|
||||
},
|
||||
},
|
||||
),
|
||||
@ -143,6 +152,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
Domain: "zitadel.ch",
|
||||
InstanceID: "inst-id",
|
||||
IsGenerated: true,
|
||||
IsPrimary: true,
|
||||
},
|
||||
{
|
||||
CreationDate: testNow,
|
||||
@ -151,6 +161,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
Domain: "zitadel.com",
|
||||
InstanceID: "inst-id",
|
||||
IsGenerated: false,
|
||||
IsPrimary: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -166,6 +177,7 @@ func Test_InstanceDomainPrepares(t *testing.T) {
|
||||
` projections.instance_domains.domain,`+
|
||||
` projections.instance_domains.instance_id,`+
|
||||
` projections.instance_domains.is_generated,`+
|
||||
` projections.instance_domains.is_primary,`+
|
||||
` COUNT(*) OVER ()`+
|
||||
` FROM projections.instance_domains`),
|
||||
sql.ErrConnDone,
|
||||
|
@ -39,6 +39,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
` projections.instances.global_org_id,`+
|
||||
` projections.instances.iam_project_id,`+
|
||||
` projections.instances.console_client_id,`+
|
||||
` projections.instances.console_app_id,`+
|
||||
` projections.instances.setup_started,`+
|
||||
` projections.instances.setup_done,`+
|
||||
` projections.instances.default_language`+
|
||||
@ -68,6 +69,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
` projections.instances.global_org_id,`+
|
||||
` projections.instances.iam_project_id,`+
|
||||
` projections.instances.console_client_id,`+
|
||||
` projections.instances.console_app_id,`+
|
||||
` projections.instances.setup_started,`+
|
||||
` projections.instances.setup_done,`+
|
||||
` projections.instances.default_language`+
|
||||
@ -79,6 +81,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
"global_org_id",
|
||||
"iam_project_id",
|
||||
"console_client_id",
|
||||
"console_app_id",
|
||||
"setup_started",
|
||||
"setup_done",
|
||||
"default_language",
|
||||
@ -90,6 +93,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
"global-org-id",
|
||||
"project-id",
|
||||
"client-id",
|
||||
"app-id",
|
||||
domain.Step2,
|
||||
domain.Step1,
|
||||
"en",
|
||||
@ -103,6 +107,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
GlobalOrgID: "global-org-id",
|
||||
IAMProjectID: "project-id",
|
||||
ConsoleID: "client-id",
|
||||
ConsoleAppID: "app-id",
|
||||
SetupStarted: domain.Step2,
|
||||
SetupDone: domain.Step1,
|
||||
DefaultLanguage: language.English,
|
||||
@ -121,6 +126,7 @@ func Test_InstancePrepares(t *testing.T) {
|
||||
` projections.instances.global_org_id,`+
|
||||
` projections.instances.iam_project_id,`+
|
||||
` projections.instances.console_client_id,`+
|
||||
` projections.instances.console_app_id,`+
|
||||
` projections.instances.setup_started,`+
|
||||
` projections.instances.setup_done,`+
|
||||
` projections.instances.default_language`+
|
||||
|
@ -18,6 +18,7 @@ const (
|
||||
InstanceColumnGlobalOrgID = "global_org_id"
|
||||
InstanceColumnProjectID = "iam_project_id"
|
||||
InstanceColumnConsoleID = "console_client_id"
|
||||
InstanceColumnConsoleAppID = "console_app_id"
|
||||
InstanceColumnSequence = "sequence"
|
||||
InstanceColumnSetUpStarted = "setup_started"
|
||||
InstanceColumnSetUpDone = "setup_done"
|
||||
@ -129,6 +130,7 @@ func (p *InstanceProjection) reduceConsoleSet(event eventstore.Event) (*handler.
|
||||
handler.NewCol(InstanceColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(InstanceColumnSequence, e.Sequence()),
|
||||
handler.NewCol(InstanceColumnConsoleID, e.ClientID),
|
||||
handler.NewCol(InstanceColumnConsoleAppID, e.AppID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ const (
|
||||
InstanceDomainSequenceCol = "sequence"
|
||||
InstanceDomainDomainCol = "domain"
|
||||
InstanceDomainIsGeneratedCol = "is_generated"
|
||||
InstanceDomainIsPrimaryCol = "is_primary"
|
||||
)
|
||||
|
||||
type InstanceDomainProjection struct {
|
||||
@ -37,6 +38,7 @@ func NewInstanceDomainProjection(ctx context.Context, config crdb.StatementHandl
|
||||
crdb.NewColumn(InstanceDomainSequenceCol, crdb.ColumnTypeInt64),
|
||||
crdb.NewColumn(InstanceDomainDomainCol, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(InstanceDomainIsGeneratedCol, crdb.ColumnTypeBool),
|
||||
crdb.NewColumn(InstanceDomainIsPrimaryCol, crdb.ColumnTypeBool),
|
||||
},
|
||||
crdb.NewPrimaryKey(InstanceDomainInstanceIDCol, InstanceDomainDomainCol),
|
||||
),
|
||||
@ -54,6 +56,10 @@ func (p *InstanceDomainProjection) reducers() []handler.AggregateReducer {
|
||||
Event: instance.InstanceDomainAddedEventType,
|
||||
Reduce: p.reduceDomainAdded,
|
||||
},
|
||||
{
|
||||
Event: instance.InstanceDomainAddedEventType,
|
||||
Reduce: p.reduceDomainPrimarySet,
|
||||
},
|
||||
{
|
||||
Event: instance.InstanceDomainRemovedEventType,
|
||||
Reduce: p.reduceDomainRemoved,
|
||||
@ -77,10 +83,43 @@ func (p *InstanceDomainProjection) reduceDomainAdded(event eventstore.Event) (*h
|
||||
handler.NewCol(InstanceDomainDomainCol, e.Domain),
|
||||
handler.NewCol(InstanceDomainInstanceIDCol, e.Aggregate().ID),
|
||||
handler.NewCol(InstanceDomainIsGeneratedCol, e.Generated),
|
||||
handler.NewCol(InstanceDomainIsPrimaryCol, false),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *InstanceDomainProjection) reduceDomainPrimarySet(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*instance.DomainPrimarySetEvent)
|
||||
if !ok {
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "PROJE-f8nlw", "reduce.wrong.event.type %s", instance.InstanceDomainPrimarySetEventType)
|
||||
}
|
||||
return crdb.NewMultiStatement(
|
||||
e,
|
||||
crdb.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(InstanceDomainChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(InstanceDomainSequenceCol, e.Sequence()),
|
||||
handler.NewCol(InstanceDomainIsPrimaryCol, false),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(InstanceDomainInstanceIDCol, e.Aggregate().InstanceID),
|
||||
handler.NewCond(InstanceDomainIsPrimaryCol, true),
|
||||
},
|
||||
),
|
||||
crdb.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(InstanceDomainChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(InstanceDomainSequenceCol, e.Sequence()),
|
||||
handler.NewCol(InstanceDomainIsPrimaryCol, true),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(InstanceDomainDomainCol, e.Domain),
|
||||
handler.NewCond(InstanceDomainInstanceIDCol, e.Aggregate().ID),
|
||||
},
|
||||
),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *InstanceDomainProjection) reduceDomainRemoved(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*instance.DomainRemovedEvent)
|
||||
if !ok {
|
||||
|
@ -38,7 +38,7 @@ func TestInstanceDomainProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.instance_domains (creation_date, change_date, sequence, domain, instance_id, is_generated) VALUES ($1, $2, $3, $4, $5, $6)",
|
||||
expectedStmt: "INSERT INTO projections.instance_domains (creation_date, change_date, sequence, domain, instance_id, is_generated, is_primary) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@ -46,6 +46,7 @@ func TestInstanceDomainProjection_reduces(t *testing.T) {
|
||||
"domain.new",
|
||||
"agg-id",
|
||||
true,
|
||||
false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -11,10 +11,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UniqueInstanceDomain = "instance_domain"
|
||||
domainEventPrefix = instanceEventTypePrefix + "domain."
|
||||
InstanceDomainAddedEventType = domainEventPrefix + "added"
|
||||
InstanceDomainRemovedEventType = domainEventPrefix + "removed"
|
||||
UniqueInstanceDomain = "instance_domain"
|
||||
domainEventPrefix = instanceEventTypePrefix + "domain."
|
||||
InstanceDomainAddedEventType = domainEventPrefix + "added"
|
||||
InstanceDomainPrimarySetEventType = domainEventPrefix + "primary.set"
|
||||
InstanceDomainRemovedEventType = domainEventPrefix + "removed"
|
||||
)
|
||||
|
||||
func NewAddInstanceDomainUniqueConstraint(orgDomain string) *eventstore.EventUniqueConstraint {
|
||||
@ -69,6 +70,43 @@ func DomainAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
return orgDomainAdded, nil
|
||||
}
|
||||
|
||||
type DomainPrimarySetEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
Domain string `json:"domain,omitempty"`
|
||||
}
|
||||
|
||||
func (e *DomainPrimarySetEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *DomainPrimarySetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewDomainPrimarySetEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string) *DomainPrimarySetEvent {
|
||||
return &DomainPrimarySetEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
InstanceDomainPrimarySetEventType,
|
||||
),
|
||||
Domain: domain,
|
||||
}
|
||||
}
|
||||
|
||||
func DomainPrimarySetEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
orgDomainAdded := &DomainAddedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
err := json.Unmarshal(event.Data, orgDomainAdded)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "INSTANCE-29jöF", "unable to unmarshal instance domain added")
|
||||
}
|
||||
|
||||
return orgDomainAdded, nil
|
||||
}
|
||||
|
||||
type DomainRemovedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
|
@ -60,6 +60,7 @@ type ConsoleSetEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
ClientID string `json:"clientId"`
|
||||
AppID string `json:"appId"`
|
||||
}
|
||||
|
||||
func (e *ConsoleSetEvent) Data() interface{} {
|
||||
@ -73,7 +74,8 @@ func (e *ConsoleSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstrain
|
||||
func NewIAMConsoleSetEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
clientID *string,
|
||||
clientID,
|
||||
appID *string,
|
||||
) *ConsoleSetEvent {
|
||||
return &ConsoleSetEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
@ -82,6 +84,7 @@ func NewIAMConsoleSetEvent(
|
||||
ConsoleSetEventType,
|
||||
),
|
||||
ClientID: *clientID,
|
||||
AppID: *appID,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||
RegisterFilterEventMapper(CustomTextTemplateRemovedEventType, CustomTextTemplateRemovedEventMapper).
|
||||
RegisterFilterEventMapper(FeaturesSetEventType, FeaturesSetEventMapper).
|
||||
RegisterFilterEventMapper(InstanceDomainAddedEventType, DomainAddedEventMapper).
|
||||
RegisterFilterEventMapper(InstanceDomainPrimarySetEventType, DomainPrimarySetEventMapper).
|
||||
RegisterFilterEventMapper(InstanceDomainRemovedEventType, DomainRemovedEventMapper).
|
||||
RegisterFilterEventMapper(InstanceAddedEventType, InstanceAddedEventMapper).
|
||||
RegisterFilterEventMapper(InstanceChangedEventType, InstanceChangedEventMapper).
|
||||
|
@ -1,6 +1,7 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "zitadel/idp.proto";
|
||||
import "zitadel/instance.proto";
|
||||
import "zitadel/user.proto";
|
||||
import "zitadel/object.proto";
|
||||
import "zitadel/options.proto";
|
||||
@ -184,6 +185,13 @@ service AdminService {
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the domains of an instance
|
||||
rpc ListInstanceDomains(ListInstanceDomainsRequest) returns (ListInstanceDomainsResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/domains";
|
||||
};
|
||||
}
|
||||
|
||||
// Set the default language
|
||||
rpc ListSecretGenerators(ListSecretGeneratorsRequest) returns (ListSecretGeneratorsResponse) {
|
||||
option (google.api.http) = {
|
||||
@ -2642,6 +2650,20 @@ message GetDefaultLanguageResponse {
|
||||
string language = 1;
|
||||
}
|
||||
|
||||
message ListInstanceDomainsRequest {
|
||||
zitadel.v1.ListQuery query = 1;
|
||||
// the field the result is sorted
|
||||
zitadel.instance.v1.DomainFieldName sorting_column = 2;
|
||||
//criterias the client is looking for
|
||||
repeated zitadel.instance.v1.DomainSearchQuery queries = 3;
|
||||
}
|
||||
|
||||
message ListInstanceDomainsResponse {
|
||||
zitadel.v1.ListDetails details = 1;
|
||||
zitadel.instance.v1.DomainFieldName sorting_column = 2;
|
||||
repeated zitadel.instance.v1.Domain result = 3;
|
||||
}
|
||||
|
||||
message ListSecretGeneratorsRequest {
|
||||
//list limitations and ordering
|
||||
zitadel.v1.ListQuery query = 1;
|
||||
|
@ -20,22 +20,12 @@ message Instance {
|
||||
description: "current state of the instance";
|
||||
}
|
||||
];
|
||||
string generated_domain = 4 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"organization.zitadel.com\"";
|
||||
}
|
||||
];
|
||||
repeated string custom_domains = 5 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "[\"zitadel.com\", \"zitadel.cloud\", \"zitadel.ch\"]";
|
||||
}
|
||||
];
|
||||
string name = 6 [
|
||||
string name = 4 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"ZITADEL\"";
|
||||
}
|
||||
];
|
||||
string version = 7 [
|
||||
string version = 5 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"v1.0.0\"";
|
||||
}
|
||||
@ -55,8 +45,7 @@ message Query {
|
||||
option (validate.required) = true;
|
||||
|
||||
IdQuery id_query = 1;
|
||||
DomainsQuery domains_query = 2;
|
||||
StateQuery state_query = 3;
|
||||
StateQuery state_query = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,21 +59,6 @@ message IdQuery {
|
||||
];
|
||||
}
|
||||
|
||||
message DomainsQuery {
|
||||
repeated string domains = 1 [
|
||||
(validate.rules).string = {max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"caos.ch\"";
|
||||
}
|
||||
];
|
||||
zitadel.v1.ListQueryMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "defines which list equality method is used";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
//StateQuery is always equals
|
||||
message StateQuery {
|
||||
State state = 1 [
|
||||
@ -98,7 +72,68 @@ message StateQuery {
|
||||
enum FieldName {
|
||||
FIELD_NAME_UNSPECIFIED = 0;
|
||||
FIELD_NAME_ID = 1;
|
||||
FIELD_NAME_GENERATED_DOMAIN = 2;
|
||||
FIELD_NAME_NAME = 3;
|
||||
FIELD_NAME_CREATION_DATE = 4;
|
||||
FIELD_NAME_NAME = 2;
|
||||
FIELD_NAME_CREATION_DATE = 3;
|
||||
}
|
||||
|
||||
message Domain {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
string domain = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"zitadel.com\""
|
||||
}
|
||||
];
|
||||
bool primary = 3;
|
||||
bool generated = 4;
|
||||
}
|
||||
|
||||
message DomainSearchQuery {
|
||||
oneof query {
|
||||
option (validate.required) = true;
|
||||
|
||||
DomainQuery domain_query = 1;
|
||||
DomainGeneratedQuery generated_query = 2;
|
||||
DomainPrimaryQuery primary_query = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message DomainQuery {
|
||||
string domain = 1 [
|
||||
(validate.rules).string = {max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "zitadel.com";
|
||||
}
|
||||
];
|
||||
zitadel.v1.TextQueryMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "defines which text equality method is used";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
//DomainGeneratedQuery is always equals
|
||||
message DomainGeneratedQuery {
|
||||
bool generated = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "generated domains";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
//DomainPrimaryQuery is always equals
|
||||
message DomainPrimaryQuery {
|
||||
bool primary = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "primary domains";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
enum DomainFieldName {
|
||||
DOMAIN_FIELD_NAME_UNSPECIFIED = 0;
|
||||
DOMAIN_FIELD_NAME_DOMAIN = 1;
|
||||
DOMAIN_FIELD_NAME_PRIMARY = 2;
|
||||
DOMAIN_FIELD_NAME_GENERATED = 3;
|
||||
DOMAIN_FIELD_NAME_CREATION_DATE = 4;
|
||||
}
|
||||
|
@ -141,28 +141,37 @@ service SystemService {
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the domain of an instance
|
||||
rpc GetGeneratedDomain(GetGeneratedDomainRequest) returns (GetGeneratedDomainResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/instances/{id}/domains/generated";
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the custom domains of an instance
|
||||
rpc GetCustomDomains(GetCustomDomainsRequest) returns (GetCustomDomainsResponse) {
|
||||
rpc ListDomains(ListDomainsRequest) returns (ListDomainsResponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/instances/{id}/domains/custom";
|
||||
get: "/instances/{id}/domains";
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the domain of an instance
|
||||
rpc AddCustomDomain(AddCustomDomainRequest) returns (AddCustomDomainResponse) {
|
||||
rpc AddDomain(AddDomainRequest) returns (AddDomainResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/instances/{id}/domains/custom";
|
||||
post: "/instances/{id}/domains";
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the domain of an instance
|
||||
rpc RemoveDomain(RemoveDomainRequest) returns (RemoveDomainResponse) {
|
||||
option (google.api.http) = {
|
||||
delete: "/instances/{id}/domains/{domain}";
|
||||
};
|
||||
}
|
||||
|
||||
// Returns the domain of an instance
|
||||
rpc SetPrimaryDomain(SetPrimaryDomainRequest) returns (SetPrimaryDomainResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/instances/{id}/domains/_set_primary";
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//Returns all stored read models of ZITADEL
|
||||
// views are used for search optimisation and optimise request latencies
|
||||
// they represent the delta of the event happend on the objects
|
||||
@ -346,30 +355,45 @@ message GetUsageResponse {
|
||||
uint64 executed_action_mins = 3;
|
||||
}
|
||||
|
||||
message GetGeneratedDomainRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
message ListDomainsRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];//list limitations and ordering
|
||||
zitadel.v1.ListQuery query = 2;
|
||||
// the field the result is sorted
|
||||
zitadel.instance.v1.DomainFieldName sorting_column = 3;
|
||||
//criterias the client is looking for
|
||||
repeated zitadel.instance.v1.DomainSearchQuery queries = 4;
|
||||
}
|
||||
|
||||
message GetGeneratedDomainResponse {
|
||||
message ListDomainsResponse {
|
||||
zitadel.v1.ListDetails details = 1;
|
||||
zitadel.instance.v1.DomainFieldName sorting_column = 2;
|
||||
repeated zitadel.instance.v1.Domain result = 3;
|
||||
}
|
||||
|
||||
message AddDomainRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string domain = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message AddDomainResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
string domain = 2;
|
||||
}
|
||||
|
||||
message GetCustomDomainsRequest {
|
||||
message RemoveDomainRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string domain = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message GetCustomDomainsResponse {
|
||||
message RemoveDomainResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
repeated string domains = 2;
|
||||
}
|
||||
|
||||
message AddCustomDomainRequest {
|
||||
message SetPrimaryDomainRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string custom_domain = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string domain = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
}
|
||||
|
||||
message AddCustomDomainResponse {
|
||||
message SetPrimaryDomainResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user