Commit f490c5f1 by Dan Cech Committed by Torkel Ödegaard

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

parent fb163450
......@@ -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
......
......@@ -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.
......
......@@ -117,6 +117,7 @@ type GetSignedInUserQuery struct {
UserId int64
Login string
Email string
OrgId int64
Result *SignedInUser
}
......
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 {
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment