package server import ( "context" "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/noop" "github.com/influxdata/chronograf/organizations" "github.com/influxdata/chronograf/roles" ) // hasOrganizationContext retrieves organization specified on context // under the organizations.ContextKey func hasOrganizationContext(ctx context.Context) (string, bool) { // prevents panic in case of nil context if ctx == nil { return "", false } orgID, ok := ctx.Value(organizations.ContextKey).(string) // should never happen if !ok { return "", false } if orgID == "" { return "", false } return orgID, true } // hasRoleContext retrieves organization specified on context // under the organizations.ContextKey func hasRoleContext(ctx context.Context) (string, bool) { // prevents panic in case of nil context if ctx == nil { return "", false } role, ok := ctx.Value(roles.ContextKey).(string) // should never happen if !ok { return "", false } switch role { case roles.MemberRoleName, roles.ViewerRoleName, roles.EditorRoleName, roles.AdminRoleName: return role, true default: return "", false } } type userContextKey string // UserContextKey is the context key for retrieving the user off of context const UserContextKey = userContextKey("user") // hasUserContext speficies if the context contains // the UserContextKey and that the value stored there is chronograf.User func hasUserContext(ctx context.Context) (*chronograf.User, bool) { // prevents panic in case of nil context if ctx == nil { return nil, false } u, ok := ctx.Value(UserContextKey).(*chronograf.User) // should never happen if !ok { return nil, false } if u == nil { return nil, false } return u, true } // hasSuperAdminContext speficies if the context contains // the UserContextKey user is a super admin func hasSuperAdminContext(ctx context.Context) bool { u, ok := hasUserContext(ctx) if !ok { return false } return u.SuperAdmin } // DataStore is collection of resources that are used by the Service // Abstracting this into an interface was useful for isolated testing type DataStore interface { Sources(ctx context.Context) chronograf.SourcesStore Servers(ctx context.Context) chronograf.ServersStore Layouts(ctx context.Context) chronograf.LayoutsStore Users(ctx context.Context) chronograf.UsersStore Organizations(ctx context.Context) chronograf.OrganizationsStore Dashboards(ctx context.Context) chronograf.DashboardsStore } // ensure that Store implements a DataStore var _ DataStore = &Store{} // Store implements the DataStore interface type Store struct { SourcesStore chronograf.SourcesStore ServersStore chronograf.ServersStore LayoutsStore chronograf.LayoutsStore UsersStore chronograf.UsersStore DashboardsStore chronograf.DashboardsStore OrganizationsStore chronograf.OrganizationsStore } // 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 { return roles.NewSourcesStore(store, role) } } return &noop.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) } return &noop.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) } return &noop.LayoutsStore{} } // Users returns a 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 isServer := hasServerContext(ctx); isServer { return s.UsersStore } if org, ok := hasOrganizationContext(ctx); ok { return organizations.NewUsersStore(s.UsersStore, org) } return &noop.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) } return &noop.DashboardsStore{} } // Organizations returns the underlying OrganizationsStore. func (s *Store) Organizations(ctx context.Context) chronograf.OrganizationsStore { if isServer := hasServerContext(ctx); isServer { return s.OrganizationsStore } if isSuperAdmin := hasSuperAdminContext(ctx); isSuperAdmin { return s.OrganizationsStore } if org, ok := hasOrganizationContext(ctx); ok { return organizations.NewOrganizationsStore(s.OrganizationsStore, org) } return &noop.OrganizationsStore{} }