Differentiate between SuperAdminContext and ServerContext

Previously, the server just hijacked the super admin context in order to
get raw access to the underlying data stores, this introduces a way to
specify the it is explicitly the server making the request and no longer
hijack the super admin context.

This also adds test coverage to ensure that the correct values are being
set on context in the AuthorizedUser method.
pull/2316/head
Michael Desa 2017-11-08 16:56:34 -05:00
parent 214525a257
commit 4e46b4b2c7
6 changed files with 238 additions and 94 deletions

View File

@ -63,24 +63,8 @@ func AuthorizedUser(
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !useAuth {
ctx := r.Context()
defaultOrg, err := store.Organizations(ctx).DefaultOrganization(ctx)
if err != nil {
unknownErrorWithMessage(w, err, logger)
return
}
// To access resources (servers, sources, databases, layouts) within a DataStore,
// an organization and a role are required even if you are a super admin or are
// not using auth. Every user's current organization is set on context to filter
// the resources accessed within a DataStore, including for super admin or when
// not using auth. In this way, a DataStore can treat all requests the same,
// including those from a super admin and when not using auth.
//
// As for roles, in the case of super admin or when not using auth, the user's
// role on context (though not on their JWT or user) is set to be admin. In order
// to access all resources belonging to their current organization.
ctx = context.WithValue(ctx, organizations.ContextKey, fmt.Sprintf("%d", defaultOrg.ID))
ctx = context.WithValue(ctx, roles.ContextKey, roles.AdminRoleName)
r = r.WithContext(ctx)
// If there is no auth, then give the user raw access to the DataStore
r = r.WithContext(serverContext(ctx))
next(w, r)
return
}
@ -92,6 +76,7 @@ func AuthorizedUser(
WithField("url", r.URL)
ctx := r.Context()
serverCtx := serverContext(ctx)
p, err := getValidPrincipal(ctx)
if err != nil {
@ -108,7 +93,7 @@ func AuthorizedUser(
// This is as if the user was logged into the default organization
if p.Organization == "" {
defaultOrg, err := store.Organizations(ctx).DefaultOrganization(ctx)
defaultOrg, err := store.Organizations(serverCtx).DefaultOrganization(serverCtx)
if err != nil {
unknownErrorWithMessage(w, err, logger)
return
@ -123,26 +108,14 @@ func AuthorizedUser(
Error(w, http.StatusUnauthorized, "User is not authorized", logger)
return
}
_, err = store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &orgID})
_, err = store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
if err != nil {
log.Error(fmt.Sprintf("Failed to retrieve organization %d from organizations store", orgID))
Error(w, http.StatusUnauthorized, "User is not authorized", logger)
return
}
ctx = context.WithValue(ctx, organizations.ContextKey, p.Organization)
serverCtx := context.WithValue(ctx, SuperAdminKey, true)
// To access resources (servers, sources, databases, layouts) within a DataStore,
// an organization and a role are required even if you are a super admin or are
// not using auth. Every user's current organization is set on context to filter
// the resources accessed within a DataStore, including for super admin or when
// not using auth. In this way, a DataStore can treat all requests the same,
// including those from a super admin and when not using auth.
//
// As for roles, in the case of super admin or when not using auth, the user's
// role on context (though not on their JWT or user) is set to be admin. In order
// to access all resources belonging to their current organization.
serverCtx = context.WithValue(serverCtx, roles.ContextKey, roles.AdminRoleName)
// TODO: seems silly to look up a user twice
u, err := store.Users(serverCtx).Get(serverCtx, chronograf.UserQuery{
Name: &p.Subject,
@ -157,8 +130,21 @@ func AuthorizedUser(
}
if u.SuperAdmin {
// This context is where superadmin gets set for all things
r = r.WithContext(serverCtx)
// To access resources (servers, sources, databases, layouts) within a DataStore,
// an organization and a role are required even if you are a super admin or are
// not using auth. Every user's current organization is set on context to filter
// the resources accessed within a DataStore, including for super admin or when
// not using auth. In this way, a DataStore can treat all requests the same,
// including those from a super admin and when not using auth.
//
// As for roles, in the case of super admin or when not using auth, the user's
// role on context (though not on their JWT or user) is set to be admin. In order
// to access all resources belonging to their current organization.
ctx = context.WithValue(ctx, roles.ContextKey, roles.AdminRoleName)
// In particular this is used by sever/users.go so that we know when and when not to
// allow users to make someone a super admin
ctx = context.WithValue(ctx, SuperAdminKey, true)
r = r.WithContext(ctx)
next(w, r)
return
}

View File

@ -1,4 +1,4 @@
package server_test
package server
import (
"context"
@ -13,7 +13,6 @@ import (
"github.com/influxdata/chronograf/mocks"
"github.com/influxdata/chronograf/oauth2"
"github.com/influxdata/chronograf/roles"
"github.com/influxdata/chronograf/server"
)
func TestAuthorizedToken(t *testing.T) {
@ -55,7 +54,7 @@ func TestAuthorizedToken(t *testing.T) {
}
logger := clog.New(clog.DebugLevel)
handler := server.AuthorizedToken(a, logger, next)
handler := AuthorizedToken(a, logger, next)
handler.ServeHTTP(w, req)
if w.Code != test.Code {
t.Errorf("Status code expected: %d actual %d", test.Code, w.Code)
@ -78,10 +77,14 @@ func TestAuthorizedUser(t *testing.T) {
role string
}
tests := []struct {
name string
fields fields
args args
authorized bool
name string
fields fields
args args
hasOrgContext bool
hasSuperAdminContext bool
hasRoleContext bool
hasServerContext bool
authorized bool
}{
{
name: "Not using auth",
@ -99,7 +102,11 @@ func TestAuthorizedUser(t *testing.T) {
args: args{
useAuth: false,
},
authorized: true,
hasOrgContext: false,
hasSuperAdminContext: false,
hasRoleContext: false,
hasServerContext: true,
authorized: true,
},
{
name: "User with viewer role is viewer authorized",
@ -151,7 +158,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "viewer",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: false,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "User with editor role is viewer authorized",
@ -203,7 +214,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "viewer",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: false,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "User with admin role is viewer authorized",
@ -255,7 +270,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "viewer",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: false,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "User with viewer role is editor unauthorized",
@ -359,7 +378,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "editor",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: false,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "User with admin role is editor authorized",
@ -411,7 +434,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "editor",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: false,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "User with viewer role is admin unauthorized",
@ -567,7 +594,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "admin",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: false,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "User with no role is viewer unauthorized",
@ -1065,7 +1096,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "viewer",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: true,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "SuperAdmin is Editor authorized",
@ -1118,7 +1153,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "editor",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: true,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "SuperAdmin is Admin authorized",
@ -1171,7 +1210,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "admin",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: true,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "SuperAdmin is SuperAdmin authorized",
@ -1224,7 +1267,11 @@ func TestAuthorizedUser(t *testing.T) {
role: "superadmin",
useAuth: true,
},
authorized: true,
authorized: true,
hasOrgContext: true,
hasSuperAdminContext: true,
hasRoleContext: true,
hasServerContext: false,
},
{
name: "Invalid principal principal is nil",
@ -1496,11 +1543,20 @@ func TestAuthorizedUser(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var authorized bool
var hasServerCtx bool
var hasSuperAdminCtx bool
var hasOrgCtx bool
var hasRoleCtx bool
next := func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
hasServerCtx = hasServerContext(ctx)
hasSuperAdminCtx = hasSuperAdminContext(ctx)
_, hasOrgCtx = hasOrganizationContext(ctx)
_, hasRoleCtx = hasRoleContext(ctx)
authorized = true
}
fn := server.AuthorizedUser(
&server.Store{
fn := AuthorizedUser(
&Store{
UsersStore: tt.fields.UsersStore,
OrganizationsStore: tt.fields.OrganizationsStore,
},
@ -1527,6 +1583,22 @@ func TestAuthorizedUser(t *testing.T) {
t.Errorf("%q. AuthorizedUser() = %v, expected %v", tt.name, authorized, tt.authorized)
}
if hasServerCtx != tt.hasServerContext {
t.Errorf("%q. AuthorizedUser().Context().Server = %v, expected %v", tt.name, hasServerCtx, tt.hasServerContext)
}
if hasSuperAdminCtx != tt.hasSuperAdminContext {
t.Errorf("%q. AuthorizedUser().Context().SuperAdmin = %v, expected %v", tt.name, hasSuperAdminCtx, tt.hasSuperAdminContext)
}
if hasOrgCtx != tt.hasOrgContext {
t.Errorf("%q. AuthorizedUser.Context().Organization = %v, expected %v", tt.name, hasOrgCtx, tt.hasOrgContext)
}
if hasRoleCtx != tt.hasRoleContext {
t.Errorf("%q. AuthorizedUser().Context().Role = %v, expected %v", tt.name, hasRoleCtx, tt.hasRoleContext)
}
})
}
}

30
server/context.go Normal file
View File

@ -0,0 +1,30 @@
package server
import (
"context"
)
type serverContextKey string
// ServerContextKey is the key used to specify that the
// server is making the requet via context
const ServerContextKey = serverContextKey("server")
// hasServerContext speficies if the context contains
// the ServerContextKey and that the value stored there is true
func hasServerContext(ctx context.Context) bool {
// prevents panic in case of nil context
if ctx == nil {
return false
}
sa, ok := ctx.Value(ServerContextKey).(bool)
// should never happen
if !ok {
return false
}
return sa
}
func serverContext(ctx context.Context) context.Context {
return context.WithValue(ctx, ServerContextKey, true)
}

View File

@ -83,8 +83,8 @@ func (s *Service) MeOrganization(auth oauth2.Authenticator) func(http.ResponseWr
ctx := r.Context()
principal, err := auth.Validate(ctx, r)
if err != nil {
s.Logger.Error("Invalid principal")
w.WriteHeader(http.StatusForbidden)
s.Logger.Error(fmt.Sprintf("Invalid principal: %v", err))
Error(w, http.StatusForbidden, "invalid principal", s.Logger)
return
}
var req meOrganizationRequest
@ -176,10 +176,10 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
}
ctx = context.WithValue(ctx, organizations.ContextKey, p.Organization)
ctx = context.WithValue(ctx, SuperAdminKey, true)
serverCtx := serverContext(ctx)
if p.Organization == "" {
defaultOrg, err := s.Store.Organizations(ctx).DefaultOrganization(ctx)
defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
@ -187,7 +187,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
p.Organization = fmt.Sprintf("%d", defaultOrg.ID)
}
usr, err := s.Store.Users(ctx).Get(ctx, chronograf.UserQuery{
usr, err := s.Store.Users(serverCtx).Get(serverCtx, chronograf.UserQuery{
Name: &p.Subject,
Provider: &p.Issuer,
Scheme: &scheme,
@ -198,17 +198,12 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
}
if usr != nil {
orgs, err := s.usersOrganizations(ctx, usr)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
orgID, err := parseOrganizationID(p.Organization)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
currentOrg, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &orgID})
currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
@ -222,11 +217,16 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
Organization: "0",
Name: roles.MemberRoleName,
})
if err := s.Store.Users(ctx).Update(ctx, usr); err != nil {
if err := s.Store.Users(serverCtx).Update(serverCtx, usr); err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
}
orgs, err := s.usersOrganizations(serverCtx, usr)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
}
res := newMeResponse(usr)
res.Organizations = orgs
res.CurrentOrganization = currentOrg
@ -234,7 +234,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
return
}
defaultOrg, err := s.Store.Organizations(ctx).DefaultOrganization(ctx)
defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
@ -258,14 +258,14 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
SuperAdmin: s.firstUser(),
}
newUser, err := s.Store.Users(ctx).Add(ctx, user)
newUser, err := s.Store.Users(serverCtx).Add(serverCtx, user)
if err != nil {
msg := fmt.Errorf("error storing user %s: %v", user.Name, err)
unknownErrorWithMessage(w, msg, s.Logger)
return
}
orgs, err := s.usersOrganizations(ctx, newUser)
orgs, err := s.usersOrganizations(serverCtx, newUser)
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
@ -275,7 +275,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
unknownErrorWithMessage(w, err, s.Logger)
return
}
currentOrg, err := s.Store.Organizations(ctx).Get(ctx, chronograf.OrganizationQuery{ID: &orgID})
currentOrg, err := s.Store.Organizations(serverCtx).Get(serverCtx, chronograf.OrganizationQuery{ID: &orgID})
if err != nil {
unknownErrorWithMessage(w, err, s.Logger)
return
@ -288,8 +288,8 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
// TODO(desa): very slow
func (s *Service) firstUser() bool {
ctx := context.WithValue(context.Background(), SuperAdminKey, true)
users, err := s.Store.Users(ctx).All(ctx)
serverCtx := serverContext(context.Background())
users, err := s.Store.Users(serverCtx).All(serverCtx)
if err != nil {
return false
}

View File

@ -51,14 +51,24 @@ func TestService_Me(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: 0,
Name: "Default",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
Name: "The Bad Place",
}, nil
switch *q.ID {
case 0:
return &chronograf.Organization{
ID: 0,
Name: "Default",
}, nil
case 1:
return &chronograf.Organization{
ID: 1,
Name: "The Bad Place",
}, nil
}
return nil, nil
},
},
UsersStore: &mocks.UsersStore{
@ -87,7 +97,7 @@ func TestService_Me(t *testing.T) {
},
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"name":"me","provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"currentOrganization":{"id":"0","name":"The Bad Place"},"roles":[{"name":"member","organization":"0"}]}
wantBody: `{"name":"me","roles":[{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default"}],"currentOrganization":{"id":"0","name":"Default"}}
`,
},
{
@ -309,17 +319,27 @@ func TestService_MeOrganizations(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: 0,
Name: "Default",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
if q.ID == nil {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
Name: "The ShillBillThrilliettas",
}, nil
switch *q.ID {
case 0:
return &chronograf.Organization{
ID: 0,
Name: "Default",
}, nil
case 1337:
return &chronograf.Organization{
ID: 1337,
Name: "The ShillBillThrilliettas",
}, nil
}
return nil, nil
},
},
},
@ -329,7 +349,7 @@ func TestService_MeOrganizations(t *testing.T) {
},
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"1337","name":"The ShillBillThrilliettas"}],"currentOrganization":{"id":"1337","name":"The ShillBillThrilliettas"}}`,
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"1337","name":"The ShillBillThrilliettas"},{"id":"0","name":"Default"}],"currentOrganization":{"id":"1337","name":"The ShillBillThrilliettas"}}`,
},
{
name: "Change the current User's organization",
@ -368,17 +388,27 @@ func TestService_MeOrganizations(t *testing.T) {
OrganizationsStore: &mocks.OrganizationsStore{
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
return &chronograf.Organization{
ID: 0,
ID: 0,
Name: "Default",
}, nil
},
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
if q.ID == nil {
return nil, fmt.Errorf("Invalid organization query: missing ID")
}
return &chronograf.Organization{
ID: 1337,
Name: "The ThrillShilliettos",
}, nil
switch *q.ID {
case 1337:
return &chronograf.Organization{
ID: 1337,
Name: "The ThrillShilliettos",
}, nil
case 0:
return &chronograf.Organization{
ID: 0,
Name: "Default",
}, nil
}
return nil, nil
},
},
},
@ -389,7 +419,7 @@ func TestService_MeOrganizations(t *testing.T) {
},
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"1337","name":"The ThrillShilliettos"}],"currentOrganization":{"id":"1337","name":"The ThrillShilliettos"}}
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"1337","name":"The ThrillShilliettos"},{"id":"0","name":"Default"}],"currentOrganization":{"id":"1337","name":"The ThrillShilliettos"}}
`,
},
{
@ -410,7 +440,17 @@ func TestService_MeOrganizations(t *testing.T) {
if q.Name == nil || q.Provider == nil || q.Scheme == nil {
return nil, fmt.Errorf("Invalid user query: missing Name, Provider, and/or Scheme")
}
return nil, chronograf.ErrUserNotFound
return &chronograf.User{
Name: "me",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Name: roles.AdminRoleName,
Organization: "1338",
},
},
}, nil
},
UpdateF: func(ctx context.Context, u *chronograf.User) error {
return nil

View File

@ -95,6 +95,9 @@ type Store struct {
// Sources returns a noop.SourcesStore if the context has no organization specified
// and a organization.SourcesStore otherwise.
func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
if isServer := hasServerContext(ctx); isServer {
return s.SourcesStore
}
if org, ok := hasOrganizationContext(ctx); ok {
store := organizations.NewSourcesStore(s.SourcesStore, org)
if role, ok := hasRoleContext(ctx); ok {
@ -108,6 +111,9 @@ func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
// Servers returns a noop.ServersStore if the context has no organization specified
// and a organization.ServersStore otherwise.
func (s *Store) Servers(ctx context.Context) chronograf.ServersStore {
if isServer := hasServerContext(ctx); isServer {
return s.ServersStore
}
if org, ok := hasOrganizationContext(ctx); ok {
return organizations.NewServersStore(s.ServersStore, org)
}
@ -118,6 +124,9 @@ func (s *Store) Servers(ctx context.Context) chronograf.ServersStore {
// Layouts returns a noop.LayoutsStore if the context has no organization specified
// and a organization.LayoutsStore otherwise.
func (s *Store) Layouts(ctx context.Context) chronograf.LayoutsStore {
if isServer := hasServerContext(ctx); isServer {
return s.LayoutsStore
}
if org, ok := hasOrganizationContext(ctx); ok {
return organizations.NewLayoutsStore(s.LayoutsStore, org)
}
@ -126,13 +135,13 @@ func (s *Store) Layouts(ctx context.Context) chronograf.LayoutsStore {
}
// Users returns a chronograf.UsersStore.
// If the context is a super admin context, then the underlying chronograf.UsersStore
// If the context is a server context, then the underlying chronograf.UsersStore
// is returned.
// If there is an organization specified on context, then an organizations.UsersStore
// is returned.
// If niether are specified, a noop.UsersStore is returned.
func (s *Store) Users(ctx context.Context) chronograf.UsersStore {
if superAdmin := hasSuperAdminContext(ctx); superAdmin {
if isServer := hasServerContext(ctx); isServer {
return s.UsersStore
}
if org, ok := hasOrganizationContext(ctx); ok {
@ -145,6 +154,9 @@ func (s *Store) Users(ctx context.Context) chronograf.UsersStore {
// Dashboards returns a noop.DashboardsStore if the context has no organization specified
// and a organization.DashboardsStore otherwise.
func (s *Store) Dashboards(ctx context.Context) chronograf.DashboardsStore {
if isServer := hasServerContext(ctx); isServer {
return s.DashboardsStore
}
if org, ok := hasOrganizationContext(ctx); ok {
return organizations.NewDashboardsStore(s.DashboardsStore, org)
}
@ -154,5 +166,9 @@ func (s *Store) Dashboards(ctx context.Context) chronograf.DashboardsStore {
// Organizations returns the underlying OrganizationsStore.
func (s *Store) Organizations(ctx context.Context) chronograf.OrganizationsStore {
// TODO(desa): added for when https://github.com/influxdata/chronograf/pull/2294 lands
if isServer := hasServerContext(ctx); isServer {
return s.OrganizationsStore
}
return s.OrganizationsStore
}