fix: allow start and retrieve IdP intents with IdPs from other organizations (#7871)

* fix: correct resourceowner of intent to instance

* fix: correct resourceowner of intent to instance

* fix: correct resourceowner of intent to instance

* fix: correct resourceowner of intent to instance

* fix: correct resourceowner of intent to instance

* docs: expand the login example with org specific parameters

* fix: existence of idp is not checked through resourceowner

* fix: existence of idp is not checked through resourceowner

* fix: existence of idp is not checked through resourceowner

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Stefan Benz 2024-05-07 08:11:20 +02:00 committed by GitHub
parent 5bf195d374
commit 72c5b057f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 439 additions and 156 deletions

View File

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

View File

@ -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(),

View File

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

View File

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

View File

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

View File

@ -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())

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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("<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" validUntil=\"2023-09-16T09:00:32.986Z\" cacheDuration=\"PT48H\" entityID=\"http://localhost:8000/metadata\">\n <IDPSSODescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n <KeyDescriptor use=\"signing\">\n <KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">\n <X509Data xmlns=\"http://www.w3.org/2000/09/xmldsig#\">\n <X509Certificate xmlns=\"http://www.w3.org/2000/09/xmldsig#\">MIIDBzCCAe+gAwIBAgIJAPr/Mrlc8EGhMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xNTEyMjgxOTE5NDVaFw0yNTEyMjUxOTE5NDVaMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANDoWzLos4LWxTn8Gyu2lEbl4WcelUbgLN5zYm4ron8Ahs+rvcsu2zkdD/s6jdGJI8WqJKhYK2u61ygnXgAZqC6ggtFPnBpizcDzjgND2g+aucSoUODHt67f0fQuAmupN/zp5MZysJ6IHLJnYLNpfJYk96lRz9ODnO1Mpqtr9PWxm+pz7nzq5F0vRepkgpcRxv6ufQBjlrFytccyEVdXrvFtkjXcnhVVNSR4kHuOOMS6D7pebSJ1mrCmshbD5SX1jXPBKFPAjozYX6PxqLxUx1Y4faFEf4MBBVcInyB4oURNB2s59hEEi2jq9izNE7EbEK6BY5sEhoCPl9m32zE6ljkCAwEAAaNQME4wHQYDVR0OBBYEFB9ZklC1Ork2zl56zg08ei7ss/+iMB8GA1UdIwQYMBaAFB9ZklC1Ork2zl56zg08ei7ss/+iMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAVoTSQ5pAirw8OR9FZ1bRSuTDhY9uxzl/OL7lUmsv2cMNeCB3BRZqm3mFt+cwN8GsH6f3uvNONIhgFpTGN5LEcXQz89zJEzB+qaHqmbFpHQl/sx2B8ezNgT/882H2IH00dXESEfy/+1gHg2pxjGnhRBN6el/gSaDiySIMKbilDrffuvxiCfbpPN0NRRiPJhd2ay9KuL/RxQRl1gl9cHaWiouWWba1bSBb2ZPhv2rPMUsFo98ntkGCObDX6Y1SpkqmoTbrsbGFsTG2DLxnvr4GdN1BSr0Uu/KV3adj47WkXVPeMYQti/bQmxQB8tRFhrw80qakTLUzreO96WzlBBMtY=</X509Certificate>\n </X509Data>\n </KeyInfo>\n </KeyDescriptor>\n <KeyDescriptor use=\"encryption\">\n <KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">\n <X509Data xmlns=\"http://www.w3.org/2000/09/xmldsig#\">\n <X509Certificate xmlns=\"http://www.w3.org/2000/09/xmldsig#\">MIIDBzCCAe+gAwIBAgIJAPr/Mrlc8EGhMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xNTEyMjgxOTE5NDVaFw0yNTEyMjUxOTE5NDVaMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANDoWzLos4LWxTn8Gyu2lEbl4WcelUbgLN5zYm4ron8Ahs+rvcsu2zkdD/s6jdGJI8WqJKhYK2u61ygnXgAZqC6ggtFPnBpizcDzjgND2g+aucSoUODHt67f0fQuAmupN/zp5MZysJ6IHLJnYLNpfJYk96lRz9ODnO1Mpqtr9PWxm+pz7nzq5F0vRepkgpcRxv6ufQBjlrFytccyEVdXrvFtkjXcnhVVNSR4kHuOOMS6D7pebSJ1mrCmshbD5SX1jXPBKFPAjozYX6PxqLxUx1Y4faFEf4MBBVcInyB4oURNB2s59hEEi2jq9izNE7EbEK6BY5sEhoCPl9m32zE6ljkCAwEAAaNQME4wHQYDVR0OBBYEFB9ZklC1Ork2zl56zg08ei7ss/+iMB8GA1UdIwQYMBaAFB9ZklC1Ork2zl56zg08ei7ss/+iMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAVoTSQ5pAirw8OR9FZ1bRSuTDhY9uxzl/OL7lUmsv2cMNeCB3BRZqm3mFt+cwN8GsH6f3uvNONIhgFpTGN5LEcXQz89zJEzB+qaHqmbFpHQl/sx2B8ezNgT/882H2IH00dXESEfy/+1gHg2pxjGnhRBN6el/gSaDiySIMKbilDrffuvxiCfbpPN0NRRiPJhd2ay9KuL/RxQRl1gl9cHaWiouWWba1bSBb2ZPhv2rPMUsFo98ntkGCObDX6Y1SpkqmoTbrsbGFsTG2DLxnvr4GdN1BSr0Uu/KV3adj47WkXVPeMYQti/bQmxQB8tRFhrw80qakTLUzreO96WzlBBMtY=</X509Certificate>\n </X509Data>\n </KeyInfo>\n <EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#aes128-cbc\"></EncryptionMethod>\n <EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#aes192-cbc\"></EncryptionMethod>\n <EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#aes256-cbc\"></EncryptionMethod>\n <EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p\"></EncryptionMethod>\n </KeyDescriptor>\n <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>\n <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"http://localhost:8000/sso\"></SingleSignOnService>\n <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8000/sso\"></SingleSignOnService>\n </IDPSSODescriptor>\n</EntityDescriptor>"),
@ -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{