diff --git a/cmd/start/start.go b/cmd/start/start.go index 4091213d2d..470aa24e3d 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -143,10 +143,6 @@ type Server struct { func startZitadel(ctx context.Context, config *Config, masterKey string, server chan<- *Server) error { showBasicInformation(config) - // sink Server is stubbed out in production builds, see function's godoc. - closeSink := sink.StartServer() - defer closeSink() - i18n.MustLoadSupportedLanguagesFromDir() dbClient, err := database.Connect(config.Database, false) @@ -254,6 +250,10 @@ func startZitadel(ctx context.Context, config *Config, masterKey string, server } defer commands.Close(ctx) // wait for background jobs + // sink Server is stubbed out in production builds, see function's godoc. + closeSink := sink.StartServer(commands) + defer closeSink() + clock := clockpkg.New() actionsExecutionStdoutEmitter, err := logstore.NewEmitter[*record.ExecutionLog](ctx, clock, &logstore.EmitterConfig{Enabled: config.LogStore.Execution.Stdout.Enabled}, stdout.NewStdoutEmitter[*record.ExecutionLog]()) if err != nil { diff --git a/internal/api/grpc/session/v2/integration_test/server_test.go b/internal/api/grpc/session/v2/integration_test/server_test.go index 70e2146069..6ea2b4dbda 100644 --- a/internal/api/grpc/session/v2/integration_test/server_test.go +++ b/internal/api/grpc/session/v2/integration_test/server_test.go @@ -19,6 +19,7 @@ var ( CTX context.Context IAMOwnerCTX context.Context UserCTX context.Context + LoginCTX context.Context Instance *integration.Instance Client session.SessionServiceClient User *user.AddHumanUserResponse @@ -37,6 +38,7 @@ func TestMain(m *testing.M) { CTX = Instance.WithAuthorization(ctx, integration.UserTypeOrgOwner) IAMOwnerCTX = Instance.WithAuthorization(ctx, integration.UserTypeIAMOwner) UserCTX = Instance.WithAuthorization(ctx, integration.UserTypeNoPermission) + LoginCTX = Instance.WithAuthorization(ctx, integration.UserTypeLogin) User = createFullUser(CTX) DeactivatedUser = createDeactivatedUser(CTX) LockedUser = createLockedUser(CTX) diff --git a/internal/api/grpc/session/v2/integration_test/session_test.go b/internal/api/grpc/session/v2/integration_test/session_test.go index 7622550b15..b9a060c749 100644 --- a/internal/api/grpc/session/v2/integration_test/session_test.go +++ b/internal/api/grpc/session/v2/integration_test/session_test.go @@ -21,6 +21,7 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/internal/integration/sink" mgmt "github.com/zitadel/zitadel/pkg/grpc/management" "github.com/zitadel/zitadel/pkg/grpc/object/v2" "github.com/zitadel/zitadel/pkg/grpc/session/v2" @@ -339,10 +340,9 @@ func TestServer_CreateSession_webauthn(t *testing.T) { verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId(), wantUserFactor, wantWebAuthNFactorUserVerified) } -/* func TestServer_CreateSession_successfulIntent(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) - createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ + idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId() + createResp, err := Client.CreateSession(LoginCTX, &session.CreateSessionRequest{ Checks: &session.Checks{ User: &session.CheckUser{ Search: &session.CheckUser_UserId{ @@ -354,8 +354,9 @@ 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, User.GetUserId()) - intentID, token, _, _ := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID, User.GetUserId(), "id") - updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{ + intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId()) + require.NoError(t, err) + updateResp, err := Client.SetSession(LoginCTX, &session.SetSessionRequest{ SessionId: createResp.GetSessionId(), Checks: &session.Checks{ IdpIntent: &session.CheckIDPIntent{ @@ -369,9 +370,10 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { } func TestServer_CreateSession_successfulIntent_instant(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) + idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId() - intentID, token, _, _ := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID, User.GetUserId(), "id") + intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId()) + require.NoError(t, err) createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ User: &session.CheckUser{ @@ -390,11 +392,11 @@ func TestServer_CreateSession_successfulIntent_instant(t *testing.T) { } func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) + idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId() // successful intent without known / linked user idpUserID := "id" - intentID, token, _, _ := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID, "", idpUserID) + intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, idpUserID, "") // link the user (with info from intent) Instance.CreateUserIDPlink(CTX, User.GetUserId(), idpUserID, idpID, User.GetUserId()) @@ -418,7 +420,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { } func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) + idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId() createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ @@ -432,19 +434,18 @@ 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, User.GetUserId()) - intentID := Instance.CreateIntent(t, CTX, idpID) + intent := Instance.CreateIntent(CTX, idpID) _, err = Client.SetSession(CTX, &session.SetSessionRequest{ SessionId: createResp.GetSessionId(), Checks: &session.Checks{ IdpIntent: &session.CheckIDPIntent{ - IdpIntentId: intentID, + IdpIntentId: intent.GetIdpIntent().GetIdpIntentId(), IdpIntentToken: "false", }, }, }) require.Error(t, err) } -*/ func registerTOTP(ctx context.Context, t *testing.T, userID string) (secret string) { resp, err := Instance.Client.UserV2.RegisterTOTP(ctx, &user.RegisterTOTPRequest{ diff --git a/internal/api/grpc/session/v2beta/integration_test/session_test.go b/internal/api/grpc/session/v2beta/integration_test/session_test.go index 26d2291629..d0fc1179ef 100644 --- a/internal/api/grpc/session/v2beta/integration_test/session_test.go +++ b/internal/api/grpc/session/v2beta/integration_test/session_test.go @@ -21,6 +21,7 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/internal/integration/sink" mgmt "github.com/zitadel/zitadel/pkg/grpc/management" object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta" session "github.com/zitadel/zitadel/pkg/grpc/session/v2beta" @@ -339,9 +340,8 @@ func TestServer_CreateSession_webauthn(t *testing.T) { verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, User.GetUserId(), wantUserFactor, wantWebAuthNFactorUserVerified) } -/* func TestServer_CreateSession_successfulIntent(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) + idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId() createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ User: &session.CheckUser{ @@ -354,7 +354,8 @@ 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, User.GetUserId()) - intentID, token, _, _ := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID, User.GetUserId(), "id") + intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId()) + require.NoError(t, err) updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{ SessionId: createResp.GetSessionId(), Checks: &session.Checks{ @@ -369,9 +370,10 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) { } func TestServer_CreateSession_successfulIntent_instant(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) + idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId() - intentID, token, _, _ := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID, User.GetUserId(), "id") + intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId()) + require.NoError(t, err) createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ User: &session.CheckUser{ @@ -390,11 +392,12 @@ func TestServer_CreateSession_successfulIntent_instant(t *testing.T) { } func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) + idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId() // successful intent without known / linked user idpUserID := "id" - intentID, token, _, _ := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID, "", idpUserID) + intentID, token, _, _, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", User.GetUserId()) + require.NoError(t, err) // link the user (with info from intent) Instance.CreateUserIDPlink(CTX, User.GetUserId(), idpUserID, idpID, User.GetUserId()) @@ -418,7 +421,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) { } func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) + idpID := Instance.AddGenericOAuthProvider(IAMOwnerCTX, gofakeit.AppName()).GetId() createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{ Checks: &session.Checks{ @@ -432,19 +435,18 @@ 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, User.GetUserId()) - intentID := Instance.CreateIntent(t, CTX, idpID) + intent := Instance.CreateIntent(CTX, idpID) _, err = Client.SetSession(CTX, &session.SetSessionRequest{ SessionId: createResp.GetSessionId(), Checks: &session.Checks{ IdpIntent: &session.CheckIDPIntent{ - IdpIntentId: intentID, + IdpIntentId: intent.GetIdpIntent().GetIdpIntentId(), IdpIntentToken: "false", }, }, }) require.Error(t, err) } -*/ func registerTOTP(ctx context.Context, t *testing.T, userID string) (secret string) { resp, err := Instance.Client.UserV2.RegisterTOTP(ctx, &user.RegisterTOTPRequest{ diff --git a/internal/api/grpc/user/v2/integration_test/user_test.go b/internal/api/grpc/user/v2/integration_test/user_test.go index 0ed93fd92e..6d5d112e98 100644 --- a/internal/api/grpc/user/v2/integration_test/user_test.go +++ b/internal/api/grpc/user/v2/integration_test/user_test.go @@ -10,16 +10,20 @@ import ( "testing" "time" + "github.com/zitadel/logging" + "google.golang.org/protobuf/types/known/structpb" + "github.com/brianvoe/gofakeit/v6" "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/zitadel/logging" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/zitadel/zitadel/internal/api/grpc" "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/internal/integration/sink" "github.com/zitadel/zitadel/pkg/grpc/auth" "github.com/zitadel/zitadel/pkg/grpc/idp" mgmt "github.com/zitadel/zitadel/pkg/grpc/management" @@ -2110,15 +2114,20 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) { } } -/* func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) - intentID := Instance.CreateIntent(t, CTX, idpID) - successfulID, token, changeDate, sequence := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID, "", "id") - successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID, "user", "id") - ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence := Instance.CreateSuccessfulLDAPIntent(t, CTX, idpID, "", "id") - ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence := Instance.CreateSuccessfulLDAPIntent(t, CTX, idpID, "user", "id") - samlSuccessfulID, samlToken, samlChangeDate, samlSequence := Instance.CreateSuccessfulSAMLIntent(t, CTX, idpID, "", "id") + idpID := Instance.AddGenericOAuthProvider(IamCTX, gofakeit.AppName()).GetId() + intentID := Instance.CreateIntent(CTX, idpID).GetIdpIntent().GetIdpIntentId() + + successfulID, token, changeDate, sequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", "") + require.NoError(t, err) + successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", "user") + require.NoError(t, err) + ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence, err := sink.SuccessfulLDAPIntent(Instance.ID(), idpID, "id", "") + require.NoError(t, err) + ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence, err := sink.SuccessfulLDAPIntent(Instance.ID(), idpID, "id", "user") + require.NoError(t, err) + samlSuccessfulID, samlToken, samlChangeDate, samlSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), idpID, "id", "") + require.NoError(t, err) type args struct { ctx context.Context req *user.RetrieveIdentityProviderIntentRequest @@ -2369,7 +2378,6 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { }) } } -*/ func ctxFromNewUserWithRegisteredPasswordlessLegacy(t *testing.T) (context.Context, string, *auth.AddMyPasswordlessResponse) { userID := Instance.CreateHumanUser(CTX).GetUserId() diff --git a/internal/api/grpc/user/v2beta/integration_test/user_test.go b/internal/api/grpc/user/v2beta/integration_test/user_test.go index 9cf59ae563..ab2e3215ee 100644 --- a/internal/api/grpc/user/v2beta/integration_test/user_test.go +++ b/internal/api/grpc/user/v2beta/integration_test/user_test.go @@ -16,9 +16,12 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/zitadel/zitadel/internal/api/grpc" "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/internal/integration/sink" "github.com/zitadel/zitadel/pkg/grpc/idp" mgmt "github.com/zitadel/zitadel/pkg/grpc/management" object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta" @@ -2142,15 +2145,19 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) { } } -/* func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { - idpID := Instance.AddGenericOAuthProvider(t, CTX) - intentID := Instance.CreateIntent(t, CTX, idpID) - successfulID, token, changeDate, sequence := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID.Id, "", "id") - successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence := Instance.CreateSuccessfulOAuthIntent(t, CTX, idpID.Id, "user", "id") - ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence := Instance.CreateSuccessfulLDAPIntent(t, CTX, idpID.Id, "", "id") - ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence := Instance.CreateSuccessfulLDAPIntent(t, CTX, idpID.Id, "user", "id") - samlSuccessfulID, samlToken, samlChangeDate, samlSequence := Instance.CreateSuccessfulSAMLIntent(t, CTX, idpID.Id, "", "id") + idpID := Instance.AddGenericOAuthProvider(IamCTX, gofakeit.AppName()).GetId() + intentID := Instance.CreateIntent(CTX, idpID).GetIdpIntent().GetIdpIntentId() + successfulID, token, changeDate, sequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", "") + require.NoError(t, err) + successfulWithUserID, withUsertoken, withUserchangeDate, withUsersequence, err := sink.SuccessfulOAuthIntent(Instance.ID(), idpID, "id", "user") + require.NoError(t, err) + ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence, err := sink.SuccessfulLDAPIntent(Instance.ID(), idpID, "id", "") + require.NoError(t, err) + ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence, err := sink.SuccessfulLDAPIntent(Instance.ID(), idpID, "id", "user") + require.NoError(t, err) + samlSuccessfulID, samlToken, samlChangeDate, samlSequence, err := sink.SuccessfulSAMLIntent(Instance.ID(), idpID, "id", "") + require.NoError(t, err) type args struct { ctx context.Context req *user.RetrieveIdentityProviderIntentRequest @@ -2205,7 +2212,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { IdToken: gu.Ptr("idToken"), }, }, - IdpId: idpID.Id, + IdpId: idpID, UserId: "id", UserName: "username", RawInformation: func() *structpb.Struct { @@ -2243,7 +2250,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { IdToken: gu.Ptr("idToken"), }, }, - IdpId: idpID.Id, + IdpId: idpID, UserId: "id", UserName: "username", RawInformation: func() *structpb.Struct { @@ -2287,7 +2294,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { }(), }, }, - IdpId: idpID.Id, + IdpId: idpID, UserId: "id", UserName: "username", RawInformation: func() *structpb.Struct { @@ -2333,7 +2340,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { }(), }, }, - IdpId: idpID.Id, + IdpId: idpID, UserId: "id", UserName: "username", RawInformation: func() *structpb.Struct { @@ -2370,7 +2377,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { Assertion: []byte(""), }, }, - IdpId: idpID.Id, + IdpId: idpID, UserId: "id", UserName: "", RawInformation: func() *structpb.Struct { @@ -2401,7 +2408,6 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) { }) } } -*/ func TestServer_ListAuthenticationMethodTypes(t *testing.T) { userIDWithoutAuth := Instance.CreateHumanUser(CTX).GetUserId() diff --git a/internal/integration/client.go b/internal/integration/client.go index cefaf0ef42..a480a86ce0 100644 --- a/internal/integration/client.go +++ b/internal/integration/client.go @@ -526,101 +526,20 @@ func (i *Instance) AddSAMLPostProvider(ctx context.Context) string { return resp.GetId() } -/* -func (s *Instance) CreateIntent(t *testing.T, ctx context.Context, idpID string) string { - resp, err := i.Client.UserV2.StartIdentityProviderIntent(ctx, &user.StartIdentityProviderIntentRequest{ +func (i *Instance) CreateIntent(ctx context.Context, idpID string) *user_v2.StartIdentityProviderIntentResponse { + resp, err := i.Client.UserV2.StartIdentityProviderIntent(ctx, &user_v2.StartIdentityProviderIntentRequest{ IdpId: idpID, - Content: &user.StartIdentityProviderIntentRequest_Urls{ - Urls: &user.RedirectURLs{ + Content: &user_v2.StartIdentityProviderIntentRequest_Urls{ + Urls: &user_v2.RedirectURLs{ SuccessUrl: "https://example.com/success", FailureUrl: "https://example.com/failure", }, - AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, }, }) logging.OnError(err).Fatal("create generic OAuth idp") return resp } -func (i *Instance) 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 (i *Instance) 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{ - Subject: idpUserID, - UserInfoProfile: oidc.UserInfoProfile{ - PreferredUsername: "username", - }, - }, - ) - idpSession := &openid.Session{ - Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{ - Token: &oauth2.Token{ - AccessToken: "accessToken", - }, - IDToken: "idToken", - }, - } - token, err := s.Commands.SucceedIDPIntent(ctx, writeModel, idpUser, idpSession, userID) - require.NoError(t, err) - return intentID, token, writeModel.ChangeDate, writeModel.ProcessedSequence -} - -func (s *Instance) 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") - idpUser := ldap.NewUser( - idpUserID, - "", - "", - "", - "", - username, - "", - false, - "", - false, - lang, - "", - "", - ) - attributes := map[string][]string{"id": {idpUserID}, "username": {username}, "language": {lang.String()}} - token, err := s.Commands.SucceedLDAPIDPIntent(ctx, writeModel, idpUser, userID, attributes) - require.NoError(t, err) - return intentID, token, writeModel.ChangeDate, writeModel.ProcessedSequence -} - -func (s *Instance) 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{ - ID: idpUserID, - Attributes: map[string][]string{"attribute1": {"value1"}}, - } - assertion := &crewjam_saml.Assertion{ID: "id"} - - token, err := s.Server.Commands.SucceedSAMLIDPIntent(ctx, writeModel, idpUser, userID, assertion) - require.NoError(t, err) - return intentID, token, writeModel.ChangeDate, writeModel.ProcessedSequence -} -*/ - func (i *Instance) CreateVerifiedWebAuthNSession(t *testing.T, ctx context.Context, userID string) (id, token string, start, change time.Time) { return i.CreateVerifiedWebAuthNSessionWithLifetime(t, ctx, userID, 0) } diff --git a/internal/integration/sink/server.go b/internal/integration/sink/server.go index 959353ae5f..eea7c893b6 100644 --- a/internal/integration/sink/server.go +++ b/internal/integration/sink/server.go @@ -3,6 +3,9 @@ package sink import ( + "bytes" + "context" + "encoding/json" "errors" "io" "net/http" @@ -10,11 +13,23 @@ import ( "path" "sync" "sync/atomic" + "time" "github.com/go-chi/chi/v5" "github.com/gorilla/websocket" "github.com/sirupsen/logrus" "github.com/zitadel/logging" + "github.com/zitadel/oidc/v3/pkg/oidc" + "golang.org/x/oauth2" + "golang.org/x/text/language" + + crewjam_saml "github.com/crewjam/saml" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command" + "github.com/zitadel/zitadel/internal/idp/providers/ldap" + openid "github.com/zitadel/zitadel/internal/idp/providers/oidc" + "github.com/zitadel/zitadel/internal/idp/providers/saml" ) const ( @@ -33,6 +48,60 @@ func CallURL(ch Channel) string { return u.String() } +func SuccessfulOAuthIntent(instanceID, idpID, idpUserID, userID string) (string, string, time.Time, uint64, error) { + u := url.URL{ + Scheme: "http", + Host: host, + Path: successfulIntentOAuthPath(), + } + resp, err := callIntent(u.String(), &SuccessfulIntentRequest{ + InstanceID: instanceID, + IDPID: idpID, + IDPUserID: idpUserID, + UserID: userID, + }) + if err != nil { + return "", "", time.Time{}, uint64(0), err + } + return resp.IntentID, resp.Token, resp.ChangeDate, resp.Sequence, nil +} + +func SuccessfulSAMLIntent(instanceID, idpID, idpUserID, userID string) (string, string, time.Time, uint64, error) { + u := url.URL{ + Scheme: "http", + Host: host, + Path: successfulIntentSAMLPath(), + } + resp, err := callIntent(u.String(), &SuccessfulIntentRequest{ + InstanceID: instanceID, + IDPID: idpID, + IDPUserID: idpUserID, + UserID: userID, + }) + if err != nil { + return "", "", time.Time{}, uint64(0), err + } + return resp.IntentID, resp.Token, resp.ChangeDate, resp.Sequence, nil +} + +func SuccessfulLDAPIntent(instanceID, idpID, idpUserID, userID string) (string, string, time.Time, uint64, error) { + u := url.URL{ + Scheme: "http", + Host: host, + Path: successfulIntentLDAPPath(), + } + resp, err := callIntent(u.String(), &SuccessfulIntentRequest{ + InstanceID: instanceID, + IDPID: idpID, + IDPUserID: idpUserID, + UserID: userID, + }) + if err != nil { + return "", "", time.Time{}, uint64(0), err + } + return resp.IntentID, resp.Token, resp.ChangeDate, resp.Sequence, nil +} + // StartServer starts a simple HTTP server on localhost:8081 // ZITADEL can use the server to send HTTP requests which can be // used to validate tests through [Subscribe]rs. @@ -41,7 +110,7 @@ func CallURL(ch Channel) string { // [CallURL] can be used to obtain the full URL for a given Channel. // // This function is only active when the `integration` build tag is enabled -func StartServer() (close func()) { +func StartServer(commands *command.Commands) (close func()) { router := chi.NewRouter() for _, ch := range ChannelValues() { fwd := &forwarder{ @@ -50,6 +119,9 @@ func StartServer() (close func()) { } router.HandleFunc(rootPath(ch), fwd.receiveHandler) router.HandleFunc(subscribePath(ch), fwd.subscriptionHandler) + router.HandleFunc(successfulIntentOAuthPath(), successfulIntentHandler(commands, createSuccessfulOAuthIntent)) + router.HandleFunc(successfulIntentSAMLPath(), successfulIntentHandler(commands, createSuccessfulSAMLIntent)) + router.HandleFunc(successfulIntentLDAPPath(), successfulIntentHandler(commands, createSuccessfulLDAPIntent)) } s := &http.Server{ Addr: listenAddr, @@ -76,6 +148,26 @@ func subscribePath(c Channel) string { return path.Join("/", c.String(), "subscribe") } +func intentPath() string { + return path.Join("/", "intent") +} + +func successfulIntentPath() string { + return path.Join(intentPath(), "/", "successful") +} + +func successfulIntentOAuthPath() string { + return path.Join(successfulIntentPath(), "/", "oauth") +} + +func successfulIntentSAMLPath() string { + return path.Join(successfulIntentPath(), "/", "saml") +} + +func successfulIntentLDAPPath() string { + return path.Join(successfulIntentPath(), "/", "ldap") +} + // forwarder handles incoming HTTP requests from ZITADEL and // forwards them to all subscribed web sockets. type forwarder struct { @@ -165,3 +257,165 @@ func readLoop(ws *websocket.Conn) (done chan error) { return done } + +type SuccessfulIntentRequest struct { + InstanceID string `json:"instance_id"` + IDPID string `json:"idp_id"` + IDPUserID string `json:"idp_user_id"` + UserID string `json:"user_id"` +} +type SuccessfulIntentResponse struct { + IntentID string `json:"intent_id"` + Token string `json:"token"` + ChangeDate time.Time `json:"change_date"` + Sequence uint64 `json:"sequence"` +} + +func callIntent(url string, req *SuccessfulIntentRequest) (*SuccessfulIntentResponse, error) { + data, err := json.Marshal(req) + if err != nil { + return nil, err + } + + resp, err := http.Post(url, "application/json", io.NopCloser(bytes.NewReader(data))) + if err != nil { + return nil, err + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, errors.New(string(body)) + } + result := new(SuccessfulIntentResponse) + if err := json.Unmarshal(body, result); err != nil { + return nil, err + } + return result, nil +} + +func successfulIntentHandler(cmd *command.Commands, createIntent func(ctx context.Context, cmd *command.Commands, req *SuccessfulIntentRequest) (*SuccessfulIntentResponse, error)) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + req := &SuccessfulIntentRequest{} + if err := json.Unmarshal(body, req); err != nil { + } + + ctx := authz.WithInstanceID(r.Context(), req.InstanceID) + resp, err := createIntent(ctx, cmd, req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + data, err := json.Marshal(resp) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + w.Write(data) + return + } +} + +func createIntent(ctx context.Context, cmd *command.Commands, instanceID, idpID string) (string, error) { + writeModel, _, err := cmd.CreateIntent(ctx, idpID, "https://example.com/success", "https://example.com/failure", instanceID) + if err != nil { + return "", err + } + return writeModel.AggregateID, nil +} + +func createSuccessfulOAuthIntent(ctx context.Context, cmd *command.Commands, req *SuccessfulIntentRequest) (*SuccessfulIntentResponse, error) { + intentID, err := createIntent(ctx, cmd, req.InstanceID, req.IDPID) + writeModel, err := cmd.GetIntentWriteModel(ctx, intentID, req.InstanceID) + idpUser := openid.NewUser( + &oidc.UserInfo{ + Subject: req.IDPUserID, + UserInfoProfile: oidc.UserInfoProfile{ + PreferredUsername: "username", + }, + }, + ) + idpSession := &openid.Session{ + Tokens: &oidc.Tokens[*oidc.IDTokenClaims]{ + Token: &oauth2.Token{ + AccessToken: "accessToken", + }, + IDToken: "idToken", + }, + } + token, err := cmd.SucceedIDPIntent(ctx, writeModel, idpUser, idpSession, req.UserID) + if err != nil { + return nil, err + } + return &SuccessfulIntentResponse{ + intentID, + token, + writeModel.ChangeDate, + writeModel.ProcessedSequence, + }, nil +} + +func createSuccessfulSAMLIntent(ctx context.Context, cmd *command.Commands, req *SuccessfulIntentRequest) (*SuccessfulIntentResponse, error) { + intentID, err := createIntent(ctx, cmd, req.InstanceID, req.IDPID) + writeModel, err := cmd.GetIntentWriteModel(ctx, intentID, req.InstanceID) + + idpUser := &saml.UserMapper{ + ID: req.IDPUserID, + Attributes: map[string][]string{"attribute1": {"value1"}}, + } + assertion := &crewjam_saml.Assertion{ID: "id"} + + token, err := cmd.SucceedSAMLIDPIntent(ctx, writeModel, idpUser, req.UserID, assertion) + if err != nil { + return nil, err + } + return &SuccessfulIntentResponse{ + intentID, + token, + writeModel.ChangeDate, + writeModel.ProcessedSequence, + }, nil +} + +func createSuccessfulLDAPIntent(ctx context.Context, cmd *command.Commands, req *SuccessfulIntentRequest) (*SuccessfulIntentResponse, error) { + intentID, err := createIntent(ctx, cmd, req.InstanceID, req.IDPID) + writeModel, err := cmd.GetIntentWriteModel(ctx, intentID, req.InstanceID) + username := "username" + lang := language.Make("en") + idpUser := ldap.NewUser( + req.IDPUserID, + "", + "", + "", + "", + username, + "", + false, + "", + false, + lang, + "", + "", + ) + attributes := map[string][]string{"id": {req.IDPUserID}, "username": {username}, "language": {lang.String()}} + token, err := cmd.SucceedLDAPIDPIntent(ctx, writeModel, idpUser, req.UserID, attributes) + if err != nil { + return nil, err + } + return &SuccessfulIntentResponse{ + intentID, + token, + writeModel.ChangeDate, + writeModel.ProcessedSequence, + }, nil +} diff --git a/internal/integration/sink/stub.go b/internal/integration/sink/stub.go index 01d1047f34..62e1d541e1 100644 --- a/internal/integration/sink/stub.go +++ b/internal/integration/sink/stub.go @@ -2,8 +2,10 @@ package sink +import "github.com/zitadel/zitadel/internal/command" + // StartServer and its returned close function are a no-op // when the `integration` build tag is disabled. -func StartServer() (close func()) { +func StartServer(cmd *command.Commands) (close func()) { return func() {} }