package meta import ( "fmt" "github.com/influxdata/influxdb/query" "github.com/influxdata/influxql" ) // QueryAuthorizer determines whether a user is authorized to execute a given query. type QueryAuthorizer struct { Client *Client } // NewQueryAuthorizer returns a new instance of QueryAuthorizer. func NewQueryAuthorizer(c *Client) *QueryAuthorizer { return &QueryAuthorizer{ Client: c, } } // AuthorizeQuery authorizes u to execute q on 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 // a root user. func (a *QueryAuthorizer) AuthorizeQuery(u User, q *influxql.Query, database string) (query.FineAuthorizer, error) { // Special case if no users exist. if n := a.Client.UserCount(); n == 0 { // Ensure there is at least one statement. if len(q.Statements) > 0 { // First statement in the query must create a user with admin privilege. cu, ok := q.Statements[0].(*influxql.CreateUserStatement) if ok && cu.Admin { return query.OpenAuthorizer, nil } } return nil, &ErrAuthorize{ Query: q, Database: database, Message: "create admin user first or disable authentication", } } if u == nil { return nil, &ErrAuthorize{ Query: q, Database: database, Message: "no user provided", } } // There is only one OSS implementation of the User interface, and the OSS QueryAuthorizer only works // with the OSS UserInfo. There is a similar tight coupling between the Enterprise QueryAuthorizer and // Enterprise UserInfo in closed-source code. switch user := u.(type) { case *UserInfo: // Admin privilege allows the user to execute all statements. if user.Admin { return query.OpenAuthorizer, nil } // Check each statement in the query. for _, stmt := range q.Statements { // Get the privileges required to execute the statement. privs, err := stmt.RequiredPrivileges() if err != nil { return nil, err } // Make sure the user has the privileges required to execute // each statement. for _, p := range privs { if p.Admin { // Admin privilege already checked so statement requiring admin // privilege cannot be run. return nil, &ErrAuthorize{ Query: q, User: user.Name, Database: database, Message: fmt.Sprintf("statement '%s', requires admin privilege", stmt), } } // Use the db name specified by the statement or the db // name passed by the caller if one wasn't specified by // the statement. db := p.Name if db == "" { db = database } if !user.AuthorizeDatabase(p.Privilege, db) { return nil, &ErrAuthorize{ Query: q, User: user.Name, Database: database, Message: fmt.Sprintf("statement '%s', requires %s on %s", stmt, p.Privilege.String(), db), } } } } return query.OpenAuthorizer, nil default: } return nil, &ErrAuthorize{ Query: q, User: u.ID(), Database: database, Message: fmt.Sprintf("Invalid OSS user type %T", u), } } func (a *QueryAuthorizer) AuthorizeDatabase(u User, priv influxql.Privilege, database string) error { if u == nil { return &ErrAuthorize{ Database: database, Message: "no user provided", } } switch user := u.(type) { case *UserInfo: if !user.AuthorizeDatabase(priv, database) { return &ErrAuthorize{ Database: database, Message: fmt.Sprintf("user %q, requires %s for database %q", u.ID(), priv.String(), database), } } return nil default: } return &ErrAuthorize{ Database: database, User: u.ID(), Message: fmt.Sprintf("Internal error - incorrect oss user type %T", u), } } func (a *QueryAuthorizer) AuthorizeCreateDatabase(u User) error { if u == nil { return &ErrAuthorize{ Message: "no user provided", } } switch user := u.(type) { case *UserInfo: if !user.Admin { return &ErrAuthorize{ Message: fmt.Sprintf("user %q, must be an administrator to create a database", user.Name), } } return nil default: } return &ErrAuthorize{ User: u.ID(), Message: fmt.Sprintf("Internal error - incorrect oss user type %T", u), } } func (a *QueryAuthorizer) AuthorizeCreateRetentionPolicy(u User, db string) error { if u == nil { return &ErrAuthorize{ Message: "no user provided", } } switch user := u.(type) { case *UserInfo: if !user.Admin { return &ErrAuthorize{ Message: fmt.Sprintf("user %q, must be an administrator to create a retention policy", user.Name), } } return nil default: } return &ErrAuthorize{ User: u.ID(), Message: fmt.Sprintf("Internal error - incorrect oss user type %T", u), } } func (a *QueryAuthorizer) AuthorizeDeleteRetentionPolicy(u User, db string) error { if u == nil { return &ErrAuthorize{ Database: db, Message: "no user provided", } } switch user := u.(type) { case *UserInfo: if !user.Admin { return &ErrAuthorize{ Database: db, Message: fmt.Sprintf("user %q, must be an administrator to delete a retention policy", u.ID()), } } return nil default: } return &ErrAuthorize{ User: u.ID(), Message: fmt.Sprintf("Internal error - incorrect oss user type %T", u), } } // ErrAuthorize represents an authorization error. type ErrAuthorize struct { Query *influxql.Query User string Database string Message string } // Error returns the text of the error. func (e ErrAuthorize) Error() string { if e.User == "" { return fmt.Sprint(e.Message) } return fmt.Sprintf("%s not authorized to execute %s", e.User, e.Message) }