fix: improve documentation of the config object

This commit is contained in:
Vikram Rangnekar 2020-04-23 21:21:45 -04:00
parent 5857efdd70
commit 03fe29b088
11 changed files with 77 additions and 36 deletions

View File

@ -38,12 +38,7 @@ func main() {
log.Fatalf(err)
}
conf, err := core.ReadInConfig("./config/dev.yml")
if err != nil {
log.Fatalf(err)
}
sg, err := core.NewSuperGraph(conf, db)
sg, err := core.NewSuperGraph(nil, db)
if err != nil {
log.Fatalf(err)
}

View File

@ -19,12 +19,7 @@
log.Fatalf(err)
}
conf, err := core.ReadInConfig("./config/dev.yml")
if err != nil {
log.Fatalf(err)
}
sg, err := core.NewSuperGraph(conf, db)
sg, err := core.NewSuperGraph(nil, db)
if err != nil {
log.Fatalf(err)
}

View File

@ -19,8 +19,8 @@ func BenchmarkGraphQL(b *testing.B) {
defer db.Close()
// mock.ExpectQuery(`^SELECT jsonb_build_object`).WithArgs()
sg, err := newSuperGraph(nil, db, psql.GetTestDBInfo())
c := &Config{DefaultBlock: true}
sg, err := newSuperGraph(c, db, psql.GetTestDBInfo())
if err != nil {
b.Fatal(err)
}

View File

@ -10,16 +10,56 @@ import (
// Core struct contains core specific config value
type Config struct {
// SecretKey is used to encrypt opaque values such as
// the cursor. Auto-generated if not set
SecretKey string `mapstructure:"secret_key"`
// UseAllowList (aka production mode) when set to true ensures
// only queries lists in the allow.list file can be used. All
// queries are pre-prepared so no compiling happens and things are
// very fast.
UseAllowList bool `mapstructure:"use_allow_list"`
// AllowListFile if the path to allow list file if not set the
// path is assumed to tbe the same as the config path (allow.list)
AllowListFile string `mapstructure:"allow_list_file"`
// SetUserID forces the database session variable `user.id` to
// be set to the user id. This variables can be used by triggers
// or other database functions
SetUserID bool `mapstructure:"set_user_id"`
// DefaultBlock ensures only tables configured under the `anon` role
// config can be queries if the `anon` role. For example if the table
// `users` is not listed under the anon role then it will be filtered
// out of any unauthenticated queries that mention it.
DefaultBlock bool `mapstructure:"default_block"`
// Vars is a map of hardcoded variables that can be leveraged in your
// queries (eg variable admin_id will be $admin_id in the query)
Vars map[string]string `mapstructure:"variables"`
// Blocklist is a list of tables and columns that should be filtered
// out from any and all queries
Blocklist []string
// Tables contains all table specific configuration such as aliased tables
// creating relationships between tables, etc
Tables []Table
// RolesQuery if set enabled attributed based access control. This query
// is use to fetch the user attributes that then dynamically define the users
// role.
RolesQuery string `mapstructure:"roles_query"`
// Roles contains all the configuration for all the roles you want to support
// `user` and `anon` are two default roles. User role is for when a user ID is
// available and Anon when it's not.
Roles []Role
Inflections map[string]string
// Inflections is to add additionally singular to plural mappings
// to the engine (eg. sheep: sheep)
Inflections map[string]string `mapstructure:"inflections"`
}
// Table struct defines a database table

View File

@ -75,6 +75,7 @@ func (sg *SuperGraph) initCompilers() error {
}
sg.qc, err = qcode.NewCompiler(qcode.Config{
DefaultBlock: sg.conf.DefaultBlock,
Blocklist: sg.conf.Blocklist,
})
if err != nil {

View File

@ -70,6 +70,16 @@ func (sg *SuperGraph) initConfig() error {
sg.roles["user"] = &ur
}
// If anon role is not defined and DefaultBlock is not then then create it
if _, ok := sg.roles["anon"]; !ok && !c.DefaultBlock {
ur := Role{
Name: "anon",
tm: make(map[string]*RoleTable),
}
c.Roles = append(c.Roles, ur)
sg.roles["anon"] = &ur
}
// Roles: validate and sanitize
c.RolesQuery = sanitizeVars(c.RolesQuery)

View File

@ -50,7 +50,7 @@ func DropSchema(t *testing.T, db *sql.DB) {
}
func TestSuperGraph(t *testing.T, db *sql.DB, before func(t *testing.T)) {
config := core.Config{}
config := core.Config{DefaultBlock: true}
config.UseAllowList = false
config.AllowListFile = "./allow.list"
config.RolesQuery = `SELECT * FROM users WHERE id = $user_id`

View File

@ -8,6 +8,7 @@ import (
type Config struct {
Blocklist []string
DefaultBlock bool
}
type QueryConfig struct {

View File

@ -170,6 +170,7 @@ const (
)
type Compiler struct {
db bool // default block tables if not defined in anon role
tr map[string]map[string]*trval
bl map[string]struct{}
}
@ -179,7 +180,7 @@ var expPool = sync.Pool{
}
func NewCompiler(c Config) (*Compiler, error) {
co := &Compiler{}
co := &Compiler{db: c.DefaultBlock}
co.tr = make(map[string]map[string]*trval)
co.bl = make(map[string]struct{}, len(c.Blocklist))
@ -413,12 +414,12 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
func (com *Compiler) AddFilters(qc *QCode, sel *Select, role string) {
var fil *Exp
var nu bool
var nu bool // user required (or not) in this filter
if trv, ok := com.tr[role][sel.Name]; ok {
fil, nu = trv.filter(qc.Type)
} else if role == "anon" {
} else if com.db && role == "anon" {
// Tables not defined under the anon role will not be rendered
sel.SkipRender = true
}

View File

@ -148,12 +148,7 @@ func main() {
log.Fatalf(err)
}
conf, err := config.NewConfig("./config")
if err != nil {
log.Fatalf(err)
}
sg, err = core.NewSuperGraph(conf, db)
sg, err = core.NewSuperGraph(nil, db)
if err != nil {
log.Fatalf(err)
}

View File

@ -100,6 +100,9 @@ func initConf() (*Config, error) {
c.UseAllowList = true
}
// In anon role block all tables that are not defined in the role
c.DefaultBlock = true
return c, nil
}