From 315b92ecdc506eb21e80a06097f9e864c8dc5531 Mon Sep 17 00:00:00 2001 From: yesoreyeram <153843+yesoreyeram@users.noreply.github.com> Date: Mon, 24 Jun 2024 08:41:35 +0100 Subject: [PATCH 1/2] added secure form data --- .changeset/clean-penguins-fly.md | 5 +++++ docs/sources/references/url.md | 4 ++++ pkg/infinity/client.go | 10 ++++++++-- pkg/infinity/request.go | 2 +- pkg/models/settings.go | 2 ++ pkg/models/settings_test.go | 18 +++++++++++++++--- src/editors/config.editor.tsx | 14 ++++++++++++-- 7 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 .changeset/clean-penguins-fly.md diff --git a/.changeset/clean-penguins-fly.md b/.changeset/clean-penguins-fly.md new file mode 100644 index 000000000..f9b6766db --- /dev/null +++ b/.changeset/clean-penguins-fly.md @@ -0,0 +1,5 @@ +--- +'grafana-infinity-datasource': minor +--- + +Added support for configuring secure HTTP POST form data diff --git a/docs/sources/references/url.md b/docs/sources/references/url.md index 8baed77b7..6d139e984 100644 --- a/docs/sources/references/url.md +++ b/docs/sources/references/url.md @@ -60,6 +60,10 @@ You can configure the headers required for the URL in the datasource config and Note: We suggest adding secure headers only via configuration and not in query. +## Sending secure params as HTTP POST Form data + +When you are using HTTP POST method and one of the HTTP Form body types (**Form Data**/**X-WWW-FORM-URLENCODED**), you can pass additional secure form data via config option. The parameters set in config will be set in addition to the form parameters set in the query. You can find the settings under **HTTP Options** in the datasource config + ## Allowed Hosts Leaving blank will allow all the hosts. This is by default. diff --git a/pkg/infinity/client.go b/pkg/infinity/client.go index de7732bd0..18bbb8e6a 100644 --- a/pkg/infinity/client.go +++ b/pkg/infinity/client.go @@ -270,7 +270,7 @@ func (client *Client) GetResults(ctx context.Context, query models.Query, reques } switch strings.ToUpper(query.URLOptions.Method) { case http.MethodPost: - body := GetQueryBody(query) + body := GetQueryBody(query, client.Settings) return client.req(ctx, query.URL, body, client.Settings, query, requestHeaders) default: return client.req(ctx, query.URL, nil, client.Settings, query, requestHeaders) @@ -303,7 +303,7 @@ func CanAllowURL(url string, allowedHosts []string) bool { return allow } -func GetQueryBody(query models.Query) io.Reader { +func GetQueryBody(query models.Query, settings models.InfinitySettings) io.Reader { var body io.Reader if strings.EqualFold(query.URLOptions.Method, http.MethodPost) { switch query.URLOptions.BodyType { @@ -315,6 +315,9 @@ func GetQueryBody(query models.Query) io.Reader { for _, f := range query.URLOptions.BodyForm { _ = writer.WriteField(f.Key, f.Value) } + for k, v := range settings.FormPostItems { + _ = writer.WriteField(k, v) + } if err := writer.Close(); err != nil { backend.Logger.Error("error closing the query body reader") return nil @@ -325,6 +328,9 @@ func GetQueryBody(query models.Query) io.Reader { for _, f := range query.URLOptions.BodyForm { form.Set(f.Key, f.Value) } + for k, v := range settings.FormPostItems { + form.Set(k, v) + } body = strings.NewReader(form.Encode()) case "graphql": var variables map[string]interface{} diff --git a/pkg/infinity/request.go b/pkg/infinity/request.go index f481e4183..bdca8b3ae 100644 --- a/pkg/infinity/request.go +++ b/pkg/infinity/request.go @@ -95,7 +95,7 @@ func NormalizeURL(u string) string { func (client *Client) GetExecutedURL(ctx context.Context, query models.Query) string { out := []string{} if query.Source != "inline" && query.Source != "azure-blob" { - req, err := GetRequest(ctx, client.Settings, GetQueryBody(query), query, map[string]string{}, false) + req, err := GetRequest(ctx, client.Settings, GetQueryBody(query, models.InfinitySettings{}), query, map[string]string{}, false) if err != nil { return fmt.Sprintf("error retrieving full url. %s", query.URL) } diff --git a/pkg/models/settings.go b/pkg/models/settings.go index e6ffd04d2..94e9ac078 100644 --- a/pkg/models/settings.go +++ b/pkg/models/settings.go @@ -98,6 +98,7 @@ type InfinitySettings struct { ForwardOauthIdentity bool CustomHeaders map[string]string SecureQueryFields map[string]string + FormPostItems map[string]string InsecureSkipVerify bool ServerName string TimeoutInSeconds int64 @@ -294,6 +295,7 @@ func LoadSettings(ctx context.Context, config backend.DataSourceInstanceSettings } settings.CustomHeaders = GetSecrets(config, "httpHeaderName", "httpHeaderValue") settings.SecureQueryFields = GetSecrets(config, "secureQueryName", "secureQueryValue") + settings.FormPostItems = GetSecrets(config, "formPostItemName", "formPostItemValue") settings.OAuth2Settings.EndpointParams = GetSecrets(config, "oauth2EndPointParamsName", "oauth2EndPointParamsValue") if settings.AuthenticationMethod == "" { settings.AuthenticationMethod = AuthenticationMethodNone diff --git a/pkg/models/settings_test.go b/pkg/models/settings_test.go index 8019028dc..f97f1fe72 100644 --- a/pkg/models/settings_test.go +++ b/pkg/models/settings_test.go @@ -26,6 +26,7 @@ func TestLoadSettings(t *testing.T) { OAuth2Settings: models.OAuth2Settings{ EndpointParams: map[string]string{}, }, + FormPostItems: map[string]string{}, CustomHeaders: map[string]string{}, SecureQueryFields: map[string]string{}, }, @@ -45,12 +46,14 @@ func TestLoadSettings(t *testing.T) { JSONData: []byte(`{ "datasource_mode" : "advanced", "secureQueryName1" : "foo", + "formPostItemName1" : "hello", "httpHeaderName1" : "header1" }`), DecryptedSecureJSONData: map[string]string{ - "basicAuthPassword": "password", - "secureQueryValue1": "bar", - "httpHeaderValue1": "headervalue1", + "basicAuthPassword": "password", + "secureQueryValue1": "bar", + "formPostItemValue1": "world", + "httpHeaderValue1": "headervalue1", }, }, wantSettings: models.InfinitySettings{ @@ -66,6 +69,9 @@ func TestLoadSettings(t *testing.T) { OAuth2Settings: models.OAuth2Settings{ EndpointParams: map[string]string{}, }, + FormPostItems: map[string]string{ + "hello": "world", + }, CustomHeaders: map[string]string{ "header1": "headervalue1", }, @@ -105,6 +111,7 @@ func TestLoadSettings(t *testing.T) { OAuth2Settings: models.OAuth2Settings{ EndpointParams: map[string]string{}, }, + FormPostItems: map[string]string{}, CustomHeaders: map[string]string{ "header1": "headervalue1", }, @@ -144,6 +151,7 @@ func TestAllSettingsAgainstFrontEnd(t *testing.T) { "apiKeyType" : "query", "datasource_mode" : "advanced", "secureQueryName1" : "foo", + "formPostItemName1": "hello", "httpHeaderName1" : "header1", "timeoutInSeconds" : 30, "tlsAuth" : true, @@ -178,6 +186,7 @@ func TestAllSettingsAgainstFrontEnd(t *testing.T) { "tlsClientKey": "myTlsClientKey", "basicAuthPassword": "password", "secureQueryValue1": "bar", + "formPostItemValue1": "world", "httpHeaderValue1": "headervalue1", "apiKeyValue": "earth", "bearerToken": "myBearerToken", @@ -244,6 +253,9 @@ func TestAllSettingsAgainstFrontEnd(t *testing.T) { CustomHealthCheckEnabled: true, CustomHealthCheckUrl: "https://foo-check/", UnsecuredQueryHandling: models.UnsecuredQueryHandlingDeny, + FormPostItems: map[string]string{ + "hello": "world", + }, CustomHeaders: map[string]string{ "header1": "headervalue1", }, diff --git a/src/editors/config.editor.tsx b/src/editors/config.editor.tsx index d28ad3e7f..9ef70fe47 100644 --- a/src/editors/config.editor.tsx +++ b/src/editors/config.editor.tsx @@ -80,6 +80,16 @@ export const HeadersEditor = (props: DataSourcePluginOptionsEditorProps + + + @@ -138,7 +148,7 @@ export const MiscEditor = (props: DataSourcePluginOptionsEditorProps = [ { value: 'main', label: 'Main' }, { value: 'auth', label: 'Authentication' }, - { value: 'headers_and_params', label: 'Headers & URL params' }, + { value: 'http_options', label: 'HTTP Options' }, { value: 'network', label: 'Network' }, { value: 'security', label: 'Security' }, { value: 'health_check', label: 'Health check' }, @@ -214,7 +224,7 @@ export const InfinityConfigEditor = (props: DataSourcePluginOptionsEditorProps ) : activeTab === 'auth' ? ( - ) : activeTab === 'headers_and_params' ? ( + ) : activeTab === 'http_options' ? ( ) : activeTab === 'network' ? ( From 22e0b80e34be7a96d4c67d165e6260620d01fc48 Mon Sep 17 00:00:00 2001 From: yesoreyeram <153843+yesoreyeram@users.noreply.github.com> Date: Mon, 24 Jun 2024 08:59:06 +0100 Subject: [PATCH 2/2] throw error when allowed hosts not configured --- pkg/models/settings.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/models/settings.go b/pkg/models/settings.go index 94e9ac078..ada5e5c42 100644 --- a/pkg/models/settings.go +++ b/pkg/models/settings.go @@ -172,6 +172,9 @@ func (s *InfinitySettings) HaveSecureHeaders() bool { } return false } + if len(s.FormPostItems) > 0 { + return true + } return false }