2017-08-22 11:14:15 -04:00
package pluginproxy
import (
"bytes"
"errors"
"fmt"
2022-08-10 09:37:51 -04:00
"io"
2017-08-22 11:14:15 -04:00
"net/http"
"net/url"
2017-08-23 11:18:43 -04:00
"strconv"
2017-08-22 11:14:15 -04:00
"strings"
"time"
2022-06-27 12:23:15 -04:00
"go.opentelemetry.io/otel/attribute"
2023-10-03 08:54:20 -04:00
"go.opentelemetry.io/otel/trace"
2022-06-27 12:23:15 -04:00
2026-06-08 06:28:30 -04:00
"github.com/grafana/grafana/pkg/api/datasource/validation"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
2026-05-23 06:36:49 -04:00
datasourcesV0 "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
2021-05-19 17:53:41 -04:00
"github.com/grafana/grafana/pkg/infra/httpclient"
2020-02-18 07:26:01 -05:00
glog "github.com/grafana/grafana/pkg/infra/log"
2022-01-20 05:10:12 -05:00
"github.com/grafana/grafana/pkg/infra/tracing"
2026-06-08 06:28:30 -04:00
"github.com/grafana/grafana/pkg/models/usertoken"
2017-08-22 11:14:15 -04:00
"github.com/grafana/grafana/pkg/plugins"
2021-10-07 10:33:50 -04:00
"github.com/grafana/grafana/pkg/services/datasources"
2023-10-02 03:14:10 -04:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2020-10-23 19:34:38 -04:00
"github.com/grafana/grafana/pkg/services/oauthtoken"
2024-07-25 10:22:42 -04:00
pluginac "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol"
2017-08-22 11:14:15 -04:00
"github.com/grafana/grafana/pkg/util"
2020-03-03 05:45:16 -05:00
"github.com/grafana/grafana/pkg/util/proxyutil"
2017-08-22 11:14:15 -04:00
)
var (
2020-02-18 07:26:01 -05:00
logger = glog . New ( "data-proxy-log" )
2018-09-06 09:50:16 -04:00
client = newHTTPClient ( )
2026-01-14 15:12:18 -05:00
errPluginProxyRouteAccessDenied = errors . New ( "plugin proxy route access denied" )
2017-08-22 11:14:15 -04:00
)
2026-05-13 04:17:20 -04:00
// maxForwardedUserAgentLen is the maximum byte length of the client User-Agent
// appended to the data proxy User-Agent when forward_user_agent is enabled.
const maxForwardedUserAgentLen = 255
2026-06-08 06:28:30 -04:00
type HTTPContext struct {
Req * http . Request
Resp http . ResponseWriter
// TODO? eventually this should come from the user in the request context
UserToken * usertoken . UserToken
}
2017-08-22 11:14:15 -04:00
type DataSourceProxy struct {
2026-06-08 06:28:30 -04:00
ds * datasourcesV0 . DataSource
requester identity . Requester
dataSource DataSourceLoader
ctx HTTPContext
targetUrl * url . URL
proxyPath string
matchedRoute * plugins . Route
pluginRoutes [ ] * plugins . Route
settings * DataSourceProxySettings
clientProvider httpclient . Provider
oAuthTokenService oauthtoken . OAuthTokenService
tracer tracing . Tracer
features featuremgmt . FeatureToggles
2018-06-12 11:39:38 -04:00
}
2018-06-14 07:39:46 -04:00
type httpClient interface {
2018-06-12 11:39:38 -04:00
Do ( req * http . Request ) ( * http . Response , error )
2017-08-22 11:14:15 -04:00
}
2020-02-18 07:26:01 -05:00
// NewDataSourceProxy creates a new Datasource proxy
2026-06-08 06:28:30 -04:00
func NewDataSourceProxy ( dataSource DataSourceLoader ,
pluginRoutes [ ] * plugins . Route , ctx HTTPContext ,
2026-05-21 05:09:14 -04:00
proxyPath string , settings * DataSourceProxySettings , clientProvider httpclient . Provider ,
2026-06-08 06:28:30 -04:00
oAuthTokenService oauthtoken . OAuthTokenService ,
2024-11-27 05:06:39 -05:00
tracer tracing . Tracer , features featuremgmt . FeatureToggles ,
) ( * DataSourceProxy , error ) {
2026-06-08 06:28:30 -04:00
ds , err := dataSource . DataSource ( ctx . Req . Context ( ) )
if err != nil {
return nil , fmt . Errorf ( "failed to load datasource: %w" , err )
}
targetURL , err := validation . ValidateURL ( dataSource . PluginType ( ) , ds . Spec . URL ( ) )
2020-04-22 04:30:06 -04:00
if err != nil {
2020-05-12 07:04:18 -04:00
return nil , err
2020-04-22 04:30:06 -04:00
}
2017-08-23 04:52:31 -04:00
2026-06-08 06:28:30 -04:00
requester , err := identity . GetRequester ( ctx . Req . Context ( ) )
if err != nil {
return nil , fmt . Errorf ( "failed to get requester from context: %w" , err )
}
2017-08-22 11:14:15 -04:00
return & DataSourceProxy {
2026-06-08 06:28:30 -04:00
ds : ds ,
requester : requester ,
dataSource : dataSource ,
pluginRoutes : pluginRoutes ,
ctx : ctx ,
proxyPath : proxyPath ,
targetUrl : targetURL ,
settings : settings ,
clientProvider : clientProvider ,
oAuthTokenService : oAuthTokenService ,
tracer : tracer ,
features : features ,
2020-04-22 04:30:06 -04:00
} , nil
2018-06-14 07:39:46 -04:00
}
func newHTTPClient ( ) httpClient {
return & http . Client {
2019-02-11 07:42:05 -05:00
Timeout : 30 * time . Second ,
2018-06-14 07:39:46 -04:00
Transport : & http . Transport { Proxy : http . ProxyFromEnvironment } ,
2017-08-22 11:14:15 -04:00
}
}
func ( proxy * DataSourceProxy ) HandleRequest ( ) {
if err := proxy . validateRequest ( ) ; err != nil {
2026-06-08 06:28:30 -04:00
writeJSONErr ( proxy . ctx . Resp , proxy . ctx . Req , 403 , err . Error ( ) , nil )
2017-08-22 11:14:15 -04:00
return
}
2026-06-08 06:28:30 -04:00
userid , _ := proxy . requester . GetInternalID ( )
2022-04-11 07:17:08 -04:00
proxyErrorLogger := logger . New (
2026-06-08 06:28:30 -04:00
"userId" , userid ,
"orgId" , proxy . requester . GetOrgID ( ) ,
"uname" , proxy . requester . GetLogin ( ) ,
2022-04-11 07:17:08 -04:00
"path" , proxy . ctx . Req . URL . Path ,
2026-06-08 06:28:30 -04:00
"remote_addr" , proxy . ctx . Req . RemoteAddr ,
2022-04-11 07:17:08 -04:00
"referer" , proxy . ctx . Req . Referer ( ) ,
)
2017-08-22 11:14:15 -04:00
2026-06-08 06:28:30 -04:00
transport , err := proxy . dataSource . GetHTTPTransport ( proxy . ctx . Req . Context ( ) , proxy . clientProvider )
2017-08-22 11:14:15 -04:00
if err != nil {
2026-06-08 06:28:30 -04:00
writeJSONErr ( proxy . ctx . Resp , proxy . ctx . Req , 400 , "Unable to load TLS certificate" , err )
2017-08-22 11:14:15 -04:00
return
}
2022-04-11 07:17:08 -04:00
modifyResponse := func ( resp * http . Response ) error {
if resp . StatusCode == 401 {
// The data source rejected the request as unauthorized, convert to 400 (bad request)
2022-08-10 09:37:51 -04:00
body , err := io . ReadAll ( resp . Body )
2022-04-11 07:17:08 -04:00
if err != nil {
return fmt . Errorf ( "failed to read data source response body: %w" , err )
}
_ = resp . Body . Close ( )
2022-09-20 12:32:06 -04:00
ctxLogger := proxyErrorLogger . FromContext ( resp . Request . Context ( ) )
ctxLogger . Info ( "Authentication to data source failed" , "body" , string ( body ) , "statusCode" ,
2022-04-11 07:17:08 -04:00
resp . StatusCode )
msg := "Authentication to data source failed"
* resp = http . Response {
StatusCode : 400 ,
Status : "Bad Request" ,
2022-08-10 09:37:51 -04:00
Body : io . NopCloser ( strings . NewReader ( msg ) ) ,
2022-04-11 07:17:08 -04:00
ContentLength : int64 ( len ( msg ) ) ,
Header : http . Header { } ,
2023-09-11 06:13:13 -04:00
Request : resp . Request ,
2020-11-13 07:21:43 -05:00
}
2022-04-11 07:17:08 -04:00
}
return nil
2020-01-15 07:03:12 -05:00
}
2022-04-11 07:17:08 -04:00
reverseProxy := proxyutil . NewReverseProxy (
proxyErrorLogger ,
proxy . director ,
proxyutil . WithTransport ( transport ) ,
proxyutil . WithModifyResponse ( modifyResponse ) ,
)
2017-08-22 11:14:15 -04:00
proxy . logRequest ( )
2022-01-20 05:10:12 -05:00
ctx , span := proxy . tracer . Start ( proxy . ctx . Req . Context ( ) , "datasource reverse proxy" )
defer span . End ( )
2020-11-13 07:21:43 -05:00
2021-09-01 05:18:30 -04:00
proxy . ctx . Req = proxy . ctx . Req . WithContext ( ctx )
2017-09-12 04:25:40 -04:00
2023-10-03 08:54:20 -04:00
span . SetAttributes (
2026-06-08 06:28:30 -04:00
attribute . String ( "datasource_name" , proxy . ds . Spec . Title ( ) ) ,
attribute . String ( "datasource_type" , proxy . dataSource . PluginType ( ) ) ,
attribute . String ( "user" , proxy . requester . GetLogin ( ) ) ,
attribute . Int64 ( "org_id" , proxy . requester . GetOrgID ( ) ) ,
2023-10-03 08:54:20 -04:00
)
2017-09-12 04:25:40 -04:00
2018-03-20 17:21:24 -04:00
proxy . addTraceFromHeaderValue ( span , "X-Panel-Id" , "panel_id" )
proxy . addTraceFromHeaderValue ( span , "X-Dashboard-Id" , "dashboard_id" )
2022-01-20 05:10:12 -05:00
proxy . tracer . Inject ( ctx , proxy . ctx . Req . Header , span )
2017-09-12 05:06:37 -04:00
2021-09-01 05:18:30 -04:00
reverseProxy . ServeHTTP ( proxy . ctx . Resp , proxy . ctx . Req )
2017-08-22 11:14:15 -04:00
}
2023-10-03 08:54:20 -04:00
func ( proxy * DataSourceProxy ) addTraceFromHeaderValue ( span trace . Span , headerName string , tagName string ) {
2018-03-20 17:21:24 -04:00
panelId := proxy . ctx . Req . Header . Get ( headerName )
dashId , err := strconv . Atoi ( panelId )
if err == nil {
2023-10-03 08:54:20 -04:00
span . SetAttributes ( attribute . Int ( tagName , dashId ) )
2018-03-20 17:21:24 -04:00
}
}
2020-11-13 07:21:43 -05:00
func ( proxy * DataSourceProxy ) director ( req * http . Request ) {
req . URL . Scheme = proxy . targetUrl . Scheme
req . URL . Host = proxy . targetUrl . Host
req . Host = proxy . targetUrl . Host
reqQueryVals := req . URL . Query ( )
2022-09-20 12:32:06 -04:00
ctxLogger := logger . FromContext ( req . Context ( ) )
2026-06-08 06:28:30 -04:00
ds := proxy . ds
2022-09-20 12:32:06 -04:00
2026-06-08 06:28:30 -04:00
switch proxy . dataSource . PluginType ( ) {
2022-06-27 12:23:15 -04:00
case datasources . DS_INFLUXDB_08 :
2026-06-08 06:28:30 -04:00
password , err := proxy . dataSource . DecryptedPassword ( req . Context ( ) )
2022-04-25 12:57:45 -04:00
if err != nil {
2022-09-20 12:32:06 -04:00
ctxLogger . Error ( "Error interpolating proxy url" , "error" , err )
2022-04-25 12:57:45 -04:00
return
}
2026-06-08 06:28:30 -04:00
req . URL . RawPath = util . JoinURLFragments ( proxy . targetUrl . Path , "db/" + ds . Spec . Database ( ) + "/" + proxy . proxyPath )
reqQueryVals . Add ( "u" , ds . Spec . User ( ) )
2022-04-25 12:57:45 -04:00
reqQueryVals . Add ( "p" , password )
2020-11-13 07:21:43 -05:00
req . URL . RawQuery = reqQueryVals . Encode ( )
2022-06-27 12:23:15 -04:00
case datasources . DS_INFLUXDB :
2026-06-08 06:28:30 -04:00
password , err := proxy . dataSource . DecryptedPassword ( req . Context ( ) )
2022-04-25 12:57:45 -04:00
if err != nil {
2022-09-20 12:32:06 -04:00
ctxLogger . Error ( "Error interpolating proxy url" , "error" , err )
2022-04-25 12:57:45 -04:00
return
}
2021-03-17 07:17:41 -04:00
req . URL . RawPath = util . JoinURLFragments ( proxy . targetUrl . Path , proxy . proxyPath )
2020-11-13 07:21:43 -05:00
req . URL . RawQuery = reqQueryVals . Encode ( )
2026-06-08 06:28:30 -04:00
if ! ds . Spec . BasicAuth ( ) {
2021-10-07 10:33:50 -04:00
req . Header . Set (
"Authorization" ,
2026-06-08 06:28:30 -04:00
util . GetBasicAuthHeader ( ds . Spec . User ( ) , password ) ,
2021-10-07 10:33:50 -04:00
)
2017-08-22 11:14:15 -04:00
}
2020-11-13 07:21:43 -05:00
default :
2021-03-17 07:17:41 -04:00
req . URL . RawPath = util . JoinURLFragments ( proxy . targetUrl . Path , proxy . proxyPath )
2020-11-13 07:21:43 -05:00
}
2020-04-24 04:32:13 -04:00
2021-03-17 07:17:41 -04:00
unescapedPath , err := url . PathUnescape ( req . URL . RawPath )
if err != nil {
2022-09-20 12:32:06 -04:00
ctxLogger . Error ( "Failed to unescape raw path" , "rawPath" , req . URL . RawPath , "error" , err )
2021-03-17 07:17:41 -04:00
return
}
req . URL . Path = unescapedPath
2026-06-08 06:28:30 -04:00
if ds . Spec . BasicAuth ( ) {
password , err := proxy . dataSource . DecryptedBasicAuthPassword ( req . Context ( ) )
2022-04-25 12:57:45 -04:00
if err != nil {
2022-09-20 12:32:06 -04:00
ctxLogger . Error ( "Error interpolating proxy url" , "error" , err )
2022-04-25 12:57:45 -04:00
return
}
2026-06-08 06:28:30 -04:00
req . Header . Set ( "Authorization" , util . GetBasicAuthHeader ( ds . Spec . BasicAuthUser ( ) ,
2022-04-25 12:57:45 -04:00
password ) )
2020-11-13 07:21:43 -05:00
}
2017-08-22 11:14:15 -04:00
2020-11-13 07:21:43 -05:00
dsAuth := req . Header . Get ( "X-DS-Authorization" )
if len ( dsAuth ) > 0 {
req . Header . Del ( "X-DS-Authorization" )
req . Header . Set ( "Authorization" , dsAuth )
}
2017-08-22 11:14:15 -04:00
2026-06-08 06:28:30 -04:00
proxyutil . ApplyUserHeader ( proxy . settings . SendUserHeader , req , proxy . requester )
2019-03-14 08:04:47 -04:00
2026-05-23 06:36:49 -04:00
proxyutil . ClearCookieHeader ( req , ds . Spec . KeepCookies ( ) , [ ] string { proxy . settings . LoginCookieName } )
2026-05-21 05:09:14 -04:00
ua := proxy . settings . DataProxyUserAgent
if proxy . settings . DataProxyForwardUserAgent {
2026-05-06 06:15:38 -04:00
if originalUA := req . Header . Get ( "User-Agent" ) ; originalUA != "" {
2026-05-13 04:17:20 -04:00
if len ( originalUA ) > maxForwardedUserAgentLen {
originalUA = originalUA [ : maxForwardedUserAgentLen ]
}
2026-05-06 06:15:38 -04:00
if ua != "" {
ua = ua + " " + originalUA
} else {
ua = originalUA
}
}
}
req . Header . Set ( "User-Agent" , ua )
2017-08-22 11:14:15 -04:00
2021-11-01 05:53:33 -04:00
if proxy . matchedRoute != nil {
2026-06-08 06:28:30 -04:00
decryptedValues , err := proxy . dataSource . DecryptedValues ( req . Context ( ) )
2022-04-25 12:57:45 -04:00
if err != nil {
2022-09-20 12:32:06 -04:00
ctxLogger . Error ( "Error interpolating proxy url" , "error" , err )
2022-04-25 12:57:45 -04:00
return
}
2026-06-08 06:28:30 -04:00
ApplyRoute ( req . Context ( ) , req , proxy . proxyPath , proxy . matchedRoute , dsInfo ( ds , decryptedValues ) , proxy . settings )
2020-11-13 07:21:43 -05:00
}
2019-02-01 19:40:57 -05:00
2026-05-23 06:36:49 -04:00
if ds . Spec . IsOAuthPassThruEnabled ( ) {
2026-06-08 06:28:30 -04:00
if token := proxy . oAuthTokenService . GetCurrentOAuthToken ( req . Context ( ) , proxy . requester , proxy . ctx . UserToken ) ; token != nil {
2020-11-13 07:21:43 -05:00
req . Header . Set ( "Authorization" , fmt . Sprintf ( "%s %s" , token . Type ( ) , token . AccessToken ) )
2021-11-29 09:40:05 -05:00
idToken , ok := token . Extra ( "id_token" ) . ( string )
if ok && idToken != "" {
req . Header . Set ( "X-ID-Token" , idToken )
}
2019-02-01 19:40:57 -05:00
}
2017-08-22 11:14:15 -04:00
}
2023-10-02 03:14:10 -04:00
2026-06-08 06:28:30 -04:00
proxyutil . ApplyForwardIDHeader ( req , proxy . requester )
}
// dsInfo builds the DSInfo that ApplyRoute needs from a v0 datasource.
func dsInfo ( ds * datasourcesV0 . DataSource , decryptedValues map [ string ] string ) DSInfo {
// JSONData may be absent or stored as a non-map value; ApplyRoute requires a non-nil map, so fall back to empty.
jsonData , ok := ds . Spec . JSONData ( ) . ( map [ string ] any )
if ! ok {
jsonData = make ( map [ string ] any )
}
meta , _ := utils . MetaAccessor ( ds )
updated := ds . CreationTimestamp . Time
if ts , _ := meta . GetUpdatedTimestamp ( ) ; ts != nil {
updated = * ts
}
return DSInfo {
ID : meta . GetDeprecatedInternalID ( ) , // nolint:staticcheck
URL : ds . Spec . URL ( ) ,
Updated : updated ,
JSONData : jsonData ,
DecryptedSecureJSONData : decryptedValues ,
}
2017-08-22 11:14:15 -04:00
}
func ( proxy * DataSourceProxy ) validateRequest ( ) error {
2024-01-23 06:36:22 -05:00
if ! proxy . checkWhiteList ( ) {
2020-11-05 07:07:06 -05:00
return errors . New ( "target URL is not a valid target" )
2017-08-22 11:14:15 -04:00
}
2026-06-08 06:28:30 -04:00
if proxy . dataSource . PluginType ( ) == datasources . DS_ES {
2021-09-01 05:18:30 -04:00
if proxy . ctx . Req . Method == "DELETE" {
2020-11-05 07:07:06 -05:00
return errors . New ( "deletes not allowed on proxied Elasticsearch datasource" )
2017-08-22 11:14:15 -04:00
}
2021-09-01 05:18:30 -04:00
if proxy . ctx . Req . Method == "PUT" {
2020-11-05 07:07:06 -05:00
return errors . New ( "puts not allowed on proxied Elasticsearch datasource" )
2017-08-22 11:14:15 -04:00
}
2021-09-01 05:18:30 -04:00
if proxy . ctx . Req . Method == "POST" && proxy . proxyPath != "_msearch" {
2020-11-05 07:07:06 -05:00
return errors . New ( "posts not allowed on proxied Elasticsearch datasource except on /_msearch" )
2017-08-22 11:14:15 -04:00
}
}
// found route if there are any
2021-11-01 05:53:33 -04:00
for _ , route := range proxy . pluginRoutes {
// method match
if route . Method != "" && route . Method != "*" && route . Method != proxy . ctx . Req . Method {
continue
}
2017-08-22 11:14:15 -04:00
2021-11-01 05:53:33 -04:00
// route match
2026-01-05 06:12:31 -05:00
r1 , err := plugins . CleanRelativePath ( proxy . proxyPath )
2025-04-24 15:15:17 -04:00
if err != nil {
return err
}
2026-01-05 06:12:31 -05:00
r2 , err := plugins . CleanRelativePath ( route . Path )
2025-04-24 15:15:17 -04:00
if err != nil {
return err
}
2026-01-14 15:12:18 -05:00
// issues/116273: When we have an empty input route (or input that becomes relative to "."), we do not want it
// to be ".". This is because the `CleanRelativePath` function will never return "./" prefixes, and as such,
// the common prefix we need is an empty string.
if r1 == "." && proxy . proxyPath != "." {
r1 = ""
}
if r2 == "." && route . Path != "." {
r2 = ""
}
2025-04-24 15:15:17 -04:00
if ! strings . HasPrefix ( r1 , r2 ) {
2021-11-01 05:53:33 -04:00
continue
}
2021-04-14 13:06:20 -04:00
2025-02-25 12:30:58 -05:00
if ! proxy . hasAccessToRoute ( route ) {
2026-01-14 15:12:18 -05:00
return errPluginProxyRouteAccessDenied
2021-04-14 13:06:20 -04:00
}
2021-11-01 05:53:33 -04:00
proxy . matchedRoute = route
return nil
2021-04-14 13:06:20 -04:00
}
// Trailing validation below this point for routes that were not matched
2026-06-08 06:28:30 -04:00
switch proxy . dataSource . PluginType ( ) {
case datasources . DS_PROMETHEUS ,
datasources . DS_AMAZON_PROMETHEUS ,
datasources . DS_AZURE_PROMETHEUS ,
datasources . DS_LOKI :
switch proxy . ctx . Req . Method {
case "DELETE" , "PUT" , "POST" :
return fmt . Errorf ( "non allow-listed %ss not allowed on proxied %s datasource" , proxy . ctx . Req . Method , proxy . dataSource . PluginType ( ) )
2021-04-29 03:20:51 -04:00
}
2017-08-22 11:14:15 -04:00
}
return nil
}
2024-05-21 09:05:16 -04:00
func ( proxy * DataSourceProxy ) hasAccessToRoute ( route * plugins . Route ) bool {
ctxLogger := logger . FromContext ( proxy . ctx . Req . Context ( ) )
2025-02-25 07:44:40 -05:00
if route . ReqAction != "" {
2026-06-08 06:28:30 -04:00
routeEval := pluginac . GetDataSourceRouteEvaluator ( proxy . ds . Name , route . ReqAction )
hasAccess := routeEval . Evaluate ( proxy . requester . GetPermissions ( ) )
2024-07-25 10:22:42 -04:00
if ! hasAccess {
2024-05-21 09:05:16 -04:00
ctxLogger . Debug ( "plugin route is covered by RBAC, user doesn't have access" , "route" , proxy . ctx . Req . URL . Path , "action" , route . ReqAction , "path" , route . Path , "method" , route . Method )
}
2024-07-25 10:22:42 -04:00
return hasAccess
2024-05-21 09:05:16 -04:00
}
if route . ReqRole . IsValid ( ) {
2026-06-08 06:28:30 -04:00
if hasUserRole := proxy . requester . GetOrgRole ( ) . Includes ( route . ReqRole ) ; ! hasUserRole {
2024-05-21 09:05:16 -04:00
ctxLogger . Debug ( "plugin route is covered by org role, user doesn't have access" , "route" , proxy . ctx . Req . URL . Path , "role" , route . ReqRole , "path" , route . Path , "method" , route . Method )
return false
}
}
return true
}
2017-08-22 11:14:15 -04:00
func ( proxy * DataSourceProxy ) logRequest ( ) {
2026-05-21 05:09:14 -04:00
if ! proxy . settings . DataProxyLogging {
2017-08-22 11:14:15 -04:00
return
}
var body string
2021-09-01 05:18:30 -04:00
if proxy . ctx . Req . Body != nil {
2022-08-10 09:37:51 -04:00
buffer , err := io . ReadAll ( proxy . ctx . Req . Body )
2017-08-22 11:14:15 -04:00
if err == nil {
2022-08-10 09:37:51 -04:00
proxy . ctx . Req . Body = io . NopCloser ( bytes . NewBuffer ( buffer ) )
2017-08-22 11:14:15 -04:00
body = string ( buffer )
}
}
2026-06-08 06:28:30 -04:00
ctxLogger := logger . FromContext ( proxy . ctx . Req . Context ( ) )
2024-02-21 03:38:42 -05:00
panelPluginId := proxy . ctx . Req . Header . Get ( "X-Panel-Plugin-Id" )
2024-01-30 03:06:31 -05:00
2024-06-24 08:53:42 -04:00
uri , err := util . SanitizeURI ( proxy . ctx . Req . RequestURI )
2024-10-28 10:34:07 -04:00
if err != nil {
2026-06-08 06:28:30 -04:00
ctxLogger . Error ( "Could not sanitize RequestURI" , "error" , err )
2024-06-24 08:53:42 -04:00
}
2026-06-08 06:28:30 -04:00
userid , _ := proxy . requester . GetInternalID ( )
2022-09-20 12:32:06 -04:00
ctxLogger . Info ( "Proxying incoming request" ,
2026-06-08 06:28:30 -04:00
"userid" , userid ,
"orgid" , proxy . requester . GetOrgID ( ) ,
"username" , proxy . requester . GetLogin ( ) ,
"datasource" , proxy . dataSource . PluginType ( ) ,
2024-06-24 08:53:42 -04:00
"uri" , uri ,
2021-09-01 05:18:30 -04:00
"method" , proxy . ctx . Req . Method ,
2024-02-21 03:38:42 -05:00
"panelPluginId" , panelPluginId ,
2017-08-22 11:14:15 -04:00
"body" , body )
}
2024-01-23 06:36:22 -05:00
func ( proxy * DataSourceProxy ) checkWhiteList ( ) bool {
2026-05-21 05:09:14 -04:00
if proxy . targetUrl . Host != "" && len ( proxy . settings . DataProxyWhiteList ) > 0 {
if _ , exists := proxy . settings . DataProxyWhiteList [ proxy . targetUrl . Host ] ; ! exists {
2026-06-08 06:28:30 -04:00
writeJSONErr ( proxy . ctx . Resp , proxy . ctx . Req , 403 , "Data proxy hostname and ip are not included in whitelist" , nil )
2017-08-22 11:14:15 -04:00
return false
}
}
return true
}