Add custom authorizer, update noop search

This commit is contained in:
Mihaly Gyongyosi
2026-01-14 17:13:25 +01:00
parent 039bec7c18
commit 13a921a9fa
4 changed files with 183 additions and 13 deletions
+18 -1
View File
@@ -8,6 +8,7 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
iamauthorizer "github.com/grafana/grafana/pkg/registry/apis/iam/authorizer"
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
@@ -40,6 +41,22 @@ func newIAMAuthorizer(
return authorizer.DecisionAllow, "", nil
})
serviceIdentityAuthorizer := authorizer.AuthorizerFunc(func(
ctx context.Context, attr authorizer.Attributes,
) (authorized authorizer.Decision, reason string, err error) {
if identity.IsServiceIdentity(ctx) {
// A Grafana sub-system should have full access. We trust them to make wise decisions.
return authorizer.DecisionAllow, "", nil
}
req, err := identity.GetRequester(ctx)
if err == nil && req != nil && req.GetIsGrafanaAdmin() {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionDeny, "", nil
})
// Identity specific resources
legacyAuthorizer := gfauthorizer.NewResourceAuthorizer(legacyAccessClient)
resourceAuthorizer["display"] = legacyAuthorizer
@@ -58,7 +75,7 @@ func newIAMAuthorizer(
resourceAuthorizer["searchUsers"] = serviceAuthorizer
resourceAuthorizer["searchTeams"] = serviceAuthorizer
// TODO: Implement fine-grained authorization for external group mapping search on the search level
resourceAuthorizer["searchExternalGroupMappings"] = allowAuthorizer
resourceAuthorizer["searchExternalGroupMappings"] = serviceIdentityAuthorizer
return &iamAuthorizer{resourceAuthorizer: resourceAuthorizer}
}
@@ -97,8 +97,8 @@ func (r *TeamBindingAuthorizer) beforeWrite(ctx context.Context, obj runtime.Obj
teamName := concreteObj.Spec.TeamRef.Name
checkReq := types.CheckRequest{
Namespace: authInfo.GetNamespace(),
Group: iamv0.GROUP,
Resource: iamv0.TeamResourceInfo.GetName(),
Group: iamv0.TeamResourceInfo.GroupResource().Group,
Resource: iamv0.TeamResourceInfo.GroupResource().Resource,
Verb: utils.VerbSetPermissions,
Name: teamName,
}
@@ -28,11 +28,40 @@ func (n *NoopSearchREST) GetAPIRoutes(defs map[string]common.OpenAPIDefinition)
{
Path: "searchExternalGroupMappings",
Spec: &spec3.PathProps{
Get: &spec3.Operation{
Post: &spec3.Operation{
OperationProps: spec3.OperationProps{
Description: "External Group Mapping search",
Tags: []string{"Search"},
OperationId: "searchExternalGroupMappings",
RequestBody: &spec3.RequestBody{
RequestBodyProps: spec3.RequestBodyProps{
Content: map[string]*spec3.MediaType{
"application/json": {
MediaTypeProps: spec3.MediaTypeProps{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"externalGroups": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
},
},
},
},
},
},
},
},
},
},
},
Parameters: []*spec3.Parameter{
{
ParameterProps: spec3.ParameterProps{
@@ -44,15 +73,6 @@ func (n *NoopSearchREST) GetAPIRoutes(defs map[string]common.OpenAPIDefinition)
Schema: spec.StringProperty(),
},
},
{
ParameterProps: spec3.ParameterProps{
Name: "externalGroup",
In: "query",
Required: false,
Description: "External group name",
Schema: spec.StringProperty(),
},
},
{
ParameterProps: spec3.ParameterProps{
Name: "teamName",
@@ -912,6 +912,139 @@
}
]
},
"/apis/iam.grafana.app/v0alpha1/namespaces/{namespace}/searchExternalGroupMappings": {
"post": {
"tags": [
"Search"
],
"description": "External Group Mapping search",
"operationId": "searchExternalGroupMappings",
"parameters": [
{
"name": "namespace",
"in": "path",
"description": "workspace",
"required": true,
"schema": {
"type": "string"
},
"example": "default"
},
{
"name": "teamName",
"in": "query",
"description": "Team name",
"schema": {
"type": "string"
}
},
{
"name": "limit",
"in": "query",
"description": "number of results to return",
"schema": {
"type": "integer",
"format": "int64"
},
"example": 30
},
{
"name": "page",
"in": "query",
"description": "page number (starting from 1)",
"schema": {
"type": "integer",
"format": "int64"
},
"example": 1
},
{
"name": "offset",
"in": "query",
"description": "number of results to skip",
"schema": {
"type": "integer",
"format": "int64"
},
"example": 0
},
{
"name": "sort",
"in": "query",
"description": "sortable field",
"schema": {
"type": "string"
},
"examples": {
"": {
"summary": "default sorting",
"value": "externalGroup"
},
"-externalGroup": {
"summary": "externalGroup descending",
"value": "-externalGroup"
},
"externalGroup": {
"summary": "externalGroup ascending",
"value": "externalGroup"
}
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"externalGroups": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
},
"responses": {
"default": {
"description": "Default OK response",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"metadata",
"items"
],
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"items": {
"type": "array",
"items": {
"default": {}
}
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"metadata": {
"default": {}
}
}
}
}
}
}
}
}
},
"/apis/iam.grafana.app/v0alpha1/namespaces/{namespace}/searchTeams": {
"get": {
"tags": [