2023-07-10 13:27:00 +00:00
|
|
|
package integration
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2024-08-17 14:48:06 +00:00
|
|
|
"io"
|
2023-07-10 13:27:00 +00:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2023-12-05 17:01:03 +00:00
|
|
|
"github.com/brianvoe/gofakeit/v6"
|
2023-10-17 15:19:51 +00:00
|
|
|
"github.com/zitadel/oidc/v3/pkg/client"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/client/rs"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
2023-12-05 17:01:03 +00:00
|
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
2023-07-10 13:27:00 +00:00
|
|
|
|
|
|
|
http_util "github.com/zitadel/zitadel/internal/api/http"
|
|
|
|
oidc_internal "github.com/zitadel/zitadel/internal/api/oidc"
|
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/app"
|
2023-12-05 17:01:03 +00:00
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/authn"
|
2023-07-10 13:27:00 +00:00
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/management"
|
2023-12-05 17:01:03 +00:00
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/user"
|
2024-09-17 13:21:49 +00:00
|
|
|
user_v2 "github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
2023-07-10 13:27:00 +00:00
|
|
|
)
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string, appType app.OIDCAppType, authMethod app.OIDCAuthMethodType, devMode bool, grantTypes ...app.OIDCGrantType) (*management.AddOIDCAppResponse, error) {
|
2024-03-20 10:18:46 +00:00
|
|
|
if len(grantTypes) == 0 {
|
|
|
|
grantTypes = []app.OIDCGrantType{app.OIDCGrantType_OIDC_GRANT_TYPE_AUTHORIZATION_CODE, app.OIDCGrantType_OIDC_GRANT_TYPE_REFRESH_TOKEN}
|
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
resp, err := i.Client.Mgmt.AddOIDCApp(ctx, &management.AddOIDCAppRequest{
|
2023-07-14 11:16:16 +00:00
|
|
|
ProjectId: projectID,
|
2023-07-10 13:27:00 +00:00
|
|
|
Name: fmt.Sprintf("app-%d", time.Now().UnixNano()),
|
|
|
|
RedirectUris: []string{redirectURI},
|
|
|
|
ResponseTypes: []app.OIDCResponseType{app.OIDCResponseType_OIDC_RESPONSE_TYPE_CODE},
|
2024-03-20 10:18:46 +00:00
|
|
|
GrantTypes: grantTypes,
|
2023-12-05 17:01:03 +00:00
|
|
|
AppType: appType,
|
|
|
|
AuthMethodType: authMethod,
|
2023-07-19 11:17:39 +00:00
|
|
|
PostLogoutRedirectUris: []string{logoutRedirectURI},
|
2023-07-10 13:27:00 +00:00
|
|
|
Version: app.OIDCVersion_OIDC_VERSION_1_0,
|
2023-12-28 09:25:18 +00:00
|
|
|
DevMode: devMode,
|
2023-07-10 13:27:00 +00:00
|
|
|
AccessTokenType: app.OIDCTokenType_OIDC_TOKEN_TYPE_JWT,
|
|
|
|
AccessTokenRoleAssertion: false,
|
|
|
|
IdTokenRoleAssertion: false,
|
|
|
|
IdTokenUserinfoAssertion: false,
|
|
|
|
ClockSkew: nil,
|
|
|
|
AdditionalOrigins: nil,
|
|
|
|
SkipNativeAppSuccessPage: false,
|
|
|
|
})
|
2024-08-17 14:48:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, await(func() error {
|
2024-10-23 07:36:50 +00:00
|
|
|
_, err := i.Client.Mgmt.GetProjectByID(ctx, &management.GetProjectByIDRequest{
|
|
|
|
Id: projectID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = i.Client.Mgmt.GetAppByID(ctx, &management.GetAppByIDRequest{
|
2024-08-17 14:48:06 +00:00
|
|
|
ProjectId: projectID,
|
|
|
|
AppId: resp.GetAppId(),
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
2023-07-10 13:27:00 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCNativeClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string, devMode bool) (*management.AddOIDCAppResponse, error) {
|
|
|
|
return i.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_NATIVE, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_NONE, devMode)
|
2023-12-05 17:01:03 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCWebClientBasic(ctx context.Context, redirectURI, logoutRedirectURI, projectID string) (*management.AddOIDCAppResponse, error) {
|
|
|
|
return i.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_WEB, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_BASIC, false)
|
2023-12-05 17:01:03 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCWebClientJWT(ctx context.Context, redirectURI, logoutRedirectURI, projectID string, grantTypes ...app.OIDCGrantType) (client *management.AddOIDCAppResponse, keyData []byte, err error) {
|
|
|
|
client, err = i.CreateOIDCClient(ctx, redirectURI, logoutRedirectURI, projectID, app.OIDCAppType_OIDC_APP_TYPE_WEB, app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, false, grantTypes...)
|
2023-12-05 17:01:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
key, err := i.Client.Mgmt.AddAppKey(ctx, &management.AddAppKeyRequest{
|
2023-12-05 17:01:03 +00:00
|
|
|
ProjectId: projectID,
|
|
|
|
AppId: client.GetAppId(),
|
|
|
|
Type: authn.KeyType_KEY_TYPE_JSON,
|
|
|
|
ExpirationDate: timestamppb.New(time.Now().Add(time.Hour)),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
mustAwait(func() error {
|
|
|
|
_, err := i.Client.Mgmt.GetAppByID(ctx, &management.GetAppByIDRequest{
|
|
|
|
ProjectId: projectID,
|
|
|
|
AppId: client.GetAppId(),
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
2023-12-05 17:01:03 +00:00
|
|
|
return client, key.GetKeyDetails(), nil
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCInactivateClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string) (*management.AddOIDCAppResponse, error) {
|
|
|
|
client, err := i.CreateOIDCNativeClient(ctx, redirectURI, logoutRedirectURI, projectID, false)
|
2023-12-05 17:01:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
_, err = i.Client.Mgmt.DeactivateApp(ctx, &management.DeactivateAppRequest{
|
2023-12-05 17:01:03 +00:00
|
|
|
ProjectId: projectID,
|
|
|
|
AppId: client.GetAppId(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return client, err
|
|
|
|
}
|
|
|
|
|
2024-09-17 11:34:14 +00:00
|
|
|
func (i *Instance) CreateOIDCInactivateProjectClient(ctx context.Context, redirectURI, logoutRedirectURI, projectID string) (*management.AddOIDCAppResponse, error) {
|
|
|
|
client, err := i.CreateOIDCNativeClient(ctx, redirectURI, logoutRedirectURI, projectID, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
_, err = i.Client.Mgmt.DeactivateProject(ctx, &management.DeactivateProjectRequest{
|
|
|
|
Id: projectID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return client, err
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCImplicitFlowClient(ctx context.Context, redirectURI string) (*management.AddOIDCAppResponse, error) {
|
|
|
|
project, err := i.Client.Mgmt.AddProject(ctx, &management.AddProjectRequest{
|
2023-07-10 13:27:00 +00:00
|
|
|
Name: fmt.Sprintf("project-%d", time.Now().UnixNano()),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
resp, err := i.Client.Mgmt.AddOIDCApp(ctx, &management.AddOIDCAppRequest{
|
2023-07-10 13:27:00 +00:00
|
|
|
ProjectId: project.GetId(),
|
|
|
|
Name: fmt.Sprintf("app-%d", time.Now().UnixNano()),
|
|
|
|
RedirectUris: []string{redirectURI},
|
|
|
|
ResponseTypes: []app.OIDCResponseType{app.OIDCResponseType_OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN},
|
|
|
|
GrantTypes: []app.OIDCGrantType{app.OIDCGrantType_OIDC_GRANT_TYPE_IMPLICIT},
|
|
|
|
AppType: app.OIDCAppType_OIDC_APP_TYPE_USER_AGENT,
|
|
|
|
AuthMethodType: app.OIDCAuthMethodType_OIDC_AUTH_METHOD_TYPE_NONE,
|
|
|
|
PostLogoutRedirectUris: nil,
|
|
|
|
Version: app.OIDCVersion_OIDC_VERSION_1_0,
|
|
|
|
DevMode: true,
|
|
|
|
AccessTokenType: app.OIDCTokenType_OIDC_TOKEN_TYPE_JWT,
|
|
|
|
AccessTokenRoleAssertion: false,
|
|
|
|
IdTokenRoleAssertion: false,
|
|
|
|
IdTokenUserinfoAssertion: false,
|
|
|
|
ClockSkew: nil,
|
|
|
|
AdditionalOrigins: nil,
|
|
|
|
SkipNativeAppSuccessPage: false,
|
|
|
|
})
|
2024-08-17 14:48:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return resp, await(func() error {
|
2024-10-23 07:36:50 +00:00
|
|
|
_, err := i.Client.Mgmt.GetProjectByID(ctx, &management.GetProjectByIDRequest{
|
|
|
|
Id: project.GetId(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = i.Client.Mgmt.GetAppByID(ctx, &management.GetAppByIDRequest{
|
2024-08-17 14:48:06 +00:00
|
|
|
ProjectId: project.GetId(),
|
|
|
|
AppId: resp.GetAppId(),
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
2023-07-10 13:27:00 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCTokenExchangeClient(ctx context.Context) (client *management.AddOIDCAppResponse, keyData []byte, err error) {
|
|
|
|
project, err := i.Client.Mgmt.AddProject(ctx, &management.AddProjectRequest{
|
2024-03-20 10:18:46 +00:00
|
|
|
Name: fmt.Sprintf("project-%d", time.Now().UnixNano()),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
return i.CreateOIDCWebClientJWT(ctx, "", "", project.GetId(), app.OIDCGrantType_OIDC_GRANT_TYPE_TOKEN_EXCHANGE, app.OIDCGrantType_OIDC_GRANT_TYPE_AUTHORIZATION_CODE, app.OIDCGrantType_OIDC_GRANT_TYPE_REFRESH_TOKEN)
|
2024-03-20 10:18:46 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateProject(ctx context.Context) (*management.AddProjectResponse, error) {
|
|
|
|
return i.Client.Mgmt.AddProject(ctx, &management.AddProjectRequest{
|
2023-07-14 11:16:16 +00:00
|
|
|
Name: fmt.Sprintf("project-%d", time.Now().UnixNano()),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateAPIClientJWT(ctx context.Context, projectID string) (*management.AddAPIAppResponse, error) {
|
|
|
|
return i.Client.Mgmt.AddAPIApp(ctx, &management.AddAPIAppRequest{
|
2023-07-14 11:16:16 +00:00
|
|
|
ProjectId: projectID,
|
|
|
|
Name: fmt.Sprintf("api-%d", time.Now().UnixNano()),
|
|
|
|
AuthMethodType: app.APIAuthMethodType_API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateAPIClientBasic(ctx context.Context, projectID string) (*management.AddAPIAppResponse, error) {
|
|
|
|
return i.Client.Mgmt.AddAPIApp(ctx, &management.AddAPIAppRequest{
|
2023-12-28 13:31:41 +00:00
|
|
|
ProjectId: projectID,
|
|
|
|
Name: fmt.Sprintf("api-%d", time.Now().UnixNano()),
|
|
|
|
AuthMethodType: app.APIAuthMethodType_API_AUTH_METHOD_TYPE_BASIC,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-05 17:01:03 +00:00
|
|
|
const CodeVerifier = "codeVerifier"
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCAuthRequest(ctx context.Context, clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) {
|
|
|
|
return i.CreateOIDCAuthRequestWithDomain(ctx, i.Domain, clientID, loginClient, redirectURI, scope...)
|
2024-06-12 04:49:14 +00:00
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCAuthRequestWithDomain(ctx context.Context, domain, clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) {
|
|
|
|
provider, err := i.CreateRelyingPartyForDomain(ctx, domain, clientID, redirectURI, scope...)
|
2023-07-10 13:27:00 +00:00
|
|
|
if err != nil {
|
2024-09-06 12:47:57 +00:00
|
|
|
return "", fmt.Errorf("create relying party: %w", err)
|
2023-07-10 13:27:00 +00:00
|
|
|
}
|
2023-12-05 17:01:03 +00:00
|
|
|
codeChallenge := oidc.NewSHACodeChallenge(CodeVerifier)
|
2023-07-10 13:27:00 +00:00
|
|
|
authURL := rp.AuthURL("state", provider, rp.WithCodeChallenge(codeChallenge))
|
|
|
|
|
2023-09-29 09:26:14 +00:00
|
|
|
req, err := GetRequest(authURL, map[string]string{oidc_internal.LoginClientHeader: loginClient})
|
|
|
|
if err != nil {
|
2024-09-06 12:47:57 +00:00
|
|
|
return "", fmt.Errorf("get request: %w", err)
|
2023-09-29 09:26:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
loc, err := CheckRedirect(req)
|
2023-07-10 13:27:00 +00:00
|
|
|
if err != nil {
|
2024-09-06 12:47:57 +00:00
|
|
|
return "", fmt.Errorf("check redirect: %w", err)
|
2023-07-10 13:27:00 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
prefixWithHost := provider.Issuer() + i.Config.LoginURLV2
|
2023-07-10 13:27:00 +00:00
|
|
|
if !strings.HasPrefix(loc.String(), prefixWithHost) {
|
|
|
|
return "", fmt.Errorf("login location has not prefix %s, but is %s", prefixWithHost, loc.String())
|
|
|
|
}
|
|
|
|
return strings.TrimPrefix(loc.String(), prefixWithHost), nil
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCAuthRequestImplicit(ctx context.Context, clientID, loginClient, redirectURI string, scope ...string) (authRequestID string, err error) {
|
|
|
|
provider, err := i.CreateRelyingParty(ctx, clientID, redirectURI, scope...)
|
2023-07-10 13:27:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
authURL := rp.AuthURL("state", provider)
|
|
|
|
|
|
|
|
// implicit is not natively supported so let's just overwrite the response type
|
|
|
|
parsed, _ := url.Parse(authURL)
|
|
|
|
queries := parsed.Query()
|
|
|
|
queries.Set("response_type", string(oidc.ResponseTypeIDToken))
|
|
|
|
parsed.RawQuery = queries.Encode()
|
|
|
|
authURL = parsed.String()
|
|
|
|
|
2023-09-29 09:26:14 +00:00
|
|
|
req, err := GetRequest(authURL, map[string]string{oidc_internal.LoginClientHeader: loginClient})
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
loc, err := CheckRedirect(req)
|
2023-07-10 13:27:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
prefixWithHost := provider.Issuer() + i.Config.LoginURLV2
|
2023-07-10 13:27:00 +00:00
|
|
|
if !strings.HasPrefix(loc.String(), prefixWithHost) {
|
|
|
|
return "", fmt.Errorf("login location has not prefix %s, but is %s", prefixWithHost, loc.String())
|
|
|
|
}
|
|
|
|
return strings.TrimPrefix(loc.String(), prefixWithHost), nil
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) OIDCIssuer() string {
|
|
|
|
return http_util.BuildHTTP(i.Domain, i.Config.Port, i.Config.Secure)
|
2023-07-14 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateRelyingParty(ctx context.Context, clientID, redirectURI string, scope ...string) (rp.RelyingParty, error) {
|
|
|
|
return i.CreateRelyingPartyForDomain(ctx, i.Domain, clientID, redirectURI, scope...)
|
2024-06-12 04:49:14 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateRelyingPartyForDomain(ctx context.Context, domain, clientID, redirectURI string, scope ...string) (rp.RelyingParty, error) {
|
2023-07-10 13:27:00 +00:00
|
|
|
if len(scope) == 0 {
|
|
|
|
scope = []string{oidc.ScopeOpenID}
|
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
loginClient := &http.Client{Transport: &loginRoundTripper{http.DefaultTransport, i.Users.Get(UserTypeLogin).Username}}
|
|
|
|
return rp.NewRelyingPartyOIDC(ctx, http_util.BuildHTTP(domain, i.Config.Port, i.Config.Secure), clientID, "", redirectURI, scope, rp.WithHTTPClient(loginClient))
|
2023-07-19 11:17:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type loginRoundTripper struct {
|
|
|
|
http.RoundTripper
|
2024-09-06 12:47:57 +00:00
|
|
|
loginUsername string
|
2023-07-19 11:17:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *loginRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
2024-09-06 12:47:57 +00:00
|
|
|
req.Header.Set(oidc_internal.LoginClientHeader, c.loginUsername)
|
2023-07-19 11:17:39 +00:00
|
|
|
return c.RoundTripper.RoundTrip(req)
|
2023-07-14 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateResourceServerJWTProfile(ctx context.Context, keyFileData []byte) (rs.ResourceServer, error) {
|
2023-07-14 11:16:16 +00:00
|
|
|
keyFile, err := client.ConfigFromKeyFileData(keyFileData)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
return rs.NewResourceServerJWTProfile(ctx, i.OIDCIssuer(), keyFile.ClientID, keyFile.KeyID, []byte(keyFile.Key))
|
2023-07-10 13:27:00 +00:00
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateResourceServerClientCredentials(ctx context.Context, clientID, clientSecret string) (rs.ResourceServer, error) {
|
|
|
|
return rs.NewResourceServerClientCredentials(ctx, i.OIDCIssuer(), clientID, clientSecret)
|
2023-12-28 13:31:41 +00:00
|
|
|
}
|
|
|
|
|
2023-09-29 09:26:14 +00:00
|
|
|
func GetRequest(url string, headers map[string]string) (*http.Request, error) {
|
2023-07-10 13:27:00 +00:00
|
|
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for key, value := range headers {
|
|
|
|
req.Header.Set(key, value)
|
|
|
|
}
|
2023-09-29 09:26:14 +00:00
|
|
|
return req, nil
|
|
|
|
}
|
2023-07-10 13:27:00 +00:00
|
|
|
|
2024-02-19 06:50:37 +00:00
|
|
|
func CheckPost(url string, values url.Values) (*url.URL, error) {
|
|
|
|
client := &http.Client{
|
|
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
|
|
return http.ErrUseLastResponse
|
|
|
|
},
|
|
|
|
}
|
|
|
|
resp, err := client.PostForm(url, values)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
return resp.Location()
|
|
|
|
}
|
|
|
|
|
2023-09-29 09:26:14 +00:00
|
|
|
func CheckRedirect(req *http.Request) (*url.URL, error) {
|
2023-07-10 13:27:00 +00:00
|
|
|
client := &http.Client{
|
|
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
|
|
return http.ErrUseLastResponse
|
|
|
|
},
|
|
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
2024-08-17 14:48:06 +00:00
|
|
|
if resp.StatusCode < 300 || resp.StatusCode >= 400 {
|
|
|
|
body, _ := io.ReadAll(resp.Body)
|
|
|
|
return nil, fmt.Errorf("check redirect unexpected status: %q; body: %q", resp.Status, body)
|
|
|
|
}
|
2023-07-10 13:27:00 +00:00
|
|
|
|
|
|
|
return resp.Location()
|
|
|
|
}
|
2023-12-05 17:01:03 +00:00
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCCredentialsClient(ctx context.Context) (machine *management.AddMachineUserResponse, name, clientID, clientSecret string, err error) {
|
2024-05-31 10:10:18 +00:00
|
|
|
name = gofakeit.Username()
|
2024-09-06 12:47:57 +00:00
|
|
|
machine, err = i.Client.Mgmt.AddMachineUser(ctx, &management.AddMachineUserRequest{
|
2023-12-05 17:01:03 +00:00
|
|
|
Name: name,
|
|
|
|
UserName: name,
|
|
|
|
AccessTokenType: user.AccessTokenType_ACCESS_TOKEN_TYPE_JWT,
|
|
|
|
})
|
|
|
|
if err != nil {
|
2024-05-31 10:10:18 +00:00
|
|
|
return nil, "", "", "", err
|
2023-12-05 17:01:03 +00:00
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
secret, err := i.Client.Mgmt.GenerateMachineSecret(ctx, &management.GenerateMachineSecretRequest{
|
2024-05-31 10:10:18 +00:00
|
|
|
UserId: machine.GetUserId(),
|
2023-12-05 17:01:03 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
2024-05-31 10:10:18 +00:00
|
|
|
return nil, "", "", "", err
|
2023-12-05 17:01:03 +00:00
|
|
|
}
|
2024-05-31 10:10:18 +00:00
|
|
|
return machine, name, secret.GetClientId(), secret.GetClientSecret(), nil
|
2023-12-05 17:01:03 +00:00
|
|
|
}
|
2024-05-16 05:07:56 +00:00
|
|
|
|
2024-09-17 13:21:49 +00:00
|
|
|
func (i *Instance) CreateOIDCCredentialsClientInactive(ctx context.Context) (machine *management.AddMachineUserResponse, name, clientID, clientSecret string, err error) {
|
|
|
|
name = gofakeit.Username()
|
|
|
|
machine, err = i.Client.Mgmt.AddMachineUser(ctx, &management.AddMachineUserRequest{
|
|
|
|
Name: name,
|
|
|
|
UserName: name,
|
|
|
|
AccessTokenType: user.AccessTokenType_ACCESS_TOKEN_TYPE_JWT,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", "", "", err
|
|
|
|
}
|
|
|
|
secret, err := i.Client.Mgmt.GenerateMachineSecret(ctx, &management.GenerateMachineSecretRequest{
|
|
|
|
UserId: machine.GetUserId(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", "", "", err
|
|
|
|
}
|
|
|
|
_, err = i.Client.UserV2.DeactivateUser(ctx, &user_v2.DeactivateUserRequest{
|
|
|
|
UserId: machine.GetUserId(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", "", "", err
|
|
|
|
}
|
|
|
|
return machine, name, secret.GetClientId(), secret.GetClientSecret(), nil
|
|
|
|
}
|
|
|
|
|
2024-09-06 12:47:57 +00:00
|
|
|
func (i *Instance) CreateOIDCJWTProfileClient(ctx context.Context) (machine *management.AddMachineUserResponse, name string, keyData []byte, err error) {
|
2024-05-31 10:10:18 +00:00
|
|
|
name = gofakeit.Username()
|
2024-09-06 12:47:57 +00:00
|
|
|
machine, err = i.Client.Mgmt.AddMachineUser(ctx, &management.AddMachineUserRequest{
|
2024-05-16 05:07:56 +00:00
|
|
|
Name: name,
|
|
|
|
UserName: name,
|
|
|
|
AccessTokenType: user.AccessTokenType_ACCESS_TOKEN_TYPE_JWT,
|
|
|
|
})
|
|
|
|
if err != nil {
|
2024-05-31 10:10:18 +00:00
|
|
|
return nil, "", nil, err
|
2024-05-16 05:07:56 +00:00
|
|
|
}
|
2024-09-06 12:47:57 +00:00
|
|
|
keyResp, err := i.Client.Mgmt.AddMachineKey(ctx, &management.AddMachineKeyRequest{
|
2024-05-31 10:10:18 +00:00
|
|
|
UserId: machine.GetUserId(),
|
2024-05-16 05:07:56 +00:00
|
|
|
Type: authn.KeyType_KEY_TYPE_JSON,
|
|
|
|
ExpirationDate: timestamppb.New(time.Now().Add(time.Hour)),
|
|
|
|
})
|
|
|
|
if err != nil {
|
2024-05-31 10:10:18 +00:00
|
|
|
return nil, "", nil, err
|
2024-05-16 05:07:56 +00:00
|
|
|
}
|
perf(query): remove transactions for queries (#8614)
# Which Problems Are Solved
Queries currently execute 3 statements, begin, query, commit
# How the Problems Are Solved
remove transaction handling from query methods in database package
# Additional Changes
- Bump versions of `core_grpc_dependencies`-receipt in Makefile
# Additional info
During load tests we saw a lot of idle transactions of `zitadel_queries`
application name which is the connection pool used to query data in
zitadel. Executed query:
`select query_start - xact_start, pid, application_name, backend_start,
xact_start, query_start, state_change, wait_event_type,
wait_event,substring(query, 1, 200) query from pg_stat_activity where
datname = 'zitadel' and state <> 'idle';`
Mostly the last query executed was `begin isolation level read committed
read only`.
example:
```
?column? | pid | application_name | backend_start | xact_start | query_start | state_change | wait_event_type | wait_event | query
-----------------+-------+----------------------------+-------------------------------+-------------------------------+-------------------------------+-------------------------------+-----------------+--------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
00:00:00 | 33030 | zitadel_queries | 2024-10-16 16:25:53.906036+00 | 2024-10-16 16:30:19.191661+00 | 2024-10-16 16:30:19.191661+00 | 2024-10-16 16:30:19.19169+00 | Client | ClientRead | begin isolation level read committed read only
00:00:00 | 33035 | zitadel_queries | 2024-10-16 16:25:53.909629+00 | 2024-10-16 16:30:19.19179+00 | 2024-10-16 16:30:19.19179+00 | 2024-10-16 16:30:19.191805+00 | Client | ClientRead | begin isolation level read committed read only
00:00:00.00412 | 33028 | zitadel_queries | 2024-10-16 16:25:53.904247+00 | 2024-10-16 16:30:19.187734+00 | 2024-10-16 16:30:19.191854+00 | 2024-10-16 16:30:19.191964+00 | Client | ClientRead | SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2 WHERE instance_id = $1 AND aggregate_type
00:00:00.084662 | 33134 | zitadel_es_pusher | 2024-10-16 16:29:54.979692+00 | 2024-10-16 16:30:19.178578+00 | 2024-10-16 16:30:19.26324+00 | 2024-10-16 16:30:19.263267+00 | Client | ClientRead | RELEASE SAVEPOINT cockroach_restart
00:00:00.084768 | 33139 | zitadel_es_pusher | 2024-10-16 16:29:54.979585+00 | 2024-10-16 16:30:19.180762+00 | 2024-10-16 16:30:19.26553+00 | 2024-10-16 16:30:19.265531+00 | LWLock | WALWriteLock | commit
00:00:00.077377 | 33136 | zitadel_es_pusher | 2024-10-16 16:29:54.978582+00 | 2024-10-16 16:30:19.187883+00 | 2024-10-16 16:30:19.26526+00 | 2024-10-16 16:30:19.265431+00 | Client | ClientRead | WITH existing AS ( +
| | | | | | | | | (SELECT instance_id, aggregate_type, aggregate_id, "sequence" FROM eventstore.events2 WHERE instance_id = $1 AND aggregate_type = $2 AND aggregate_id = $3 ORDER BY "sequence" DE
00:00:00.012309 | 33123 | zitadel_es_pusher | 2024-10-16 16:29:54.963484+00 | 2024-10-16 16:30:19.175066+00 | 2024-10-16 16:30:19.187375+00 | 2024-10-16 16:30:19.187376+00 | IO | WalSync | commit
00:00:00 | 33034 | zitadel_queries | 2024-10-16 16:25:53.90791+00 | 2024-10-16 16:30:19.262921+00 | 2024-10-16 16:30:19.262921+00 | 2024-10-16 16:30:19.263133+00 | Client | ClientRead | begin isolation level read committed read only
00:00:00 | 33039 | zitadel_queries | 2024-10-16 16:25:53.914106+00 | 2024-10-16 16:30:19.191676+00 | 2024-10-16 16:30:19.191676+00 | 2024-10-16 16:30:19.191687+00 | Client | ClientRead | begin isolation level read committed read only
00:00:00.24539 | 33083 | zitadel_projection_spooler | 2024-10-16 16:27:49.895548+00 | 2024-10-16 16:30:19.020058+00 | 2024-10-16 16:30:19.265448+00 | 2024-10-16 16:30:19.26546+00 | Client | ClientRead | SAVEPOINT exec_stmt
00:00:00 | 33125 | zitadel_es_pusher | 2024-10-16 16:29:54.963859+00 | 2024-10-16 16:30:19.191715+00 | 2024-10-16 16:30:19.191715+00 | 2024-10-16 16:30:19.191729+00 | Client | ClientRead | begin
00:00:00.004292 | 33032 | zitadel_queries | 2024-10-16 16:25:53.906624+00 | 2024-10-16 16:30:19.187713+00 | 2024-10-16 16:30:19.192005+00 | 2024-10-16 16:30:19.192062+00 | Client | ClientRead | SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2 WHERE instance_id = $1 AND aggregate_type
00:00:00 | 33031 | zitadel_queries | 2024-10-16 16:25:53.906422+00 | 2024-10-16 16:30:19.191625+00 | 2024-10-16 16:30:19.191625+00 | 2024-10-16 16:30:19.191645+00 | Client | ClientRead | begin isolation level read committed read only
```
The amount of idle transactions is significantly less if the query
transactions are removed:
example:
```
?column? | pid | application_name | backend_start | xact_start | query_start | state_change | wait_event_type | wait_event | query
-----------------+-------+----------------------------+-------------------------------+-------------------------------+-------------------------------+-------------------------------+-----------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
00:00:00.000094 | 32741 | zitadel_queries | 2024-10-16 16:23:49.73935+00 | 2024-10-16 16:24:59.785589+00 | 2024-10-16 16:24:59.785683+00 | 2024-10-16 16:24:59.785684+00 | | | SELECT created_at, event_type, "sequence", "position", payload, creator, "owner", instance_id, aggregate_type, aggregate_id, revision FROM eventstore.events2 WHERE instance_id = $1 AND aggregate_type
00:00:00 | 32762 | zitadel_es_pusher | 2024-10-16 16:24:02.275136+00 | 2024-10-16 16:24:59.784586+00 | 2024-10-16 16:24:59.784586+00 | 2024-10-16 16:24:59.784607+00 | Client | ClientRead | begin
00:00:00.000167 | 32742 | zitadel_queries | 2024-10-16 16:23:49.740489+00 | 2024-10-16 16:24:59.784274+00 | 2024-10-16 16:24:59.784441+00 | 2024-10-16 16:24:59.784442+00 | | | with usr as ( +
| | | | | | | | | select u.id, u.creation_date, u.change_date, u.sequence, u.state, u.resource_owner, u.username, n.login_name as preferred_login_name +
| | | | | | | | | from projections.users13 u +
| | | | | | | | | left join projections.l
00:00:00.256014 | 32759 | zitadel_projection_spooler | 2024-10-16 16:24:01.418429+00 | 2024-10-16 16:24:59.52959+00 | 2024-10-16 16:24:59.785604+00 | 2024-10-16 16:24:59.785649+00 | Client | ClientRead | UPDATE projections.milestones SET reached_date = $1 WHERE (instance_id = $2) AND (type = $3) AND (reached_date IS NULL)
00:00:00.014199 | 32773 | zitadel_es_pusher | 2024-10-16 16:24:02.320404+00 | 2024-10-16 16:24:59.769509+00 | 2024-10-16 16:24:59.783708+00 | 2024-10-16 16:24:59.783709+00 | IO | WalSync | commit
00:00:00 | 32765 | zitadel_es_pusher | 2024-10-16 16:24:02.28173+00 | 2024-10-16 16:24:59.780413+00 | 2024-10-16 16:24:59.780413+00 | 2024-10-16 16:24:59.780426+00 | Client | ClientRead | begin
00:00:00.012729 | 32777 | zitadel_es_pusher | 2024-10-16 16:24:02.339737+00 | 2024-10-16 16:24:59.767432+00 | 2024-10-16 16:24:59.780161+00 | 2024-10-16 16:24:59.780195+00 | Client | ClientRead | RELEASE SAVEPOINT cockroach_restart
```
---------
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
Co-authored-by: Livio Spring <livio.a@gmail.com>
Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Elio Bischof <elio@zitadel.com>
Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
Co-authored-by: Miguel Cabrerizo <30386061+doncicuto@users.noreply.github.com>
Co-authored-by: Joakim Lodén <Loddan@users.noreply.github.com>
Co-authored-by: Yxnt <Yxnt@users.noreply.github.com>
Co-authored-by: Stefan Benz <stefan@caos.ch>
Co-authored-by: Harsha Reddy <harsha.reddy@klaviyo.com>
Co-authored-by: Zach H <zhirschtritt@gmail.com>
2024-11-04 09:06:14 +00:00
|
|
|
mustAwait(func() error {
|
|
|
|
_, err := i.Client.Mgmt.GetMachineKeyByIDs(ctx, &management.GetMachineKeyByIDsRequest{
|
|
|
|
UserId: machine.GetUserId(),
|
|
|
|
KeyId: keyResp.GetKeyId(),
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
2024-05-31 10:10:18 +00:00
|
|
|
return machine, name, keyResp.GetKeyDetails(), nil
|
2024-05-16 05:07:56 +00:00
|
|
|
}
|