Fix bugs and add new production mode
This commit is contained in:
parent
c7e63a6200
commit
605ec63466
|
@ -5,11 +5,11 @@ web_ui: true
|
||||||
# debug, info, warn, error, fatal, panic
|
# debug, info, warn, error, fatal, panic
|
||||||
log_level: "debug"
|
log_level: "debug"
|
||||||
|
|
||||||
# Disable this in development to get a list of
|
# When production mode is 'true' only queries
|
||||||
# queries used. When enabled super graph
|
# from the allow list are permitted.
|
||||||
# will only allow queries from this list
|
# When it's 'false' all queries are saved to the
|
||||||
# List saved to ./config/allow.list
|
# the allow list in ./config/allow.list
|
||||||
use_allow_list: false
|
production: true
|
||||||
|
|
||||||
# Throw a 401 on auth failure for queries that need auth
|
# Throw a 401 on auth failure for queries that need auth
|
||||||
auth_fail_block: false
|
auth_fail_block: false
|
||||||
|
|
|
@ -9,11 +9,11 @@ web_ui: false
|
||||||
# debug, info, warn, error, fatal, panic, disable
|
# debug, info, warn, error, fatal, panic, disable
|
||||||
log_level: "info"
|
log_level: "info"
|
||||||
|
|
||||||
# Disable this in development to get a list of
|
# When production mode is 'true' only queries
|
||||||
# queries used. When enabled super graph
|
# from the allow list are permitted.
|
||||||
# will only allow queries from this list
|
# When it's 'false' all queries are saved to the
|
||||||
# List saved to ./config/allow.list
|
# the allow list in ./config/allow.list
|
||||||
use_allow_list: true
|
production: true
|
||||||
|
|
||||||
# Throw a 401 on auth failure for queries that need auth
|
# Throw a 401 on auth failure for queries that need auth
|
||||||
auth_fail_block: true
|
auth_fail_block: true
|
||||||
|
|
|
@ -137,16 +137,23 @@ func (c *compilerContext) renderInsertUpdateColumns(qc *qcode.QCode, w io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range root.PresetList {
|
for i := range root.PresetList {
|
||||||
|
cn := root.PresetList[i]
|
||||||
|
col, ok := ti.Columns[cn]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
io.WriteString(c.w, `, `)
|
io.WriteString(c.w, `, `)
|
||||||
}
|
}
|
||||||
if values {
|
if values {
|
||||||
io.WriteString(c.w, `'`)
|
io.WriteString(c.w, `'`)
|
||||||
io.WriteString(c.w, root.PresetMap[root.PresetList[i]])
|
io.WriteString(c.w, root.PresetMap[cn])
|
||||||
io.WriteString(c.w, `'`)
|
io.WriteString(c.w, `' :: `)
|
||||||
|
io.WriteString(c.w, col.Type)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
io.WriteString(c.w, `"`)
|
io.WriteString(c.w, `"`)
|
||||||
io.WriteString(c.w, root.PresetList[i])
|
io.WriteString(c.w, cn)
|
||||||
io.WriteString(c.w, `"`)
|
io.WriteString(c.w, `"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,7 +250,7 @@ func simpleInsertWithPresets(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "products" AS (WITH "input" AS (SELECT {{data}}::json AS j) INSERT INTO "products" ("name", "price", "created_at", "updated_at", "user_id") SELECT "name", "price", 'now', 'now', '$user_id' FROM input i, json_populate_record(NULL::products, i.j) t RETURNING *) SELECT json_object_agg('product', sel_json_0) FROM (SELECT row_to_json((SELECT "sel_0" FROM (SELECT "products_0"."id" AS "id") AS "sel_0")) AS "sel_json_0" FROM (SELECT "products"."id" FROM "products") AS "products_0") AS "done_1337"`
|
sql := `WITH "products" AS (WITH "input" AS (SELECT {{data}}::json AS j) INSERT INTO "products" ("name", "price", "created_at", "updated_at", "user_id") SELECT "name", "price", 'now' :: timestamp without time zone, 'now' :: timestamp without time zone, '$user_id' :: bigint FROM input i, json_populate_record(NULL::products, i.j) t RETURNING *) SELECT json_object_agg('product', sel_json_0) FROM (SELECT row_to_json((SELECT "sel_0" FROM (SELECT "products_0"."id" AS "id") AS "sel_0")) AS "sel_json_0" FROM (SELECT "products"."id" FROM "products") AS "products_0") AS "done_1337"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{"name": "Tomato", "price": 5.76}`),
|
"data": json.RawMessage(`{"name": "Tomato", "price": 5.76}`),
|
||||||
|
@ -273,7 +273,7 @@ func simpleUpdateWithPresets(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "products" AS (WITH "input" AS (SELECT {{data}}::json AS j) UPDATE "products" SET ("name", "price", "updated_at") = (SELECT "name", "price", 'now' FROM input i, json_populate_record(NULL::products, i.j) t) WHERE (("products"."user_id") = {{user_id}}) RETURNING *) SELECT json_object_agg('product', sel_json_0) FROM (SELECT row_to_json((SELECT "sel_0" FROM (SELECT "products_0"."id" AS "id") AS "sel_0")) AS "sel_json_0" FROM (SELECT "products"."id" FROM "products") AS "products_0") AS "done_1337"`
|
sql := `WITH "products" AS (WITH "input" AS (SELECT {{data}}::json AS j) UPDATE "products" SET ("name", "price", "updated_at") = (SELECT "name", "price", 'now' :: timestamp without time zone FROM input i, json_populate_record(NULL::products, i.j) t) WHERE (("products"."user_id") = {{user_id}}) RETURNING *) SELECT json_object_agg('product', sel_json_0) FROM (SELECT row_to_json((SELECT "sel_0" FROM (SELECT "products_0"."id" AS "id") AS "sel_0")) AS "sel_json_0" FROM (SELECT "products"."id" FROM "products") AS "products_0") AS "done_1337"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{"name": "Apple", "price": 1.25}`),
|
"data": json.RawMessage(`{"name": "Apple", "price": 1.25}`),
|
||||||
|
|
|
@ -340,9 +340,9 @@ func (c *compilerContext) renderLateralJoinClose(sel *qcode.Select) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderJoin(sel *qcode.Select) error {
|
func (c *compilerContext) renderJoin(sel *qcode.Select, ti *DBTableInfo) error {
|
||||||
parent := &c.s[sel.ParentID]
|
parent := &c.s[sel.ParentID]
|
||||||
return c.renderJoinByName(sel.Table, parent.Table, parent.ID)
|
return c.renderJoinByName(ti.Name, parent.Table, parent.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderJoinByName(table, parent string, id int32) error {
|
func (c *compilerContext) renderJoinByName(table, parent string, id int32) error {
|
||||||
|
@ -607,7 +607,7 @@ func (c *compilerContext) renderBaseSelect(sel *qcode.Select, ti *DBTableInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isRoot {
|
if !isRoot {
|
||||||
if err := c.renderJoin(sel); err != nil {
|
if err := c.renderJoin(sel, ti); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,7 +691,7 @@ func (c *compilerContext) renderOrderByColumns(sel *qcode.Select, ti *DBTableInf
|
||||||
|
|
||||||
func (c *compilerContext) renderRelationship(sel *qcode.Select, ti *DBTableInfo) error {
|
func (c *compilerContext) renderRelationship(sel *qcode.Select, ti *DBTableInfo) error {
|
||||||
parent := c.s[sel.ParentID]
|
parent := c.s[sel.ParentID]
|
||||||
return c.renderRelationshipByName(sel.Table, parent.Table, parent.ID)
|
return c.renderRelationshipByName(ti.Name, parent.Table, parent.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderRelationshipByName(table, parent string, id int32) error {
|
func (c *compilerContext) renderRelationshipByName(table, parent string, id int32) error {
|
||||||
|
|
|
@ -71,7 +71,7 @@ func initAllowList(cpath string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(_allowList.filepath) == 0 {
|
if len(_allowList.filepath) == 0 {
|
||||||
if conf.UseAllowList {
|
if conf.Production {
|
||||||
logger.Fatal().Msg("allow.list not found")
|
logger.Fatal().Msg("allow.list not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ func cmdDBSeed(cmd *cobra.Command, args []string) {
|
||||||
logger.Fatal().Err(err).Msg("failed to read config")
|
logger.Fatal().Err(err).Msg("failed to read config")
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.UseAllowList = false
|
conf.Production = false
|
||||||
|
|
||||||
db, err = initDBPool(conf)
|
db, err = initDBPool(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -23,6 +23,7 @@ type config struct {
|
||||||
LogLevel string `mapstructure:"log_level"`
|
LogLevel string `mapstructure:"log_level"`
|
||||||
EnableTracing bool `mapstructure:"enable_tracing"`
|
EnableTracing bool `mapstructure:"enable_tracing"`
|
||||||
UseAllowList bool `mapstructure:"use_allow_list"`
|
UseAllowList bool `mapstructure:"use_allow_list"`
|
||||||
|
Production bool
|
||||||
WatchAndReload bool `mapstructure:"reload_on_config_change"`
|
WatchAndReload bool `mapstructure:"reload_on_config_change"`
|
||||||
AuthFailBlock bool `mapstructure:"auth_fail_block"`
|
AuthFailBlock bool `mapstructure:"auth_fail_block"`
|
||||||
SeedFile string `mapstructure:"seed_file"`
|
SeedFile string `mapstructure:"seed_file"`
|
||||||
|
@ -142,9 +143,10 @@ type configRoleTable struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type configRole struct {
|
type configRole struct {
|
||||||
Name string
|
Name string
|
||||||
Match string
|
Match string
|
||||||
Tables []configRoleTable
|
Tables []configRoleTable
|
||||||
|
tablesMap map[string]*configRoleTable
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConfig(name string) *viper.Viper {
|
func newConfig(name string) *viper.Viper {
|
||||||
|
@ -195,6 +197,10 @@ func (c *config) Init(vi *viper.Viper) error {
|
||||||
c.Tables = c.DB.Tables
|
c.Tables = c.DB.Tables
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.UseAllowList {
|
||||||
|
c.Production = true
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := range c.Inflections {
|
for k, v := range c.Inflections {
|
||||||
flect.AddPlural(k, v)
|
flect.AddPlural(k, v)
|
||||||
}
|
}
|
||||||
|
@ -219,13 +225,19 @@ func (c *config) Init(vi *viper.Viper) error {
|
||||||
rolesMap := make(map[string]struct{})
|
rolesMap := make(map[string]struct{})
|
||||||
|
|
||||||
for i := range c.Roles {
|
for i := range c.Roles {
|
||||||
role := c.Roles[i]
|
role := &c.Roles[i]
|
||||||
|
|
||||||
if _, ok := rolesMap[role.Name]; ok {
|
if _, ok := rolesMap[role.Name]; ok {
|
||||||
logger.Fatal().Msgf("duplicate role '%s' found", role.Name)
|
logger.Fatal().Msgf("duplicate role '%s' found", role.Name)
|
||||||
}
|
}
|
||||||
role.Name = sanitize(role.Name)
|
role.Name = sanitize(role.Name)
|
||||||
role.Match = sanitize(role.Match)
|
role.Match = sanitize(role.Match)
|
||||||
|
role.tablesMap = make(map[string]*configRoleTable)
|
||||||
|
|
||||||
|
for n, table := range role.Tables {
|
||||||
|
role.tablesMap[table.Name] = &role.Tables[n]
|
||||||
|
}
|
||||||
|
|
||||||
rolesMap[role.Name] = struct{}{}
|
rolesMap[role.Name] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ func (c *coreContext) execQuery() ([]byte, error) {
|
||||||
|
|
||||||
logger.Debug().Str("role", c.req.role).Msg(c.req.Query)
|
logger.Debug().Str("role", c.req.role).Msg(c.req.Query)
|
||||||
|
|
||||||
if conf.UseAllowList {
|
if conf.Production {
|
||||||
var ps *preparedItem
|
var ps *preparedItem
|
||||||
|
|
||||||
data, ps, err = c.resolvePreparedSQL()
|
data, ps, err = c.resolvePreparedSQL()
|
||||||
|
@ -256,7 +256,7 @@ func (c *coreContext) resolveSQL() ([]byte, uint32, error) {
|
||||||
stime)
|
stime)
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.UseAllowList == false {
|
if conf.Production == false {
|
||||||
_allowList.add(&c.req)
|
_allowList.add(&c.req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,17 +41,22 @@ func (c *coreContext) buildStmt() ([]stmt, error) {
|
||||||
mutation := (qc.Type != qcode.QTQuery)
|
mutation := (qc.Type != qcode.QTQuery)
|
||||||
w := &bytes.Buffer{}
|
w := &bytes.Buffer{}
|
||||||
|
|
||||||
for i := range conf.Roles {
|
for i := 1; i < len(conf.Roles); i++ {
|
||||||
role := &conf.Roles[i]
|
role := &conf.Roles[i]
|
||||||
|
|
||||||
|
// For mutations only render sql for a single role from the request
|
||||||
if mutation && len(c.req.role) != 0 && role.Name != c.req.role {
|
if mutation && len(c.req.role) != 0 && role.Name != c.req.role {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if i > 0 {
|
qc, err = qcompile.Compile(gql, role.Name)
|
||||||
qc, err = qcompile.Compile(gql, role.Name)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
|
|
||||||
|
if conf.Production && role.Name == "anon" {
|
||||||
|
if _, ok := role.tablesMap[qc.Selects[0].Table]; !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ func Do(log func(string, ...interface{}), additional ...dir) error {
|
||||||
// Ensure that we use the correct events, as they are not uniform across
|
// Ensure that we use the correct events, as they are not uniform across
|
||||||
// platforms. See https://github.com/fsnotify/fsnotify/issues/74
|
// platforms. See https://github.com/fsnotify/fsnotify/issues/74
|
||||||
|
|
||||||
if conf.UseAllowList == false && strings.HasSuffix(event.Name, "/allow.list") {
|
if conf.Production == false && strings.HasSuffix(event.Name, "/allow.list") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
tmpl/dev.yml
10
tmpl/dev.yml
|
@ -5,11 +5,11 @@ web_ui: true
|
||||||
# debug, info, warn, error, fatal, panic
|
# debug, info, warn, error, fatal, panic
|
||||||
log_level: "debug"
|
log_level: "debug"
|
||||||
|
|
||||||
# Disable this in development to get a list of
|
# When production mode is 'true' only queries
|
||||||
# queries used. When enabled super graph
|
# from the allow list are permitted.
|
||||||
# will only allow queries from this list
|
# When it's 'false' all queries are saved to the
|
||||||
# List saved to ./config/allow.list
|
# the allow list in ./config/allow.list
|
||||||
use_allow_list: false
|
production: false
|
||||||
|
|
||||||
# Throw a 401 on auth failure for queries that need auth
|
# Throw a 401 on auth failure for queries that need auth
|
||||||
auth_fail_block: false
|
auth_fail_block: false
|
||||||
|
|
|
@ -8,12 +8,11 @@ web_ui: false
|
||||||
|
|
||||||
# debug, info, warn, error, fatal, panic, disable
|
# debug, info, warn, error, fatal, panic, disable
|
||||||
log_level: "info"
|
log_level: "info"
|
||||||
|
# When production mode is 'true' only queries
|
||||||
# Disable this in development to get a list of
|
# from the allow list are permitted.
|
||||||
# queries used. When enabled super graph
|
# When it's 'false' all queries are saved to the
|
||||||
# will only allow queries from this list
|
# the allow list in ./config/allow.list
|
||||||
# List saved to ./config/allow.list
|
production: true
|
||||||
use_allow_list: true
|
|
||||||
|
|
||||||
# Throw a 401 on auth failure for queries that need auth
|
# Throw a 401 on auth failure for queries that need auth
|
||||||
auth_fail_block: true
|
auth_fail_block: true
|
||||||
|
|
Loading…
Reference in New Issue