diff --git a/internal/api/grpc/org/v2/org_integration_test.go b/internal/api/grpc/org/v2/org_integration_test.go index d369cbeea7..7276e8d5eb 100644 --- a/internal/api/grpc/org/v2/org_integration_test.go +++ b/internal/api/grpc/org/v2/org_integration_test.go @@ -41,7 +41,7 @@ func TestMain(m *testing.M) { } func TestServer_AddOrganization(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) + idpID := Tester.AddGenericOAuthProvider(t, CTX) tests := []struct { name string diff --git a/internal/api/grpc/session/v2/session_integration_test.go b/internal/api/grpc/session/v2/session_integration_test.go index cc70c75ea7..6818b627e8 100644 --- a/internal/api/grpc/session/v2/session_integration_test.go +++ b/internal/api/grpc/session/v2/session_integration_test.go @@ -361,8 +361,7 @@ func TestServer_CreateSession_webauthn(t *testing.T) { } func TestServer_CreateSession_successfulIntent(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) - + idpID := Tester.AddGenericOAuthProvider(t, CTX) createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ User: &session.CheckUser{ @@ -375,7 +374,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { require.NoError(t, err) verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0) - intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, User.GetUserId(), "id") + intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, CTX, idpID, User.GetUserId(), "id") updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{ SessionId: createResp.GetSessionId(), SessionToken: createResp.GetSessionToken(), @@ -391,9 +390,9 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { } func TestServer_CreateSession_successfulIntent_instant(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) + idpID := Tester.AddGenericOAuthProvider(t, CTX) - intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, User.GetUserId(), "id") + intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, CTX, idpID, User.GetUserId(), "id") createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ User: &session.CheckUser{ @@ -412,7 +411,7 @@ func TestServer_CreateSession_successfulIntent_instant(t *testing.T) { } func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) + idpID := Tester.AddGenericOAuthProvider(t, CTX) createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ @@ -427,7 +426,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0) idpUserID := "id" - intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, "", idpUserID) + intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, CTX, idpID, "", idpUserID) updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{ SessionId: createResp.GetSessionId(), SessionToken: createResp.GetSessionToken(), @@ -440,6 +439,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { }) require.Error(t, err) Tester.CreateUserIDPlink(CTX, User.GetUserId(), idpUserID, idpID, User.GetUserId()) + intentID, token, _, _ = Tester.CreateSuccessfulOAuthIntent(t, CTX, idpID, User.GetUserId(), idpUserID) updateResp, err = Client.SetSession(CTX, &session.SetSessionRequest{ SessionId: createResp.GetSessionId(), SessionToken: createResp.GetSessionToken(), @@ -455,7 +455,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { } func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) + idpID := Tester.AddGenericOAuthProvider(t, CTX) createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ @@ -469,7 +469,7 @@ func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { require.NoError(t, err) verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0) - intentID := Tester.CreateIntent(t, idpID) + intentID := Tester.CreateIntent(t, CTX, idpID) _, err = Client.SetSession(CTX, &session.SetSessionRequest{ SessionId: createResp.GetSessionId(), SessionToken: createResp.GetSessionToken(), diff --git a/internal/api/grpc/user/v2/user.go b/internal/api/grpc/user/v2/user.go index d08df3d8f9..6ff420a44b 100644 --- a/internal/api/grpc/user/v2/user.go +++ b/internal/api/grpc/user/v2/user.go @@ -370,7 +370,7 @@ func (s *Server) StartIdentityProviderIntent(ctx context.Context, req *user.Star } func (s *Server) startIDPIntent(ctx context.Context, idpID string, urls *user.RedirectURLs) (*user.StartIdentityProviderIntentResponse, error) { - intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, urls.GetSuccessUrl(), urls.GetFailureUrl(), authz.GetCtxData(ctx).OrgID) + intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, urls.GetSuccessUrl(), urls.GetFailureUrl(), authz.GetInstance(ctx).InstanceID()) if err != nil { return nil, err } @@ -394,7 +394,7 @@ func (s *Server) startIDPIntent(ctx context.Context, idpID string, urls *user.Re } func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredentials *user.LDAPCredentials) (*user.StartIdentityProviderIntentResponse, error) { - intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, "", "", authz.GetCtxData(ctx).OrgID) + intentWriteModel, details, err := s.command.CreateIntent(ctx, idpID, "", "", authz.GetInstance(ctx).InstanceID()) if err != nil { return nil, err } @@ -473,7 +473,7 @@ func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string } func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.RetrieveIdentityProviderIntentRequest) (_ *user.RetrieveIdentityProviderIntentResponse, err error) { - intent, err := s.command.GetIntentWriteModel(ctx, req.GetIdpIntentId(), authz.GetCtxData(ctx).OrgID) + intent, err := s.command.GetIntentWriteModel(ctx, req.GetIdpIntentId(), "") if err != nil { return nil, err } @@ -481,7 +481,7 @@ func (s *Server) RetrieveIdentityProviderIntent(ctx context.Context, req *user.R return nil, err } if intent.State != domain.IDPIntentStateSucceeded { - return nil, zerrors.ThrowPreconditionFailed(nil, "IDP-Hk38e", "Errors.Intent.NotSucceeded") + return nil, zerrors.ThrowPreconditionFailed(nil, "IDP-nme4gszsvx", "Errors.Intent.NotSucceeded") } return idpIntentToIDPIntentPb(intent, s.idpAlg) } diff --git a/internal/api/grpc/user/v2/user_integration_test.go b/internal/api/grpc/user/v2/user_integration_test.go index 8c070caab0..132ed0b30f 100644 --- a/internal/api/grpc/user/v2/user_integration_test.go +++ b/internal/api/grpc/user/v2/user_integration_test.go @@ -54,7 +54,7 @@ func TestMain(m *testing.M) { } func TestServer_AddHumanUser(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) + idpID := Tester.AddGenericOAuthProvider(t, CTX) type args struct { ctx context.Context req *user.AddHumanUserRequest @@ -1752,7 +1752,7 @@ func TestServer_DeleteUser(t *testing.T) { } func TestServer_AddIDPLink(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) + idpID := Tester.AddGenericOAuthProvider(t, CTX) type args struct { ctx context.Context req *user.AddIDPLinkRequest @@ -1832,10 +1832,13 @@ func TestServer_AddIDPLink(t *testing.T) { } func TestServer_StartIdentityProviderIntent(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) - samlIdpID := Tester.AddSAMLProvider(t) - samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t) - samlPostIdpID := Tester.AddSAMLPostProvider(t) + idpID := Tester.AddGenericOAuthProvider(t, CTX) + orgIdpID := Tester.AddOrgGenericOAuthProvider(t, CTX, Tester.Organisation.ID) + orgResp := Tester.CreateOrganization(IamCTX, fmt.Sprintf("NotDefaultOrg%d", time.Now().UnixNano()), fmt.Sprintf("%d@mouse.com", time.Now().UnixNano())) + notDefaultOrgIdpID := Tester.AddOrgGenericOAuthProvider(t, CTX, orgResp.OrganizationId) + samlIdpID := Tester.AddSAMLProvider(t, CTX) + samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t, CTX) + samlPostIdpID := Tester.AddSAMLPostProvider(t, CTX) type args struct { ctx context.Context req *user.StartIdentityProviderIntentRequest @@ -1880,7 +1883,100 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) { want: want{ details: &object.Details{ ChangeDate: timestamppb.Now(), - ResourceOwner: Tester.Organisation.ID, + ResourceOwner: Tester.Instance.InstanceID(), + }, + url: "https://example.com/oauth/v2/authorize", + parametersEqual: map[string]string{ + "client_id": "clientID", + "prompt": "select_account", + "redirect_uri": "http://" + Tester.Config.ExternalDomain + ":8080/idps/callback", + "response_type": "code", + "scope": "openid profile email", + }, + parametersExisting: []string{"state"}, + }, + wantErr: false, + }, + { + name: "next step oauth auth url, default org", + args: args{ + CTX, + &user.StartIdentityProviderIntentRequest{ + IdpId: orgIdpID, + Content: &user.StartIdentityProviderIntentRequest_Urls{ + Urls: &user.RedirectURLs{ + SuccessUrl: "https://example.com/success", + FailureUrl: "https://example.com/failure", + }, + }, + }, + }, + want: want{ + details: &object.Details{ + ChangeDate: timestamppb.Now(), + ResourceOwner: Tester.Instance.InstanceID(), + }, + url: "https://example.com/oauth/v2/authorize", + parametersEqual: map[string]string{ + "client_id": "clientID", + "prompt": "select_account", + "redirect_uri": "http://" + Tester.Config.ExternalDomain + ":8080/idps/callback", + "response_type": "code", + "scope": "openid profile email", + }, + parametersExisting: []string{"state"}, + }, + wantErr: false, + }, + { + name: "next step oauth auth url, default org", + args: args{ + CTX, + &user.StartIdentityProviderIntentRequest{ + IdpId: notDefaultOrgIdpID, + Content: &user.StartIdentityProviderIntentRequest_Urls{ + Urls: &user.RedirectURLs{ + SuccessUrl: "https://example.com/success", + FailureUrl: "https://example.com/failure", + }, + }, + }, + }, + want: want{ + details: &object.Details{ + ChangeDate: timestamppb.Now(), + ResourceOwner: Tester.Instance.InstanceID(), + }, + url: "https://example.com/oauth/v2/authorize", + parametersEqual: map[string]string{ + "client_id": "clientID", + "prompt": "select_account", + "redirect_uri": "http://" + Tester.Config.ExternalDomain + ":8080/idps/callback", + "response_type": "code", + "scope": "openid profile email", + }, + parametersExisting: []string{"state"}, + }, + wantErr: false, + }, + { + name: "next step oauth auth url org", + args: args{ + CTX, + &user.StartIdentityProviderIntentRequest{ + IdpId: orgIdpID, + Content: &user.StartIdentityProviderIntentRequest_Urls{ + Urls: &user.RedirectURLs{ + SuccessUrl: "https://example.com/success", + FailureUrl: "https://example.com/failure", + }, + }, + }, + }, + want: want{ + details: &object.Details{ + ChangeDate: timestamppb.Now(), + ResourceOwner: Tester.Instance.InstanceID(), }, url: "https://example.com/oauth/v2/authorize", parametersEqual: map[string]string{ @@ -1911,7 +2007,7 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) { want: want{ details: &object.Details{ ChangeDate: timestamppb.Now(), - ResourceOwner: Tester.Organisation.ID, + ResourceOwner: Tester.Instance.InstanceID(), }, url: "http://" + Tester.Config.ExternalDomain + ":8000/sso", parametersExisting: []string{"RelayState", "SAMLRequest"}, @@ -1935,7 +2031,7 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) { want: want{ details: &object.Details{ ChangeDate: timestamppb.Now(), - ResourceOwner: Tester.Organisation.ID, + ResourceOwner: Tester.Instance.InstanceID(), }, url: "http://" + Tester.Config.ExternalDomain + ":8000/sso", parametersExisting: []string{"RelayState", "SAMLRequest"}, @@ -1959,7 +2055,7 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) { want: want{ details: &object.Details{ ChangeDate: timestamppb.Now(), - ResourceOwner: Tester.Organisation.ID, + ResourceOwner: Tester.Instance.InstanceID(), }, postForm: true, }, @@ -1999,13 +2095,13 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) { } func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { - idpID := Tester.AddGenericOAuthProvider(t) - intentID := Tester.CreateIntent(t, idpID) - successfulID, token, changeDate, sequence := Tester.CreateSuccessfulOAuthIntent(t, idpID, "", "id") - successfulWithUserID, WithUsertoken, WithUserchangeDate, WithUsersequence := Tester.CreateSuccessfulOAuthIntent(t, idpID, "user", "id") - ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence := Tester.CreateSuccessfulLDAPIntent(t, idpID, "", "id") - ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence := Tester.CreateSuccessfulLDAPIntent(t, idpID, "user", "id") - samlSuccessfulID, samlToken, samlChangeDate, samlSequence := Tester.CreateSuccessfulSAMLIntent(t, idpID, "", "id") + idpID := Tester.AddGenericOAuthProvider(t, CTX) + intentID := Tester.CreateIntent(t, CTX, idpID) + successfulID, token, changeDate, sequence := Tester.CreateSuccessfulOAuthIntent(t, CTX, idpID, "", "id") + successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence := Tester.CreateSuccessfulOAuthIntent(t, CTX, idpID, "user", "id") + ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence := Tester.CreateSuccessfulLDAPIntent(t, CTX, idpID, "", "id") + ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence := Tester.CreateSuccessfulLDAPIntent(t, CTX, idpID, "user", "id") + samlSuccessfulID, samlToken, samlChangeDate, samlSequence := Tester.CreateSuccessfulSAMLIntent(t, CTX, idpID, "", "id") type args struct { ctx context.Context req *user.RetrieveIdentityProviderIntentRequest @@ -2050,7 +2146,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { want: &user.RetrieveIdentityProviderIntentResponse{ Details: &object.Details{ ChangeDate: timestamppb.New(changeDate), - ResourceOwner: Tester.Organisation.ID, + ResourceOwner: Tester.Instance.InstanceID(), Sequence: sequence, }, IdpInformation: &user.IDPInformation{ @@ -2081,14 +2177,14 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { CTX, &user.RetrieveIdentityProviderIntentRequest{ IdpIntentId: successfulWithUserID, - IdpIntentToken: WithUsertoken, + IdpIntentToken: withUsertoken, }, }, want: &user.RetrieveIdentityProviderIntentResponse{ Details: &object.Details{ - ChangeDate: timestamppb.New(WithUserchangeDate), - ResourceOwner: Tester.Organisation.ID, - Sequence: WithUsersequence, + ChangeDate: timestamppb.New(withUserchangeDate), + ResourceOwner: Tester.Instance.InstanceID(), + Sequence: withUsersequence, }, UserId: "user", IdpInformation: &user.IDPInformation{ @@ -2125,7 +2221,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { want: &user.RetrieveIdentityProviderIntentResponse{ Details: &object.Details{ ChangeDate: timestamppb.New(ldapChangeDate), - ResourceOwner: Tester.Organisation.ID, + ResourceOwner: Tester.Instance.InstanceID(), Sequence: ldapSequence, }, IdpInformation: &user.IDPInformation{ @@ -2170,7 +2266,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { want: &user.RetrieveIdentityProviderIntentResponse{ Details: &object.Details{ ChangeDate: timestamppb.New(ldapWithUserChangeDate), - ResourceOwner: Tester.Organisation.ID, + ResourceOwner: Tester.Instance.InstanceID(), Sequence: ldapWithUserSequence, }, UserId: "user", @@ -2216,7 +2312,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { want: &user.RetrieveIdentityProviderIntentResponse{ Details: &object.Details{ ChangeDate: timestamppb.New(samlChangeDate), - ResourceOwner: Tester.Organisation.ID, + ResourceOwner: Tester.Instance.InstanceID(), Sequence: samlSequence, }, IdpInformation: &user.IDPInformation{ diff --git a/internal/api/idp/idp_integration_test.go b/internal/api/idp/idp_integration_test.go index 8ad76a475d..1ade5d7f3e 100644 --- a/internal/api/idp/idp_integration_test.go +++ b/internal/api/idp/idp_integration_test.go @@ -52,8 +52,8 @@ func TestMain(m *testing.M) { } func TestServer_SAMLCertificate(t *testing.T) { - samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t) - oauthIdpID := Tester.AddGenericOAuthProvider(t) + samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t, CTX) + oauthIdpID := Tester.AddGenericOAuthProvider(t, CTX) type args struct { ctx context.Context @@ -109,8 +109,8 @@ func TestServer_SAMLCertificate(t *testing.T) { } func TestServer_SAMLMetadata(t *testing.T) { - samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t) - oauthIdpID := Tester.AddGenericOAuthProvider(t) + samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t, CTX) + oauthIdpID := Tester.AddGenericOAuthProvider(t, CTX) type args struct { ctx context.Context @@ -167,7 +167,7 @@ func TestServer_SAMLMetadata(t *testing.T) { func TestServer_SAMLACS(t *testing.T) { userHuman := Tester.CreateHumanUser(CTX) - samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t) + samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t, CTX) externalUserID := "test1" linkedExternalUserID := "test2" Tester.CreateUserIDPlink(CTX, userHuman.UserId, linkedExternalUserID, samlRedirectIdpID, linkedExternalUserID) diff --git a/internal/command/idp.go b/internal/command/idp.go index 997a772786..a8b041c335 100644 --- a/internal/command/idp.go +++ b/internal/command/idp.go @@ -4,7 +4,6 @@ import ( "context" "time" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/zerrors" @@ -129,7 +128,8 @@ type AppleProvider struct { IDPOptions idp.Options } -func ExistsIDP(ctx context.Context, filter preparation.FilterToQueryReducer, id, orgID string) (exists bool, err error) { +// ExistsIDPOnOrgOrInstance query first org level IDPs and then instance level IDPs, no check if the IDP is active +func ExistsIDPOnOrgOrInstance(ctx context.Context, filter preparation.FilterToQueryReducer, instanceID, orgID, id string) (exists bool, err error) { writeModel := NewOrgIDPRemoveWriteModel(orgID, id) events, err := filter(ctx, writeModel.Query()) if err != nil { @@ -144,7 +144,7 @@ func ExistsIDP(ctx context.Context, filter preparation.FilterToQueryReducer, id, return writeModel.State.Exists(), nil } - instanceWriteModel := NewInstanceIDPRemoveWriteModel(authz.GetInstance(ctx).InstanceID(), id) + instanceWriteModel := NewInstanceIDPRemoveWriteModel(instanceID, id) events, err = filter(ctx, instanceWriteModel.Query()) if err != nil { return false, err @@ -160,6 +160,23 @@ func ExistsIDP(ctx context.Context, filter preparation.FilterToQueryReducer, id, return instanceWriteModel.State.Exists(), nil } +// ExistsIDP query IDPs only with the ID, no check if the IDP is active +func ExistsIDP(ctx context.Context, filter preparation.FilterToQueryReducer, id string) (exists bool, err error) { + writeModel := NewIDPTypeWriteModel(id) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return false, err + } + if len(events) == 0 { + return false, nil + } + writeModel.AppendEvents(events...) + if err := writeModel.Reduce(); err != nil { + return false, err + } + return writeModel.State.Exists(), nil +} + func IDPProviderWriteModel(ctx context.Context, filter preparation.FilterToQueryReducer, id string) (_ *AllIDPWriteModel, err error) { writeModel := NewIDPTypeWriteModel(id) events, err := filter(ctx, writeModel.Query()) diff --git a/internal/command/idp_intent.go b/internal/command/idp_intent.go index 38fc9c91b4..e79977dc52 100644 --- a/internal/command/idp_intent.go +++ b/internal/command/idp_intent.go @@ -25,7 +25,7 @@ import ( "github.com/zitadel/zitadel/internal/zerrors" ) -func (c *Commands) prepareCreateIntent(writeModel *IDPIntentWriteModel, idpID string, successURL, failureURL string) preparation.Validation { +func (c *Commands) prepareCreateIntent(writeModel *IDPIntentWriteModel, idpID, successURL, failureURL string) preparation.Validation { return func() (_ preparation.CreateCommands, err error) { if idpID == "" { return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-x8j2bk", "Errors.Intent.IDPMissing") @@ -43,12 +43,17 @@ func (c *Commands) prepareCreateIntent(writeModel *IDPIntentWriteModel, idpID st if err != nil { return nil, err } - exists, err := ExistsIDP(ctx, filter, idpID, writeModel.ResourceOwner) + exists, err := ExistsIDP(ctx, filter, idpID) if !exists || err != nil { return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-39n221fs", "Errors.IDPConfig.NotExisting") } return []eventstore.Command{ - idpintent.NewStartedEvent(ctx, writeModel.aggregate, successURL, failureURL, idpID), + idpintent.NewStartedEvent(ctx, + IDPIntentAggregateFromWriteModel(&writeModel.WriteModel), + successURL, + failureURL, + idpID, + ), }, nil }, nil } @@ -64,6 +69,7 @@ func (c *Commands) CreateIntent(ctx context.Context, idpID, successURL, failureU return nil, nil, err } + //nolint: staticcheck cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareCreateIntent(writeModel, idpID, successURL, failureURL)) if err != nil { return nil, nil, err @@ -117,7 +123,7 @@ func (c *Commands) GetActiveIntent(ctx context.Context, intentID string) (*IDPIn return nil, err } if intent.State == domain.IDPIntentStateUnspecified { - return nil, zerrors.ThrowNotFound(nil, "IDP-Hk38e", "Errors.Intent.NotStarted") + return nil, zerrors.ThrowNotFound(nil, "IDP-gy3ctgkqe7", "Errors.Intent.NotStarted") } if intent.State != domain.IDPIntentStateStarted { return nil, zerrors.ThrowInvalidArgument(nil, "IDP-Sfrgs", "Errors.Intent.NotStarted") @@ -166,7 +172,7 @@ func (c *Commands) SucceedIDPIntent(ctx context.Context, writeModel *IDPIntentWr } cmd := idpintent.NewSucceededEvent( ctx, - &idpintent.NewAggregate(writeModel.AggregateID, writeModel.ResourceOwner).Aggregate, + IDPIntentAggregateFromWriteModel(&writeModel.WriteModel), idpInfo, idpUser.GetID(), idpUser.GetPreferredUsername(), @@ -200,7 +206,7 @@ func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPInte } cmd := idpintent.NewSAMLSucceededEvent( ctx, - &idpintent.NewAggregate(writeModel.AggregateID, writeModel.ResourceOwner).Aggregate, + IDPIntentAggregateFromWriteModel(&writeModel.WriteModel), idpInfo, idpUser.GetID(), idpUser.GetPreferredUsername(), @@ -217,7 +223,7 @@ func (c *Commands) SucceedSAMLIDPIntent(ctx context.Context, writeModel *IDPInte func (c *Commands) RequestSAMLIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, requestID string) error { return c.pushAppendAndReduce(ctx, writeModel, idpintent.NewSAMLRequestEvent( ctx, - &idpintent.NewAggregate(writeModel.AggregateID, writeModel.ResourceOwner).Aggregate, + IDPIntentAggregateFromWriteModel(&writeModel.WriteModel), requestID, )) } @@ -241,7 +247,7 @@ func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPInte } cmd := idpintent.NewLDAPSucceededEvent( ctx, - &idpintent.NewAggregate(writeModel.AggregateID, writeModel.ResourceOwner).Aggregate, + IDPIntentAggregateFromWriteModel(&writeModel.WriteModel), idpInfo, idpUser.GetID(), idpUser.GetPreferredUsername(), @@ -258,7 +264,7 @@ func (c *Commands) SucceedLDAPIDPIntent(ctx context.Context, writeModel *IDPInte func (c *Commands) FailIDPIntent(ctx context.Context, writeModel *IDPIntentWriteModel, reason string) error { cmd := idpintent.NewFailedEvent( ctx, - &idpintent.NewAggregate(writeModel.AggregateID, writeModel.ResourceOwner).Aggregate, + IDPIntentAggregateFromWriteModel(&writeModel.WriteModel), reason, ) _, err := c.eventstore.Push(ctx, cmd) diff --git a/internal/command/idp_intent_model.go b/internal/command/idp_intent_model.go index b2242c3832..62794323e1 100644 --- a/internal/command/idp_intent_model.go +++ b/internal/command/idp_intent_model.go @@ -28,8 +28,7 @@ type IDPIntentWriteModel struct { RequestID string Assertion *crypto.CryptoValue - State domain.IDPIntentState - aggregate *eventstore.Aggregate + State domain.IDPIntentState } func NewIDPIntentWriteModel(id, resourceOwner string) *IDPIntentWriteModel { @@ -38,7 +37,6 @@ func NewIDPIntentWriteModel(id, resourceOwner string) *IDPIntentWriteModel { AggregateID: id, ResourceOwner: resourceOwner, }, - aggregate: &idpintent.NewAggregate(id, resourceOwner).Aggregate, } } @@ -121,3 +119,13 @@ func (wm *IDPIntentWriteModel) reduceSAMLRequestEvent(e *idpintent.SAMLRequestEv func (wm *IDPIntentWriteModel) reduceFailedEvent(e *idpintent.FailedEvent) { wm.State = domain.IDPIntentStateFailed } + +func IDPIntentAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate { + return &eventstore.Aggregate{ + Type: idpintent.AggregateType, + Version: idpintent.AggregateVersion, + ID: wm.AggregateID, + ResourceOwner: wm.ResourceOwner, + InstanceID: wm.InstanceID, + } +} diff --git a/internal/command/idp_intent_test.go b/internal/command/idp_intent_test.go index 3043c7848a..8bdbcb4716 100644 --- a/internal/command/idp_intent_test.go +++ b/internal/command/idp_intent_test.go @@ -28,20 +28,21 @@ import ( rep_idp "github.com/zitadel/zitadel/internal/repository/idp" "github.com/zitadel/zitadel/internal/repository/idpintent" "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/zerrors" ) func TestCommands_CreateIntent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(t *testing.T) *eventstore.Eventstore idGenerator id.Generator } type args struct { - ctx context.Context - idpID string - successURL string - failureURL string - resourceOwner string + ctx context.Context + idpID string + successURL string + failureURL string + instanceID string } type res struct { intentID string @@ -57,11 +58,11 @@ func TestCommands_CreateIntent(t *testing.T) { { "error no id generator", fields{ - eventstore: eventstoreExpect(t), + eventstore: expectEventstore(), idGenerator: mock.NewIDGeneratorExpectError(t, zerrors.ThrowInternal(nil, "", "error id")), }, args{ - ctx: authz.SetCtxData(context.Background(), authz.CtxData{OrgID: "ro"}), + ctx: context.Background(), idpID: "", successURL: "https://success.url", failureURL: "https://failure.url", @@ -73,11 +74,11 @@ func TestCommands_CreateIntent(t *testing.T) { { "error no idpID", fields{ - eventstore: eventstoreExpect(t), + eventstore: expectEventstore(), idGenerator: mock.ExpectID(t, "id"), }, args{ - ctx: authz.SetCtxData(context.Background(), authz.CtxData{OrgID: "ro"}), + ctx: context.Background(), idpID: "", successURL: "https://success.url", failureURL: "https://failure.url", @@ -89,11 +90,11 @@ func TestCommands_CreateIntent(t *testing.T) { { "error no successURL", fields{ - eventstore: eventstoreExpect(t), + eventstore: expectEventstore(), idGenerator: mock.ExpectID(t, "id"), }, args{ - ctx: authz.SetCtxData(context.Background(), authz.CtxData{OrgID: "ro"}), + ctx: context.Background(), idpID: "idp", successURL: ":", failureURL: "https://failure.url", @@ -105,11 +106,11 @@ func TestCommands_CreateIntent(t *testing.T) { { "error no failureURL", fields{ - eventstore: eventstoreExpect(t), + eventstore: expectEventstore(), idGenerator: mock.ExpectID(t, "id"), }, args{ - ctx: authz.SetCtxData(context.Background(), authz.CtxData{OrgID: "ro"}), + ctx: context.Background(), idpID: "idp", successURL: "https://success.url", failureURL: ":", @@ -119,18 +120,18 @@ func TestCommands_CreateIntent(t *testing.T) { }, }, { - "error idp not existing", + "error idp not existing org", fields{ - eventstore: eventstoreExpect(t, - expectFilter(), + eventstore: expectEventstore( expectFilter(), expectFilter(), ), idGenerator: mock.ExpectID(t, "id"), }, args{ - ctx: authz.SetCtxData(context.Background(), authz.CtxData{OrgID: "ro"}), + ctx: context.Background(), idpID: "idp", + instanceID: "instance", successURL: "https://success.url", failureURL: "https://failure.url", }, @@ -139,14 +140,33 @@ func TestCommands_CreateIntent(t *testing.T) { }, }, { - "push", + "error idp not existing instance", fields{ - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectFilter(), expectFilter(), + ), + idGenerator: mock.ExpectID(t, "id"), + }, + args{ + ctx: context.Background(), + idpID: "idp", + instanceID: "instance", + successURL: "https://success.url", + failureURL: "https://failure.url", + }, + res{ + err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-39n221fs", "Errors.IDPConfig.NotExisting"), + }, + }, + { + "push, org", + fields{ + eventstore: expectEventstore( + expectFilter(), expectFilter( eventFromEventPusher( - instance.NewOAuthIDPAddedEvent(context.Background(), &instance.NewAggregate("ro").Aggregate, + org.NewOAuthIDPAddedEvent(context.Background(), &org.NewAggregate("org").Aggregate, "idp", "name", "clientID", @@ -170,7 +190,7 @@ func TestCommands_CreateIntent(t *testing.T) { failure, _ := url.Parse("https://failure.url") return idpintent.NewStartedEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, success, failure, "idp", @@ -181,25 +201,131 @@ func TestCommands_CreateIntent(t *testing.T) { idGenerator: mock.ExpectID(t, "id"), }, args{ - ctx: context.Background(), - resourceOwner: "ro", - idpID: "idp", - successURL: "https://success.url", - failureURL: "https://failure.url", + ctx: context.Background(), + instanceID: "instance", + idpID: "idp", + successURL: "https://success.url", + failureURL: "https://failure.url", }, res{ intentID: "id", - details: &domain.ObjectDetails{ResourceOwner: "ro"}, + details: &domain.ObjectDetails{ResourceOwner: "instance"}, + }, + }, + { + "push, instance", + fields{ + eventstore: expectEventstore( + expectFilter(), + expectFilter( + eventFromEventPusher( + instance.NewOAuthIDPAddedEvent(context.Background(), &instance.NewAggregate("instance").Aggregate, + "idp", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + "idAttribute", + nil, + rep_idp.Options{}, + )), + ), + expectPush( + func() eventstore.Command { + success, _ := url.Parse("https://success.url") + failure, _ := url.Parse("https://failure.url") + return idpintent.NewStartedEvent( + context.Background(), + &idpintent.NewAggregate("id", "instance").Aggregate, + success, + failure, + "idp", + ) + }(), + ), + ), + idGenerator: mock.ExpectID(t, "id"), + }, + args{ + ctx: context.Background(), + instanceID: "instance", + idpID: "idp", + successURL: "https://success.url", + failureURL: "https://failure.url", + }, + res{ + intentID: "id", + details: &domain.ObjectDetails{ResourceOwner: "instance"}, + }, + }, + { + "push, instance without org", + fields{ + eventstore: expectEventstore( + expectFilter(), + expectFilter( + eventFromEventPusher( + instance.NewOAuthIDPAddedEvent(context.Background(), &instance.NewAggregate("instance").Aggregate, + "idp", + "name", + "clientID", + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("clientSecret"), + }, + "auth", + "token", + "user", + "idAttribute", + nil, + rep_idp.Options{}, + )), + ), + expectPush( + func() eventstore.Command { + success, _ := url.Parse("https://success.url") + failure, _ := url.Parse("https://failure.url") + return idpintent.NewStartedEvent( + context.Background(), + &idpintent.NewAggregate("id", "instance").Aggregate, + success, + failure, + "idp", + ) + }(), + ), + ), + idGenerator: mock.ExpectID(t, "id"), + }, + args{ + ctx: context.Background(), + instanceID: "instance", + idpID: "idp", + successURL: "https://success.url", + failureURL: "https://failure.url", + }, + res{ + intentID: "id", + details: &domain.ObjectDetails{ResourceOwner: "instance"}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), idGenerator: tt.fields.idGenerator, } - intentWriteModel, details, err := c.CreateIntent(tt.args.ctx, tt.args.idpID, tt.args.successURL, tt.args.failureURL, tt.args.resourceOwner) + intentWriteModel, details, err := c.CreateIntent(tt.args.ctx, tt.args.idpID, tt.args.successURL, tt.args.failureURL, tt.args.instanceID) require.ErrorIs(t, err, tt.res.err) if intentWriteModel != nil { assert.Equal(t, tt.res.intentID, intentWriteModel.AggregateID) @@ -213,7 +339,7 @@ func TestCommands_CreateIntent(t *testing.T) { func TestCommands_AuthFromProvider(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(t *testing.T) *eventstore.Eventstore secretCrypto crypto.EncryptionAlgorithm } type args struct { @@ -238,7 +364,7 @@ func TestCommands_AuthFromProvider(t *testing.T) { "idp not existing", fields{ secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -256,7 +382,7 @@ func TestCommands_AuthFromProvider(t *testing.T) { "idp removed", fields{ secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectFilter( eventFromEventPusherWithInstanceID( "instance", @@ -300,7 +426,7 @@ func TestCommands_AuthFromProvider(t *testing.T) { "oauth auth redirect", fields{ secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectFilter( eventFromEventPusherWithInstanceID( "instance", @@ -360,7 +486,7 @@ func TestCommands_AuthFromProvider(t *testing.T) { "migrated and push", fields{ secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectFilter( eventFromEventPusherWithInstanceID( "instance", @@ -450,7 +576,7 @@ func TestCommands_AuthFromProvider(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), idpConfigEncryption: tt.fields.secretCrypto, } content, redirect, err := c.AuthFromProvider(tt.args.ctx, tt.args.idpID, tt.args.state, tt.args.callbackURL, tt.args.samlRootURL) @@ -463,7 +589,7 @@ func TestCommands_AuthFromProvider(t *testing.T) { func TestCommands_AuthFromProvider_SAML(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(t *testing.T) *eventstore.Eventstore secretCrypto crypto.EncryptionAlgorithm } type args struct { @@ -488,7 +614,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { "saml auth default redirect", fields{ secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectFilter( eventFromEventPusherWithInstanceID( "instance", @@ -534,7 +660,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { failure, _ := url.Parse("https://failure.url") return idpintent.NewStartedEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, success, failure, "idp", @@ -546,7 +672,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { []eventstore.Command{ idpintent.NewSAMLRequestEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, "request", ), }, @@ -572,7 +698,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), idpConfigEncryption: tt.fields.secretCrypto, } content, _, err := c.AuthFromProvider(tt.args.ctx, tt.args.idpID, tt.args.state, tt.args.callbackURL, tt.args.samlRootURL) @@ -595,7 +721,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { func TestCommands_SucceedIDPIntent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(t *testing.T) *eventstore.Eventstore idpConfigEncryption crypto.EncryptionAlgorithm } type args struct { @@ -623,6 +749,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) { m.EXPECT().Encrypt(gomock.Any()).Return(nil, zerrors.ThrowInternal(nil, "id", "encryption failed")) return m }(), + eventstore: expectEventstore(), }, args{ ctx: context.Background(), @@ -643,6 +770,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) { m.EXPECT().Encrypt(gomock.Any()).Return(nil, zerrors.ThrowInternal(nil, "id", "encryption failed")) return m }(), + eventstore: expectEventstore(), }, args{ ctx: context.Background(), @@ -663,12 +791,12 @@ func TestCommands_SucceedIDPIntent(t *testing.T) { "push", fields{ idpConfigEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectPush( func() eventstore.Command { event := idpintent.NewSucceededEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, []byte(`{"sub":"id","preferred_username":"username"}`), "id", "username", @@ -688,7 +816,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) { }, args{ ctx: context.Background(), - writeModel: NewIDPIntentWriteModel("id", "ro"), + writeModel: NewIDPIntentWriteModel("id", "instance"), idpSession: &openid.Session{ Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{ Token: &oauth2.Token{ @@ -712,7 +840,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), idpConfigEncryption: tt.fields.idpConfigEncryption, } got, err := c.SucceedIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.idpSession, tt.args.userID) @@ -724,7 +852,7 @@ func TestCommands_SucceedIDPIntent(t *testing.T) { func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(t *testing.T) *eventstore.Eventstore idpConfigEncryption crypto.EncryptionAlgorithm } type args struct { @@ -752,6 +880,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { m.EXPECT().Encrypt(gomock.Any()).Return(nil, zerrors.ThrowInternal(nil, "id", "encryption failed")) return m }(), + eventstore: expectEventstore(), }, args{ ctx: context.Background(), @@ -765,11 +894,11 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { "push", fields{ idpConfigEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectPush( idpintent.NewSAMLSucceededEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, []byte(`{"sub":"id","preferred_username":"username"}`), "id", "username", @@ -786,7 +915,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { }, args{ ctx: context.Background(), - writeModel: NewIDPIntentWriteModel("id", "ro"), + writeModel: NewIDPIntentWriteModel("id", "instance"), assertion: &saml.Assertion{ID: "id"}, idpUser: openid.NewUser(&oidc.UserInfo{ Subject: "id", @@ -803,11 +932,11 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { "push with userID", fields{ idpConfigEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectPush( idpintent.NewSAMLSucceededEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, []byte(`{"sub":"id","preferred_username":"username"}`), "id", "username", @@ -824,7 +953,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { }, args{ ctx: context.Background(), - writeModel: NewIDPIntentWriteModel("id", "ro"), + writeModel: NewIDPIntentWriteModel("id", "instance"), assertion: &saml.Assertion{ID: "id"}, idpUser: openid.NewUser(&oidc.UserInfo{ Subject: "id", @@ -842,7 +971,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), idpConfigEncryption: tt.fields.idpConfigEncryption, } got, err := c.SucceedSAMLIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.assertion) @@ -854,7 +983,7 @@ func TestCommands_SucceedSAMLIDPIntent(t *testing.T) { func TestCommands_RequestSAMLIDPIntent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(t *testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -873,11 +1002,11 @@ func TestCommands_RequestSAMLIDPIntent(t *testing.T) { { "push", fields{ - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectPush( idpintent.NewSAMLRequestEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, "request", ), ), @@ -885,7 +1014,7 @@ func TestCommands_RequestSAMLIDPIntent(t *testing.T) { }, args{ ctx: context.Background(), - writeModel: NewIDPIntentWriteModel("id", "ro"), + writeModel: NewIDPIntentWriteModel("id", "instance"), request: "request", }, res{}, @@ -894,7 +1023,7 @@ func TestCommands_RequestSAMLIDPIntent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } err := c.RequestSAMLIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.request) require.ErrorIs(t, err, tt.res.err) @@ -905,7 +1034,7 @@ func TestCommands_RequestSAMLIDPIntent(t *testing.T) { func TestCommands_SucceedLDAPIDPIntent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(t *testing.T) *eventstore.Eventstore idpConfigEncryption crypto.EncryptionAlgorithm } type args struct { @@ -933,10 +1062,11 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) { m.EXPECT().Encrypt(gomock.Any()).Return(nil, zerrors.ThrowInternal(nil, "id", "encryption failed")) return m }(), + eventstore: expectEventstore(), }, args{ ctx: context.Background(), - writeModel: NewIDPIntentWriteModel("id", "ro"), + writeModel: NewIDPIntentWriteModel("id", "instance"), }, res{ err: zerrors.ThrowInternal(nil, "id", "encryption failed"), @@ -946,11 +1076,11 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) { "push", fields{ idpConfigEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectPush( idpintent.NewLDAPSucceededEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, []byte(`{"id":"id","preferredUsername":"username","preferredLanguage":"und"}`), "id", "username", @@ -962,7 +1092,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) { }, args{ ctx: context.Background(), - writeModel: NewIDPIntentWriteModel("id", "ro"), + writeModel: NewIDPIntentWriteModel("id", "instance"), attributes: map[string][]string{"id": {"id"}}, idpUser: ldap.NewUser( "id", @@ -988,7 +1118,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), idpConfigEncryption: tt.fields.idpConfigEncryption, } got, err := c.SucceedLDAPIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.idpUser, tt.args.userID, tt.args.attributes) @@ -1000,7 +1130,7 @@ func TestCommands_SucceedLDAPIDPIntent(t *testing.T) { func TestCommands_FailIDPIntent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(t *testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -1019,11 +1149,11 @@ func TestCommands_FailIDPIntent(t *testing.T) { { "push", fields{ - eventstore: eventstoreExpect(t, + eventstore: expectEventstore( expectPush( idpintent.NewFailedEvent( context.Background(), - &idpintent.NewAggregate("id", "ro").Aggregate, + &idpintent.NewAggregate("id", "instance").Aggregate, "reason", ), ), @@ -1031,7 +1161,7 @@ func TestCommands_FailIDPIntent(t *testing.T) { }, args{ ctx: context.Background(), - writeModel: NewIDPIntentWriteModel("id", "ro"), + writeModel: NewIDPIntentWriteModel("id", "instance"), reason: "reason", }, res{ @@ -1042,7 +1172,7 @@ func TestCommands_FailIDPIntent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } err := c.FailIDPIntent(tt.args.ctx, tt.args.writeModel, tt.args.reason) require.ErrorIs(t, err, tt.res.err) diff --git a/internal/command/org_policy_login.go b/internal/command/org_policy_login.go index cf5bb7d1e5..a7b2378ec3 100644 --- a/internal/command/org_policy_login.go +++ b/internal/command/org_policy_login.go @@ -416,7 +416,7 @@ func prepareAddLoginPolicy(a *org.Aggregate, policy *AddLoginPolicy) preparation return nil, zerrors.ThrowAlreadyExists(nil, "Org-Dgfb2", "Errors.Org.LoginPolicy.AlreadyExists") } for _, idp := range policy.IDPProviders { - exists, err := ExistsIDP(ctx, filter, idp.ConfigID, authz.GetCtxData(ctx).OrgID) + exists, err := ExistsIDPOnOrgOrInstance(ctx, filter, authz.GetInstance(ctx).InstanceID(), a.ResourceOwner, idp.ConfigID) if !exists || err != nil { return nil, zerrors.ThrowPreconditionFailed(err, "Org-FEd32", "Errors.IDPConfig.NotExisting") } diff --git a/internal/command/session_test.go b/internal/command/session_test.go index 16308432b3..58f5f73706 100644 --- a/internal/command/session_test.go +++ b/internal/command/session_test.go @@ -683,7 +683,8 @@ func TestCommands_updateSession(t *testing.T) { "username", "", "", "", "", language.English, domain.GenderUnspecified, "", false), ), eventFromEventPusher( - idpintent.NewSucceededEvent(context.Background(), &idpintent.NewAggregate("intent", "org1").Aggregate, + idpintent.NewSucceededEvent(context.Background(), + &idpintent.NewAggregate("id", "instance1").Aggregate, nil, "idpUserID", "idpUserName", @@ -775,7 +776,8 @@ func TestCommands_updateSession(t *testing.T) { "username", "", "", "", "", language.English, domain.GenderUnspecified, "", false), ), eventFromEventPusher( - idpintent.NewSucceededEvent(context.Background(), &idpintent.NewAggregate("intent", "org1").Aggregate, + idpintent.NewSucceededEvent(context.Background(), + &idpintent.NewAggregate("id", "instance1").Aggregate, nil, "idpUserID", "idpUsername", diff --git a/internal/command/user_human.go b/internal/command/user_human.go index 4a7ef97add..41ce13f950 100644 --- a/internal/command/user_human.go +++ b/internal/command/user_human.go @@ -6,6 +6,7 @@ import ( "golang.org/x/text/language" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" @@ -294,7 +295,7 @@ func (c *Commands) addHumanCommandEmail(ctx context.Context, filter preparation. } func addLink(ctx context.Context, filter preparation.FilterToQueryReducer, a *user.Aggregate, link *AddLink) (eventstore.Command, error) { - exists, err := ExistsIDP(ctx, filter, link.IDPID, a.ResourceOwner) + exists, err := ExistsIDPOnOrgOrInstance(ctx, filter, authz.GetInstance(ctx).InstanceID(), a.ResourceOwner, link.IDPID) if !exists || err != nil { return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-39nf2", "Errors.IDPConfig.NotExisting") } diff --git a/internal/integration/client.go b/internal/integration/client.go index cb22e5d552..3ebd4fea51 100644 --- a/internal/integration/client.go +++ b/internal/integration/client.go @@ -288,8 +288,8 @@ func (s *Tester) SetUserPassword(ctx context.Context, userID, password string, c logging.OnError(err).Fatal("set user password") } -func (s *Tester) AddGenericOAuthProvider(t *testing.T) string { - ctx := authz.WithInstance(context.Background(), s.Instance) +func (s *Tester) AddGenericOAuthProvider(t *testing.T, ctx context.Context) string { + ctx = authz.WithInstance(ctx, s.Instance) id, _, err := s.Commands.AddInstanceGenericOAuthProvider(ctx, command.GenericOAuthProvider{ Name: "idp", ClientID: "clientID", @@ -310,8 +310,31 @@ func (s *Tester) AddGenericOAuthProvider(t *testing.T) string { return id } -func (s *Tester) AddSAMLProvider(t *testing.T) string { - ctx := authz.WithInstance(context.Background(), s.Instance) +func (s *Tester) AddOrgGenericOAuthProvider(t *testing.T, ctx context.Context, orgID string) string { + ctx = authz.WithInstance(ctx, s.Instance) + id, _, err := s.Commands.AddOrgGenericOAuthProvider(ctx, orgID, + command.GenericOAuthProvider{ + Name: "idp", + ClientID: "clientID", + ClientSecret: "clientSecret", + AuthorizationEndpoint: "https://example.com/oauth/v2/authorize", + TokenEndpoint: "https://example.com/oauth/v2/token", + UserEndpoint: "https://api.example.com/user", + Scopes: []string{"openid", "profile", "email"}, + IDAttribute: "id", + IDPOptions: idp.Options{ + IsLinkingAllowed: true, + IsCreationAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + }, + }) + require.NoError(t, err) + return id +} + +func (s *Tester) AddSAMLProvider(t *testing.T, ctx context.Context) string { + ctx = authz.WithInstance(ctx, s.Instance) id, _, err := s.Server.Commands.AddInstanceSAMLProvider(ctx, command.SAMLProvider{ Name: "saml-idp", Metadata: []byte("\n \n \n \n \n MIIDBzCCAe+gAwIBAgIJAPr/Mrlc8EGhMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xNTEyMjgxOTE5NDVaFw0yNTEyMjUxOTE5NDVaMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANDoWzLos4LWxTn8Gyu2lEbl4WcelUbgLN5zYm4ron8Ahs+rvcsu2zkdD/s6jdGJI8WqJKhYK2u61ygnXgAZqC6ggtFPnBpizcDzjgND2g+aucSoUODHt67f0fQuAmupN/zp5MZysJ6IHLJnYLNpfJYk96lRz9ODnO1Mpqtr9PWxm+pz7nzq5F0vRepkgpcRxv6ufQBjlrFytccyEVdXrvFtkjXcnhVVNSR4kHuOOMS6D7pebSJ1mrCmshbD5SX1jXPBKFPAjozYX6PxqLxUx1Y4faFEf4MBBVcInyB4oURNB2s59hEEi2jq9izNE7EbEK6BY5sEhoCPl9m32zE6ljkCAwEAAaNQME4wHQYDVR0OBBYEFB9ZklC1Ork2zl56zg08ei7ss/+iMB8GA1UdIwQYMBaAFB9ZklC1Ork2zl56zg08ei7ss/+iMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAVoTSQ5pAirw8OR9FZ1bRSuTDhY9uxzl/OL7lUmsv2cMNeCB3BRZqm3mFt+cwN8GsH6f3uvNONIhgFpTGN5LEcXQz89zJEzB+qaHqmbFpHQl/sx2B8ezNgT/882H2IH00dXESEfy/+1gHg2pxjGnhRBN6el/gSaDiySIMKbilDrffuvxiCfbpPN0NRRiPJhd2ay9KuL/RxQRl1gl9cHaWiouWWba1bSBb2ZPhv2rPMUsFo98ntkGCObDX6Y1SpkqmoTbrsbGFsTG2DLxnvr4GdN1BSr0Uu/KV3adj47WkXVPeMYQti/bQmxQB8tRFhrw80qakTLUzreO96WzlBBMtY=\n \n \n \n \n \n \n MIIDBzCCAe+gAwIBAgIJAPr/Mrlc8EGhMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xNTEyMjgxOTE5NDVaFw0yNTEyMjUxOTE5NDVaMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANDoWzLos4LWxTn8Gyu2lEbl4WcelUbgLN5zYm4ron8Ahs+rvcsu2zkdD/s6jdGJI8WqJKhYK2u61ygnXgAZqC6ggtFPnBpizcDzjgND2g+aucSoUODHt67f0fQuAmupN/zp5MZysJ6IHLJnYLNpfJYk96lRz9ODnO1Mpqtr9PWxm+pz7nzq5F0vRepkgpcRxv6ufQBjlrFytccyEVdXrvFtkjXcnhVVNSR4kHuOOMS6D7pebSJ1mrCmshbD5SX1jXPBKFPAjozYX6PxqLxUx1Y4faFEf4MBBVcInyB4oURNB2s59hEEi2jq9izNE7EbEK6BY5sEhoCPl9m32zE6ljkCAwEAAaNQME4wHQYDVR0OBBYEFB9ZklC1Ork2zl56zg08ei7ss/+iMB8GA1UdIwQYMBaAFB9ZklC1Ork2zl56zg08ei7ss/+iMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAVoTSQ5pAirw8OR9FZ1bRSuTDhY9uxzl/OL7lUmsv2cMNeCB3BRZqm3mFt+cwN8GsH6f3uvNONIhgFpTGN5LEcXQz89zJEzB+qaHqmbFpHQl/sx2B8ezNgT/882H2IH00dXESEfy/+1gHg2pxjGnhRBN6el/gSaDiySIMKbilDrffuvxiCfbpPN0NRRiPJhd2ay9KuL/RxQRl1gl9cHaWiouWWba1bSBb2ZPhv2rPMUsFo98ntkGCObDX6Y1SpkqmoTbrsbGFsTG2DLxnvr4GdN1BSr0Uu/KV3adj47WkXVPeMYQti/bQmxQB8tRFhrw80qakTLUzreO96WzlBBMtY=\n \n \n \n \n \n \n \n urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n \n \n \n"), @@ -326,8 +349,8 @@ func (s *Tester) AddSAMLProvider(t *testing.T) string { return id } -func (s *Tester) AddSAMLRedirectProvider(t *testing.T) string { - ctx := authz.WithInstance(context.Background(), s.Instance) +func (s *Tester) AddSAMLRedirectProvider(t *testing.T, ctx context.Context) string { + ctx = authz.WithInstance(ctx, s.Instance) id, _, err := s.Server.Commands.AddInstanceSAMLProvider(ctx, command.SAMLProvider{ Name: "saml-idp-redirect", Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", @@ -343,8 +366,8 @@ func (s *Tester) AddSAMLRedirectProvider(t *testing.T) string { return id } -func (s *Tester) AddSAMLPostProvider(t *testing.T) string { - ctx := authz.WithInstance(context.Background(), s.Instance) +func (s *Tester) AddSAMLPostProvider(t *testing.T, ctx context.Context) string { + ctx = authz.WithInstance(ctx, s.Instance) id, _, err := s.Server.Commands.AddInstanceSAMLProvider(ctx, command.SAMLProvider{ Name: "saml-idp-post", Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", @@ -360,17 +383,17 @@ func (s *Tester) AddSAMLPostProvider(t *testing.T) string { return id } -func (s *Tester) CreateIntent(t *testing.T, idpID string) string { - ctx := authz.WithInstance(context.Background(), s.Instance) - writeModel, _, err := s.Commands.CreateIntent(ctx, idpID, "https://example.com/success", "https://example.com/failure", s.Organisation.ID) +func (s *Tester) CreateIntent(t *testing.T, ctx context.Context, idpID string) string { + ctx = authz.WithInstance(context.WithoutCancel(ctx), s.Instance) + writeModel, _, err := s.Commands.CreateIntent(ctx, idpID, "https://example.com/success", "https://example.com/failure", s.Instance.InstanceID()) require.NoError(t, err) return writeModel.AggregateID } -func (s *Tester) CreateSuccessfulOAuthIntent(t *testing.T, idpID, userID, idpUserID string) (string, string, time.Time, uint64) { - ctx := authz.WithInstance(context.Background(), s.Instance) - intentID := s.CreateIntent(t, idpID) - writeModel, err := s.Commands.GetIntentWriteModel(ctx, intentID, s.Organisation.ID) +func (s *Tester) CreateSuccessfulOAuthIntent(t *testing.T, ctx context.Context, idpID, userID, idpUserID string) (string, string, time.Time, uint64) { + ctx = authz.WithInstance(context.WithoutCancel(ctx), s.Instance) + intentID := s.CreateIntent(t, ctx, idpID) + writeModel, err := s.Commands.GetIntentWriteModel(ctx, intentID, s.Instance.InstanceID()) require.NoError(t, err) idpUser := openid.NewUser( &oidc.UserInfo{ @@ -393,10 +416,10 @@ func (s *Tester) CreateSuccessfulOAuthIntent(t *testing.T, idpID, userID, idpUse return intentID, token, writeModel.ChangeDate, writeModel.ProcessedSequence } -func (s *Tester) CreateSuccessfulLDAPIntent(t *testing.T, idpID, userID, idpUserID string) (string, string, time.Time, uint64) { - ctx := authz.WithInstance(context.Background(), s.Instance) - intentID := s.CreateIntent(t, idpID) - writeModel, err := s.Commands.GetIntentWriteModel(ctx, intentID, s.Organisation.ID) +func (s *Tester) CreateSuccessfulLDAPIntent(t *testing.T, ctx context.Context, idpID, userID, idpUserID string) (string, string, time.Time, uint64) { + ctx = authz.WithInstance(context.WithoutCancel(ctx), s.Instance) + intentID := s.CreateIntent(t, ctx, idpID) + writeModel, err := s.Commands.GetIntentWriteModel(ctx, intentID, s.Instance.InstanceID()) require.NoError(t, err) username := "username" lang := language.Make("en") @@ -421,10 +444,10 @@ func (s *Tester) CreateSuccessfulLDAPIntent(t *testing.T, idpID, userID, idpUser return intentID, token, writeModel.ChangeDate, writeModel.ProcessedSequence } -func (s *Tester) CreateSuccessfulSAMLIntent(t *testing.T, idpID, userID, idpUserID string) (string, string, time.Time, uint64) { - ctx := authz.WithInstance(context.Background(), s.Instance) - intentID := s.CreateIntent(t, idpID) - writeModel, err := s.Server.Commands.GetIntentWriteModel(ctx, intentID, s.Organisation.ID) +func (s *Tester) CreateSuccessfulSAMLIntent(t *testing.T, ctx context.Context, idpID, userID, idpUserID string) (string, string, time.Time, uint64) { + ctx = authz.WithInstance(context.WithoutCancel(ctx), s.Instance) + intentID := s.CreateIntent(t, ctx, idpID) + writeModel, err := s.Server.Commands.GetIntentWriteModel(ctx, intentID, s.Instance.InstanceID()) require.NoError(t, err) idpUser := &saml.UserMapper{