* add legacy search (wip) * fix search field name * implement team search endpoint * generate openapi spec * generate endpoints for frontend * minor fixes * fix issues found while testing * add more fields to search result * add basic unit tests * add more unit tests * improve getColumns() func in legacy search * configure search endpoint in team.cue * add team search handler * add the searchTeams endpoint to manifest.cue * make gofmt * update openapi spec * generate frontend endpoints * remove unused field * move fields defiitions to separate builder * fix legacy search * fix unit tests * fix unit test * address feedback * fix unit test * update openapi specs * yarn generate-apis * add missing unit tests
107 lines
3.8 KiB
Go
107 lines
3.8 KiB
Go
package iam
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
authlib "github.com/grafana/authlib/types"
|
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
|
|
|
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
|
"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"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
gfauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
|
|
)
|
|
|
|
type iamAuthorizer struct {
|
|
resourceAuthorizer map[string]authorizer.Authorizer // Map resource to its authorizer
|
|
}
|
|
|
|
func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient authlib.AccessClient) authorizer.Authorizer {
|
|
resourceAuthorizer := make(map[string]authorizer.Authorizer)
|
|
|
|
// Authorizer that allows any authenticated user
|
|
// To be used when authorization is handled at the storage layer
|
|
allowAuthorizer := 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
|
|
})
|
|
|
|
// Identity specific resources
|
|
legacyAuthorizer := gfauthorizer.NewResourceAuthorizer(legacyAccessClient)
|
|
resourceAuthorizer[iamv0.TeamBindingResourceInfo.GetName()] = legacyAuthorizer
|
|
resourceAuthorizer["display"] = legacyAuthorizer
|
|
|
|
// Access specific resources
|
|
authorizer := gfauthorizer.NewResourceAuthorizer(accessClient)
|
|
resourceAuthorizer[iamv0.CoreRoleInfo.GetName()] = iamauthorizer.NewCoreRoleAuthorizer(accessClient)
|
|
resourceAuthorizer[iamv0.RoleInfo.GetName()] = authorizer
|
|
resourceAuthorizer[iamv0.ResourcePermissionInfo.GetName()] = allowAuthorizer // Handled by the backend wrapper
|
|
resourceAuthorizer[iamv0.RoleBindingInfo.GetName()] = authorizer
|
|
resourceAuthorizer[iamv0.ServiceAccountResourceInfo.GetName()] = authorizer
|
|
resourceAuthorizer[iamv0.UserResourceInfo.GetName()] = authorizer
|
|
resourceAuthorizer[iamv0.ExternalGroupMappingResourceInfo.GetName()] = authorizer
|
|
resourceAuthorizer[iamv0.TeamResourceInfo.GetName()] = authorizer
|
|
|
|
serviceAuthorizer := gfauthorizer.NewServiceAuthorizer()
|
|
resourceAuthorizer["searchTeams"] = serviceAuthorizer
|
|
|
|
return &iamAuthorizer{resourceAuthorizer: resourceAuthorizer}
|
|
}
|
|
|
|
func (s *iamAuthorizer) Authorize(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) {
|
|
if !attr.IsResourceRequest() {
|
|
return authorizer.DecisionNoOpinion, "", nil
|
|
}
|
|
|
|
authz, ok := s.resourceAuthorizer[attr.GetResource()]
|
|
if !ok {
|
|
return authorizer.DecisionDeny, "", fmt.Errorf("no authorizer found for resource %s", attr.GetResource())
|
|
}
|
|
|
|
return authz.Authorize(ctx, attr)
|
|
}
|
|
|
|
func newLegacyAccessClient(ac accesscontrol.AccessControl, store legacy.LegacyIdentityStore) authlib.AccessClient {
|
|
client := accesscontrol.NewLegacyAccessClient(
|
|
ac,
|
|
accesscontrol.ResourceAuthorizerOptions{
|
|
Resource: "display",
|
|
Unchecked: map[string]bool{
|
|
utils.VerbGet: true,
|
|
utils.VerbList: true,
|
|
},
|
|
},
|
|
accesscontrol.ResourceAuthorizerOptions{
|
|
Resource: "searchTeams",
|
|
Unchecked: map[string]bool{
|
|
utils.VerbGet: true,
|
|
utils.VerbList: true,
|
|
},
|
|
},
|
|
accesscontrol.ResourceAuthorizerOptions{
|
|
Resource: iamv0.TeamResourceInfo.GetName(),
|
|
Attr: "id",
|
|
Resolver: accesscontrol.ResourceResolverFunc(func(ctx context.Context, ns authlib.NamespaceInfo, name string) ([]string, error) {
|
|
res, err := store.GetTeamInternalID(ctx, ns, legacy.GetTeamInternalIDQuery{
|
|
UID: name,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return []string{fmt.Sprintf("teams:id:%d", res.ID)}, nil
|
|
}),
|
|
},
|
|
)
|
|
|
|
return client
|
|
}
|