Skip to content

Commit fd9031c

Browse files
Access Control: Get org from request data for authorization (#84359)
* Access Control: Get org from request data for authorization * move type to models * Update pkg/services/accesscontrol/middleware.go Co-authored-by: Ieva <[email protected]> * refactor * refactor * Fix linter --------- Co-authored-by: Ieva <[email protected]>
1 parent d480d32 commit fd9031c

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

pkg/services/accesscontrol/middleware.go

+78
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"errors"
77
"fmt"
8+
"io"
89
"net/http"
910
"net/url"
1011
"regexp"
@@ -320,6 +321,83 @@ func UseGlobalOrSingleOrg(cfg *setting.Cfg) OrgIDGetter {
320321
}
321322
}
322323

324+
// UseOrgFromRequestData returns the organization from the request data.
325+
// If no org is specified, then the org where user is logged in is returned.
326+
func UseOrgFromRequestData(c *contextmodel.ReqContext) (int64, error) {
327+
query, err := getOrgQueryFromRequest(c)
328+
if err != nil {
329+
// Special case of macaron handling invalid params
330+
return NoOrgID, org.ErrOrgNotFound.Errorf("failed to get organization from context: %w", err)
331+
}
332+
333+
if query.OrgId == nil {
334+
return c.SignedInUser.GetOrgID(), nil
335+
}
336+
337+
return *query.OrgId, nil
338+
}
339+
340+
// UseGlobalOrgFromRequestData returns global org if `global` flag is set or the org where user is logged in.
341+
func UseGlobalOrgFromRequestData(c *contextmodel.ReqContext) (int64, error) {
342+
query, err := getOrgQueryFromRequest(c)
343+
if err != nil {
344+
// Special case of macaron handling invalid params
345+
return NoOrgID, org.ErrOrgNotFound.Errorf("failed to get organization from context: %w", err)
346+
}
347+
348+
if query.Global {
349+
return GlobalOrgID, nil
350+
}
351+
352+
return c.SignedInUser.GetOrgID(), nil
353+
}
354+
355+
func getOrgQueryFromRequest(c *contextmodel.ReqContext) (*QueryWithOrg, error) {
356+
query := &QueryWithOrg{}
357+
358+
req, err := CloneRequest(c.Req)
359+
if err != nil {
360+
return nil, err
361+
}
362+
363+
if err := web.Bind(req, query); err != nil {
364+
// Special case of macaron handling invalid params
365+
return nil, err
366+
}
367+
368+
return query, nil
369+
}
370+
371+
// CloneRequest creates request copy including request body
372+
func CloneRequest(req *http.Request) (*http.Request, error) {
373+
// Get copy of body to prevent error when reading closed body in request handler
374+
bodyCopy, err := CopyRequestBody(req)
375+
if err != nil {
376+
return nil, err
377+
}
378+
reqCopy := req.Clone(req.Context())
379+
reqCopy.Body = bodyCopy
380+
return reqCopy, nil
381+
}
382+
383+
// CopyRequestBody returns copy of request body and keeps the original one to prevent error when reading closed body
384+
func CopyRequestBody(req *http.Request) (io.ReadCloser, error) {
385+
if req.Body == nil {
386+
return nil, nil
387+
}
388+
389+
body := req.Body
390+
var buf bytes.Buffer
391+
if _, err := buf.ReadFrom(body); err != nil {
392+
return nil, err
393+
}
394+
if err := body.Close(); err != nil {
395+
return nil, err
396+
}
397+
req.Body = io.NopCloser(&buf)
398+
return io.NopCloser(bytes.NewReader(buf.Bytes())), nil
399+
}
400+
323401
// scopeParams holds the parameters used to fill in scope templates
324402
type scopeParams struct {
325403
OrgID int64

pkg/services/accesscontrol/models.go

+5
Original file line numberDiff line numberDiff line change
@@ -562,3 +562,8 @@ var OrgsCreateAccessEvaluator = EvalAll(
562562

563563
// ApiKeyAccessEvaluator is used to protect the "Configuration > API keys" page access
564564
var ApiKeyAccessEvaluator = EvalPermission(ActionAPIKeyRead)
565+
566+
type QueryWithOrg struct {
567+
OrgId *int64 `json:"orgId"`
568+
Global bool `json:"global"`
569+
}

0 commit comments

Comments
 (0)