From da374527f2118154bb347132edac62384fe7f552 Mon Sep 17 00:00:00 2001 From: Ezequiel Victorero Date: Tue, 25 Nov 2025 16:34:10 -0300 Subject: [PATCH] ShortURL: K8s Implement custom authorizer (#114192) --- apps/shorturl/go.mod | 2 +- apps/shorturl/pkg/app/authorizer.go | 20 ++++++++++++++++++++ pkg/registry/apps/shorturl/register.go | 5 +++++ pkg/tests/api/shorturl/short_url_test.go | 2 +- pkg/tests/apis/shorturl/shorturl_test.go | 16 ++++++++-------- 5 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 apps/shorturl/pkg/app/authorizer.go diff --git a/apps/shorturl/go.mod b/apps/shorturl/go.mod index b3db27ba205..740aea32a72 100644 --- a/apps/shorturl/go.mod +++ b/apps/shorturl/go.mod @@ -7,6 +7,7 @@ require ( github.com/grafana/grafana-app-sdk/logging v0.48.1 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250915132226-585b53bc7dba k8s.io/apimachinery v0.34.2 + k8s.io/apiserver v0.34.2 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 ) @@ -90,7 +91,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.34.2 // indirect k8s.io/apiextensions-apiserver v0.34.2 // indirect - k8s.io/apiserver v0.34.2 // indirect k8s.io/client-go v0.34.2 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect diff --git a/apps/shorturl/pkg/app/authorizer.go b/apps/shorturl/pkg/app/authorizer.go new file mode 100644 index 00000000000..4cde905414d --- /dev/null +++ b/apps/shorturl/pkg/app/authorizer.go @@ -0,0 +1,20 @@ +package app + +import ( + "context" + + "k8s.io/apiserver/pkg/authorization/authorizer" +) + +func GetAuthorizer() authorizer.Authorizer { + return authorizer.AuthorizerFunc(func( + ctx context.Context, attr authorizer.Attributes, + ) (authorized authorizer.Decision, reason string, err error) { + if !attr.IsResourceRequest() { + return authorizer.DecisionNoOpinion, "", nil + } + + // Any authenticated user can access the API + return authorizer.DecisionAllow, "", nil + }) +} diff --git a/pkg/registry/apps/shorturl/register.go b/pkg/registry/apps/shorturl/register.go index c5d314ff2bc..e15950dbc56 100644 --- a/pkg/registry/apps/shorturl/register.go +++ b/pkg/registry/apps/shorturl/register.go @@ -5,6 +5,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/registry/rest" restclient "k8s.io/client-go/rest" @@ -55,6 +56,10 @@ func RegisterAppInstaller( return installer, nil } +func (a *ShortURLAppInstaller) GetAuthorizer() authorizer.Authorizer { + return shorturlapp.GetAuthorizer() +} + func (s *ShortURLAppInstaller) GetLegacyStorage(requested schema.GroupVersionResource) grafanarest.Storage { gvr := shorturl.ShortURLKind().GroupVersionResource() if requested.String() != gvr.String() { diff --git a/pkg/tests/api/shorturl/short_url_test.go b/pkg/tests/api/shorturl/short_url_test.go index cc014662457..e3c4d10c675 100644 --- a/pkg/tests/api/shorturl/short_url_test.go +++ b/pkg/tests/api/shorturl/short_url_test.go @@ -42,7 +42,7 @@ func TestShortURL(t *testing.T) { // Test that the endpoint is accessible with authentication. username, password := "viewer", "viewer" createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ - DefaultOrgRole: string(org.RoleEditor), + DefaultOrgRole: string(org.RoleNone), Password: user.Password(password), Login: username, }) diff --git a/pkg/tests/apis/shorturl/shorturl_test.go b/pkg/tests/apis/shorturl/shorturl_test.go index be7023c0c6a..becb0b2b564 100644 --- a/pkg/tests/apis/shorturl/shorturl_test.go +++ b/pkg/tests/apis/shorturl/shorturl_test.go @@ -123,7 +123,7 @@ func TestIntegrationShortURL(t *testing.T) { // Only legacy API should be used, no K8s API interaction func doLegacyOnlyTests(t *testing.T, helper *apis.K8sTestHelper) { client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Editor, + User: helper.Org1.None, GVR: gvr, }) @@ -189,7 +189,7 @@ func doDualWriteTests(t *testing.T, helper *apis.K8sTestHelper, mode grafanarest t.Run("Legacy API -> K8s API visibility", func(t *testing.T) { client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Editor, + User: helper.Org1.None, GVR: gvr, }) @@ -223,7 +223,7 @@ func doDualWriteTests(t *testing.T, helper *apis.K8sTestHelper, mode grafanarest t.Run("K8s API -> Legacy API visibility", func(t *testing.T) { client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Editor, + User: helper.Org1.None, GVR: gvr, }) @@ -259,7 +259,7 @@ func doDualWriteTests(t *testing.T, helper *apis.K8sTestHelper, mode grafanarest t.Run("Redirect functionality", func(t *testing.T) { t.Skip("Skipping redirect functionality tests for now - flaky test") client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Editor, + User: helper.Org1.None, GVR: gvr, }) @@ -312,7 +312,7 @@ func doUnifiedOnlyTests(t *testing.T, helper *apis.K8sTestHelper) { t.Run("K8s API CRUD (unified storage only)", func(t *testing.T) { client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Editor, + User: helper.Org1.None, GVR: gvr, }) @@ -349,7 +349,7 @@ func doUnifiedOnlyTests(t *testing.T, helper *apis.K8sTestHelper) { t.Run("K8s API validation - invalid paths", func(t *testing.T) { client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Editor, + User: helper.Org1.None, GVR: gvr, }) @@ -402,7 +402,7 @@ func doUnifiedOnlyTests(t *testing.T, helper *apis.K8sTestHelper) { t.Run("K8s API validation - valid edge cases", func(t *testing.T) { client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Editor, + User: helper.Org1.None, GVR: gvr, }) @@ -441,7 +441,7 @@ func doUnifiedOnlyTests(t *testing.T, helper *apis.K8sTestHelper) { t.Run("Redirect functionality (unified only)", func(t *testing.T) { client := helper.GetResourceClient(apis.ResourceClientArgs{ - User: helper.Org1.Editor, + User: helper.Org1.None, GVR: gvr, })