浏览代码

use X-Grafana-Org-Id header to ensure backend uses correct org (#8122)

Dan Cech 8 年之前
父节点
当前提交
f490c5f12c
共有 5 个文件被更改,包括 77 次插入52 次删除
  1. 3 2
      pkg/middleware/auth_proxy.go
  2. 42 35
      pkg/middleware/middleware.go
  3. 1 0
      pkg/models/user.go
  4. 19 13
      pkg/services/sqlstore/user.go
  5. 12 2
      public/app/core/services/backend_srv.ts

+ 3 - 2
pkg/middleware/auth_proxy.go

@@ -13,7 +13,7 @@ import (
 	"github.com/grafana/grafana/pkg/setting"
 )
 
-func initContextWithAuthProxy(ctx *Context) bool {
+func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
 	if !setting.AuthProxyEnabled {
 		return false
 	}
@@ -30,6 +30,7 @@ func initContextWithAuthProxy(ctx *Context) bool {
 	}
 
 	query := getSignedInUserQueryForProxyAuth(proxyHeaderValue)
+	query.OrgId = orgId
 	if err := bus.Dispatch(query); err != nil {
 		if err != m.ErrUserNotFound {
 			ctx.Handle(500, "Failed to find user specified in auth proxy header", err)
@@ -46,7 +47,7 @@ func initContextWithAuthProxy(ctx *Context) bool {
 				ctx.Handle(500, "Failed to create user specified in auth proxy header", err)
 				return true
 			}
-			query = &m.GetSignedInUserQuery{UserId: cmd.Result.Id}
+			query = &m.GetSignedInUserQuery{UserId: cmd.Result.Id, OrgId: orgId}
 			if err := bus.Dispatch(query); err != nil {
 				ctx.Handle(500, "Failed find user after creation", err)
 				return true

+ 42 - 35
pkg/middleware/middleware.go

@@ -39,6 +39,12 @@ func GetContextHandler() macaron.Handler {
 			Logger:         log.New("context"),
 		}
 
+		orgId := int64(0)
+		orgIdHeader := ctx.Req.Header.Get("X-Grafana-Org-Id")
+		if orgIdHeader != "" {
+			orgId, _ = strconv.ParseInt(orgIdHeader, 10, 64)
+		}
+
 		// the order in which these are tested are important
 		// look for api key in Authorization header first
 		// then init session and look for userId in session
@@ -46,9 +52,9 @@ func GetContextHandler() macaron.Handler {
 		// then test if anonymous access is enabled
 		if initContextWithRenderAuth(ctx) ||
 			initContextWithApiKey(ctx) ||
-			initContextWithBasicAuth(ctx) ||
-			initContextWithAuthProxy(ctx) ||
-			initContextWithUserSessionCookie(ctx) ||
+			initContextWithBasicAuth(ctx, orgId) ||
+			initContextWithAuthProxy(ctx, orgId) ||
+			initContextWithUserSessionCookie(ctx, orgId) ||
 			initContextWithAnonymousUser(ctx) {
 		}
 
@@ -68,18 +74,18 @@ func initContextWithAnonymousUser(ctx *Context) bool {
 	if err := bus.Dispatch(&orgQuery); err != nil {
 		log.Error(3, "Anonymous access organization error: '%s': %s", setting.AnonymousOrgName, err)
 		return false
-	} else {
-		ctx.IsSignedIn = false
-		ctx.AllowAnonymous = true
-		ctx.SignedInUser = &m.SignedInUser{}
-		ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole)
-		ctx.OrgId = orgQuery.Result.Id
-		ctx.OrgName = orgQuery.Result.Name
-		return true
 	}
+
+	ctx.IsSignedIn = false
+	ctx.AllowAnonymous = true
+	ctx.SignedInUser = &m.SignedInUser{}
+	ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole)
+	ctx.OrgId = orgQuery.Result.Id
+	ctx.OrgName = orgQuery.Result.Name
+	return true
 }
 
-func initContextWithUserSessionCookie(ctx *Context) bool {
+func initContextWithUserSessionCookie(ctx *Context, orgId int64) bool {
 	// initialize session
 	if err := ctx.Session.Start(ctx); err != nil {
 		ctx.Logger.Error("Failed to start session", "error", err)
@@ -91,15 +97,15 @@ func initContextWithUserSessionCookie(ctx *Context) bool {
 		return false
 	}
 
-	query := m.GetSignedInUserQuery{UserId: userId}
+	query := m.GetSignedInUserQuery{UserId: userId, OrgId: orgId}
 	if err := bus.Dispatch(&query); err != nil {
 		ctx.Logger.Error("Failed to get user with id", "userId", userId)
 		return false
-	} else {
-		ctx.SignedInUser = query.Result
-		ctx.IsSignedIn = true
-		return true
 	}
+
+	ctx.SignedInUser = query.Result
+	ctx.IsSignedIn = true
+	return true
 }
 
 func initContextWithApiKey(ctx *Context) bool {
@@ -114,30 +120,31 @@ func initContextWithApiKey(ctx *Context) bool {
 		ctx.JsonApiErr(401, "Invalid API key", err)
 		return true
 	}
+
 	// fetch key
 	keyQuery := m.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId}
 	if err := bus.Dispatch(&keyQuery); err != nil {
 		ctx.JsonApiErr(401, "Invalid API key", err)
 		return true
-	} else {
-		apikey := keyQuery.Result
+	}
 
-		// validate api key
-		if !apikeygen.IsValid(decoded, apikey.Key) {
-			ctx.JsonApiErr(401, "Invalid API key", err)
-			return true
-		}
+	apikey := keyQuery.Result
 
-		ctx.IsSignedIn = true
-		ctx.SignedInUser = &m.SignedInUser{}
-		ctx.OrgRole = apikey.Role
-		ctx.ApiKeyId = apikey.Id
-		ctx.OrgId = apikey.OrgId
+	// validate api key
+	if !apikeygen.IsValid(decoded, apikey.Key) {
+		ctx.JsonApiErr(401, "Invalid API key", err)
 		return true
 	}
+
+	ctx.IsSignedIn = true
+	ctx.SignedInUser = &m.SignedInUser{}
+	ctx.OrgRole = apikey.Role
+	ctx.ApiKeyId = apikey.Id
+	ctx.OrgId = apikey.OrgId
+	return true
 }
 
-func initContextWithBasicAuth(ctx *Context) bool {
+func initContextWithBasicAuth(ctx *Context, orgId int64) bool {
 
 	if !setting.BasicAuthEnabled {
 		return false
@@ -168,15 +175,15 @@ func initContextWithBasicAuth(ctx *Context) bool {
 		return true
 	}
 
-	query := m.GetSignedInUserQuery{UserId: user.Id}
+	query := m.GetSignedInUserQuery{UserId: user.Id, OrgId: orgId}
 	if err := bus.Dispatch(&query); err != nil {
 		ctx.JsonApiErr(401, "Authentication error", err)
 		return true
-	} else {
-		ctx.SignedInUser = query.Result
-		ctx.IsSignedIn = true
-		return true
 	}
+
+	ctx.SignedInUser = query.Result
+	ctx.IsSignedIn = true
+	return true
 }
 
 // Handle handles and logs error by given status.

+ 1 - 0
pkg/models/user.go

@@ -117,6 +117,7 @@ type GetSignedInUserQuery struct {
 	UserId int64
 	Login  string
 	Email  string
+	OrgId  int64
 	Result *SignedInUser
 }
 

+ 19 - 13
pkg/services/sqlstore/user.go

@@ -1,6 +1,7 @@
 package sqlstore
 
 import (
+	"strconv"
 	"strings"
 	"time"
 
@@ -273,7 +274,7 @@ func SetUsingOrg(cmd *m.SetUsingOrgCommand) error {
 	}
 
 	if !valid {
-		return fmt.Errorf("user does not belong ot org")
+		return fmt.Errorf("user does not belong to org")
 	}
 
 	return inTransaction(func(sess *xorm.Session) error {
@@ -319,19 +320,24 @@ func GetUserOrgList(query *m.GetUserOrgListQuery) error {
 }
 
 func GetSignedInUser(query *m.GetSignedInUserQuery) error {
+	orgId := "u.org_id"
+	if query.OrgId > 0 {
+		orgId = strconv.FormatInt(query.OrgId, 10)
+	}
+
 	var rawSql = `SELECT
-	                u.id           as user_id,
-	                u.is_admin     as is_grafana_admin,
-	                u.email        as email,
-	                u.login        as login,
-									u.name         as name,
-									u.help_flags1  as help_flags1,
-	                org.name       as org_name,
-	                org_user.role  as org_role,
-	                org.id         as org_id
-	                FROM ` + dialect.Quote("user") + ` as u
-									LEFT OUTER JOIN org_user on org_user.org_id = u.org_id and org_user.user_id = u.id
-	                LEFT OUTER JOIN org on org.id = u.org_id `
+		u.id           as user_id,
+		u.is_admin     as is_grafana_admin,
+		u.email        as email,
+		u.login        as login,
+		u.name         as name,
+		u.help_flags1  as help_flags1,
+		org.name       as org_name,
+		org_user.role  as org_role,
+		org.id         as org_id
+		FROM ` + dialect.Quote("user") + ` as u
+		LEFT OUTER JOIN org_user on org_user.org_id = ` + orgId + ` and org_user.user_id = u.id
+		LEFT OUTER JOIN org on org.id = org_user.org_id `
 
 	sess := x.Table("user")
 	if query.UserId > 0 {

+ 12 - 2
public/app/core/services/backend_srv.ts

@@ -9,8 +9,8 @@ export class BackendSrv {
   inFlightRequests = {};
   HTTP_REQUEST_CANCELLED = -1;
 
-    /** @ngInject */
-  constructor(private $http, private alertSrv, private $rootScope, private $q, private $timeout) {
+  /** @ngInject */
+  constructor(private $http, private alertSrv, private $rootScope, private $q, private $timeout, private contextSrv) {
   }
 
   get(url, params?) {
@@ -66,6 +66,11 @@ export class BackendSrv {
     var requestIsLocal = options.url.indexOf('/') === 0;
     var firstAttempt = options.retry === 0;
 
+    if (!options.url.match('https?://') && this.contextSrv && this.contextSrv.user && this.contextSrv.user.orgId) {
+      options.headers = options.headers || {};
+      options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
+    }
+
     if (requestIsLocal && !options.hasSubUrl) {
       options.url = config.appSubUrl + options.url;
       options.hasSubUrl = true;
@@ -128,6 +133,11 @@ export class BackendSrv {
     var requestIsLocal = options.url.indexOf('/') === 0;
     var firstAttempt = options.retry === 0;
 
+    if (!options.url.match('https?://') && this.contextSrv && this.contextSrv.user && this.contextSrv.user.orgId) {
+      options.headers = options.headers || {};
+      options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
+    }
+
     if (requestIsLocal && !options.hasSubUrl && options.retry === 0) {
       options.url = config.appSubUrl + options.url;
     }