From 15e5e811b4833673b72f822c93fca4f83e1d6fe6 Mon Sep 17 00:00:00 2001 From: Daniel Kuntze Date: Wed, 28 Jun 2023 12:30:37 +0200 Subject: [PATCH 1/3] feat: support custom idp login when recording test fixtures Add support for setting the identity provider from environment variable BTP_IDP during provider configuration. This is implemented analogously to BTP_USERNAME and BTP_PASSWORD including the required redaction of recorded fixtures. To prevent impact on existing fixtures, identity provider values are replaced by the empty string during redaction. Also fixes a warning when the username has an unknown value during provider configuration. Resolves #243 --- internal/provider/provider.go | 17 +++++++++++++++-- internal/provider/provider_test.go | 10 ++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index e058e268..20d0a985 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -100,10 +100,23 @@ func (p *btpcliProvider) Configure(ctx context.Context, req provider.ConfigureRe client := btpcli.NewClientFacade(btpcli.NewV2ClientWithHttpClient(p.httpClient, u)) client.UserAgent = fmt.Sprintf("Terraform/%s terraform-provider-btp/%s", req.TerraformVersion, version.ProviderVersion) + // User may provide an idp to the provider + var idp string + if config.IdentityProvider.IsUnknown() { + resp.Diagnostics.AddWarning("Unable to create client", "Cannot use unknown value as identity provider") + return + } + + if config.IdentityProvider.IsNull() { + idp = os.Getenv("BTP_IDP") + } else { + idp = config.IdentityProvider.ValueString() + } + // User must provide a username to the provider var username string if config.Username.IsUnknown() { - resp.Diagnostics.AddWarning("Unable to create client", "Cannot use unknown value as client_certificate") + resp.Diagnostics.AddWarning("Unable to create client", "Cannot use unknown value as username") return } @@ -131,7 +144,7 @@ func (p *btpcliProvider) Configure(ctx context.Context, req provider.ConfigureRe return } - if _, err = client.Login(ctx, btpcli.NewLoginRequestWithCustomIDP(config.IdentityProvider.ValueString(), config.GlobalAccount.ValueString(), username, password)); err != nil { + if _, err = client.Login(ctx, btpcli.NewLoginRequestWithCustomIDP(idp, config.GlobalAccount.ValueString(), username, password)); err != nil { resp.Diagnostics.AddError("Unable to create Client", fmt.Sprintf("%s", err)) return } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 05206c21..5b95a694 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/SAP/terraform-provider-btp/internal/btpcli" "github.com/SAP/terraform-provider-btp/internal/validation/uuidvalidator" "github.com/stretchr/testify/assert" @@ -39,6 +40,7 @@ provider "btp" { globalaccount = "terraformintcanary" username = "john.doe@int.test" password = "redacted" + idp = "" } `, cliServerURL) } @@ -62,11 +64,19 @@ func setupVCR(t *testing.T, cassetteName string) *recorder.Recorder { hookRedactIntegrationUserCredentials := func(i *cassette.Interaction) error { intUser := os.Getenv("BTP_USERNAME") intUserPwd := os.Getenv("BTP_PASSWORD") + intUserIdp := os.Getenv("BTP_IDP") firstName, lastName := getNameFromEmail(intUser) if strings.Contains(i.Request.URL, "/login/") { i.Request.Body = strings.ReplaceAll(i.Request.Body, intUserPwd, "redacted") + i.Request.Body = strings.ReplaceAll(i.Request.Body, "\"customIdp\":\""+intUserIdp+"\"", "\"customIdp\":\"\"") + i.Response.Body = strings.ReplaceAll(i.Response.Body, "\"issuer\":\""+intUserIdp+"\"", "\"issuer\":\"accounts.sap.com\"") + i.Response.Body = strings.ReplaceAll(i.Response.Body, "\"issuer\":\""+intUserIdp+".accounts400.ondemand.com\"", "\"issuer\":\"accounts.sap.com\"") + } + + if _, exists := i.Request.Headers[http.CanonicalHeaderKey(btpcli.HeaderCLICustomIDP)]; exists { + i.Request.Headers.Set(btpcli.HeaderCLICustomIDP, "") } i.Request.Body = strings.ReplaceAll(i.Request.Body, intUser, "john.doe@int.test") From 34704f2f40b523ee7a94759d08ac8f33a00d33b5 Mon Sep 17 00:00:00 2001 From: Daniel Kuntze Date: Wed, 5 Jul 2023 14:03:27 +0200 Subject: [PATCH 2/3] incorporate review comments --- internal/btpcli/client.go | 18 +++++++++--------- internal/provider/provider_test.go | 8 +++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/internal/btpcli/client.go b/internal/btpcli/client.go index ffc244b7..647364c3 100644 --- a/internal/btpcli/client.go +++ b/internal/btpcli/client.go @@ -34,16 +34,16 @@ func NewV2ClientWithHttpClient(client *http.Client, serverURL *url.URL) *v2Clien } const ( - HeaderCorrelationID string = "X-CorrelationId" + HeaderCorrelationID string = "X-Correlationid" HeaderIDToken string = "X-Id-Token" - HeaderCLIFormat string = "X-CPCLI-Format" - HeaderCLIRefreshToken string = "X-CPCLI-RefreshToken" - HeaderCLIReplacementRefreshToken string = "X-CPCLI-ReplacementRefreshtoken" - HeaderCLISubdomain string = "X-CPCLI-Subdomain" - HeaderCLICustomIDP string = "X-CPCLI-CustomIdp" - HeaderCLIBackendStatus string = "X-CPCLI-Backend-Status" - HeaderCLIBackendMessage string = "X-CPCLI-Backend-Message" - HeaderCLIBackendMediaType string = "X-CPCLI-Backend-MediaType" + HeaderCLIFormat string = "X-Cpcli-Format" + HeaderCLIRefreshToken string = "X-Cpcli-Refreshtoken" + HeaderCLIReplacementRefreshToken string = "X-Cpcli-Replacementrefreshtoken" + HeaderCLISubdomain string = "X-Cpcli-Subdomain" + HeaderCLICustomIDP string = "X-Cpcli-Customidp" + HeaderCLIBackendStatus string = "X-Cpcli-Backend-Status" + HeaderCLIBackendMessage string = "X-Cpcli-Backend-Message" + HeaderCLIBackendMediaType string = "X-Cpcli-Backend-Mediatype" ) const cliTargetProtocolVersion string = "v2.38.0" diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 5b95a694..07933014 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -70,12 +70,18 @@ func setupVCR(t *testing.T, cassetteName string) *recorder.Recorder { if strings.Contains(i.Request.URL, "/login/") { i.Request.Body = strings.ReplaceAll(i.Request.Body, intUserPwd, "redacted") + // TODO #277 the custom idp and issuer values should be redacted to some special value + // for now the custom idp is replaced by the empty string and the issuer by "accounts.sap.com" to keep + // existing fixtures working that were recorded without selecting a custom idp during login i.Request.Body = strings.ReplaceAll(i.Request.Body, "\"customIdp\":\""+intUserIdp+"\"", "\"customIdp\":\"\"") i.Response.Body = strings.ReplaceAll(i.Response.Body, "\"issuer\":\""+intUserIdp+"\"", "\"issuer\":\"accounts.sap.com\"") i.Response.Body = strings.ReplaceAll(i.Response.Body, "\"issuer\":\""+intUserIdp+".accounts400.ondemand.com\"", "\"issuer\":\"accounts.sap.com\"") } - if _, exists := i.Request.Headers[http.CanonicalHeaderKey(btpcli.HeaderCLICustomIDP)]; exists { + if _, exists := i.Request.Headers[btpcli.HeaderCLICustomIDP]; exists { + // TODO #277 the custom idp header value should be redacted to some special value + // for now the header is replaced by the empty string to keep existing fixtures working that were + // recorded without selecting a custom idp during login i.Request.Headers.Set(btpcli.HeaderCLICustomIDP, "") } From 648b753da45b28be8c92f3d8e7d2a8c3c4b66a31 Mon Sep 17 00:00:00 2001 From: Daniel Kuntze Date: Wed, 5 Jul 2023 17:25:47 +0200 Subject: [PATCH 3/3] fix sonar issue --- internal/provider/provider.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 20d0a985..f1e1a99c 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -76,6 +76,8 @@ func (p *btpcliProvider) Metadata(_ context.Context, _ provider.MetadataRequest, } func (p *btpcliProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + const unableToCreateClient = "unableToCreateClient" + // Retrieve provider data from configuration var config providerData diags := req.Config.Get(ctx, &config) @@ -93,7 +95,7 @@ func (p *btpcliProvider) Configure(ctx context.Context, req provider.ConfigureRe u, err := url.Parse(selectedCLIServerURL) // TODO move to NewV2Client if err != nil { - resp.Diagnostics.AddError("Unable to create Client", fmt.Sprintf("%s", err)) + resp.Diagnostics.AddError(unableToCreateClient, fmt.Sprintf("%s", err)) return } @@ -103,7 +105,7 @@ func (p *btpcliProvider) Configure(ctx context.Context, req provider.ConfigureRe // User may provide an idp to the provider var idp string if config.IdentityProvider.IsUnknown() { - resp.Diagnostics.AddWarning("Unable to create client", "Cannot use unknown value as identity provider") + resp.Diagnostics.AddWarning(unableToCreateClient, "Cannot use unknown value as identity provider") return } @@ -116,7 +118,7 @@ func (p *btpcliProvider) Configure(ctx context.Context, req provider.ConfigureRe // User must provide a username to the provider var username string if config.Username.IsUnknown() { - resp.Diagnostics.AddWarning("Unable to create client", "Cannot use unknown value as username") + resp.Diagnostics.AddWarning(unableToCreateClient, "Cannot use unknown value as username") return } @@ -129,7 +131,7 @@ func (p *btpcliProvider) Configure(ctx context.Context, req provider.ConfigureRe // User must provide a password to the provider var password string if config.Password.IsUnknown() { - resp.Diagnostics.AddWarning("Unable to create client", "Cannot use unknown value as password") + resp.Diagnostics.AddWarning(unableToCreateClient, "Cannot use unknown value as password") return } @@ -140,12 +142,12 @@ func (p *btpcliProvider) Configure(ctx context.Context, req provider.ConfigureRe } if len(username) == 0 || len(password) == 0 { - resp.Diagnostics.AddError("Unable to create Client", "globalaccount, username and password must be given.") + resp.Diagnostics.AddError(unableToCreateClient, "globalaccount, username and password must be given.") return } if _, err = client.Login(ctx, btpcli.NewLoginRequestWithCustomIDP(idp, config.GlobalAccount.ValueString(), username, password)); err != nil { - resp.Diagnostics.AddError("Unable to create Client", fmt.Sprintf("%s", err)) + resp.Diagnostics.AddError(unableToCreateClient, fmt.Sprintf("%s", err)) return }