192 lines
4.7 KiB
Go
192 lines
4.7 KiB
Go
|
package influx
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/influxdata/chronograf"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// AllowAll means a user gets both read and write permissions
|
||
|
AllowAll = chronograf.Allowances{"WRITE", "READ"}
|
||
|
// 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"
|
||
|
)
|
||
|
|
||
|
// 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
|
||
|
}
|
||
|
|
||
|
// 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:
|
||
|
c.Allowed = AllowAll
|
||
|
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,
|
||
|
Allowed: AllowAll,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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 {
|
||
|
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
|
||
|
}
|