parent
3b08f65a20
commit
815f740f4c
|
@ -102,7 +102,7 @@ func (cmd *Command) Run(args ...string) error {
|
||||||
if cmd.detailed {
|
if cmd.detailed {
|
||||||
sep := strings.Index(string(key), "#!~#")
|
sep := strings.Index(string(key), "#!~#")
|
||||||
seriesKey, field := key[:sep], key[sep+4:]
|
seriesKey, field := key[:sep], key[sep+4:]
|
||||||
measurement, tags, _ := models.ParseKey(seriesKey)
|
measurement, tags := models.ParseKey(seriesKey)
|
||||||
|
|
||||||
measCount, ok := measCardinalities[measurement]
|
measCount, ok := measCardinalities[measurement]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -602,7 +602,7 @@ func stopProfile() {
|
||||||
type monitorPointsWriter coordinator.PointsWriter
|
type monitorPointsWriter coordinator.PointsWriter
|
||||||
|
|
||||||
func (pw *monitorPointsWriter) WritePoints(database, retentionPolicy string, points models.Points) error {
|
func (pw *monitorPointsWriter) WritePoints(database, retentionPolicy string, points models.Points) error {
|
||||||
return (*coordinator.PointsWriter)(pw).WritePoints(database, retentionPolicy, models.ConsistencyLevelAny, points)
|
return (*coordinator.PointsWriter)(pw).WritePointsPrivileged(database, retentionPolicy, models.ConsistencyLevelAny, points)
|
||||||
}
|
}
|
||||||
|
|
||||||
func raftDBExists(dir string) error {
|
func raftDBExists(dir string) error {
|
||||||
|
|
|
@ -14,7 +14,7 @@ type MetaClient interface {
|
||||||
CreateDatabaseWithRetentionPolicy(name string, spec *meta.RetentionPolicySpec) (*meta.DatabaseInfo, error)
|
CreateDatabaseWithRetentionPolicy(name string, spec *meta.RetentionPolicySpec) (*meta.DatabaseInfo, error)
|
||||||
CreateRetentionPolicy(database string, spec *meta.RetentionPolicySpec, makeDefault bool) (*meta.RetentionPolicyInfo, error)
|
CreateRetentionPolicy(database string, spec *meta.RetentionPolicySpec, makeDefault bool) (*meta.RetentionPolicyInfo, error)
|
||||||
CreateSubscription(database, rp, name, mode string, destinations []string) error
|
CreateSubscription(database, rp, name, mode string, destinations []string) error
|
||||||
CreateUser(name, password string, admin bool) (*meta.UserInfo, error)
|
CreateUser(name, password string, admin bool) (meta.User, error)
|
||||||
Database(name string) *meta.DatabaseInfo
|
Database(name string) *meta.DatabaseInfo
|
||||||
Databases() []meta.DatabaseInfo
|
Databases() []meta.DatabaseInfo
|
||||||
DropShard(id uint64) error
|
DropShard(id uint64) error
|
||||||
|
|
|
@ -14,7 +14,7 @@ type MetaClient struct {
|
||||||
CreateDatabaseWithRetentionPolicyFn func(name string, spec *meta.RetentionPolicySpec) (*meta.DatabaseInfo, error)
|
CreateDatabaseWithRetentionPolicyFn func(name string, spec *meta.RetentionPolicySpec) (*meta.DatabaseInfo, error)
|
||||||
CreateRetentionPolicyFn func(database string, spec *meta.RetentionPolicySpec, makeDefault bool) (*meta.RetentionPolicyInfo, error)
|
CreateRetentionPolicyFn func(database string, spec *meta.RetentionPolicySpec, makeDefault bool) (*meta.RetentionPolicyInfo, error)
|
||||||
CreateSubscriptionFn func(database, rp, name, mode string, destinations []string) error
|
CreateSubscriptionFn func(database, rp, name, mode string, destinations []string) error
|
||||||
CreateUserFn func(name, password string, admin bool) (*meta.UserInfo, error)
|
CreateUserFn func(name, password string, admin bool) (meta.User, error)
|
||||||
DatabaseFn func(name string) *meta.DatabaseInfo
|
DatabaseFn func(name string) *meta.DatabaseInfo
|
||||||
DatabasesFn func() []meta.DatabaseInfo
|
DatabasesFn func() []meta.DatabaseInfo
|
||||||
DataNodeFn func(id uint64) (*meta.NodeInfo, error)
|
DataNodeFn func(id uint64) (*meta.NodeInfo, error)
|
||||||
|
@ -63,7 +63,7 @@ func (c *MetaClient) CreateSubscription(database, rp, name, mode string, destina
|
||||||
return c.CreateSubscriptionFn(database, rp, name, mode, destinations)
|
return c.CreateSubscriptionFn(database, rp, name, mode, destinations)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MetaClient) CreateUser(name, password string, admin bool) (*meta.UserInfo, error) {
|
func (c *MetaClient) CreateUser(name, password string, admin bool) (meta.User, error) {
|
||||||
return c.CreateUserFn(name, password, admin)
|
return c.CreateUserFn(name, password, admin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,11 +281,16 @@ func (l sgList) Append(sgi meta.ShardGroupInfo) sgList {
|
||||||
// WritePointsInto is a copy of WritePoints that uses a tsdb structure instead of
|
// WritePointsInto is a copy of WritePoints that uses a tsdb structure instead of
|
||||||
// a cluster structure for information. This is to avoid a circular dependency.
|
// a cluster structure for information. This is to avoid a circular dependency.
|
||||||
func (w *PointsWriter) WritePointsInto(p *IntoWriteRequest) error {
|
func (w *PointsWriter) WritePointsInto(p *IntoWriteRequest) error {
|
||||||
return w.WritePoints(p.Database, p.RetentionPolicy, models.ConsistencyLevelOne, p.Points)
|
return w.WritePointsPrivileged(p.Database, p.RetentionPolicy, models.ConsistencyLevelOne, p.Points)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WritePoints writes across multiple local and remote data nodes according the consistency level.
|
// WritePoints writes the data to the underlying storage. consitencyLevel and user are only used for clustered scenarios
|
||||||
func (w *PointsWriter) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
func (w *PointsWriter) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, user string, points []models.Point) error {
|
||||||
|
return w.WritePointsPrivileged(database, retentionPolicy, consistencyLevel, points)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WritePointsPrivileged writes the data to the underlying storage, consitencyLevel is only used for clustered scenarios
|
||||||
|
func (w *PointsWriter) WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
||||||
atomic.AddInt64(&w.stats.WriteReq, 1)
|
atomic.AddInt64(&w.stats.WriteReq, 1)
|
||||||
atomic.AddInt64(&w.stats.PointWriteReq, int64(len(points)))
|
atomic.AddInt64(&w.stats.PointWriteReq, int64(len(points)))
|
||||||
|
|
||||||
|
|
|
@ -346,25 +346,25 @@ func TestPointsWriter_WritePoints(t *testing.T) {
|
||||||
c.Open()
|
c.Open()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
err := c.WritePoints(pr.Database, pr.RetentionPolicy, models.ConsistencyLevelOne, pr.Points)
|
err := c.WritePointsPrivileged(pr.Database, pr.RetentionPolicy, models.ConsistencyLevelOne, pr.Points)
|
||||||
if err == nil && test.expErr != nil {
|
if err == nil && test.expErr != nil {
|
||||||
t.Errorf("PointsWriter.WritePoints(): '%s' error: got %v, exp %v", test.name, err, test.expErr)
|
t.Errorf("PointsWriter.WritePointsPrivileged(): '%s' error: got %v, exp %v", test.name, err, test.expErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && test.expErr == nil {
|
if err != nil && test.expErr == nil {
|
||||||
t.Errorf("PointsWriter.WritePoints(): '%s' error: got %v, exp %v", test.name, err, test.expErr)
|
t.Errorf("PointsWriter.WritePointsPrivileged(): '%s' error: got %v, exp %v", test.name, err, test.expErr)
|
||||||
}
|
}
|
||||||
if err != nil && test.expErr != nil && err.Error() != test.expErr.Error() {
|
if err != nil && test.expErr != nil && err.Error() != test.expErr.Error() {
|
||||||
t.Errorf("PointsWriter.WritePoints(): '%s' error: got %v, exp %v", test.name, err, test.expErr)
|
t.Errorf("PointsWriter.WritePointsPrivileged(): '%s' error: got %v, exp %v", test.name, err, test.expErr)
|
||||||
}
|
}
|
||||||
if test.expErr == nil {
|
if test.expErr == nil {
|
||||||
select {
|
select {
|
||||||
case p := <-subPoints:
|
case p := <-subPoints:
|
||||||
if !reflect.DeepEqual(p, pr) {
|
if !reflect.DeepEqual(p, pr) {
|
||||||
t.Errorf("PointsWriter.WritePoints(): '%s' error: unexpected WritePointsRequest got %v, exp %v", test.name, p, pr)
|
t.Errorf("PointsWriter.WritePointsPrivileged(): '%s' error: unexpected WritePointsRequest got %v, exp %v", test.name, p, pr)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
t.Errorf("PointsWriter.WritePoints(): '%s' error: Subscriber.Points not called", test.name)
|
t.Errorf("PointsWriter.WritePointsPrivileged(): '%s' error: Subscriber.Points not called", test.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -530,6 +530,7 @@ func (e *StatementExecutor) createIterators(stmt *influxql.SelectStatement, ctx
|
||||||
InterruptCh: ctx.InterruptCh,
|
InterruptCh: ctx.InterruptCh,
|
||||||
NodeID: ctx.ExecutionOptions.NodeID,
|
NodeID: ctx.ExecutionOptions.NodeID,
|
||||||
MaxSeriesN: e.MaxSelectSeriesN,
|
MaxSeriesN: e.MaxSelectSeriesN,
|
||||||
|
Authorizer: ctx.Authorizer,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace instances of "now()" with the current time, and check the resultant times.
|
// Replace instances of "now()" with the current time, and check the resultant times.
|
||||||
|
|
|
@ -201,6 +201,18 @@ func (a *mockAuthorizer) AuthorizeDatabase(p influxql.Privilege, name string) bo
|
||||||
return a.AuthorizeDatabaseFn(p, name)
|
return a.AuthorizeDatabaseFn(p, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockAuthorizer) AuthorizeQuery(database string, query *influxql.Query) error {
|
||||||
|
panic("fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAuthorizer) AuthorizeSeriesRead(database string, series string) bool {
|
||||||
|
panic("fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAuthorizer) AuthorizeSeriesWrite(database string, series string) bool {
|
||||||
|
panic("fail")
|
||||||
|
}
|
||||||
|
|
||||||
func TestQueryExecutor_ExecuteQuery_ShowDatabases(t *testing.T) {
|
func TestQueryExecutor_ExecuteQuery_ShowDatabases(t *testing.T) {
|
||||||
qe := influxql.NewQueryExecutor()
|
qe := influxql.NewQueryExecutor()
|
||||||
qe.StatementExecutor = &coordinator.StatementExecutor{
|
qe.StatementExecutor = &coordinator.StatementExecutor{
|
||||||
|
|
|
@ -643,6 +643,12 @@ type FieldMapper interface {
|
||||||
TypeMapper
|
TypeMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SeriesAuthorizer can be used to limit access on a per-series basis
|
||||||
|
type SeriesAuthorizer interface {
|
||||||
|
AuthorizeSeriesRead(database, series string) bool
|
||||||
|
AuthorizeSeriesWrite(database, series string) bool
|
||||||
|
}
|
||||||
|
|
||||||
// IteratorOptions is an object passed to CreateIterator to specify creation options.
|
// IteratorOptions is an object passed to CreateIterator to specify creation options.
|
||||||
type IteratorOptions struct {
|
type IteratorOptions struct {
|
||||||
// Expression to iterate for.
|
// Expression to iterate for.
|
||||||
|
@ -694,10 +700,14 @@ type IteratorOptions struct {
|
||||||
// If this channel is set and is closed, the iterator should try to exit
|
// If this channel is set and is closed, the iterator should try to exit
|
||||||
// and close as soon as possible.
|
// and close as soon as possible.
|
||||||
InterruptCh <-chan struct{}
|
InterruptCh <-chan struct{}
|
||||||
|
|
||||||
|
// Authorizer can limit acccess to data
|
||||||
|
Authorizer Authorizer
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIteratorOptionsStmt creates the iterator options from stmt.
|
// newIteratorOptionsStmt creates the iterator options from stmt.
|
||||||
func newIteratorOptionsStmt(stmt *SelectStatement, sopt *SelectOptions) (opt IteratorOptions, err error) {
|
func newIteratorOptionsStmt(stmt *SelectStatement, sopt *SelectOptions) (opt IteratorOptions, err error) {
|
||||||
|
|
||||||
// Determine time range from the condition.
|
// Determine time range from the condition.
|
||||||
startTime, endTime, err := TimeRange(stmt.Condition)
|
startTime, endTime, err := TimeRange(stmt.Condition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -769,6 +779,7 @@ func newIteratorOptionsStmt(stmt *SelectStatement, sopt *SelectOptions) (opt Ite
|
||||||
if sopt != nil {
|
if sopt != nil {
|
||||||
opt.MaxSeriesN = sopt.MaxSeriesN
|
opt.MaxSeriesN = sopt.MaxSeriesN
|
||||||
opt.InterruptCh = sopt.InterruptCh
|
opt.InterruptCh = sopt.InterruptCh
|
||||||
|
opt.Authorizer = sopt.Authorizer
|
||||||
}
|
}
|
||||||
|
|
||||||
return opt, nil
|
return opt, nil
|
||||||
|
|
|
@ -60,6 +60,15 @@ func ErrMaxConcurrentQueriesLimitExceeded(n, limit int) error {
|
||||||
type Authorizer interface {
|
type Authorizer interface {
|
||||||
// AuthorizeDatabase indicates whether the given Privilege is authorized on the database with the given name.
|
// AuthorizeDatabase indicates whether the given Privilege is authorized on the database with the given name.
|
||||||
AuthorizeDatabase(p Privilege, name string) bool
|
AuthorizeDatabase(p Privilege, name string) bool
|
||||||
|
|
||||||
|
// AuthorizeQuery returns an error if the query cannot be executed
|
||||||
|
AuthorizeQuery(database string, query *Query) error
|
||||||
|
|
||||||
|
// AuthorizeSeriesRead determines if a series is authorized for reading
|
||||||
|
AuthorizeSeriesRead(database, series string) bool
|
||||||
|
|
||||||
|
// AuthorizeSeriesWrite determines if a series is authorized for writing
|
||||||
|
AuthorizeSeriesWrite(database, series string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenAuthorizer is the Authorizer used when authorization is disabled.
|
// OpenAuthorizer is the Authorizer used when authorization is disabled.
|
||||||
|
@ -69,7 +78,13 @@ type OpenAuthorizer struct{}
|
||||||
var _ Authorizer = OpenAuthorizer{}
|
var _ Authorizer = OpenAuthorizer{}
|
||||||
|
|
||||||
// AuthorizeDatabase returns true to allow any operation on a database.
|
// AuthorizeDatabase returns true to allow any operation on a database.
|
||||||
func (OpenAuthorizer) AuthorizeDatabase(Privilege, string) bool { return true }
|
func (_ OpenAuthorizer) AuthorizeDatabase(Privilege, string) bool { return true }
|
||||||
|
|
||||||
|
func (_ OpenAuthorizer) AuthorizeSeriesRead(database string, series string) bool { return true }
|
||||||
|
|
||||||
|
func (_ OpenAuthorizer) AuthorizeSeriesWrite(database string, series string) bool { return true }
|
||||||
|
|
||||||
|
func (_ OpenAuthorizer) AuthorizeQuery(_ string, _ *Query) error { return nil }
|
||||||
|
|
||||||
// ExecutionOptions contains the options for executing a query.
|
// ExecutionOptions contains the options for executing a query.
|
||||||
type ExecutionOptions struct {
|
type ExecutionOptions struct {
|
||||||
|
|
|
@ -10,6 +10,9 @@ import (
|
||||||
|
|
||||||
// SelectOptions are options that customize the select call.
|
// SelectOptions are options that customize the select call.
|
||||||
type SelectOptions struct {
|
type SelectOptions struct {
|
||||||
|
// Authorizer is used to limit access to data
|
||||||
|
Authorizer Authorizer
|
||||||
|
|
||||||
// The lower bound for a select call.
|
// The lower bound for a select call.
|
||||||
MinTime time.Time
|
MinTime time.Time
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ type MetaClientMock struct {
|
||||||
CreateRetentionPolicyFn func(database string, spec *meta.RetentionPolicySpec, makeDefault bool) (*meta.RetentionPolicyInfo, error)
|
CreateRetentionPolicyFn func(database string, spec *meta.RetentionPolicySpec, makeDefault bool) (*meta.RetentionPolicyInfo, error)
|
||||||
CreateShardGroupFn func(database, policy string, timestamp time.Time) (*meta.ShardGroupInfo, error)
|
CreateShardGroupFn func(database, policy string, timestamp time.Time) (*meta.ShardGroupInfo, error)
|
||||||
CreateSubscriptionFn func(database, rp, name, mode string, destinations []string) error
|
CreateSubscriptionFn func(database, rp, name, mode string, destinations []string) error
|
||||||
CreateUserFn func(name, password string, admin bool) (*meta.UserInfo, error)
|
CreateUserFn func(name, password string, admin bool) (meta.User, error)
|
||||||
|
|
||||||
DatabaseFn func(name string) *meta.DatabaseInfo
|
DatabaseFn func(name string) *meta.DatabaseInfo
|
||||||
DatabasesFn func() []meta.DatabaseInfo
|
DatabasesFn func() []meta.DatabaseInfo
|
||||||
|
@ -34,7 +34,7 @@ type MetaClientMock struct {
|
||||||
|
|
||||||
RetentionPolicyFn func(database, name string) (rpi *meta.RetentionPolicyInfo, err error)
|
RetentionPolicyFn func(database, name string) (rpi *meta.RetentionPolicyInfo, err error)
|
||||||
|
|
||||||
AuthenticateFn func(username, password string) (ui *meta.UserInfo, err error)
|
AuthenticateFn func(username, password string) (ui meta.User, err error)
|
||||||
AdminUserExistsFn func() bool
|
AdminUserExistsFn func() bool
|
||||||
SetAdminPrivilegeFn func(username string, admin bool) error
|
SetAdminPrivilegeFn func(username string, admin bool) error
|
||||||
SetDataFn func(*meta.Data) error
|
SetDataFn func(*meta.Data) error
|
||||||
|
@ -45,7 +45,7 @@ type MetaClientMock struct {
|
||||||
UpdateUserFn func(name, password string) error
|
UpdateUserFn func(name, password string) error
|
||||||
UserPrivilegeFn func(username, database string) (*influxql.Privilege, error)
|
UserPrivilegeFn func(username, database string) (*influxql.Privilege, error)
|
||||||
UserPrivilegesFn func(username string) (map[string]influxql.Privilege, error)
|
UserPrivilegesFn func(username string) (map[string]influxql.Privilege, error)
|
||||||
UserFn func(username string) (*meta.UserInfo, error)
|
UserFn func(username string) (meta.User, error)
|
||||||
UsersFn func() []meta.UserInfo
|
UsersFn func() []meta.UserInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ func (c *MetaClientMock) CreateSubscription(database, rp, name, mode string, des
|
||||||
return c.CreateSubscriptionFn(database, rp, name, mode, destinations)
|
return c.CreateSubscriptionFn(database, rp, name, mode, destinations)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MetaClientMock) CreateUser(name, password string, admin bool) (*meta.UserInfo, error) {
|
func (c *MetaClientMock) CreateUser(name, password string, admin bool) (meta.User, error) {
|
||||||
return c.CreateUserFn(name, password, admin)
|
return c.CreateUserFn(name, password, admin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,13 +153,13 @@ func (c *MetaClientMock) UserPrivileges(username string) (map[string]influxql.Pr
|
||||||
return c.UserPrivilegesFn(username)
|
return c.UserPrivilegesFn(username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MetaClientMock) Authenticate(username, password string) (*meta.UserInfo, error) {
|
func (c *MetaClientMock) Authenticate(username, password string) (meta.User, error) {
|
||||||
return c.AuthenticateFn(username, password)
|
return c.AuthenticateFn(username, password)
|
||||||
}
|
}
|
||||||
func (c *MetaClientMock) AdminUserExists() bool { return c.AdminUserExistsFn() }
|
func (c *MetaClientMock) AdminUserExists() bool { return c.AdminUserExistsFn() }
|
||||||
|
|
||||||
func (c *MetaClientMock) User(username string) (*meta.UserInfo, error) { return c.UserFn(username) }
|
func (c *MetaClientMock) User(username string) (meta.User, error) { return c.UserFn(username) }
|
||||||
func (c *MetaClientMock) Users() []meta.UserInfo { return c.UsersFn() }
|
func (c *MetaClientMock) Users() []meta.UserInfo { return c.UsersFn() }
|
||||||
|
|
||||||
func (c *MetaClientMock) Open() error { return c.OpenFn() }
|
func (c *MetaClientMock) Open() error { return c.OpenFn() }
|
||||||
func (c *MetaClientMock) Data() meta.Data { return c.DataFn() }
|
func (c *MetaClientMock) Data() meta.Data { return c.DataFn() }
|
||||||
|
|
|
@ -237,7 +237,7 @@ func ParsePointsString(buf string) ([]Point, error) {
|
||||||
//
|
//
|
||||||
// NOTE: to minimize heap allocations, the returned Tags will refer to subslices of buf.
|
// NOTE: to minimize heap allocations, the returned Tags will refer to subslices of buf.
|
||||||
// This can have the unintended effect preventing buf from being garbage collected.
|
// This can have the unintended effect preventing buf from being garbage collected.
|
||||||
func ParseKey(buf []byte) (string, Tags, error) {
|
func ParseKey(buf []byte) (string, Tags) {
|
||||||
// Ignore the error because scanMeasurement returns "missing fields" which we ignore
|
// Ignore the error because scanMeasurement returns "missing fields" which we ignore
|
||||||
// when just parsing a key
|
// when just parsing a key
|
||||||
state, i, _ := scanMeasurement(buf, 0)
|
state, i, _ := scanMeasurement(buf, 0)
|
||||||
|
@ -246,9 +246,9 @@ func ParseKey(buf []byte) (string, Tags, error) {
|
||||||
if state == tagKeyState {
|
if state == tagKeyState {
|
||||||
tags = parseTags(buf)
|
tags = parseTags(buf)
|
||||||
// scanMeasurement returns the location of the comma if there are tags, strip that off
|
// scanMeasurement returns the location of the comma if there are tags, strip that off
|
||||||
return string(buf[:i-1]), tags, nil
|
return string(buf[:i-1]), tags
|
||||||
}
|
}
|
||||||
return string(buf[:i]), tags, nil
|
return string(buf[:i]), tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseTags(buf []byte) (Tags, error) {
|
func ParseTags(buf []byte) (Tags, error) {
|
||||||
|
|
|
@ -2088,18 +2088,6 @@ func TestNewPointsRejectsMaxKey(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseKeyEmpty(t *testing.T) {
|
|
||||||
if _, _, err := models.ParseKey(nil); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseKeyMissingValue(t *testing.T) {
|
|
||||||
if _, _, err := models.ParseKey([]byte("cpu,foo ")); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPoint_FieldIterator_Simple(t *testing.T) {
|
func TestPoint_FieldIterator_Simple(t *testing.T) {
|
||||||
|
|
||||||
p, err := models.ParsePoints([]byte(`m v=42i,f=42 36`))
|
p, err := models.ParsePoints([]byte(`m v=42i,f=42 36`))
|
||||||
|
|
|
@ -35,7 +35,7 @@ const (
|
||||||
|
|
||||||
// pointsWriter is an internal interface to make testing easier.
|
// pointsWriter is an internal interface to make testing easier.
|
||||||
type pointsWriter interface {
|
type pointsWriter interface {
|
||||||
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// metaClient is an internal interface to make testing easier.
|
// metaClient is an internal interface to make testing easier.
|
||||||
|
@ -374,7 +374,7 @@ func (s *Service) writePoints() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.PointsWriter.WritePoints(s.Config.Database, s.Config.RetentionPolicy, models.ConsistencyLevelAny, batch); err == nil {
|
if err := s.PointsWriter.WritePointsPrivileged(s.Config.Database, s.Config.RetentionPolicy, models.ConsistencyLevelAny, batch); err == nil {
|
||||||
atomic.AddInt64(&s.stats.BatchesTransmitted, 1)
|
atomic.AddInt64(&s.stats.BatchesTransmitted, 1)
|
||||||
atomic.AddInt64(&s.stats.PointsTransmitted, int64(len(batch)))
|
atomic.AddInt64(&s.stats.PointsTransmitted, int64(len(batch)))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -365,7 +365,7 @@ func NewTestService(batchSize int, batchDuration time.Duration) *TestService {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TestService) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
func (w *TestService) WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
||||||
return w.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
return w.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ type Service struct {
|
||||||
DeregisterDiagnosticsClient(name string)
|
DeregisterDiagnosticsClient(name string)
|
||||||
}
|
}
|
||||||
PointsWriter interface {
|
PointsWriter interface {
|
||||||
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
||||||
}
|
}
|
||||||
MetaClient interface {
|
MetaClient interface {
|
||||||
CreateDatabaseWithRetentionPolicy(name string, spec *meta.RetentionPolicySpec) (*meta.DatabaseInfo, error)
|
CreateDatabaseWithRetentionPolicy(name string, spec *meta.RetentionPolicySpec) (*meta.DatabaseInfo, error)
|
||||||
|
@ -444,7 +444,7 @@ func (s *Service) processBatches(batcher *tsdb.PointBatcher) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.PointsWriter.WritePoints(s.database, s.retentionPolicy, models.ConsistencyLevelAny, batch); err == nil {
|
if err := s.PointsWriter.WritePointsPrivileged(s.database, s.retentionPolicy, models.ConsistencyLevelAny, batch); err == nil {
|
||||||
atomic.AddInt64(&s.stats.BatchesTransmitted, 1)
|
atomic.AddInt64(&s.stats.BatchesTransmitted, 1)
|
||||||
atomic.AddInt64(&s.stats.PointsTransmitted, int64(len(batch)))
|
atomic.AddInt64(&s.stats.PointsTransmitted, int64(len(batch)))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -304,6 +304,6 @@ func NewTestService(c *Config) *TestService {
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestService) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
func (s *TestService) WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
||||||
return s.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
return s.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,13 +75,13 @@ type Handler struct {
|
||||||
MetaClient interface {
|
MetaClient interface {
|
||||||
Database(name string) *meta.DatabaseInfo
|
Database(name string) *meta.DatabaseInfo
|
||||||
Databases() []meta.DatabaseInfo
|
Databases() []meta.DatabaseInfo
|
||||||
Authenticate(username, password string) (ui *meta.UserInfo, err error)
|
Authenticate(username, password string) (ui meta.User, err error)
|
||||||
User(username string) (*meta.UserInfo, error)
|
User(username string) (meta.User, error)
|
||||||
AdminUserExists() bool
|
AdminUserExists() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryAuthorizer interface {
|
QueryAuthorizer interface {
|
||||||
AuthorizeQuery(u *meta.UserInfo, query *influxql.Query, database string) error
|
AuthorizeQuery(u meta.User, query *influxql.Query, database string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteAuthorizer interface {
|
WriteAuthorizer interface {
|
||||||
|
@ -96,7 +96,7 @@ type Handler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
PointsWriter interface {
|
PointsWriter interface {
|
||||||
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, user string, points []models.Point) error
|
||||||
}
|
}
|
||||||
|
|
||||||
Config *Config
|
Config *Config
|
||||||
|
@ -217,7 +217,7 @@ func (h *Handler) AddRoutes(routes ...Route) {
|
||||||
var handler http.Handler
|
var handler http.Handler
|
||||||
|
|
||||||
// If it's a handler func that requires authorization, wrap it in authentication
|
// If it's a handler func that requires authorization, wrap it in authentication
|
||||||
if hf, ok := r.HandlerFunc.(func(http.ResponseWriter, *http.Request, *meta.UserInfo)); ok {
|
if hf, ok := r.HandlerFunc.(func(http.ResponseWriter, *http.Request, meta.User)); ok {
|
||||||
handler = authenticate(hf, h, h.Config.AuthEnabled)
|
handler = authenticate(hf, h, h.Config.AuthEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +277,7 @@ func (h *Handler) writeHeader(w http.ResponseWriter, code int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveQuery parses an incoming query and, if valid, executes the query.
|
// serveQuery parses an incoming query and, if valid, executes the query.
|
||||||
func (h *Handler) serveQuery(w http.ResponseWriter, r *http.Request, user *meta.UserInfo) {
|
func (h *Handler) serveQuery(w http.ResponseWriter, r *http.Request, user meta.User) {
|
||||||
atomic.AddInt64(&h.stats.QueryRequests, 1)
|
atomic.AddInt64(&h.stats.QueryRequests, 1)
|
||||||
defer func(start time.Time) {
|
defer func(start time.Time) {
|
||||||
atomic.AddInt64(&h.stats.QueryRequestDuration, time.Since(start).Nanoseconds())
|
atomic.AddInt64(&h.stats.QueryRequestDuration, time.Since(start).Nanoseconds())
|
||||||
|
@ -578,7 +578,7 @@ func (h *Handler) async(query *influxql.Query, results <-chan *influxql.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveWrite receives incoming series data in line protocol format and writes it to the database.
|
// serveWrite receives incoming series data in line protocol format and writes it to the database.
|
||||||
func (h *Handler) serveWrite(w http.ResponseWriter, r *http.Request, user *meta.UserInfo) {
|
func (h *Handler) serveWrite(w http.ResponseWriter, r *http.Request, user meta.User) {
|
||||||
atomic.AddInt64(&h.stats.WriteRequests, 1)
|
atomic.AddInt64(&h.stats.WriteRequests, 1)
|
||||||
atomic.AddInt64(&h.stats.ActiveWriteRequests, 1)
|
atomic.AddInt64(&h.stats.ActiveWriteRequests, 1)
|
||||||
defer func(start time.Time) {
|
defer func(start time.Time) {
|
||||||
|
@ -598,16 +598,20 @@ func (h *Handler) serveWrite(w http.ResponseWriter, r *http.Request, user *meta.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.Config.AuthEnabled && user == nil {
|
userName := ""
|
||||||
h.httpError(w, fmt.Sprintf("user is required to write to database %q", database), http.StatusForbidden)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if h.Config.AuthEnabled {
|
if h.Config.AuthEnabled {
|
||||||
if err := h.WriteAuthorizer.AuthorizeWrite(user.Name, database); err != nil {
|
if user == nil {
|
||||||
h.httpError(w, fmt.Sprintf("%q user is not authorized to write to database %q", user.Name, database), http.StatusForbidden)
|
h.httpError(w, fmt.Sprintf("user is required to write to database %q", database), http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := h.WriteAuthorizer.AuthorizeWrite(user.ID(), database); err != nil {
|
||||||
|
h.httpError(w, fmt.Sprintf("%q user is not authorized to write to database %q", user.ID(), database), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userName = user.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle gzip decoding of the body
|
// Handle gzip decoding of the body
|
||||||
|
@ -670,7 +674,7 @@ func (h *Handler) serveWrite(w http.ResponseWriter, r *http.Request, user *meta.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write points.
|
// Write points.
|
||||||
if err := h.PointsWriter.WritePoints(database, r.URL.Query().Get("rp"), consistency, points); influxdb.IsClientError(err) {
|
if err := h.PointsWriter.WritePoints(database, r.URL.Query().Get("rp"), consistency, userName, points); influxdb.IsClientError(err) {
|
||||||
atomic.AddInt64(&h.stats.PointsWrittenFail, int64(len(points)))
|
atomic.AddInt64(&h.stats.PointsWrittenFail, int64(len(points)))
|
||||||
h.httpError(w, err.Error(), http.StatusBadRequest)
|
h.httpError(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
@ -1015,14 +1019,14 @@ func parseCredentials(r *http.Request) (*credentials, error) {
|
||||||
//
|
//
|
||||||
// There is one exception: if there are no users in the system, authentication is not required. This
|
// There is one exception: if there are no users in the system, authentication is not required. This
|
||||||
// is to facilitate bootstrapping of a system with authentication enabled.
|
// is to facilitate bootstrapping of a system with authentication enabled.
|
||||||
func authenticate(inner func(http.ResponseWriter, *http.Request, *meta.UserInfo), h *Handler, requireAuthentication bool) http.Handler {
|
func authenticate(inner func(http.ResponseWriter, *http.Request, meta.User), h *Handler, requireAuthentication bool) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Return early if we are not authenticating
|
// Return early if we are not authenticating
|
||||||
if !requireAuthentication {
|
if !requireAuthentication {
|
||||||
inner(w, r, nil)
|
inner(w, r, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var user *meta.UserInfo
|
var user meta.User
|
||||||
|
|
||||||
// TODO corylanou: never allow this in the future without users
|
// TODO corylanou: never allow this in the future without users
|
||||||
if requireAuthentication && h.MetaClient.AdminUserExists() {
|
if requireAuthentication && h.MetaClient.AdminUserExists() {
|
||||||
|
|
|
@ -92,7 +92,7 @@ func TestHandler_Query_Auth(t *testing.T) {
|
||||||
// Set mock meta client functions for the handler to use.
|
// Set mock meta client functions for the handler to use.
|
||||||
h.MetaClient.AdminUserExistsFn = func() bool { return true }
|
h.MetaClient.AdminUserExistsFn = func() bool { return true }
|
||||||
|
|
||||||
h.MetaClient.UserFn = func(username string) (*meta.UserInfo, error) {
|
h.MetaClient.UserFn = func(username string) (meta.User, error) {
|
||||||
if username != "user1" {
|
if username != "user1" {
|
||||||
return nil, meta.ErrUserNotFound
|
return nil, meta.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ func TestHandler_Query_Auth(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
h.MetaClient.AuthenticateFn = func(u, p string) (*meta.UserInfo, error) {
|
h.MetaClient.AuthenticateFn = func(u, p string) (meta.User, error) {
|
||||||
if u != "user1" {
|
if u != "user1" {
|
||||||
return nil, fmt.Errorf("unexpected user: exp: user1, got: %s", u)
|
return nil, fmt.Errorf("unexpected user: exp: user1, got: %s", u)
|
||||||
} else if p != "abcd" {
|
} else if p != "abcd" {
|
||||||
|
@ -113,7 +113,7 @@ func TestHandler_Query_Auth(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set mock query authorizer for handler to use.
|
// Set mock query authorizer for handler to use.
|
||||||
h.QueryAuthorizer.AuthorizeQueryFn = func(u *meta.UserInfo, query *influxql.Query, database string) error {
|
h.QueryAuthorizer.AuthorizeQueryFn = func(u meta.User, query *influxql.Query, database string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,11 +345,11 @@ func TestHandler_Query_ErrInvalidQuery(t *testing.T) {
|
||||||
// Ensure the handler returns an appropriate 401 or 403 status when authentication or authorization fails.
|
// Ensure the handler returns an appropriate 401 or 403 status when authentication or authorization fails.
|
||||||
func TestHandler_Query_ErrAuthorize(t *testing.T) {
|
func TestHandler_Query_ErrAuthorize(t *testing.T) {
|
||||||
h := NewHandler(true)
|
h := NewHandler(true)
|
||||||
h.QueryAuthorizer.AuthorizeQueryFn = func(u *meta.UserInfo, q *influxql.Query, db string) error {
|
h.QueryAuthorizer.AuthorizeQueryFn = func(u meta.User, q *influxql.Query, db string) error {
|
||||||
return errors.New("marker")
|
return errors.New("marker")
|
||||||
}
|
}
|
||||||
h.MetaClient.AdminUserExistsFn = func() bool { return true }
|
h.MetaClient.AdminUserExistsFn = func() bool { return true }
|
||||||
h.MetaClient.AuthenticateFn = func(u, p string) (*meta.UserInfo, error) {
|
h.MetaClient.AuthenticateFn = func(u, p string) (meta.User, error) {
|
||||||
|
|
||||||
users := []meta.UserInfo{
|
users := []meta.UserInfo{
|
||||||
{
|
{
|
||||||
|
@ -643,10 +643,10 @@ func (e *HandlerStatementExecutor) ExecuteStatement(stmt influxql.Statement, ctx
|
||||||
|
|
||||||
// HandlerQueryAuthorizer is a mock implementation of Handler.QueryAuthorizer.
|
// HandlerQueryAuthorizer is a mock implementation of Handler.QueryAuthorizer.
|
||||||
type HandlerQueryAuthorizer struct {
|
type HandlerQueryAuthorizer struct {
|
||||||
AuthorizeQueryFn func(u *meta.UserInfo, query *influxql.Query, database string) error
|
AuthorizeQueryFn func(u meta.User, query *influxql.Query, database string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *HandlerQueryAuthorizer) AuthorizeQuery(u *meta.UserInfo, query *influxql.Query, database string) error {
|
func (a *HandlerQueryAuthorizer) AuthorizeQuery(u meta.User, query *influxql.Query, database string) error {
|
||||||
return a.AuthorizeQueryFn(u, query, database)
|
return a.AuthorizeQueryFn(u, query, database)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ func (rt *RequestTracker) TrackRequests() *RequestProfile {
|
||||||
return profile
|
return profile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *RequestTracker) Add(req *http.Request, user *meta.UserInfo) {
|
func (rt *RequestTracker) Add(req *http.Request, user meta.User) {
|
||||||
rt.mu.RLock()
|
rt.mu.RLock()
|
||||||
if rt.profiles.Len() == 0 {
|
if rt.profiles.Len() == 0 {
|
||||||
rt.mu.RUnlock()
|
rt.mu.RUnlock()
|
||||||
|
@ -125,7 +125,7 @@ func (rt *RequestTracker) Add(req *http.Request, user *meta.UserInfo) {
|
||||||
|
|
||||||
info.IPAddr = host
|
info.IPAddr = host
|
||||||
if user != nil {
|
if user != nil {
|
||||||
info.Username = user.Name
|
info.Username = user.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the request info to the profiles.
|
// Add the request info to the profiles.
|
||||||
|
|
|
@ -370,7 +370,7 @@ func (c *Client) Users() []UserInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
// User returns the user with the given name, or ErrUserNotFound.
|
// User returns the user with the given name, or ErrUserNotFound.
|
||||||
func (c *Client) User(name string) (*UserInfo, error) {
|
func (c *Client) User(name string) (User, error) {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
|
@ -406,14 +406,14 @@ func (c *Client) saltedHash(password string) (salt, hash []byte, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser adds a user with the given name and password and admin status.
|
// CreateUser adds a user with the given name and password and admin status.
|
||||||
func (c *Client) CreateUser(name, password string, admin bool) (*UserInfo, error) {
|
func (c *Client) CreateUser(name, password string, admin bool) (User, error) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
data := c.cacheData.Clone()
|
data := c.cacheData.Clone()
|
||||||
|
|
||||||
// See if the user already exists.
|
// See if the user already exists.
|
||||||
if u := data.User(name); u != nil {
|
if u := data.user(name); u != nil {
|
||||||
if err := bcrypt.CompareHashAndPassword([]byte(u.Hash), []byte(password)); err != nil || u.Admin != admin {
|
if err := bcrypt.CompareHashAndPassword([]byte(u.Hash), []byte(password)); err != nil || u.Admin != admin {
|
||||||
return nil, ErrUserExists
|
return nil, ErrUserExists
|
||||||
}
|
}
|
||||||
|
@ -430,7 +430,7 @@ func (c *Client) CreateUser(name, password string, admin bool) (*UserInfo, error
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
u := data.User(name)
|
u := data.user(name)
|
||||||
|
|
||||||
if err := c.commit(data); err != nil {
|
if err := c.commit(data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -551,10 +551,10 @@ func (c *Client) AdminUserExists() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate returns a UserInfo if the username and password match an existing entry.
|
// Authenticate returns a UserInfo if the username and password match an existing entry.
|
||||||
func (c *Client) Authenticate(username, password string) (*UserInfo, error) {
|
func (c *Client) Authenticate(username, password string) (User, error) {
|
||||||
// Find user.
|
// Find user.
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
userInfo := c.cacheData.User(username)
|
userInfo := c.cacheData.user(username)
|
||||||
c.mu.RUnlock()
|
c.mu.RUnlock()
|
||||||
if userInfo == nil {
|
if userInfo == nil {
|
||||||
return nil, ErrUserNotFound
|
return nil, ErrUserNotFound
|
||||||
|
|
|
@ -596,15 +596,15 @@ func TestMetaClient_CreateUser(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if exp, got := "fred", u.Name; exp != got {
|
if exp, got := "fred", u.ID(); exp != got {
|
||||||
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
||||||
}
|
}
|
||||||
if !u.Admin {
|
if !u.IsAdmin() {
|
||||||
t.Fatalf("expected user to be admin")
|
t.Fatalf("expected user to be admin")
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err = c.Authenticate("fred", "supersecure")
|
u, err = c.Authenticate("fred", "supersecure")
|
||||||
if u == nil || err != nil || u.Name != "fred" {
|
if u == nil || err != nil || u.ID() != "fred" {
|
||||||
t.Fatalf("failed to authenticate")
|
t.Fatalf("failed to authenticate")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,7 +633,7 @@ func TestMetaClient_CreateUser(t *testing.T) {
|
||||||
|
|
||||||
// Auth for new password should succeed.
|
// Auth for new password should succeed.
|
||||||
u, err = c.Authenticate("fred", "moresupersecure")
|
u, err = c.Authenticate("fred", "moresupersecure")
|
||||||
if u == nil || err != nil || u.Name != "fred" {
|
if u == nil || err != nil || u.ID() != "fred" {
|
||||||
t.Fatalf("failed to authenticate")
|
t.Fatalf("failed to authenticate")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,10 +647,10 @@ func TestMetaClient_CreateUser(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if exp, got := "wilma", u.Name; exp != got {
|
if exp, got := "wilma", u.ID(); exp != got {
|
||||||
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
||||||
}
|
}
|
||||||
if u.Admin {
|
if u.IsAdmin() {
|
||||||
t.Fatalf("expected user not to be an admin")
|
t.Fatalf("expected user not to be an admin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,10 +667,10 @@ func TestMetaClient_CreateUser(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if exp, got := "wilma", u.Name; exp != got {
|
if exp, got := "wilma", u.ID(); exp != got {
|
||||||
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
||||||
}
|
}
|
||||||
if !u.Admin {
|
if !u.IsAdmin() {
|
||||||
t.Fatalf("expected user to be an admin")
|
t.Fatalf("expected user to be an admin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,10 +683,10 @@ func TestMetaClient_CreateUser(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if exp, got := "wilma", u.Name; exp != got {
|
if exp, got := "wilma", u.ID(); exp != got {
|
||||||
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
||||||
}
|
}
|
||||||
if u.Admin {
|
if u.IsAdmin() {
|
||||||
t.Fatalf("expected user not to be an admin")
|
t.Fatalf("expected user not to be an admin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -519,8 +519,7 @@ func (data *Data) DropSubscription(database, rp, name string) error {
|
||||||
return ErrSubscriptionNotFound
|
return ErrSubscriptionNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// User returns a user by username.
|
func (data *Data) user(username string) *UserInfo {
|
||||||
func (data *Data) User(username string) *UserInfo {
|
|
||||||
for i := range data.Users {
|
for i := range data.Users {
|
||||||
if data.Users[i].Name == username {
|
if data.Users[i].Name == username {
|
||||||
return &data.Users[i]
|
return &data.Users[i]
|
||||||
|
@ -529,6 +528,16 @@ func (data *Data) User(username string) *UserInfo {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User returns a user by username.
|
||||||
|
func (data *Data) User(username string) User {
|
||||||
|
u := data.user(username)
|
||||||
|
if u == nil {
|
||||||
|
// prevent non-nil interface with nil pointer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
// CreateUser creates a new user.
|
// CreateUser creates a new user.
|
||||||
func (data *Data) CreateUser(name, hash string, admin bool) error {
|
func (data *Data) CreateUser(name, hash string, admin bool) error {
|
||||||
// Ensure the user doesn't already exist.
|
// Ensure the user doesn't already exist.
|
||||||
|
@ -597,7 +606,7 @@ func (data *Data) CloneUsers() []UserInfo {
|
||||||
|
|
||||||
// SetPrivilege sets a privilege for a user on a database.
|
// SetPrivilege sets a privilege for a user on a database.
|
||||||
func (data *Data) SetPrivilege(name, database string, p influxql.Privilege) error {
|
func (data *Data) SetPrivilege(name, database string, p influxql.Privilege) error {
|
||||||
ui := data.User(name)
|
ui := data.user(name)
|
||||||
if ui == nil {
|
if ui == nil {
|
||||||
return ErrUserNotFound
|
return ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
@ -612,7 +621,7 @@ func (data *Data) SetPrivilege(name, database string, p influxql.Privilege) erro
|
||||||
|
|
||||||
// SetAdminPrivilege sets the admin privilege for a user.
|
// SetAdminPrivilege sets the admin privilege for a user.
|
||||||
func (data *Data) SetAdminPrivilege(name string, admin bool) error {
|
func (data *Data) SetAdminPrivilege(name string, admin bool) error {
|
||||||
ui := data.User(name)
|
ui := data.user(name)
|
||||||
if ui == nil {
|
if ui == nil {
|
||||||
return ErrUserNotFound
|
return ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
@ -632,7 +641,7 @@ func (data Data) AdminUserExists() bool {
|
||||||
|
|
||||||
// UserPrivileges gets the privileges for a user.
|
// UserPrivileges gets the privileges for a user.
|
||||||
func (data *Data) UserPrivileges(name string) (map[string]influxql.Privilege, error) {
|
func (data *Data) UserPrivileges(name string) (map[string]influxql.Privilege, error) {
|
||||||
ui := data.User(name)
|
ui := data.user(name)
|
||||||
if ui == nil {
|
if ui == nil {
|
||||||
return nil, ErrUserNotFound
|
return nil, ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
@ -642,7 +651,7 @@ func (data *Data) UserPrivileges(name string) (map[string]influxql.Privilege, er
|
||||||
|
|
||||||
// UserPrivilege gets the privilege for a user on a database.
|
// UserPrivilege gets the privilege for a user on a database.
|
||||||
func (data *Data) UserPrivilege(name, database string) (*influxql.Privilege, error) {
|
func (data *Data) UserPrivilege(name, database string) (*influxql.Privilege, error) {
|
||||||
ui := data.User(name)
|
ui := data.user(name)
|
||||||
if ui == nil {
|
if ui == nil {
|
||||||
return nil, ErrUserNotFound
|
return nil, ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
@ -1419,6 +1428,8 @@ func (cqi *ContinuousQueryInfo) unmarshal(pb *internal.ContinuousQueryInfo) {
|
||||||
cqi.Query = pb.GetQuery()
|
cqi.Query = pb.GetQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ influxql.Authorizer = (*UserInfo)(nil)
|
||||||
|
|
||||||
// UserInfo represents metadata about a user in the system.
|
// UserInfo represents metadata about a user in the system.
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
// User's name.
|
// User's name.
|
||||||
|
@ -1434,7 +1445,19 @@ type UserInfo struct {
|
||||||
Privileges map[string]influxql.Privilege
|
Privileges map[string]influxql.Privilege
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ influxql.Authorizer = (*UserInfo)(nil)
|
type User interface {
|
||||||
|
influxql.Authorizer
|
||||||
|
ID() string
|
||||||
|
IsAdmin() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserInfo) ID() string {
|
||||||
|
return u.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserInfo) IsAdmin() bool {
|
||||||
|
return u.Admin
|
||||||
|
}
|
||||||
|
|
||||||
// AuthorizeDatabase returns true if the user is authorized for the given privilege on the given database.
|
// AuthorizeDatabase returns true if the user is authorized for the given privilege on the given database.
|
||||||
func (ui *UserInfo) AuthorizeDatabase(privilege influxql.Privilege, database string) bool {
|
func (ui *UserInfo) AuthorizeDatabase(privilege influxql.Privilege, database string) bool {
|
||||||
|
@ -1445,6 +1468,16 @@ func (ui *UserInfo) AuthorizeDatabase(privilege influxql.Privilege, database str
|
||||||
return ok && (p == privilege || p == influxql.AllPrivileges)
|
return ok && (p == privilege || p == influxql.AllPrivileges)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthorizeSeriesRead is used to limit access per-series (enterprise only)
|
||||||
|
func (u *UserInfo) AuthorizeSeriesRead(database string, series string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizeSeriesWrite is used to limit access per-series (enterprise only)
|
||||||
|
func (u *UserInfo) AuthorizeSeriesWrite(database string, series string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// clone returns a deep copy of si.
|
// clone returns a deep copy of si.
|
||||||
func (ui UserInfo) clone() UserInfo {
|
func (ui UserInfo) clone() UserInfo {
|
||||||
other := ui
|
other := ui
|
||||||
|
|
|
@ -22,7 +22,7 @@ func NewQueryAuthorizer(c *Client) *QueryAuthorizer {
|
||||||
// Database can be "" for queries that do not require a database.
|
// Database can be "" for queries that do not require a database.
|
||||||
// If no user is provided it will return an error unless the query's first statement is to create
|
// If no user is provided it will return an error unless the query's first statement is to create
|
||||||
// a root user.
|
// a root user.
|
||||||
func (a *QueryAuthorizer) AuthorizeQuery(u *UserInfo, query *influxql.Query, database string) error {
|
func (a *QueryAuthorizer) AuthorizeQuery(u User, query *influxql.Query, database string) error {
|
||||||
// Special case if no users exist.
|
// Special case if no users exist.
|
||||||
if n := a.Client.UserCount(); n == 0 {
|
if n := a.Client.UserCount(); n == 0 {
|
||||||
// Ensure there is at least one statement.
|
// Ensure there is at least one statement.
|
||||||
|
@ -48,6 +48,11 @@ func (a *QueryAuthorizer) AuthorizeQuery(u *UserInfo, query *influxql.Query, dat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return u.AuthorizeQuery(database, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserInfo) AuthorizeQuery(database string, query *influxql.Query) error {
|
||||||
|
|
||||||
// Admin privilege allows the user to execute all statements.
|
// Admin privilege allows the user to execute all statements.
|
||||||
if u.Admin {
|
if u.Admin {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -24,7 +24,7 @@ type Handler struct {
|
||||||
RetentionPolicy string
|
RetentionPolicy string
|
||||||
|
|
||||||
PointsWriter interface {
|
PointsWriter interface {
|
||||||
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger zap.Logger
|
Logger zap.Logger
|
||||||
|
@ -126,7 +126,7 @@ func (h *Handler) servePut(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write points.
|
// Write points.
|
||||||
if err := h.PointsWriter.WritePoints(h.Database, h.RetentionPolicy, models.ConsistencyLevelAny, points); influxdb.IsClientError(err) {
|
if err := h.PointsWriter.WritePointsPrivileged(h.Database, h.RetentionPolicy, models.ConsistencyLevelAny, points); influxdb.IsClientError(err) {
|
||||||
h.Logger.Info(fmt.Sprint("write series error: ", err))
|
h.Logger.Info(fmt.Sprint("write series error: ", err))
|
||||||
http.Error(w, "write series error: "+err.Error(), http.StatusBadRequest)
|
http.Error(w, "write series error: "+err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
|
@ -60,7 +60,7 @@ type Service struct {
|
||||||
RetentionPolicy string
|
RetentionPolicy string
|
||||||
|
|
||||||
PointsWriter interface {
|
PointsWriter interface {
|
||||||
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
||||||
}
|
}
|
||||||
MetaClient interface {
|
MetaClient interface {
|
||||||
CreateDatabase(name string) (*meta.DatabaseInfo, error)
|
CreateDatabase(name string) (*meta.DatabaseInfo, error)
|
||||||
|
@ -459,7 +459,7 @@ func (s *Service) processBatches(batcher *tsdb.PointBatcher) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.PointsWriter.WritePoints(s.Database, s.RetentionPolicy, models.ConsistencyLevelAny, batch); err == nil {
|
if err := s.PointsWriter.WritePointsPrivileged(s.Database, s.RetentionPolicy, models.ConsistencyLevelAny, batch); err == nil {
|
||||||
atomic.AddInt64(&s.stats.BatchesTransmitted, 1)
|
atomic.AddInt64(&s.stats.BatchesTransmitted, 1)
|
||||||
atomic.AddInt64(&s.stats.PointsTransmitted, int64(len(batch)))
|
atomic.AddInt64(&s.stats.PointsTransmitted, int64(len(batch)))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -290,6 +290,6 @@ func NewTestService(database string, bind string) *TestService {
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestService) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
func (s *TestService) WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
||||||
return s.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
return s.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ type Service struct {
|
||||||
config Config
|
config Config
|
||||||
|
|
||||||
PointsWriter interface {
|
PointsWriter interface {
|
||||||
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaClient interface {
|
MetaClient interface {
|
||||||
|
@ -162,7 +162,7 @@ func (s *Service) writer() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.PointsWriter.WritePoints(s.config.Database, s.config.RetentionPolicy, models.ConsistencyLevelAny, batch); err == nil {
|
if err := s.PointsWriter.WritePointsPrivileged(s.config.Database, s.config.RetentionPolicy, models.ConsistencyLevelAny, batch); err == nil {
|
||||||
atomic.AddInt64(&s.stats.BatchesTransmitted, 1)
|
atomic.AddInt64(&s.stats.BatchesTransmitted, 1)
|
||||||
atomic.AddInt64(&s.stats.PointsTransmitted, int64(len(batch)))
|
atomic.AddInt64(&s.stats.PointsTransmitted, int64(len(batch)))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -155,6 +155,6 @@ func NewTestService(c *Config) *TestService {
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestService) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
func (s *TestService) WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
||||||
return s.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
return s.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ type Server interface {
|
||||||
|
|
||||||
Write(db, rp, body string, params url.Values) (results string, err error)
|
Write(db, rp, body string, params url.Values) (results string, err error)
|
||||||
MustWrite(db, rp, body string, params url.Values) string
|
MustWrite(db, rp, body string, params url.Values) string
|
||||||
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, user string, points []models.Point) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoteServer is a Server that is accessed remotely via the HTTP API
|
// RemoteServer is a Server that is accessed remotely via the HTTP API
|
||||||
|
@ -154,7 +154,7 @@ func (s *RemoteServer) Reset() error {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RemoteServer) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
func (s *RemoteServer) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, user string, points []models.Point) error {
|
||||||
panic("WritePoints not implemented")
|
panic("WritePoints not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,10 +328,10 @@ func (s *LocalServer) Reset() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalServer) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
func (s *LocalServer) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, user string, points []models.Point) error {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
return s.PointsWriter.WritePoints(database, retentionPolicy, consistencyLevel, points)
|
return s.PointsWriter.WritePoints(database, retentionPolicy, consistencyLevel, user, points)
|
||||||
}
|
}
|
||||||
|
|
||||||
// client abstract querying and writing to a Server using HTTP
|
// client abstract querying and writing to a Server using HTTP
|
||||||
|
|
|
@ -7930,7 +7930,7 @@ func TestServer_ConcurrentPointsWriter_Subscriber(t *testing.T) {
|
||||||
Database: "db0",
|
Database: "db0",
|
||||||
RetentionPolicy: "rp0",
|
RetentionPolicy: "rp0",
|
||||||
}
|
}
|
||||||
s.WritePoints(wpr.Database, wpr.RetentionPolicy, models.ConsistencyLevelAny, wpr.Points)
|
s.WritePoints(wpr.Database, wpr.RetentionPolicy, models.ConsistencyLevelAny, "", wpr.Points)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -1747,7 +1747,7 @@ func (e *Engine) createTagSetGroupIterators(ref *influxql.VarRef, name string, s
|
||||||
|
|
||||||
// createVarRefSeriesIterator creates an iterator for a variable reference for a series.
|
// createVarRefSeriesIterator creates an iterator for a variable reference for a series.
|
||||||
func (e *Engine) createVarRefSeriesIterator(ref *influxql.VarRef, name string, seriesKey string, t *influxql.TagSet, filter influxql.Expr, conditionFields []influxql.VarRef, opt influxql.IteratorOptions) (influxql.Iterator, error) {
|
func (e *Engine) createVarRefSeriesIterator(ref *influxql.VarRef, name string, seriesKey string, t *influxql.TagSet, filter influxql.Expr, conditionFields []influxql.VarRef, opt influxql.IteratorOptions) (influxql.Iterator, error) {
|
||||||
_, tfs, _ := models.ParseKey([]byte(seriesKey))
|
_, tfs := models.ParseKey([]byte(seriesKey))
|
||||||
tags := influxql.NewTags(tfs.Map())
|
tags := influxql.NewTags(tfs.Map())
|
||||||
|
|
||||||
// Create options specific for this series.
|
// Create options specific for this series.
|
||||||
|
|
|
@ -529,10 +529,7 @@ func (i *Index) DropSeries(key []byte) error {
|
||||||
i.mu.RLock()
|
i.mu.RLock()
|
||||||
defer i.mu.RUnlock()
|
defer i.mu.RUnlock()
|
||||||
|
|
||||||
name, tags, err := models.ParseKey(key)
|
name, tags := models.ParseKey(key)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
mname := []byte(name)
|
mname := []byte(name)
|
||||||
if err := i.activeLogFile.DeleteSeries(mname, tags); err != nil {
|
if err := i.activeLogFile.DeleteSeries(mname, tags); err != nil {
|
||||||
|
|
Loading…
Reference in New Issue