@@ -6,55 +6,98 @@ import (
66
77 "k8s.io/apiserver/pkg/authorization/authorizer"
88
9+ "github.com/grafana/authlib/authz"
10+ "github.com/grafana/grafana-app-sdk/logging"
911 "github.com/grafana/grafana/pkg/apimachinery/identity"
1012 "github.com/grafana/grafana/pkg/registry/apis/preferences/utils"
1113)
1214
13- func (b * APIBuilder ) GetAuthorizer () authorizer.Authorizer {
14- return authorizer .AuthorizerFunc (
15- func (ctx context.Context , attr authorizer.Attributes ) (authorizer.Decision , string , error ) {
16- user , err := identity .GetRequester (ctx )
17- if err != nil {
18- return authorizer .DecisionDeny , "valid user is required" , err
19- }
20-
21- if ! attr .IsResourceRequest () || user .GetIsGrafanaAdmin () || attr .GetName () == "" {
22- return authorizer .DecisionAllow , "" , nil
23- }
24-
25- name , found := utils .ParseOwnerFromName (attr .GetName ())
26- if ! found {
27- return authorizer .DecisionDeny , "invalid name" , nil
28- }
29-
30- if attr .GetResource () == "stars" && name .Owner != utils .UserResourceOwner {
31- return authorizer .DecisionDeny , "stars only support users" , nil
32- }
33-
34- switch name .Owner {
35- case utils .NamespaceResourceOwner :
36- return authorizer .DecisionAllow , "" , nil
37-
38- case utils .UserResourceOwner :
39- if user .GetUID () == name .Name {
40- return authorizer .DecisionAllow , "" , nil
41- }
42- return authorizer .DecisionDeny , "you may only fetch your own preferences" , nil
43-
44- case utils .TeamResourceOwner :
45- admin := ! attr .IsReadOnly () // we need admin to for non read only commands
46- teams , err := b .sql .GetTeams (ctx , user .GetOrgID (), user .GetUID (), admin )
47- if err != nil {
48- return authorizer .DecisionDeny , "error fetching teams" , err
49- }
50- if slices .Contains (teams , name .Name ) {
51- return authorizer .DecisionAllow , "" , nil
52- }
53- return authorizer .DecisionDeny , "not a team member" , nil
54-
55- default :
56- }
57-
58- return authorizer .DecisionDeny , "invalid name" , nil
59- })
15+ type authorizeFromName struct {
16+ teams utils.TeamService
17+ oknames []string
18+ resource map [string ][]utils.ResourceOwner // may include unknown
19+ }
20+
21+ func (a * authorizeFromName ) Authorize (ctx context.Context , attr authorizer.Attributes ) (authorizer.Decision , string , error ) {
22+ user , err := identity .GetRequester (ctx )
23+ if err != nil || user == nil {
24+ return authorizer .DecisionDeny , "valid user is required" , err
25+ }
26+
27+ if ! attr .IsResourceRequest () {
28+ return authorizer .DecisionNoOpinion , "" , nil
29+ }
30+
31+ owners , ok := a .resource [attr .GetResource ()]
32+ if ! ok {
33+ return authorizer .DecisionDeny , "missing resource name" , nil
34+ }
35+
36+ // Check if the request includes explicit permissions
37+ res := authz .CheckServicePermissions (user , attr .GetAPIGroup (), attr .GetResource (), attr .GetVerb ())
38+ if ! res .Allowed {
39+ log := logging .FromContext (ctx )
40+ log .Info ("calling service lacks required permissions" ,
41+ "isServiceCall" , res .ServiceCall ,
42+ "apiGroup" , attr .GetAPIGroup (),
43+ "resource" , attr .GetResource (),
44+ "verb" , attr .GetVerb (),
45+ "permissions" , len (res .Permissions ),
46+ )
47+ return authorizer .DecisionDeny , "calling service lacks required permissions" , nil
48+ }
49+
50+ if attr .GetName () == "" {
51+ if attr .IsReadOnly () {
52+ return authorizer .DecisionAllow , "" , nil
53+ }
54+ return authorizer .DecisionDeny , "mutating request without a name" , nil
55+ }
56+
57+ // the pseudo sub-resource
58+ if a .oknames != nil && slices .Contains (a .oknames , attr .GetName ()) {
59+ return authorizer .DecisionAllow , "" , nil
60+ }
61+
62+ info , _ := utils .ParseOwnerFromName (attr .GetName ())
63+ if ! slices .Contains (owners , info .Owner ) {
64+ return authorizer .DecisionDeny , "unsupported owner type" , nil
65+ }
66+
67+ switch info .Owner {
68+ case utils .NamespaceResourceOwner :
69+ if attr .IsReadOnly () {
70+ // Everyone can see the namespace
71+ return authorizer .DecisionAllow , "" , nil
72+ }
73+ if user .GetOrgRole () == identity .RoleAdmin {
74+ return authorizer .DecisionAllow , "" , nil
75+ }
76+ return authorizer .DecisionDeny , "must be an org admin to edit" , nil
77+
78+ case utils .UserResourceOwner :
79+ if user .GetIdentifier () == info .Identifier {
80+ return authorizer .DecisionAllow , "" , nil
81+ }
82+ return authorizer .DecisionDeny , "your are not the owner of the resource" , nil
83+
84+ case utils .TeamResourceOwner :
85+ if a .teams == nil {
86+ return authorizer .DecisionDeny , "team checker not configured" , err
87+ }
88+ ok , err := a .teams .InTeam (ctx , user , info .Identifier , ! attr .IsReadOnly ())
89+ if err != nil {
90+ return authorizer .DecisionDeny , "error fetching teams" , err
91+ }
92+ if ok {
93+ return authorizer .DecisionAllow , "" , nil
94+ }
95+ return authorizer .DecisionDeny , "you are not a member of the referenced team" , nil
96+
97+ case utils .UnknownResourceOwner :
98+ return authorizer .DecisionAllow , "" , nil
99+ }
100+
101+ // the owner was not explicitly allowed
102+ return authorizer .DecisionDeny , "" , nil
60103}
0 commit comments