feat: select idp and auto register (#2336)

* faet: auto regsiter config on idp

* feat: auto register on login

* feat: auto register on register

* feat: redirect to selected identity provider

* fix: test

* fix: test

* fix: user by id request org id

* fix: migration version and test

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi
2021-09-10 09:49:49 +02:00
committed by GitHub
parent 79fb8aa37a
commit e4bdaf26b0
42 changed files with 272 additions and 111 deletions

View File

@@ -12,10 +12,11 @@ import (
func addOIDCIDPRequestToDomain(req *admin_pb.AddOIDCIDPRequest) *domain.IDPConfig {
return &domain.IDPConfig{
Name: req.Name,
OIDCConfig: addOIDCIDPRequestToDomainOIDCIDPConfig(req),
StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
Type: domain.IDPConfigTypeOIDC,
Name: req.Name,
OIDCConfig: addOIDCIDPRequestToDomainOIDCIDPConfig(req),
StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
Type: domain.IDPConfigTypeOIDC,
AutoRegister: req.AutoRegister,
}
}
@@ -32,9 +33,10 @@ func addOIDCIDPRequestToDomainOIDCIDPConfig(req *admin_pb.AddOIDCIDPRequest) *do
func updateIDPToDomain(req *admin_pb.UpdateIDPRequest) *domain.IDPConfig {
return &domain.IDPConfig{
IDPConfigID: req.IdpId,
Name: req.Name,
StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
IDPConfigID: req.IdpId,
Name: req.Name,
StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
AutoRegister: req.AutoRegister,
}
}

View File

@@ -28,6 +28,7 @@ func Test_addOIDCIDPRequestToDomain(t *testing.T) {
Scopes: []string{"email", "profile"},
DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL,
UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
AutoRegister: true,
},
},
},
@@ -98,9 +99,10 @@ func Test_updateIDPToDomain(t *testing.T) {
name: "all fields filled",
args: args{
req: &admin_pb.UpdateIDPRequest{
IdpId: "13523",
Name: "new name",
StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
IdpId: "13523",
Name: "new name",
StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
AutoRegister: true,
},
},
},

View File

@@ -18,29 +18,31 @@ func IDPViewsToPb(idps []*iam_model.IDPConfigView) []*idp_pb.IDP {
func ModelIDPViewToPb(idp *iam_model.IDPConfigView) *idp_pb.IDP {
return &idp_pb.IDP{
Id: idp.IDPConfigID,
State: ModelIDPStateToPb(idp.State),
Name: idp.Name,
StylingType: ModelIDPStylingTypeToPb(idp.StylingType),
Owner: ModelIDPProviderTypeToPb(idp.IDPProviderType),
Config: ModelIDPViewToConfigPb(idp),
Id: idp.IDPConfigID,
State: ModelIDPStateToPb(idp.State),
Name: idp.Name,
StylingType: ModelIDPStylingTypeToPb(idp.StylingType),
AutoRegister: idp.AutoRegister,
Owner: ModelIDPProviderTypeToPb(idp.IDPProviderType),
Config: ModelIDPViewToConfigPb(idp),
Details: obj_grpc.ToViewDetailsPb(
idp.Sequence,
idp.CreationDate,
idp.ChangeDate,
"", //TODO: backend
idp.AggregateID,
),
}
}
func IDPViewToPb(idp *domain.IDPConfigView) *idp_pb.IDP {
mapped := &idp_pb.IDP{
Id: idp.AggregateID,
State: IDPStateToPb(idp.State),
Name: idp.Name,
StylingType: IDPStylingTypeToPb(idp.StylingType),
Config: IDPViewToConfigPb(idp),
Details: obj_grpc.ToViewDetailsPb(idp.Sequence, idp.CreationDate, idp.ChangeDate, ""), //TODO: resource owner in view
Id: idp.AggregateID,
State: IDPStateToPb(idp.State),
Name: idp.Name,
StylingType: IDPStylingTypeToPb(idp.StylingType),
AutoRegister: idp.AutoRegister,
Config: IDPViewToConfigPb(idp),
Details: obj_grpc.ToViewDetailsPb(idp.Sequence, idp.CreationDate, idp.ChangeDate, idp.AggregateID),
}
return mapped
}

View File

@@ -32,9 +32,10 @@ func addOIDCIDPRequestToDomainOIDCIDPConfig(req *mgmt_pb.AddOrgOIDCIDPRequest) *
func updateIDPToDomain(req *mgmt_pb.UpdateOrgIDPRequest) *domain.IDPConfig {
return &domain.IDPConfig{
IDPConfigID: req.IdpId,
Name: req.Name,
StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
IDPConfigID: req.IdpId,
Name: req.Name,
StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
AutoRegister: req.AutoRegister,
}
}

View File

@@ -28,6 +28,7 @@ func Test_addOIDCIDPRequestToDomain(t *testing.T) {
Scopes: []string{"email", "profile"},
DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL,
UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
AutoRegister: true,
},
},
},
@@ -45,6 +46,7 @@ func Test_addOIDCIDPRequestToDomain(t *testing.T) {
"OIDCConfig.AuthorizationEndpoint",
"OIDCConfig.TokenEndpoint",
"Type", //TODO: default (0) is oidc
"AutoRegister",
)
})
}
@@ -98,9 +100,10 @@ func Test_updateIDPToDomain(t *testing.T) {
name: "all fields filled",
args: args{
req: &mgmt_pb.UpdateOrgIDPRequest{
IdpId: "13523",
Name: "new name",
StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
IdpId: "13523",
Name: "new name",
StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
AutoRegister: true,
},
},
},

View File

@@ -3,6 +3,7 @@ package oidc
import (
"context"
"net"
"strings"
"time"
"github.com/caos/oidc/pkg/oidc"
@@ -10,6 +11,7 @@ import (
"golang.org/x/text/language"
http_utils "github.com/caos/zitadel/internal/api/http"
model2 "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/user/model"
@@ -113,18 +115,19 @@ func AuthRequestFromBusiness(authReq *domain.AuthRequest) (_ op.AuthRequest, err
func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *domain.AuthRequest {
return &domain.AuthRequest{
CreationDate: time.Now(),
AgentID: userAgentID,
BrowserInfo: ParseBrowserInfoFromContext(ctx),
ApplicationID: authReq.ClientID,
CallbackURI: authReq.RedirectURI,
TransferState: authReq.State,
Prompt: PromptToBusiness(authReq.Prompt),
PossibleLOAs: ACRValuesToBusiness(authReq.ACRValues),
UiLocales: UILocalesToBusiness(authReq.UILocales),
LoginHint: authReq.LoginHint,
MaxAuthAge: MaxAgeToBusiness(authReq.MaxAge),
UserID: userID,
CreationDate: time.Now(),
AgentID: userAgentID,
BrowserInfo: ParseBrowserInfoFromContext(ctx),
ApplicationID: authReq.ClientID,
CallbackURI: authReq.RedirectURI,
TransferState: authReq.State,
Prompt: PromptToBusiness(authReq.Prompt),
PossibleLOAs: ACRValuesToBusiness(authReq.ACRValues),
UiLocales: UILocalesToBusiness(authReq.UILocales),
LoginHint: authReq.LoginHint,
SelectedIDPConfigID: GetSelectedIDPIDFromScopes(authReq.Scopes),
MaxAuthAge: MaxAgeToBusiness(authReq.MaxAge),
UserID: userID,
Request: &domain.AuthRequestOIDC{
Scopes: authReq.Scopes,
ResponseType: ResponseTypeToBusiness(authReq.ResponseType),
@@ -196,6 +199,15 @@ func UILocalesToBusiness(tags []language.Tag) []string {
return locales
}
func GetSelectedIDPIDFromScopes(scopes oidc.SpaceDelimitedArray) string {
for _, scope := range scopes {
if strings.HasPrefix(scope, model2.SelectIDPScope) {
return strings.TrimPrefix(scope, model2.SelectIDPScope)
}
}
return ""
}
func MaxAgeToBusiness(maxAge *uint) *time.Duration {
if maxAge == nil {
return nil

View File

@@ -106,6 +106,9 @@ func (c *Client) IsScopeAllowed(scope string) bool {
if strings.HasPrefix(scope, authreq_model.ProjectIDScope) {
return true
}
if strings.HasPrefix(scope, authreq_model.SelectIDPScope) {
return true
}
if strings.HasPrefix(scope, ScopeUserMetaData) {
return true
}

View File

@@ -133,6 +133,7 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *dom
err = repo.checkLoginName(ctx, request, request.LoginHint)
logging.LogWithFields("EVENT-aG311", "login name", request.LoginHint, "id", request.ID, "applicationID", request.ApplicationID, "traceID", tracing.TraceIDFromCtx(ctx)).OnError(err).Debug("login hint invalid")
}
err = repo.AuthRequests.SaveAuthRequest(ctx, request)
if err != nil {
return nil, err
@@ -642,7 +643,13 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
if err != nil {
return nil, err
}
if len(users) > 0 || domain.IsPrompt(request.Prompt, domain.PromptSelectAccount) {
if domain.IsPrompt(request.Prompt, domain.PromptSelectAccount) {
steps = append(steps, &domain.SelectUserStep{Users: users})
}
if request.SelectedIDPConfigID != "" {
steps = append(steps, &domain.RedirectToExternalIDPStep{})
}
if len(request.Prompt) == 0 && len(users) > 0 {
steps = append(steps, &domain.SelectUserStep{Users: users})
}
}

View File

@@ -305,6 +305,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
[]domain.NextStep{&domain.ExternalNotFoundOptionStep{}},
nil,
},
{
"user not set no active session selected idp, redirect to external idp step",
fields{
userSessionViewProvider: &mockViewNoUserSession{},
},
args{&domain.AuthRequest{SelectedIDPConfigID: "id"}, false},
[]domain.NextStep{&domain.LoginStep{}, &domain.RedirectToExternalIDPStep{}},
nil,
},
{
"user not set, prompt select account and internal error, internal error",
fields{

View File

@@ -23,6 +23,7 @@ const (
OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary"
ProjectIDScope = "urn:zitadel:iam:org:project:id:"
AudSuffix = ":aud"
SelectIDPScope = "urn:zitadel:iam:org:idp:id:"
)
type AuthRequestOIDC struct {

View File

@@ -130,11 +130,12 @@ func writeModelToPrivacyPolicy(wm *PrivacyPolicyWriteModel) *domain.PrivacyPolic
func writeModelToIDPConfig(wm *IDPConfigWriteModel) *domain.IDPConfig {
return &domain.IDPConfig{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
IDPConfigID: wm.ConfigID,
Name: wm.Name,
State: wm.State,
StylingType: wm.StylingType,
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
IDPConfigID: wm.ConfigID,
Name: wm.Name,
State: wm.State,
StylingType: wm.StylingType,
AutoRegister: wm.AutoRegister,
}
}

View File

@@ -37,6 +37,7 @@ func (c *Commands) AddDefaultIDPConfig(ctx context.Context, config *domain.IDPCo
config.Name,
config.Type,
config.StylingType,
config.AutoRegister,
),
iam_repo.NewIDPOIDCConfigAddedEvent(
ctx,
@@ -73,11 +74,11 @@ func (c *Commands) ChangeDefaultIDPConfig(ctx context.Context, config *domain.ID
return nil, err
}
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IDPConfig.NotExisting")
return nil, caos_errs.ThrowNotFound(nil, "IAM-m0e3r", "Errors.IDPConfig.NotExisting")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
changedEvent, hasChanged := existingIDP.NewChangedEvent(ctx, iamAgg, config.IDPConfigID, config.Name, config.StylingType)
changedEvent, hasChanged := existingIDP.NewChangedEvent(ctx, iamAgg, config.IDPConfigID, config.Name, config.StylingType, config.AutoRegister)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
@@ -98,7 +99,7 @@ func (c *Commands) DeactivateDefaultIDPConfig(ctx context.Context, idpID string)
return nil, err
}
if existingIDP.State != domain.IDPConfigStateActive {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotActive")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-2n0fs", "Errors.IAM.IDPConfig.NotActive")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewIDPConfigDeactivatedEvent(ctx, iamAgg, idpID))
@@ -173,7 +174,7 @@ func (c *Commands) getIAMIDPConfigByID(ctx context.Context, idpID string) (*doma
return nil, err
}
if !config.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IDPConfig.NotExisting")
return nil, caos_errs.ThrowNotFound(nil, "IAM-p0pFF", "Errors.IDPConfig.NotExisting")
}
return writeModelToIDPConfig(&config.IDPConfigWriteModel), nil
}

View File

@@ -100,6 +100,7 @@ func (wm *IAMIDPConfigWriteModel) NewChangedEvent(
configID,
name string,
stylingType domain.IDPConfigStylingType,
autoRegister bool,
) (*iam.IDPConfigChangedEvent, bool) {
changes := make([]idpconfig.IDPConfigChanges, 0)
@@ -111,6 +112,9 @@ func (wm *IAMIDPConfigWriteModel) NewChangedEvent(
if stylingType.Valid() && wm.StylingType != stylingType {
changes = append(changes, idpconfig.ChangeStyleType(stylingType))
}
if wm.AutoRegister != autoRegister {
changes = append(changes, idpconfig.ChangeAutoRegister(autoRegister))
}
if len(changes) == 0 {
return nil, false
}

View File

@@ -68,6 +68,7 @@ func TestCommandSide_AddDefaultIDPConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(
@@ -99,8 +100,9 @@ func TestCommandSide_AddDefaultIDPConfig(t *testing.T) {
args: args{
ctx: context.Background(),
config: &domain.IDPConfig{
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
AutoRegister: true,
OIDCConfig: &domain.OIDCIDPConfig{
ClientID: "clientid1",
Issuer: "issuer",
@@ -119,10 +121,11 @@ func TestCommandSide_AddDefaultIDPConfig(t *testing.T) {
AggregateID: "IAM",
ResourceOwner: "IAM",
},
IDPConfigID: "config1",
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
State: domain.IDPConfigStateActive,
IDPConfigID: "config1",
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
State: domain.IDPConfigStateActive,
AutoRegister: true,
},
},
},
@@ -212,6 +215,7 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(
@@ -237,7 +241,7 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
expectPush(
[]*repository.Event{
eventFromEventPusher(
newDefaultIDPConfigChangedEvent(context.Background(), "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified),
newDefaultIDPConfigChangedEvent(context.Background(), "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified, false),
),
},
uniqueConstraintsFromEventConstraint(idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name1", "IAM")),
@@ -248,9 +252,10 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
args: args{
ctx: context.Background(),
config: &domain.IDPConfig{
IDPConfigID: "config1",
Name: "name2",
StylingType: domain.IDPConfigStylingTypeUnspecified,
IDPConfigID: "config1",
Name: "name2",
StylingType: domain.IDPConfigStylingTypeUnspecified,
AutoRegister: false,
},
},
res: res{
@@ -259,10 +264,11 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
AggregateID: "IAM",
ResourceOwner: "IAM",
},
IDPConfigID: "config1",
Name: "name2",
StylingType: domain.IDPConfigStylingTypeUnspecified,
State: domain.IDPConfigStateActive,
IDPConfigID: "config1",
Name: "name2",
StylingType: domain.IDPConfigStylingTypeUnspecified,
State: domain.IDPConfigStateActive,
AutoRegister: false,
},
},
},
@@ -286,7 +292,7 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
}
}
func newDefaultIDPConfigChangedEvent(ctx context.Context, configID, oldName, newName string, stylingType domain.IDPConfigStylingType) *iam.IDPConfigChangedEvent {
func newDefaultIDPConfigChangedEvent(ctx context.Context, configID, oldName, newName string, stylingType domain.IDPConfigStylingType, autoRegister bool) *iam.IDPConfigChangedEvent {
event, _ := iam.NewIDPConfigChangedEvent(ctx,
&iam.NewAggregate().Aggregate,
configID,
@@ -294,6 +300,7 @@ func newDefaultIDPConfigChangedEvent(ctx context.Context, configID, oldName, new
[]idpconfig.IDPConfigChanges{
idpconfig.ChangeName(newName),
idpconfig.ChangeStyleType(stylingType),
idpconfig.ChangeAutoRegister(autoRegister),
},
)
return event

View File

@@ -84,6 +84,7 @@ func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(
@@ -138,6 +139,7 @@ func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(
@@ -193,6 +195,7 @@ func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(

View File

@@ -344,6 +344,7 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
true,
),
),
),
@@ -391,6 +392,7 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
true,
),
),
),

View File

@@ -11,9 +11,10 @@ type IDPConfigWriteModel struct {
State domain.IDPConfigState
ConfigID string
Name string
StylingType domain.IDPConfigStylingType
ConfigID string
Name string
AutoRegister bool
StylingType domain.IDPConfigStylingType
}
func (rm *IDPConfigWriteModel) AppendEvents(events ...eventstore.EventReader) {
@@ -42,6 +43,7 @@ func (rm *IDPConfigWriteModel) reduceConfigAddedEvent(e *idpconfig.IDPConfigAdde
rm.ConfigID = e.ConfigID
rm.Name = e.Name
rm.StylingType = e.StylingType
rm.AutoRegister = e.AutoRegister
rm.State = domain.IDPConfigStateActive
}
@@ -52,6 +54,9 @@ func (rm *IDPConfigWriteModel) reduceConfigChangedEvent(e *idpconfig.IDPConfigCh
if e.StylingType != nil && e.StylingType.Valid() {
rm.StylingType = *e.StylingType
}
if e.AutoRegister != nil {
rm.AutoRegister = *e.AutoRegister
}
}
func (rm *IDPConfigWriteModel) reduceConfigStateChanged(configID string, state domain.IDPConfigState) {

View File

@@ -40,6 +40,7 @@ func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig, r
config.Name,
config.Type,
config.StylingType,
config.AutoRegister,
),
org_repo.NewIDPOIDCConfigAddedEvent(
ctx,
@@ -69,12 +70,12 @@ func (c *Commands) ChangeIDPConfig(ctx context.Context, config *domain.IDPConfig
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Gh8ds", "Errors.ResourceOwnerMissing")
}
existingIDP, err := c.orgIDPConfigWriteModelByID(ctx, config.IDPConfigID, config.AggregateID)
existingIDP, err := c.orgIDPConfigWriteModelByID(ctx, config.IDPConfigID, resourceOwner)
if err != nil {
return nil, err
}
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
return nil, caos_errs.ThrowNotFound(nil, "Org-4M9so", "Errors.Org.IDPConfig.NotExisting")
return nil, caos_errs.ThrowNotFound(nil, "Org-1J9fs", "Errors.Org.IDPConfig.NotExisting")
}
orgAgg := OrgAggregateFromWriteModel(&existingIDP.WriteModel)
@@ -83,7 +84,8 @@ func (c *Commands) ChangeIDPConfig(ctx context.Context, config *domain.IDPConfig
orgAgg,
config.IDPConfigID,
config.Name,
config.StylingType)
config.StylingType,
config.AutoRegister)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4M9vs", "Errors.Org.LabelPolicy.NotChanged")
@@ -105,7 +107,7 @@ func (c *Commands) DeactivateIDPConfig(ctx context.Context, idpID, orgID string)
return nil, err
}
if existingIDP.State != domain.IDPConfigStateActive {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4M9so", "Errors.Org.IDPConfig.NotActive")
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-BBmd0", "Errors.Org.IDPConfig.NotActive")
}
orgAgg := OrgAggregateFromWriteModel(&existingIDP.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org_repo.NewIDPConfigDeactivatedEvent(ctx, orgAgg, idpID))
@@ -185,7 +187,7 @@ func (c *Commands) getOrgIDPConfigByID(ctx context.Context, idpID, orgID string)
return nil, err
}
if !config.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "ORG-4M9so", "Errors.Org.IDPConfig.NotExisting")
return nil, caos_errs.ThrowNotFound(nil, "ORG-2m90f", "Errors.Org.IDPConfig.NotExisting")
}
return writeModelToIDPConfig(&config.IDPConfigWriteModel), nil
}

View File

@@ -100,6 +100,7 @@ func (wm *OrgIDPConfigWriteModel) NewChangedEvent(
configID,
name string,
stylingType domain.IDPConfigStylingType,
autoRegister bool,
) (*org.IDPConfigChangedEvent, bool) {
changes := make([]idpconfig.IDPConfigChanges, 0)
@@ -111,6 +112,9 @@ func (wm *OrgIDPConfigWriteModel) NewChangedEvent(
if stylingType.Valid() && wm.StylingType != stylingType {
changes = append(changes, idpconfig.ChangeStyleType(stylingType))
}
if wm.AutoRegister != autoRegister {
changes = append(changes, idpconfig.ChangeAutoRegister(autoRegister))
}
if len(changes) == 0 {
return nil, false
}

View File

@@ -50,8 +50,9 @@ func TestCommandSide_AddIDPConfig(t *testing.T) {
args: args{
ctx: context.Background(),
config: &domain.IDPConfig{
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
AutoRegister: true,
OIDCConfig: &domain.OIDCIDPConfig{
ClientID: "clientid1",
Issuer: "issuer",
@@ -96,6 +97,7 @@ func TestCommandSide_AddIDPConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(
@@ -128,8 +130,9 @@ func TestCommandSide_AddIDPConfig(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
config: &domain.IDPConfig{
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
AutoRegister: true,
OIDCConfig: &domain.OIDCIDPConfig{
ClientID: "clientid1",
Issuer: "issuer",
@@ -148,10 +151,11 @@ func TestCommandSide_AddIDPConfig(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
IDPConfigID: "config1",
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
State: domain.IDPConfigStateActive,
IDPConfigID: "config1",
Name: "name1",
StylingType: domain.IDPConfigStylingTypeGoogle,
State: domain.IDPConfigStateActive,
AutoRegister: true,
},
},
},
@@ -260,6 +264,7 @@ func TestCommandSide_ChangeIDPConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(
@@ -297,9 +302,10 @@ func TestCommandSide_ChangeIDPConfig(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
config: &domain.IDPConfig{
IDPConfigID: "config1",
Name: "name2",
StylingType: domain.IDPConfigStylingTypeUnspecified,
IDPConfigID: "config1",
Name: "name2",
StylingType: domain.IDPConfigStylingTypeUnspecified,
AutoRegister: true,
},
},
res: res{
@@ -308,10 +314,11 @@ func TestCommandSide_ChangeIDPConfig(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
IDPConfigID: "config1",
Name: "name2",
StylingType: domain.IDPConfigStylingTypeUnspecified,
State: domain.IDPConfigStateActive,
IDPConfigID: "config1",
Name: "name2",
StylingType: domain.IDPConfigStylingTypeUnspecified,
State: domain.IDPConfigStateActive,
AutoRegister: true,
},
},
},

View File

@@ -104,6 +104,7 @@ func TestCommandSide_ChangeIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(
@@ -159,6 +160,7 @@ func TestCommandSide_ChangeIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(
@@ -215,6 +217,7 @@ func TestCommandSide_ChangeIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
true,
),
),
eventFromEventPusher(

View File

@@ -659,6 +659,7 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
true,
),
),
),
@@ -710,6 +711,7 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
true,
),
),
),

View File

@@ -164,6 +164,7 @@ func TestCommandSide_BulkAddExternalIDPs(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
true,
),
),
),
@@ -213,6 +214,7 @@ func TestCommandSide_BulkAddExternalIDPs(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
true,
),
),
),

View File

@@ -9,12 +9,13 @@ import (
type IDPConfig struct {
es_models.ObjectRoot
IDPConfigID string
Type IDPConfigType
Name string
StylingType IDPConfigStylingType
State IDPConfigState
OIDCConfig *OIDCIDPConfig
IDPConfigID string
Type IDPConfigType
Name string
StylingType IDPConfigStylingType
State IDPConfigState
OIDCConfig *OIDCIDPConfig
AutoRegister bool
}
type IDPConfigView struct {
@@ -27,6 +28,7 @@ type IDPConfigView struct {
ChangeDate time.Time
Sequence uint64
IDPProviderType IdentityProviderType
AutoRegister bool
IsOIDC bool
OIDCClientID string

View File

@@ -27,6 +27,7 @@ const (
NextStepPasswordlessRegistrationPrompt
NextStepRegistration
NextStepProjectRequired
NextStepRedirectToExternalIDP
)
type LoginStep struct{}
@@ -67,6 +68,12 @@ const (
UserSessionStateTerminated
)
type RedirectToExternalIDPStep struct{}
func (s *RedirectToExternalIDPStep) Type() NextStepType {
return NextStepRedirectToExternalIDP
}
type InitUserStep struct {
PasswordSet bool
}

View File

@@ -13,6 +13,7 @@ type IDPConfigView struct {
IDPConfigID string
Name string
StylingType IDPStylingType
AutoRegister bool
State IDPConfigState
CreationDate time.Time
ChangeDate time.Time

View File

@@ -33,6 +33,7 @@ type IDPConfigView struct {
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
IDPState int32 `json:"-" gorm:"column:idp_state"`
IDPProviderType int32 `json:"-" gorm:"column:idp_provider_type"`
AutoRegister bool `json:"autoRegister" gorm:"column:auto_register"`
IsOIDC bool `json:"-" gorm:"column:is_oidc"`
OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"`
@@ -54,6 +55,7 @@ func IDPConfigViewToModel(idp *IDPConfigView) *model.IDPConfigView {
State: model.IDPConfigState(idp.IDPState),
Name: idp.Name,
StylingType: model.IDPStylingType(idp.StylingType),
AutoRegister: idp.AutoRegister,
Sequence: idp.Sequence,
CreationDate: idp.CreationDate,
ChangeDate: idp.ChangeDate,

View File

@@ -12,6 +12,7 @@ type IDPConfigReadModel struct {
State domain.IDPConfigState
ConfigID string
Name string
AutoRegister bool
StylingType domain.IDPConfigStylingType
ProviderType domain.IdentityProviderType
@@ -77,6 +78,7 @@ func (rm *IDPConfigReadModel) reduceConfigAddedEvent(e *idpconfig.IDPConfigAdded
rm.Name = e.Name
rm.StylingType = e.StylingType
rm.State = domain.IDPConfigStateActive
rm.AutoRegister = e.AutoRegister
}
func (rm *IDPConfigReadModel) reduceConfigChangedEvent(e *idpconfig.IDPConfigChangedEvent) {
@@ -86,6 +88,9 @@ func (rm *IDPConfigReadModel) reduceConfigChangedEvent(e *idpconfig.IDPConfigCha
if e.StylingType != nil && e.StylingType.Valid() {
rm.StylingType = *e.StylingType
}
if e.AutoRegister != nil {
rm.AutoRegister = *e.AutoRegister
}
}
func (rm *IDPConfigReadModel) reduceConfigStateChanged(configID string, state domain.IDPConfigState) {

View File

@@ -28,6 +28,7 @@ func NewIDPConfigAddedEvent(
name string,
configType domain.IDPConfigType,
stylingType domain.IDPConfigStylingType,
autoRegister bool,
) *IDPConfigAddedEvent {
return &IDPConfigAddedEvent{
@@ -41,6 +42,7 @@ func NewIDPConfigAddedEvent(
name,
configType,
stylingType,
autoRegister,
),
}
}

View File

@@ -29,10 +29,11 @@ func NewRemoveIDPConfigNameUniqueConstraint(idpConfigName, resourceOwner string)
type IDPConfigAddedEvent struct {
eventstore.BaseEvent `json:"-"`
ConfigID string `json:"idpConfigId"`
Name string `json:"name,omitempty"`
Typ domain.IDPConfigType `json:"idpType,omitempty"`
StylingType domain.IDPConfigStylingType `json:"stylingType,omitempty"`
ConfigID string `json:"idpConfigId"`
Name string `json:"name,omitempty"`
Typ domain.IDPConfigType `json:"idpType,omitempty"`
StylingType domain.IDPConfigStylingType `json:"stylingType,omitempty"`
AutoRegister bool `json:"autoRegister,omitempty"`
}
func NewIDPConfigAddedEvent(
@@ -41,13 +42,15 @@ func NewIDPConfigAddedEvent(
name string,
configType domain.IDPConfigType,
stylingType domain.IDPConfigStylingType,
autoRegister bool,
) *IDPConfigAddedEvent {
return &IDPConfigAddedEvent{
BaseEvent: *base,
ConfigID: configID,
Name: name,
StylingType: stylingType,
Typ: configType,
BaseEvent: *base,
ConfigID: configID,
Name: name,
StylingType: stylingType,
Typ: configType,
AutoRegister: autoRegister,
}
}
@@ -75,10 +78,11 @@ func IDPConfigAddedEventMapper(event *repository.Event) (eventstore.EventReader,
type IDPConfigChangedEvent struct {
eventstore.BaseEvent `json:"-"`
ConfigID string `json:"idpConfigId"`
Name *string `json:"name,omitempty"`
StylingType *domain.IDPConfigStylingType `json:"stylingType,omitempty"`
oldName string `json:"-"`
ConfigID string `json:"idpConfigId"`
Name *string `json:"name,omitempty"`
StylingType *domain.IDPConfigStylingType `json:"stylingType,omitempty"`
AutoRegister *bool `json:"autoRegister,omitempty"`
oldName string `json:"-"`
}
func (e *IDPConfigChangedEvent) Data() interface{} {
@@ -129,6 +133,12 @@ func ChangeStyleType(styleType domain.IDPConfigStylingType) func(*IDPConfigChang
}
}
func ChangeAutoRegister(autoRegister bool) func(*IDPConfigChangedEvent) {
return func(e *IDPConfigChangedEvent) {
e.AutoRegister = &autoRegister
}
}
func IDPConfigChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &IDPConfigChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),

View File

@@ -28,6 +28,7 @@ func NewIDPConfigAddedEvent(
name string,
configType domain.IDPConfigType,
stylingType domain.IDPConfigStylingType,
autoRegister bool,
) *IDPConfigAddedEvent {
return &IDPConfigAddedEvent{
@@ -41,6 +42,7 @@ func NewIDPConfigAddedEvent(
name,
configType,
stylingType,
autoRegister,
),
}
}

View File

@@ -160,7 +160,16 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R
if errors.IsNotFound(err) {
err = nil
}
l.renderExternalNotFoundOption(w, r, authReq, err)
if !idpConfig.AutoRegister {
l.renderExternalNotFoundOption(w, r, authReq, err)
return
}
authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, userAgentID)
if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err)
return
}
l.handleAutoRegister(w, r, authReq)
return
}
l.renderNextStep(w, r, authReq)

View File

@@ -119,7 +119,27 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
return
}
user, externalIDP := l.mapTokenToLoginHumanAndExternalIDP(orgIamPolicy, tokens, idpConfig)
l.renderExternalRegisterOverview(w, r, authReq, orgIamPolicy, user, externalIDP, nil)
if !idpConfig.AutoRegister {
l.renderExternalRegisterOverview(w, r, authReq, orgIamPolicy, user, externalIDP, nil)
return
}
l.registerExternalUser(w, r, authReq, iam, user, externalIDP)
}
func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, iam *iam_model.IAM, user *domain.Human, externalIDP *domain.ExternalIDP) {
resourceOwner := iam.GlobalOrgID
memberRoles := []string{domain.RoleOrgProjectCreator}
if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != resourceOwner {
memberRoles = nil
resourceOwner = authReq.RequestedOrgID
}
_, err := l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, externalIDP, memberRoles)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
}
l.renderNextStep(w, r, authReq)
}
func (l *Login) renderExternalRegisterOverview(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, orgIAMPolicy *iam_model.OrgIAMPolicyView, human *domain.Human, idp *domain.ExternalIDP, err error) {

View File

@@ -223,7 +223,7 @@ func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
authReq, err := l.authRepo.AuthRequestByID(r.Context(), authReq.ID, userAgentID)
if err != nil {
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(err, "APP-sio0W", "could not get authreq"))
l.renderInternalError(w, r, authReq, err)
return
}
if len(authReq.PossibleSteps) == 0 {
@@ -257,6 +257,8 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *
l.renderRegisterOption(w, r, authReq, nil)
case *domain.SelectUserStep:
l.renderUserSelection(w, r, authReq, step)
case *domain.RedirectToExternalIDPStep:
l.handleIDP(w, r, authReq, authReq.SelectedIDPConfigID)
case *domain.InitPasswordStep:
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
case *domain.PasswordStep: