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
parent
214525a257
commit
4e46b4b2c7
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
36
server/me.go
36
server/me.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue