2017-02-19 06:54:52 +00:00
|
|
|
package influx
|
|
|
|
|
|
|
|
import (
|
2017-02-19 19:47:19 +00:00
|
|
|
"context"
|
2017-02-19 06:54:52 +00:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/influxdata/chronograf"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2017-03-10 20:53:30 +00:00
|
|
|
// AllowAllDB means a user gets both read and write permissions for a db
|
|
|
|
AllowAllDB = chronograf.Allowances{"WRITE", "READ"}
|
|
|
|
// AllowAllAdmin means a user gets both read and write permissions for an admin
|
|
|
|
AllowAllAdmin = chronograf.Allowances{"ALL"}
|
2017-02-19 06:54:52 +00:00
|
|
|
// AllowRead means a user is only able to read the database.
|
|
|
|
AllowRead = chronograf.Allowances{"READ"}
|
|
|
|
// AllowWrite means a user is able to only write to the database
|
|
|
|
AllowWrite = chronograf.Allowances{"WRITE"}
|
|
|
|
// NoPrivileges occasionally shows up as a response for a users grants.
|
|
|
|
NoPrivileges = "NO PRIVILEGES"
|
|
|
|
// AllPrivileges means that a user has both read and write perms
|
|
|
|
AllPrivileges = "ALL PRIVILEGES"
|
|
|
|
// All means a user has both read and write perms. Alternative to AllPrivileges
|
|
|
|
All = "ALL"
|
|
|
|
// Read means a user can read a database
|
|
|
|
Read = "READ"
|
|
|
|
// Write means a user can write to a database
|
|
|
|
Write = "WRITE"
|
|
|
|
)
|
|
|
|
|
2017-02-27 19:31:38 +00:00
|
|
|
// Permissions return just READ and WRITE for OSS Influx
|
|
|
|
func (c *Client) Permissions(context.Context) chronograf.Permissions {
|
|
|
|
return chronograf.Permissions{
|
|
|
|
{
|
|
|
|
Scope: chronograf.AllScope,
|
2017-03-10 20:53:30 +00:00
|
|
|
Allowed: AllowAllAdmin,
|
2017-02-27 19:31:38 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Scope: chronograf.DBScope,
|
2017-03-10 20:53:30 +00:00
|
|
|
Allowed: AllowAllDB,
|
2017-02-27 19:31:38 +00:00
|
|
|
},
|
|
|
|
}
|
2017-02-19 19:47:19 +00:00
|
|
|
}
|
|
|
|
|
2017-02-19 06:54:52 +00:00
|
|
|
// showResults is used to deserialize InfluxQL SHOW commands
|
|
|
|
type showResults []struct {
|
|
|
|
Series []struct {
|
|
|
|
Values [][]interface{} `json:"values"`
|
|
|
|
} `json:"series"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Users converts SHOW USERS to chronograf Users
|
|
|
|
func (r *showResults) Users() []chronograf.User {
|
|
|
|
res := []chronograf.User{}
|
|
|
|
for _, u := range *r {
|
|
|
|
for _, s := range u.Series {
|
|
|
|
for _, v := range s.Values {
|
|
|
|
if name, ok := v[0].(string); !ok {
|
|
|
|
continue
|
|
|
|
} else if admin, ok := v[1].(bool); !ok {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
c := chronograf.User{
|
|
|
|
Name: name,
|
|
|
|
Permissions: chronograf.Permissions{},
|
|
|
|
}
|
|
|
|
if admin {
|
|
|
|
c.Permissions = adminPerms()
|
|
|
|
}
|
|
|
|
res = append(res, c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2017-03-22 08:40:30 +00:00
|
|
|
// Databases converts SHOW DATABASES to chronograf Databases
|
|
|
|
func (r *showResults) Databases() []chronograf.Database {
|
|
|
|
res := []chronograf.Database{}
|
|
|
|
for _, u := range *r {
|
|
|
|
for _, s := range u.Series {
|
|
|
|
for _, v := range s.Values {
|
|
|
|
if name, ok := v[0].(string); !ok {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
d := chronograf.Database{Name: name}
|
|
|
|
res = append(res, d)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2017-03-23 10:06:59 +00:00
|
|
|
func (r *showResults) RetentionPolicies() []chronograf.RetentionPolicy {
|
|
|
|
res := []chronograf.RetentionPolicy{}
|
|
|
|
for _, u := range *r {
|
|
|
|
for _, s := range u.Series {
|
|
|
|
for _, v := range s.Values {
|
|
|
|
if name, ok := v[0].(string); !ok {
|
|
|
|
continue
|
|
|
|
} else if duration, ok := v[1].(string); !ok {
|
|
|
|
continue
|
|
|
|
} else if sduration, ok := v[2].(string); !ok {
|
|
|
|
continue
|
2017-03-23 10:37:32 +00:00
|
|
|
} else if replication, ok := v[3].(float64); !ok {
|
2017-03-23 10:06:59 +00:00
|
|
|
continue
|
|
|
|
} else if def, ok := v[4].(bool); !ok {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
d := chronograf.RetentionPolicy{
|
|
|
|
Name: name,
|
|
|
|
Duration: duration,
|
|
|
|
ShardDuration: sduration,
|
2017-03-23 10:37:32 +00:00
|
|
|
Replication: int32(replication),
|
2017-03-23 10:06:59 +00:00
|
|
|
Default: def,
|
|
|
|
}
|
|
|
|
res = append(res, d)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2017-02-19 06:54:52 +00:00
|
|
|
// Permissions converts SHOW GRANTS to chronograf.Permissions
|
|
|
|
func (r *showResults) Permissions() chronograf.Permissions {
|
|
|
|
res := []chronograf.Permission{}
|
|
|
|
for _, u := range *r {
|
|
|
|
for _, s := range u.Series {
|
|
|
|
for _, v := range s.Values {
|
|
|
|
if db, ok := v[0].(string); !ok {
|
|
|
|
continue
|
|
|
|
} else if priv, ok := v[1].(string); !ok {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
c := chronograf.Permission{
|
|
|
|
Name: db,
|
|
|
|
Scope: chronograf.DBScope,
|
|
|
|
}
|
|
|
|
switch priv {
|
|
|
|
case AllPrivileges, All:
|
2017-03-10 20:53:30 +00:00
|
|
|
c.Allowed = AllowAllDB
|
2017-02-19 06:54:52 +00:00
|
|
|
case Read:
|
|
|
|
c.Allowed = AllowRead
|
|
|
|
case Write:
|
|
|
|
c.Allowed = AllowWrite
|
|
|
|
default:
|
|
|
|
// sometimes influx reports back NO PRIVILEGES
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
res = append(res, c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
func adminPerms() chronograf.Permissions {
|
|
|
|
return []chronograf.Permission{
|
|
|
|
{
|
|
|
|
Scope: chronograf.AllScope,
|
2017-03-10 20:53:30 +00:00
|
|
|
Allowed: AllowAllAdmin,
|
2017-02-19 06:54:52 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToInfluxQL converts the permission into InfluxQL
|
|
|
|
func ToInfluxQL(action, preposition, username string, perm chronograf.Permission) string {
|
|
|
|
if perm.Scope == chronograf.AllScope {
|
|
|
|
return fmt.Sprintf(`%s ALL PRIVILEGES %s "%s"`, action, preposition, username)
|
|
|
|
} else if len(perm.Allowed) == 0 {
|
|
|
|
// All privileges are to be removed for this user on this database
|
|
|
|
return fmt.Sprintf(`%s ALL PRIVILEGES ON "%s" %s "%s"`, action, perm.Name, preposition, username)
|
|
|
|
}
|
|
|
|
priv := ToPriv(perm.Allowed)
|
|
|
|
if priv == NoPrivileges {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(`%s %s ON "%s" %s "%s"`, action, priv, perm.Name, preposition, username)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToRevoke converts the permission into InfluxQL revokes
|
|
|
|
func ToRevoke(username string, perm chronograf.Permission) string {
|
|
|
|
return ToInfluxQL("REVOKE", "FROM", username, perm)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToGrant converts the permission into InfluxQL grants
|
|
|
|
func ToGrant(username string, perm chronograf.Permission) string {
|
2017-02-19 18:16:22 +00:00
|
|
|
if len(perm.Allowed) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
2017-02-19 06:54:52 +00:00
|
|
|
return ToInfluxQL("GRANT", "TO", username, perm)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToPriv converts chronograf allowances to InfluxQL
|
|
|
|
func ToPriv(a chronograf.Allowances) string {
|
|
|
|
if len(a) == 0 {
|
|
|
|
return NoPrivileges
|
|
|
|
}
|
|
|
|
hasWrite := false
|
|
|
|
hasRead := false
|
|
|
|
for _, aa := range a {
|
|
|
|
if aa == Read {
|
|
|
|
hasRead = true
|
|
|
|
} else if aa == Write {
|
|
|
|
hasWrite = true
|
|
|
|
} else if aa == All {
|
|
|
|
hasRead, hasWrite = true, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if hasWrite && hasRead {
|
|
|
|
return All
|
|
|
|
} else if hasWrite {
|
|
|
|
return Write
|
|
|
|
} else if hasRead {
|
|
|
|
return Read
|
|
|
|
}
|
|
|
|
return NoPrivileges
|
|
|
|
}
|
|
|
|
|
|
|
|
// Difference compares two permission sets and returns a set to be revoked and a set to be added
|
|
|
|
func Difference(wants chronograf.Permissions, haves chronograf.Permissions) (revoke chronograf.Permissions, add chronograf.Permissions) {
|
|
|
|
for _, want := range wants {
|
|
|
|
found := false
|
|
|
|
for _, got := range haves {
|
|
|
|
if want.Scope != got.Scope || want.Name != got.Name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
found = true
|
|
|
|
if len(want.Allowed) == 0 {
|
|
|
|
revoke = append(revoke, want)
|
|
|
|
} else {
|
|
|
|
add = append(add, want)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
add = append(add, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, got := range haves {
|
|
|
|
found := false
|
|
|
|
for _, want := range wants {
|
|
|
|
if want.Scope != got.Scope || want.Name != got.Name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
revoke = append(revoke, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|