Compare commits
6 Commits
v11.4.7
...
release-11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7834551853 | ||
|
|
8c046836eb | ||
|
|
853b11c138 | ||
|
|
284f22d41b | ||
|
|
1c7de19702 | ||
|
|
39c44e2afb |
46
.drone.yml
46
.drone.yml
@@ -721,10 +721,8 @@ steps:
|
||||
from_secret: docker_password
|
||||
DOCKER_USER:
|
||||
from_secret: docker_username
|
||||
GITHUB_APP_ID:
|
||||
from_secret: delivery-bot-app-id
|
||||
GITHUB_APP_INSTALLATION_ID:
|
||||
from_secret: delivery-bot-app-installation-id
|
||||
GITHUB_APP_ID: "329617"
|
||||
GITHUB_APP_INSTALLATION_ID: "37346161"
|
||||
GITHUB_APP_PRIVATE_KEY:
|
||||
from_secret: delivery-bot-app-private-key
|
||||
failure: ignore
|
||||
@@ -2190,10 +2188,8 @@ steps:
|
||||
from_secret: docker_username
|
||||
GCP_KEY:
|
||||
from_secret: gcp_grafanauploads
|
||||
GITHUB_APP_ID:
|
||||
from_secret: delivery-bot-app-id
|
||||
GITHUB_APP_INSTALLATION_ID:
|
||||
from_secret: delivery-bot-app-installation-id
|
||||
GITHUB_APP_ID: "329617"
|
||||
GITHUB_APP_INSTALLATION_ID: "37346161"
|
||||
GITHUB_APP_PRIVATE_KEY:
|
||||
from_secret: delivery-bot-app-private-key
|
||||
image: google/cloud-sdk:431.0.0
|
||||
@@ -2494,10 +2490,8 @@ steps:
|
||||
from_secret: docker_username
|
||||
GCP_KEY:
|
||||
from_secret: gcp_grafanauploads
|
||||
GITHUB_APP_ID:
|
||||
from_secret: delivery-bot-app-id
|
||||
GITHUB_APP_INSTALLATION_ID:
|
||||
from_secret: delivery-bot-app-installation-id
|
||||
GITHUB_APP_ID: "329617"
|
||||
GITHUB_APP_INSTALLATION_ID: "37346161"
|
||||
GITHUB_APP_PRIVATE_KEY:
|
||||
from_secret: delivery-bot-app-private-key
|
||||
image: google/cloud-sdk:431.0.0
|
||||
@@ -3555,10 +3549,8 @@ steps:
|
||||
from_secret: docker_username
|
||||
GCP_KEY:
|
||||
from_secret: gcp_grafanauploads
|
||||
GITHUB_APP_ID:
|
||||
from_secret: delivery-bot-app-id
|
||||
GITHUB_APP_INSTALLATION_ID:
|
||||
from_secret: delivery-bot-app-installation-id
|
||||
GITHUB_APP_ID: "329617"
|
||||
GITHUB_APP_INSTALLATION_ID: "37346161"
|
||||
GITHUB_APP_PRIVATE_KEY:
|
||||
from_secret: delivery-bot-app-private-key
|
||||
image: google/cloud-sdk:431.0.0
|
||||
@@ -5539,13 +5531,13 @@ name: prerelease_bucket
|
||||
---
|
||||
get:
|
||||
name: username
|
||||
path: infra/data/ci/grafanaci-docker-hub
|
||||
path: ci/data/common/dockerhub
|
||||
kind: secret
|
||||
name: docker_username
|
||||
---
|
||||
get:
|
||||
name: password
|
||||
path: infra/data/ci/grafanaci-docker-hub
|
||||
path: ci/data/common/dockerhub
|
||||
kind: secret
|
||||
name: docker_password
|
||||
---
|
||||
@@ -5664,20 +5656,8 @@ kind: secret
|
||||
name: dagger_token
|
||||
---
|
||||
get:
|
||||
name: app-id
|
||||
path: infra/data/ci/grafana-release-eng/grafana-delivery-bot
|
||||
kind: secret
|
||||
name: delivery-bot-app-id
|
||||
---
|
||||
get:
|
||||
name: app-installation-id
|
||||
path: infra/data/ci/grafana-release-eng/grafana-delivery-bot
|
||||
kind: secret
|
||||
name: delivery-bot-app-installation-id
|
||||
---
|
||||
get:
|
||||
name: app-private-key
|
||||
path: infra/data/ci/grafana-release-eng/grafana-delivery-bot
|
||||
name: PRIVATE_KEY
|
||||
path: ci/data/repo/grafana/grafana/delivery-bot-app
|
||||
kind: secret
|
||||
name: delivery-bot-app-private-key
|
||||
---
|
||||
@@ -5688,6 +5668,6 @@ kind: secret
|
||||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: 10351df8b46f884d83178ff3abb14c25eee0a0bd7498d78e0e79d0d6f81c1f9b
|
||||
hmac: 78ac28b59a6da5289b69c043c12032c3820755dc0d6f1757ec77f0d1eff29650
|
||||
|
||||
...
|
||||
|
||||
@@ -299,7 +299,15 @@ func (proxy *DataSourceProxy) validateRequest() error {
|
||||
}
|
||||
|
||||
// route match
|
||||
if !strings.HasPrefix(proxy.proxyPath, route.Path) {
|
||||
r1, err := util.CleanRelativePath(proxy.proxyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r2, err := util.CleanRelativePath(route.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.HasPrefix(r1, r2) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -258,6 +258,14 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
err = proxy.validateRequest()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("path with slashes and user is editor", func(t *testing.T) {
|
||||
ctx, _ := setUp()
|
||||
proxy, err := setupDSProxyTest(t, ctx, ds, routes, "//api//admin")
|
||||
require.NoError(t, err)
|
||||
err = proxy.validateRequest()
|
||||
require.Error(t, err)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("plugin route with RBAC protection user is allowed", func(t *testing.T) {
|
||||
|
||||
@@ -159,16 +159,17 @@ func staticHandler(ctx *web.Context, log log.Logger, opt StaticOptions) bool {
|
||||
if fi.IsDir() {
|
||||
// Redirect if missing trailing slash.
|
||||
if !strings.HasSuffix(ctx.Req.URL.Path, "/") {
|
||||
path := fmt.Sprintf("%s/", ctx.Req.URL.Path)
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
redirectPath := path.Clean(ctx.Req.URL.Path)
|
||||
redirectPath = fmt.Sprintf("%s/", redirectPath)
|
||||
if !strings.HasPrefix(redirectPath, "/") {
|
||||
// Disambiguate that it's a path relative to this server
|
||||
path = fmt.Sprintf("/%s", path)
|
||||
redirectPath = fmt.Sprintf("/%s", redirectPath)
|
||||
} else {
|
||||
// A string starting with // or /\ is interpreted by browsers as a URL, and not a server relative path
|
||||
rePrefix := regexp.MustCompile(`^(?:/\\|/+)`)
|
||||
path = rePrefix.ReplaceAllString(path, "/")
|
||||
redirectPath = rePrefix.ReplaceAllString(redirectPath, "/")
|
||||
}
|
||||
http.Redirect(ctx.Resp, ctx.Req, path, http.StatusFound)
|
||||
http.Redirect(ctx.Resp, ctx.Req, redirectPath, http.StatusFound)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
174
pkg/api/static/static_test.go
Normal file
174
pkg/api/static/static_test.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package httpstatic
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/models/usertoken"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatic(t *testing.T) {
|
||||
// Create a temporary directory for test files
|
||||
tmpDir, err := os.MkdirTemp("", "static-test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Create test files
|
||||
testFiles := map[string]string{
|
||||
"test.txt": "Test content",
|
||||
"subdir/test.txt": "Subdir content",
|
||||
}
|
||||
|
||||
for path, content := range testFiles {
|
||||
fullPath := filepath.Join(tmpDir, path)
|
||||
err := os.MkdirAll(filepath.Dir(fullPath), 0o755)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(fullPath, []byte(content), 0o644)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
dir string
|
||||
name string
|
||||
path string
|
||||
options StaticOptions
|
||||
expectedStatus int
|
||||
expectedBody string
|
||||
expectedLocation string
|
||||
}{
|
||||
{
|
||||
name: "should serve existing file",
|
||||
path: "/test.txt",
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: "Test content",
|
||||
dir: tmpDir,
|
||||
},
|
||||
{
|
||||
name: "should serve file from subdirectory",
|
||||
path: "/subdir/test.txt",
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: "Subdir content",
|
||||
dir: tmpDir,
|
||||
},
|
||||
|
||||
{
|
||||
name: "should redirect directory without trailing slash",
|
||||
path: "/subdir",
|
||||
expectedStatus: http.StatusFound,
|
||||
expectedLocation: "/subdir/",
|
||||
dir: tmpDir,
|
||||
},
|
||||
{
|
||||
name: "should handle prefix",
|
||||
path: "/static/test.txt",
|
||||
options: StaticOptions{Prefix: "/static"},
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: "Test content",
|
||||
dir: tmpDir,
|
||||
},
|
||||
{
|
||||
name: "should handle excluded path",
|
||||
path: "/test.txt",
|
||||
options: StaticOptions{Exclude: []string{"/test.txt"}},
|
||||
expectedStatus: http.StatusNotFound,
|
||||
dir: tmpDir,
|
||||
},
|
||||
{
|
||||
name: "should add custom headers",
|
||||
path: "/test.txt",
|
||||
options: StaticOptions{AddHeaders: func(ctx *web.Context) { ctx.Resp.Header().Set("X-Test", "test") }},
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: "Test content",
|
||||
dir: tmpDir,
|
||||
},
|
||||
{
|
||||
name: "should clean up path before redirecting",
|
||||
path: "/subdir/..%2F%5C127.0.0.1:80%2F%3F%2F..%2F..",
|
||||
options: StaticOptions{Prefix: "subdir"},
|
||||
expectedStatus: http.StatusFound,
|
||||
expectedLocation: "/",
|
||||
dir: tmpDir,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sc := setupScenarioContext(t, "")
|
||||
sc.m.Use(Static(tt.dir, tt.options))
|
||||
|
||||
// Create a test request
|
||||
req := httptest.NewRequest("GET", tt.path, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Execute the handler
|
||||
sc.m.ServeHTTP(w, req)
|
||||
|
||||
// Verify the response
|
||||
resp := w.Result()
|
||||
require.Equal(t, tt.expectedStatus, resp.StatusCode)
|
||||
|
||||
if tt.expectedBody != "" {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedBody, string(body))
|
||||
}
|
||||
|
||||
if tt.options.AddHeaders != nil {
|
||||
assert.Equal(t, "test", resp.Header.Get("X-Test"))
|
||||
}
|
||||
|
||||
if tt.expectedLocation != "" {
|
||||
assert.Equal(t, tt.expectedLocation, resp.Header.Get("Location"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type scenarioContext struct {
|
||||
t *testing.T
|
||||
cfg *setting.Cfg
|
||||
m *web.Mux
|
||||
ctxHdlr *contexthandler.ContextHandler
|
||||
}
|
||||
|
||||
func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHandler {
|
||||
t.Helper()
|
||||
|
||||
if cfg == nil {
|
||||
cfg = setting.NewCfg()
|
||||
}
|
||||
|
||||
return contexthandler.ProvideService(
|
||||
cfg,
|
||||
&authntest.FakeService{ExpectedIdentity: &authn.Identity{ID: "0", Type: claims.TypeAnonymous, SessionToken: &usertoken.UserToken{}}},
|
||||
featuremgmt.WithFeatures(),
|
||||
)
|
||||
}
|
||||
|
||||
func setupScenarioContext(t *testing.T, url string) *scenarioContext {
|
||||
cfg := setting.NewCfg()
|
||||
ctxHdlr := getContextHandler(t, cfg)
|
||||
sc := &scenarioContext{
|
||||
t: t,
|
||||
cfg: cfg,
|
||||
ctxHdlr: ctxHdlr,
|
||||
}
|
||||
|
||||
sc.m = web.New()
|
||||
sc.m.Use(ctxHdlr.Middleware)
|
||||
|
||||
return sc
|
||||
}
|
||||
@@ -39,7 +39,7 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "api/v1/alerts",
|
||||
"reqRole": "Admin"
|
||||
"reqRole": "Editor"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
|
||||
@@ -641,7 +641,8 @@ function fieldValueColors(f: Field, theme: GrafanaTheme2): FieldColorValues {
|
||||
let lasti = steps.length - 1;
|
||||
|
||||
for (let i = lasti; i > 0; i--) {
|
||||
conds += `v >= ${steps[i].value} ? ${i} : `;
|
||||
let rhs = Number(steps[i].value);
|
||||
conds += `v >= ${rhs} ? ${i} : `;
|
||||
}
|
||||
|
||||
conds += '0';
|
||||
|
||||
@@ -954,8 +954,8 @@ def publish_images_step(ver_mode, docker_repo, trigger = None, depends_on = ["rg
|
||||
"GCP_KEY": from_secret(gcp_grafanauploads),
|
||||
"DOCKER_USER": from_secret("docker_username"),
|
||||
"DOCKER_PASSWORD": from_secret("docker_password"),
|
||||
"GITHUB_APP_ID": from_secret("delivery-bot-app-id"),
|
||||
"GITHUB_APP_INSTALLATION_ID": from_secret("delivery-bot-app-installation-id"),
|
||||
"GITHUB_APP_ID": "329617",
|
||||
"GITHUB_APP_INSTALLATION_ID": "37346161",
|
||||
"GITHUB_APP_PRIVATE_KEY": from_secret("delivery-bot-app-private-key"),
|
||||
}
|
||||
|
||||
@@ -972,8 +972,8 @@ def publish_images_step(ver_mode, docker_repo, trigger = None, depends_on = ["rg
|
||||
environment = {
|
||||
"DOCKER_USER": from_secret("docker_username"),
|
||||
"DOCKER_PASSWORD": from_secret("docker_password"),
|
||||
"GITHUB_APP_ID": from_secret("delivery-bot-app-id"),
|
||||
"GITHUB_APP_INSTALLATION_ID": from_secret("delivery-bot-app-installation-id"),
|
||||
"GITHUB_APP_ID": "329617",
|
||||
"GITHUB_APP_INSTALLATION_ID": "37346161",
|
||||
"GITHUB_APP_PRIVATE_KEY": from_secret("delivery-bot-app-private-key"),
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ def secrets():
|
||||
vault_secret(gar_pull_secret, "secret/data/common/gar", ".dockerconfigjson"),
|
||||
vault_secret(drone_token, "infra/data/ci/drone", "machine-user-token"),
|
||||
vault_secret(prerelease_bucket, "infra/data/ci/grafana/prerelease", "bucket"),
|
||||
vault_secret(docker_username, "infra/data/ci/grafanaci-docker-hub", "username"),
|
||||
vault_secret(docker_password, "infra/data/ci/grafanaci-docker-hub", "password"),
|
||||
vault_secret(docker_username, "ci/data/common/dockerhub", "username"),
|
||||
vault_secret(docker_password, "ci/data/common/dockerhub", "password"),
|
||||
vault_secret(
|
||||
gcp_upload_artifacts_key,
|
||||
"infra/data/ci/grafana/releng/artifacts-uploader-service-account",
|
||||
@@ -153,21 +153,10 @@ def secrets():
|
||||
"infra/data/ci/grafana-release-eng/rgm",
|
||||
"dagger_token",
|
||||
),
|
||||
# grafana-delivery-bot secrets
|
||||
vault_secret(
|
||||
"delivery-bot-app-id",
|
||||
"infra/data/ci/grafana-release-eng/grafana-delivery-bot",
|
||||
"app-id",
|
||||
),
|
||||
vault_secret(
|
||||
"delivery-bot-app-installation-id",
|
||||
"infra/data/ci/grafana-release-eng/grafana-delivery-bot",
|
||||
"app-installation-id",
|
||||
),
|
||||
vault_secret(
|
||||
"delivery-bot-app-private-key",
|
||||
"infra/data/ci/grafana-release-eng/grafana-delivery-bot",
|
||||
"app-private-key",
|
||||
"ci/data/repo/grafana/grafana/delivery-bot-app",
|
||||
"PRIVATE_KEY",
|
||||
),
|
||||
vault_secret(
|
||||
"gcr_credentials",
|
||||
|
||||
Reference in New Issue
Block a user