Add RBAC option to disable functions eg. count

This commit is contained in:
Vikram Rangnekar 2019-10-27 01:52:48 -04:00
parent 4a8af69dd0
commit 34867a2733
16 changed files with 97 additions and 52 deletions

View File

@ -164,7 +164,7 @@ roles:
limit: 50
filters: ["{ user_id: { eq: $user_id } }"]
columns: ["id", "name", "description" ]
disable_aggregation: false
disable_functions: false
insert:
filters: ["{ user_id: { eq: $user_id } }"]

View File

@ -152,7 +152,7 @@ roles:
limit: 50
filters: ["{ user_id: { eq: $user_id } }"]
columns: ["id", "name", "description" ]
disable_aggregation: false
disable_functions: false
insert:
filters: ["{ user_id: { eq: $user_id } }"]

View File

@ -1206,7 +1206,7 @@ roles:
limit: 50
filters: ["{ user_id: { eq: $user_id } }"]
columns: ["id", "name", "description" ]
disable_aggregation: false
disable_functions: false
insert:
filters: ["{ user_id: { eq: $user_id } }"]

View File

@ -1,32 +1,9 @@
// +build gofuzz
package jsn
import "bytes"
func Fuzz(data []byte) int {
err1 := Validate(string(data))
var b1 bytes.Buffer
err2 := Filter(&b1, data, []string{"id", "full_name", "embed"})
path1 := [][]byte{[]byte("data"), []byte("users")}
Strip(data, path1)
from := []Field{
{[]byte("__twitter_id"), []byte(`[{ "name": "hello" }, { "name": "world"}]`)},
{[]byte("__twitter_id"), []byte(`"ABC123"`)},
}
to := []Field{
{[]byte("__twitter_id"), []byte(`"1234567890"`)},
{[]byte("some_list"), []byte(`[{"id":1,"embed":{"id":8}},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13}]`)},
}
var b2 bytes.Buffer
err3 := Replace(&b2, data, from, to)
Keys(data)
if err1 != nil || err2 != nil || err3 != nil {
if err := unifiedTest(data); err != nil {
return 0
}

View File

@ -1,6 +1,8 @@
package jsn
import "testing"
import (
"testing"
)
func TestFuzzCrashers(t *testing.T) {
var crashers = []string{
@ -53,6 +55,6 @@ func TestFuzzCrashers(t *testing.T) {
}
for _, f := range crashers {
Fuzz([]byte(f))
unifiedTest([]byte(f))
}
}

37
jsn/test.go Normal file
View File

@ -0,0 +1,37 @@
package jsn
import (
"bytes"
"errors"
)
func unifiedTest(data []byte) error {
err1 := Validate(string(data))
var b1 bytes.Buffer
err2 := Filter(&b1, data, []string{"id", "full_name", "embed"})
path1 := [][]byte{[]byte("data"), []byte("users")}
Strip(data, path1)
from := []Field{
{[]byte("__twitter_id"), []byte(`[{ "name": "hello" }, { "name": "world"}]`)},
{[]byte("__twitter_id"), []byte(`"ABC123"`)},
}
to := []Field{
{[]byte("__twitter_id"), []byte(`"1234567890"`)},
{[]byte("some_list"), []byte(`[{"id":1,"embed":{"id":8}},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":7},{"id":8},{"id":9},{"id":10},{"id":11},{"id":12},{"id":13}]`)},
}
var b2 bytes.Buffer
err3 := Replace(&b2, data, from, to)
Keys(data)
if err1 != nil || err2 != nil || err3 != nil {
return errors.New("there was an error")
}
return nil
}

View File

@ -69,6 +69,7 @@ func TestMain(m *testing.M) {
qcompile.AddRole("bad_dude", "users", qcode.TRConfig{
Query: qcode.QueryConfig{
Filters: []string{"false"},
DisableFunctions: true,
},
Insert: qcode.InsertConfig{
Filters: []string{"false"},

View File

@ -373,16 +373,18 @@ func (c *compilerContext) renderJoinTable(sel *qcode.Select) error {
func (c *compilerContext) renderColumns(sel *qcode.Select, ti *DBTableInfo) {
i := 0
for _, col := range sel.Cols {
if len(sel.Allowed) != 0 {
n := funcPrefixLen(col.Name)
if n != 0 {
if sel.Functions == false {
continue
}
if len(sel.Allowed) != 0 {
if _, ok := sel.Allowed[col.Name[n:]]; !ok {
continue
}
}
} else {
if len(sel.Allowed) != 0 {
if _, ok := sel.Allowed[col.Name]; !ok {
continue
}

View File

@ -389,6 +389,7 @@ func TestCompileQuery(t *testing.T) {
t.Run("syntheticTables", syntheticTables)
t.Run("queryWithVariables", queryWithVariables)
t.Run("blockedQuery", blockedQuery)
t.Run("blockedFunctions", blockedFunctions)
}
var benchGQL = []byte(`query {
@ -435,6 +436,26 @@ func blockedQuery(t *testing.T) {
}
}
func blockedFunctions(t *testing.T) {
gql := `query {
users {
count_id
email
}
}`
sql := `SELECT json_object_agg('users', users) FROM (SELECT coalesce(json_agg("sel_json_0"), '[]') AS "users" FROM (SELECT row_to_json((SELECT "sel_0" FROM (SELECT "users_0"."email" AS "email") AS "sel_0")) AS "sel_json_0" FROM (SELECT "users"."email" FROM "users" WHERE (false) LIMIT ('20') :: integer) AS "users_0" LIMIT ('20') :: integer) AS "sel_json_agg_0") AS "done_1337"`
resSQL, err := compileGQLToPSQL(gql, nil, "bad_dude")
if err != nil {
t.Fatal(err)
}
if string(resSQL) != sql {
t.Fatal(errNotExpected)
}
}
func BenchmarkCompile(b *testing.B) {
w := &bytes.Buffer{}

View File

@ -1,3 +1,5 @@
// +build gofuzz
package qcode
// FuzzerEntrypoint for Fuzzbuzz

View File

@ -298,6 +298,7 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
Table: field.Name,
Children: make([]int32, 0, 5),
Allowed: trv.allowedColumns(action),
Functions: true,
})
s := &selects[(len(selects) - 1)]

View File

@ -108,7 +108,7 @@ type configRole struct {
Limit int
Filters []string
Columns []string
DisableAggregation bool `mapstructure:"disable_aggregation"`
DisableFunctions bool `mapstructure:"disable_functions"`
Block bool
}

View File

@ -1,3 +1,5 @@
// +build gofuzz
package serv
func Fuzz(data []byte) int {

View File

@ -38,7 +38,7 @@ func initCompilers(c *config) (*qcode.Compiler, *psql.Compiler, error) {
Limit: t.Query.Limit,
Filters: t.Query.Filters,
Columns: t.Query.Columns,
DisableFunctions: t.Query.DisableAggregation,
DisableFunctions: t.Query.DisableFunctions,
}
if t.Query.Block {

View File

@ -164,7 +164,7 @@ roles:
limit: 50
filters: ["{ user_id: { eq: $user_id } }"]
columns: ["id", "name", "description" ]
disable_aggregation: false
disable_functions: false
insert:
filters: ["{ user_id: { eq: $user_id } }"]

View File

@ -152,7 +152,7 @@ roles:
limit: 50
filters: ["{ user_id: { eq: $user_id } }"]
columns: ["id", "name", "description" ]
disable_aggregation: false
disable_functions: false
insert:
filters: ["{ user_id: { eq: $user_id } }"]