fix: improve documentation of the config object
This commit is contained in:
parent
5857efdd70
commit
03fe29b088
|
@ -38,12 +38,7 @@ func main() {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conf, err := core.ReadInConfig("./config/dev.yml")
|
sg, err := core.NewSuperGraph(nil, db)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sg, err := core.NewSuperGraph(conf, db)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,7 @@
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conf, err := core.ReadInConfig("./config/dev.yml")
|
sg, err := core.NewSuperGraph(nil, db)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sg, err := core.NewSuperGraph(conf, db)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ func BenchmarkGraphQL(b *testing.B) {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// mock.ExpectQuery(`^SELECT jsonb_build_object`).WithArgs()
|
// mock.ExpectQuery(`^SELECT jsonb_build_object`).WithArgs()
|
||||||
|
c := &Config{DefaultBlock: true}
|
||||||
sg, err := newSuperGraph(nil, db, psql.GetTestDBInfo())
|
sg, err := newSuperGraph(c, db, psql.GetTestDBInfo())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,56 @@ import (
|
||||||
|
|
||||||
// Core struct contains core specific config value
|
// Core struct contains core specific config value
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
// SecretKey is used to encrypt opaque values such as
|
||||||
|
// the cursor. Auto-generated if not set
|
||||||
SecretKey string `mapstructure:"secret_key"`
|
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"`
|
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"`
|
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"`
|
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"`
|
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
|
Blocklist []string
|
||||||
|
|
||||||
|
// Tables contains all table specific configuration such as aliased tables
|
||||||
|
// creating relationships between tables, etc
|
||||||
Tables []Table
|
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"`
|
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
|
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
|
// Table struct defines a database table
|
||||||
|
|
|
@ -75,6 +75,7 @@ func (sg *SuperGraph) initCompilers() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
sg.qc, err = qcode.NewCompiler(qcode.Config{
|
sg.qc, err = qcode.NewCompiler(qcode.Config{
|
||||||
|
DefaultBlock: sg.conf.DefaultBlock,
|
||||||
Blocklist: sg.conf.Blocklist,
|
Blocklist: sg.conf.Blocklist,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
10
core/init.go
10
core/init.go
|
@ -70,6 +70,16 @@ func (sg *SuperGraph) initConfig() error {
|
||||||
sg.roles["user"] = &ur
|
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
|
// Roles: validate and sanitize
|
||||||
c.RolesQuery = sanitizeVars(c.RolesQuery)
|
c.RolesQuery = sanitizeVars(c.RolesQuery)
|
||||||
|
|
||||||
|
|
|
@ -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)) {
|
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.UseAllowList = false
|
||||||
config.AllowListFile = "./allow.list"
|
config.AllowListFile = "./allow.list"
|
||||||
config.RolesQuery = `SELECT * FROM users WHERE id = $user_id`
|
config.RolesQuery = `SELECT * FROM users WHERE id = $user_id`
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Blocklist []string
|
Blocklist []string
|
||||||
|
DefaultBlock bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryConfig struct {
|
type QueryConfig struct {
|
||||||
|
|
|
@ -170,6 +170,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
|
db bool // default block tables if not defined in anon role
|
||||||
tr map[string]map[string]*trval
|
tr map[string]map[string]*trval
|
||||||
bl map[string]struct{}
|
bl map[string]struct{}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +180,7 @@ var expPool = sync.Pool{
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCompiler(c Config) (*Compiler, error) {
|
func NewCompiler(c Config) (*Compiler, error) {
|
||||||
co := &Compiler{}
|
co := &Compiler{db: c.DefaultBlock}
|
||||||
co.tr = make(map[string]map[string]*trval)
|
co.tr = make(map[string]map[string]*trval)
|
||||||
co.bl = make(map[string]struct{}, len(c.Blocklist))
|
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) {
|
func (com *Compiler) AddFilters(qc *QCode, sel *Select, role string) {
|
||||||
var fil *Exp
|
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 {
|
if trv, ok := com.tr[role][sel.Name]; ok {
|
||||||
fil, nu = trv.filter(qc.Type)
|
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
|
// Tables not defined under the anon role will not be rendered
|
||||||
sel.SkipRender = true
|
sel.SkipRender = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,12 +148,7 @@ func main() {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conf, err := config.NewConfig("./config")
|
sg, err = core.NewSuperGraph(nil, db)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sg, err = core.NewSuperGraph(conf, db)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,9 @@ func initConf() (*Config, error) {
|
||||||
c.UseAllowList = true
|
c.UseAllowList = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In anon role block all tables that are not defined in the role
|
||||||
|
c.DefaultBlock = true
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue