1package meta 2 3import ( 4 "fmt" 5 6 "github.com/influxdata/influxql" 7) 8 9// QueryAuthorizer determines whether a user is authorized to execute a given query. 10type QueryAuthorizer struct { 11 Client *Client 12} 13 14// NewQueryAuthorizer returns a new instance of QueryAuthorizer. 15func NewQueryAuthorizer(c *Client) *QueryAuthorizer { 16 return &QueryAuthorizer{ 17 Client: c, 18 } 19} 20 21// AuthorizeQuery authorizes u to execute q on database. 22// Database can be "" for queries that do not require a database. 23// If no user is provided it will return an error unless the query's first statement is to create 24// a root user. 25func (a *QueryAuthorizer) AuthorizeQuery(u User, query *influxql.Query, database string) error { 26 // Special case if no users exist. 27 if n := a.Client.UserCount(); n == 0 { 28 // Ensure there is at least one statement. 29 if len(query.Statements) > 0 { 30 // First statement in the query must create a user with admin privilege. 31 cu, ok := query.Statements[0].(*influxql.CreateUserStatement) 32 if ok && cu.Admin { 33 return nil 34 } 35 } 36 return &ErrAuthorize{ 37 Query: query, 38 Database: database, 39 Message: "create admin user first or disable authentication", 40 } 41 } 42 43 if u == nil { 44 return &ErrAuthorize{ 45 Query: query, 46 Database: database, 47 Message: "no user provided", 48 } 49 } 50 51 return u.AuthorizeQuery(database, query) 52} 53 54func (a *QueryAuthorizer) AuthorizeDatabase(u User, priv influxql.Privilege, database string) error { 55 if u == nil { 56 return &ErrAuthorize{ 57 Database: database, 58 Message: "no user provided", 59 } 60 } 61 62 if !u.AuthorizeDatabase(priv, database) { 63 return &ErrAuthorize{ 64 Database: database, 65 Message: fmt.Sprintf("user %q, requires %s for database %q", u.ID(), priv.String(), database), 66 } 67 } 68 69 return nil 70} 71 72func (u *UserInfo) AuthorizeQuery(database string, query *influxql.Query) error { 73 74 // Admin privilege allows the user to execute all statements. 75 if u.Admin { 76 return nil 77 } 78 79 // Check each statement in the query. 80 for _, stmt := range query.Statements { 81 // Get the privileges required to execute the statement. 82 privs, err := stmt.RequiredPrivileges() 83 if err != nil { 84 return err 85 } 86 87 // Make sure the user has the privileges required to execute 88 // each statement. 89 for _, p := range privs { 90 if p.Admin { 91 // Admin privilege already checked so statement requiring admin 92 // privilege cannot be run. 93 return &ErrAuthorize{ 94 Query: query, 95 User: u.Name, 96 Database: database, 97 Message: fmt.Sprintf("statement '%s', requires admin privilege", stmt), 98 } 99 } 100 101 // Use the db name specified by the statement or the db 102 // name passed by the caller if one wasn't specified by 103 // the statement. 104 db := p.Name 105 if db == "" { 106 db = database 107 } 108 if !u.AuthorizeDatabase(p.Privilege, db) { 109 return &ErrAuthorize{ 110 Query: query, 111 User: u.Name, 112 Database: database, 113 Message: fmt.Sprintf("statement '%s', requires %s on %s", stmt, p.Privilege.String(), db), 114 } 115 } 116 } 117 } 118 return nil 119} 120 121// ErrAuthorize represents an authorization error. 122type ErrAuthorize struct { 123 Query *influxql.Query 124 User string 125 Database string 126 Message string 127} 128 129// Error returns the text of the error. 130func (e ErrAuthorize) Error() string { 131 if e.User == "" { 132 return fmt.Sprint(e.Message) 133 } 134 return fmt.Sprintf("%s not authorized to execute %s", e.User, e.Message) 135} 136