| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | package accesscontrol | 
					
						
							| 
									
										
										
										
											2021-03-22 20:22:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-08-24 19:29:17 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2021-05-13 05:00:27 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-03-14 00:05:03 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2021-03-22 20:22:48 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							| 
									
										
										
										
											2022-01-15 00:55:57 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2022-08-24 19:29:17 +08:00
										 |  |  | 	"text/template" | 
					
						
							| 
									
										
										
										
											2021-05-13 05:00:27 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/middleware/cookies" | 
					
						
							| 
									
										
										
										
											2022-11-18 16:56:06 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/models/usertoken" | 
					
						
							| 
									
										
										
										
											2023-08-18 18:42:18 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/auth/identity" | 
					
						
							| 
									
										
										
										
											2023-03-23 21:39:04 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/authn" | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | 	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" | 
					
						
							| 
									
										
										
										
											2023-01-04 23:20:26 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/org" | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/team" | 
					
						
							| 
									
										
										
										
											2022-08-10 17:56:48 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/user" | 
					
						
							| 
									
										
										
										
											2021-11-04 16:59:52 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2021-08-24 17:36:28 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/util" | 
					
						
							| 
									
										
										
										
											2021-10-11 20:30:59 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/web" | 
					
						
							| 
									
										
										
										
											2021-03-22 20:22:48 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-24 16:49:42 +08:00
										 |  |  | func Middleware(ac AccessControl) func(Evaluator) web.Handler { | 
					
						
							|  |  |  | 	return func(evaluator Evaluator) web.Handler { | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | 		return func(c *contextmodel.ReqContext) { | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 			if c.AllowAnonymous { | 
					
						
							|  |  |  | 				forceLogin, _ := strconv.ParseBool(c.Req.URL.Query().Get("forceLogin")) // ignoring error, assuming false for non-true values is ok.
 | 
					
						
							|  |  |  | 				orgID, err := strconv.ParseInt(c.Req.URL.Query().Get("orgId"), 10, 64) | 
					
						
							| 
									
										
										
										
											2023-08-18 18:42:18 +08:00
										 |  |  | 				if err == nil && orgID > 0 && orgID != c.SignedInUser.GetOrgID() { | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 					forceLogin = true | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if !c.IsSignedIn && forceLogin { | 
					
						
							| 
									
										
										
										
											2024-03-01 19:08:00 +08:00
										 |  |  | 					unauthorized(c) | 
					
						
							| 
									
										
										
										
											2023-03-23 21:39:04 +08:00
										 |  |  | 					return | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 21:39:04 +08:00
										 |  |  | 			if c.LookupTokenErr != nil { | 
					
						
							|  |  |  | 				var revokedErr *usertoken.TokenRevokedError | 
					
						
							|  |  |  | 				if errors.As(c.LookupTokenErr, &revokedErr) { | 
					
						
							|  |  |  | 					tokenRevoked(c, revokedErr) | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-01 19:08:00 +08:00
										 |  |  | 				unauthorized(c) | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | 			authorize(c, ac, c.SignedInUser, evaluator) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 18:42:18 +08:00
										 |  |  | func authorize(c *contextmodel.ReqContext, ac AccessControl, user identity.Requester, evaluator Evaluator) { | 
					
						
							| 
									
										
										
										
											2022-08-24 19:29:17 +08:00
										 |  |  | 	injected, err := evaluator.MutateScopes(c.Req.Context(), scopeInjector(scopeParams{ | 
					
						
							| 
									
										
										
										
											2023-08-18 18:42:18 +08:00
										 |  |  | 		OrgID:     user.GetOrgID(), | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | 		URLParams: web.Params(c.Req), | 
					
						
							|  |  |  | 	})) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		c.JsonApiErr(http.StatusInternalServerError, "Internal server error", err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasAccess, err := ac.Evaluate(c.Req.Context(), user, injected) | 
					
						
							|  |  |  | 	if !hasAccess || err != nil { | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | 		deny(c, injected, err) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | func deny(c *contextmodel.ReqContext, evaluator Evaluator, err error) { | 
					
						
							| 
									
										
										
										
											2021-05-13 05:00:27 +08:00
										 |  |  | 	id := newID() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		c.Logger.Error("Error from access control system", "error", err, "accessErrorID", id) | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2023-08-18 18:42:18 +08:00
										 |  |  | 		namespace, identifier := c.SignedInUser.GetNamespacedID() | 
					
						
							| 
									
										
										
										
											2021-08-24 17:36:28 +08:00
										 |  |  | 		c.Logger.Info( | 
					
						
							|  |  |  | 			"Access denied", | 
					
						
							| 
									
										
										
										
											2023-08-18 18:42:18 +08:00
										 |  |  | 			"namespace", namespace, | 
					
						
							|  |  |  | 			"userID", identifier, | 
					
						
							| 
									
										
										
										
											2021-08-24 17:36:28 +08:00
										 |  |  | 			"accessErrorID", id, | 
					
						
							| 
									
										
										
										
											2022-01-28 19:17:24 +08:00
										 |  |  | 			"permissions", evaluator.GoString(), | 
					
						
							| 
									
										
										
										
											2021-08-24 17:36:28 +08:00
										 |  |  | 		) | 
					
						
							| 
									
										
										
										
											2021-05-13 05:00:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 16:59:52 +08:00
										 |  |  | 	if !c.IsApiRequest() { | 
					
						
							|  |  |  | 		// TODO(emil): I'd like to show a message after this redirect, not sure how that can be done?
 | 
					
						
							| 
									
										
										
										
											2022-10-21 22:53:17 +08:00
										 |  |  | 		writeRedirectCookie(c) | 
					
						
							| 
									
										
										
										
											2021-11-04 16:59:52 +08:00
										 |  |  | 		c.Redirect(setting.AppSubUrl + "/") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | 	message := "" | 
					
						
							|  |  |  | 	if evaluator != nil { | 
					
						
							|  |  |  | 		message = evaluator.String() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-13 05:00:27 +08:00
										 |  |  | 	// If the user triggers an error in the access control system, we
 | 
					
						
							|  |  |  | 	// don't want the user to be aware of that, so the user gets the
 | 
					
						
							|  |  |  | 	// same information from the system regardless of if it's an
 | 
					
						
							|  |  |  | 	// internal server error or access denied.
 | 
					
						
							|  |  |  | 	c.JSON(http.StatusForbidden, map[string]string{ | 
					
						
							|  |  |  | 		"title":         "Access denied", // the component needs to pick this up
 | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | 		"message":       fmt.Sprintf("You'll need additional permissions to perform this action. Permissions needed: %s", message), | 
					
						
							| 
									
										
										
										
											2021-05-13 05:00:27 +08:00
										 |  |  | 		"accessErrorId": id, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-01 19:08:00 +08:00
										 |  |  | func unauthorized(c *contextmodel.ReqContext) { | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 	if c.IsApiRequest() { | 
					
						
							| 
									
										
										
										
											2023-03-23 21:39:04 +08:00
										 |  |  | 		c.WriteErrOrFallback(http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized), c.LookupTokenErr) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 21:39:04 +08:00
										 |  |  | 	writeRedirectCookie(c) | 
					
						
							|  |  |  | 	if errors.Is(c.LookupTokenErr, authn.ErrTokenNeedsRotation) { | 
					
						
							|  |  |  | 		c.Redirect(setting.AppSubUrl + "/user/auth-tokens/rotate") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	c.Redirect(setting.AppSubUrl + "/login") | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 21:39:04 +08:00
										 |  |  | func tokenRevoked(c *contextmodel.ReqContext, err *usertoken.TokenRevokedError) { | 
					
						
							|  |  |  | 	if c.IsApiRequest() { | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		c.JSON(http.StatusUnauthorized, map[string]any{ | 
					
						
							| 
									
										
										
										
											2023-03-23 21:39:04 +08:00
										 |  |  | 			"message": "Token revoked", | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 			"error": map[string]any{ | 
					
						
							| 
									
										
										
										
											2023-03-23 21:39:04 +08:00
										 |  |  | 				"id":                    "ERR_TOKEN_REVOKED", | 
					
						
							|  |  |  | 				"maxConcurrentSessions": err.MaxConcurrentSessions, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	writeRedirectCookie(c) | 
					
						
							|  |  |  | 	c.Redirect(setting.AppSubUrl + "/login") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | func writeRedirectCookie(c *contextmodel.ReqContext) { | 
					
						
							| 
									
										
										
										
											2022-10-07 14:18:56 +08:00
										 |  |  | 	redirectTo := c.Req.RequestURI | 
					
						
							|  |  |  | 	if setting.AppSubUrl != "" && !strings.HasPrefix(redirectTo, setting.AppSubUrl) { | 
					
						
							|  |  |  | 		redirectTo = setting.AppSubUrl + c.Req.RequestURI | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// remove any forceLogin=true params
 | 
					
						
							|  |  |  | 	redirectTo = removeForceLoginParams(redirectTo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cookies.WriteCookie(c.Resp, "redirect_to", url.QueryEscape(redirectTo), 0, nil) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var forceLoginParamsRegexp = regexp.MustCompile(`&?forceLogin=true`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func removeForceLoginParams(str string) string { | 
					
						
							|  |  |  | 	return forceLoginParamsRegexp.ReplaceAllString(str, "") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-13 05:00:27 +08:00
										 |  |  | func newID() string { | 
					
						
							|  |  |  | 	// Less ambiguity than alphanumerical.
 | 
					
						
							|  |  |  | 	numerical := []byte("0123456789") | 
					
						
							|  |  |  | 	id, err := util.GetRandomString(10, numerical...) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// this should not happen, but if it does, a timestamp is as
 | 
					
						
							|  |  |  | 		// useful as anything.
 | 
					
						
							|  |  |  | 		id = fmt.Sprintf("%d", time.Now().UnixNano()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "ACE" + id | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-06 19:15:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | type OrgIDGetter func(c *contextmodel.ReqContext) (int64, error) | 
					
						
							| 
									
										
										
										
											2022-10-04 18:17:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | type userCache interface { | 
					
						
							| 
									
										
										
										
											2022-08-16 22:08:59 +08:00
										 |  |  | 	GetSignedInUserWithCacheCtx(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | type teamService interface { | 
					
						
							|  |  |  | 	GetTeamIDsByUser(ctx context.Context, query *team.GetTeamIDsByUserQuery) ([]int64, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func AuthorizeInOrgMiddleware(ac AccessControl, service Service, userService userCache, teamService teamService) func(OrgIDGetter, Evaluator) web.Handler { | 
					
						
							| 
									
										
										
										
											2023-05-24 16:49:42 +08:00
										 |  |  | 	return func(getTargetOrg OrgIDGetter, evaluator Evaluator) web.Handler { | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | 		return func(c *contextmodel.ReqContext) { | 
					
						
							| 
									
										
										
										
											2023-07-12 18:28:04 +08:00
										 |  |  | 			targetOrgID, err := getTargetOrg(c) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | 				deny(c, nil, fmt.Errorf("failed to get target org: %w", err)) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-07-12 18:28:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 			tmpUser, err := makeTmpUser(c.Req.Context(), service, userService, teamService, c.SignedInUser, targetOrgID) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				deny(c, nil, fmt.Errorf("failed to authenticate user in target org: %w", err)) | 
					
						
							|  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2022-09-09 15:07:45 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 			authorize(c, ac, tmpUser, evaluator) | 
					
						
							| 
									
										
										
										
											2022-03-24 15:58:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-12 18:28:04 +08:00
										 |  |  | 			// guard against nil map
 | 
					
						
							|  |  |  | 			if c.SignedInUser.Permissions == nil { | 
					
						
							|  |  |  | 				c.SignedInUser.Permissions = make(map[int64]map[string][]string) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 			c.SignedInUser.Permissions[tmpUser.GetOrgID()] = tmpUser.GetPermissions() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // makeTmpUser creates a temporary user that can be used to evaluate access across orgs.
 | 
					
						
							|  |  |  | func makeTmpUser(ctx context.Context, service Service, cache userCache, | 
					
						
							|  |  |  | 	teamService teamService, reqUser identity.Requester, targetOrgID int64) (identity.Requester, error) { | 
					
						
							|  |  |  | 	tmpUser := &user.SignedInUser{ | 
					
						
							|  |  |  | 		OrgID:          reqUser.GetOrgID(), | 
					
						
							|  |  |  | 		OrgName:        reqUser.GetOrgName(), | 
					
						
							|  |  |  | 		OrgRole:        reqUser.GetOrgRole(), | 
					
						
							|  |  |  | 		IsGrafanaAdmin: reqUser.GetIsGrafanaAdmin(), | 
					
						
							|  |  |  | 		Login:          reqUser.GetLogin(), | 
					
						
							|  |  |  | 		Teams:          reqUser.GetTeams(), | 
					
						
							|  |  |  | 		Permissions: map[int64]map[string][]string{ | 
					
						
							|  |  |  | 			reqUser.GetOrgID(): reqUser.GetPermissions(), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	namespace, identifier := reqUser.GetNamespacedID() | 
					
						
							|  |  |  | 	id, _ := identity.IntIdentifier(namespace, identifier) | 
					
						
							|  |  |  | 	switch namespace { | 
					
						
							|  |  |  | 	case identity.NamespaceUser: | 
					
						
							|  |  |  | 		tmpUser.UserID = id | 
					
						
							|  |  |  | 	case identity.NamespaceAPIKey: | 
					
						
							|  |  |  | 		tmpUser.ApiKeyID = id | 
					
						
							|  |  |  | 		if tmpUser.OrgID != targetOrgID { | 
					
						
							|  |  |  | 			return nil, errors.New("API key does not belong to target org") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case identity.NamespaceServiceAccount: | 
					
						
							|  |  |  | 		tmpUser.UserID = id | 
					
						
							|  |  |  | 		tmpUser.IsServiceAccount = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if tmpUser.OrgID != targetOrgID { | 
					
						
							|  |  |  | 		switch targetOrgID { | 
					
						
							|  |  |  | 		case GlobalOrgID: | 
					
						
							|  |  |  | 			tmpUser.OrgID = GlobalOrgID | 
					
						
							|  |  |  | 			tmpUser.OrgRole = org.RoleNone | 
					
						
							|  |  |  | 			tmpUser.OrgName = "" | 
					
						
							|  |  |  | 			tmpUser.Teams = []int64{} | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			if cache == nil { | 
					
						
							|  |  |  | 				return nil, errors.New("user cache is nil") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			query := user.GetSignedInUserQuery{UserID: tmpUser.UserID, OrgID: targetOrgID} | 
					
						
							|  |  |  | 			queryResult, err := cache.GetSignedInUserWithCacheCtx(ctx, &query) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			tmpUser.OrgID = queryResult.OrgID | 
					
						
							|  |  |  | 			tmpUser.OrgName = queryResult.OrgName | 
					
						
							|  |  |  | 			tmpUser.OrgRole = queryResult.OrgRole | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 19:37:01 +08:00
										 |  |  | 			// Only fetch the team membership is the user is a member of the organization
 | 
					
						
							|  |  |  | 			if queryResult.OrgID == targetOrgID { | 
					
						
							|  |  |  | 				if teamService != nil { | 
					
						
							|  |  |  | 					teamIDs, err := teamService.GetTeamIDsByUser(ctx, &team.GetTeamIDsByUserQuery{OrgID: targetOrgID, UserID: tmpUser.UserID}) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					tmpUser.Teams = teamIDs | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 19:37:01 +08:00
										 |  |  | 	// If the user is not a member of the organization
 | 
					
						
							|  |  |  | 	// evaluation must happen based on global permissions.
 | 
					
						
							|  |  |  | 	evaluationOrg := targetOrgID | 
					
						
							|  |  |  | 	if tmpUser.OrgID == NoOrgID { | 
					
						
							|  |  |  | 		evaluationOrg = GlobalOrgID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if tmpUser.Permissions[evaluationOrg] == nil || len(tmpUser.Permissions[evaluationOrg]) == 0 { | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 		permissions, err := service.GetUserPermissions(ctx, tmpUser, Options{}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 19:37:01 +08:00
										 |  |  | 		tmpUser.Permissions[evaluationOrg] = GroupScopesByAction(permissions) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return tmpUser, nil | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | func UseOrgFromContextParams(c *contextmodel.ReqContext) (int64, error) { | 
					
						
							| 
									
										
										
										
											2022-01-15 00:55:57 +08:00
										 |  |  | 	orgID, err := strconv.ParseInt(web.Params(c.Req)[":orgId"], 10, 64) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 	// Special case of macaron handling invalid params
 | 
					
						
							| 
									
										
										
										
											2023-03-06 15:57:46 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, org.ErrOrgNotFound.Errorf("failed to get organization from context: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if orgID == 0 { | 
					
						
							|  |  |  | 		return 0, org.ErrOrgNotFound.Errorf("empty org ID") | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return orgID, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 15:50:36 +08:00
										 |  |  | func UseGlobalOrg(c *contextmodel.ReqContext) (int64, error) { | 
					
						
							| 
									
										
										
										
											2022-04-28 16:46:18 +08:00
										 |  |  | 	return GlobalOrgID, nil | 
					
						
							| 
									
										
										
										
											2021-12-20 16:52:24 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-21 22:09:43 +08:00
										 |  |  | // UseGlobalOrSingleOrg returns the global organization or the current organization in a single organization setup
 | 
					
						
							|  |  |  | func UseGlobalOrSingleOrg(cfg *setting.Cfg) OrgIDGetter { | 
					
						
							|  |  |  | 	return func(c *contextmodel.ReqContext) (int64, error) { | 
					
						
							|  |  |  | 		if cfg.RBACSingleOrganization { | 
					
						
							|  |  |  | 			return c.GetOrgID(), nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return GlobalOrgID, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-14 00:05:03 +08:00
										 |  |  | // UseOrgFromRequestData returns the organization from the request data.
 | 
					
						
							|  |  |  | // If no org is specified, then the org where user is logged in is returned.
 | 
					
						
							|  |  |  | func UseOrgFromRequestData(c *contextmodel.ReqContext) (int64, error) { | 
					
						
							|  |  |  | 	query, err := getOrgQueryFromRequest(c) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// Special case of macaron handling invalid params
 | 
					
						
							|  |  |  | 		return NoOrgID, org.ErrOrgNotFound.Errorf("failed to get organization from context: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if query.OrgId == nil { | 
					
						
							|  |  |  | 		return c.SignedInUser.GetOrgID(), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return *query.OrgId, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UseGlobalOrgFromRequestData returns global org if `global` flag is set or the org where user is logged in.
 | 
					
						
							|  |  |  | func UseGlobalOrgFromRequestData(c *contextmodel.ReqContext) (int64, error) { | 
					
						
							|  |  |  | 	query, err := getOrgQueryFromRequest(c) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// Special case of macaron handling invalid params
 | 
					
						
							|  |  |  | 		return NoOrgID, org.ErrOrgNotFound.Errorf("failed to get organization from context: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if query.Global { | 
					
						
							|  |  |  | 		return GlobalOrgID, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return c.SignedInUser.GetOrgID(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getOrgQueryFromRequest(c *contextmodel.ReqContext) (*QueryWithOrg, error) { | 
					
						
							|  |  |  | 	query := &QueryWithOrg{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	req, err := CloneRequest(c.Req) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := web.Bind(req, query); err != nil { | 
					
						
							|  |  |  | 		// Special case of macaron handling invalid params
 | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return query, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CloneRequest creates request copy including request body
 | 
					
						
							|  |  |  | func CloneRequest(req *http.Request) (*http.Request, error) { | 
					
						
							|  |  |  | 	// Get copy of body to prevent error when reading closed body in request handler
 | 
					
						
							|  |  |  | 	bodyCopy, err := CopyRequestBody(req) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	reqCopy := req.Clone(req.Context()) | 
					
						
							|  |  |  | 	reqCopy.Body = bodyCopy | 
					
						
							|  |  |  | 	return reqCopy, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CopyRequestBody returns copy of request body and keeps the original one to prevent error when reading closed body
 | 
					
						
							|  |  |  | func CopyRequestBody(req *http.Request) (io.ReadCloser, error) { | 
					
						
							|  |  |  | 	if req.Body == nil { | 
					
						
							|  |  |  | 		return nil, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	body := req.Body | 
					
						
							|  |  |  | 	var buf bytes.Buffer | 
					
						
							|  |  |  | 	if _, err := buf.ReadFrom(body); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := body.Close(); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	req.Body = io.NopCloser(&buf) | 
					
						
							|  |  |  | 	return io.NopCloser(bytes.NewReader(buf.Bytes())), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-24 19:29:17 +08:00
										 |  |  | // scopeParams holds the parameters used to fill in scope templates
 | 
					
						
							|  |  |  | type scopeParams struct { | 
					
						
							|  |  |  | 	OrgID     int64 | 
					
						
							|  |  |  | 	URLParams map[string]string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // scopeInjector inject request params into the templated scopes. e.g. "settings:" + eval.Parameters(":id")
 | 
					
						
							|  |  |  | func scopeInjector(params scopeParams) ScopeAttributeMutator { | 
					
						
							|  |  |  | 	return func(_ context.Context, scope string) ([]string, error) { | 
					
						
							|  |  |  | 		tmpl, err := template.New("scope").Parse(scope) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var buf bytes.Buffer | 
					
						
							|  |  |  | 		if err = tmpl.Execute(&buf, params); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return []string{buf.String()}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |