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:
Fabi
2022-04-14 14:19:18 +02:00
committed by GitHub
parent 820a21dce3
commit c25d853820
29 changed files with 858 additions and 145 deletions

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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"
}

View File

@@ -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")
}