From 448e6bb72a2638086f3c5f5a62006748d2d1f149 Mon Sep 17 00:00:00 2001 From: Vikram Rangnekar Date: Fri, 22 May 2020 02:24:13 -0400 Subject: [PATCH] fix: add config for per role operation blocking by type --- config/dev.yml | 24 ++++++------- core/config.go | 11 +++--- core/init.go | 63 +++++++++++++++++++++++------------ core/internal/qcode/config.go | 24 ++++++++----- core/internal/qcode/qcode.go | 28 +++++++++++++++- 5 files changed, 102 insertions(+), 48 deletions(-) diff --git a/config/dev.yml b/config/dev.yml index ae8c982..7c733ca 100644 --- a/config/dev.yml +++ b/config/dev.yml @@ -8,7 +8,7 @@ log_level: "debug" # enable or disable http compression (uses gzip) http_compress: true -# When production mode is 'true' only queries +# When production mode is 'true' only queries # from the allow list are permitted. # When it's 'false' all queries are saved to the # the allow list in ./config/allow.list @@ -32,13 +32,13 @@ reload_on_config_change: true # Path pointing to where the migrations can be found migrations_path: ./migrations -# Secret key for general encryption operations like +# Secret key for general encryption operations like # encrypting the cursor data secret_key: supercalifajalistics -# CORS: A list of origins a cross-domain request can be executed from. -# If the special * value is present in the list, all origins will be allowed. -# An origin may contain a wildcard (*) to replace 0 or more +# CORS: A list of origins a cross-domain request can be executed from. +# If the special * value is present in the list, all origins will be allowed. +# An origin may contain a wildcard (*) to replace 0 or more # characters (i.e.: http://*.domain.com). cors_allowed_origins: ["*"] @@ -48,8 +48,8 @@ cors_debug: true # Default API path prefix is /api you can change it if you like # api_path: "/data" -# Cache-Control header can help cache queries if your CDN supports cache-control -# on POST requests (does not work with not mutations) +# Cache-Control header can help cache queries if your CDN supports cache-control +# on POST requests (does not work with not mutations) # cache_control: "public, max-age=300, s-maxage=600" # Postgres related environment Variables @@ -74,7 +74,7 @@ auth: cookie: _app_session # Comment this out if you want to disable setting - # the user_id via a header for testing. + # the user_id via a header for testing. # Disable in production creds_in_header: true @@ -91,7 +91,6 @@ auth: # password: "" # max_idle: 80 # max_active: 12000 - # In most cases you don't need these # salt: "encrypted cookie" # sign_salt: "signed encrypted cookie" @@ -144,7 +143,7 @@ tables: url: http://rails_app:3000/stripe/$id path: data # debug: true - pass_headers: + pass_headers: - cookie set_headers: - name: Host @@ -165,7 +164,6 @@ tables: - name: email related_to: products.name - roles_query: "SELECT * FROM users WHERE id = $user_id" roles: @@ -174,12 +172,12 @@ roles: - name: products query: limit: 10 - columns: ["id", "name", "description" ] + columns: ["id", "name", "description"] aggregation: false insert: block: false - + update: block: false diff --git a/core/config.go b/core/config.go index 7420936..bbf80df 100644 --- a/core/config.go +++ b/core/config.go @@ -106,7 +106,8 @@ type Role struct { // RoleTable struct contains role specific access control values for a database table type RoleTable struct { - Name string + Name string + ReadOnly *bool `mapstructure:"read_only"` Query Query Insert Insert @@ -120,7 +121,7 @@ type Query struct { Filters []string Columns []string DisableFunctions bool `mapstructure:"disable_functions"` - Block bool + Block *bool } // Insert struct contains access control values for insert operations @@ -128,7 +129,7 @@ type Insert struct { Filters []string Columns []string Presets map[string]string - Block bool + Block *bool } // Insert struct contains access control values for update operations @@ -136,14 +137,14 @@ type Update struct { Filters []string Columns []string Presets map[string]string - Block bool + Block *bool } // Delete struct contains access control values for delete operations type Delete struct { Filters []string Columns []string - Block bool + Block *bool } // ReadInConfig function reads in the config file for the environment specified in the GO_ENV diff --git a/core/init.go b/core/init.go index f762e83..01fefa3 100644 --- a/core/init.go +++ b/core/init.go @@ -216,53 +216,74 @@ func addRoles(c *Config, qc *qcode.Compiler) error { } func addRole(qc *qcode.Compiler, r Role, t RoleTable) error { - blockFilter := []string{"false"} + blocked := struct { + readOnly bool + query bool + insert bool + update bool + delete bool + }{true, true, true, true, true} + + if r.Name == "anon" { + blocked.query = false + } else { + blocked.readOnly = false + blocked.query = false + blocked.insert = false + blocked.update = false + blocked.delete = false + } + + if t.ReadOnly != nil { + blocked.readOnly = *t.ReadOnly + } + if t.Query.Block != nil { + blocked.query = *t.Query.Block + } + if t.Insert.Block != nil { + blocked.insert = *t.Insert.Block + } + if t.Update.Block != nil { + blocked.update = *t.Update.Block + } + if t.Delete.Block != nil { + blocked.delete = *t.Delete.Block + } query := qcode.QueryConfig{ Limit: t.Query.Limit, Filters: t.Query.Filters, Columns: t.Query.Columns, DisableFunctions: t.Query.DisableFunctions, - } - - if t.Query.Block { - query.Filters = blockFilter + Block: blocked.query, } insert := qcode.InsertConfig{ Filters: t.Insert.Filters, Columns: t.Insert.Columns, Presets: t.Insert.Presets, - } - - if t.Insert.Block { - insert.Filters = blockFilter + Block: blocked.insert, } update := qcode.UpdateConfig{ Filters: t.Update.Filters, Columns: t.Update.Columns, Presets: t.Update.Presets, - } - - if t.Update.Block { - update.Filters = blockFilter + Block: blocked.update, } delete := qcode.DeleteConfig{ Filters: t.Delete.Filters, Columns: t.Delete.Columns, - } - - if t.Delete.Block { - delete.Filters = blockFilter + Block: blocked.delete, } return qc.AddRole(r.Name, t.Name, qcode.TRConfig{ - Query: query, - Insert: insert, - Update: update, - Delete: delete, + ReadOnly: blocked.readOnly, + Query: query, + Insert: insert, + Update: update, + Delete: delete, }) } diff --git a/core/internal/qcode/config.go b/core/internal/qcode/config.go index df23899..9a30d9b 100644 --- a/core/internal/qcode/config.go +++ b/core/internal/qcode/config.go @@ -16,41 +16,46 @@ type QueryConfig struct { Filters []string Columns []string DisableFunctions bool + Block bool } type InsertConfig struct { Filters []string Columns []string Presets map[string]string + Block bool } type UpdateConfig struct { Filters []string Columns []string Presets map[string]string + Block bool } type DeleteConfig struct { Filters []string Columns []string + Block bool } type TRConfig struct { - Query QueryConfig - Insert InsertConfig - Update UpdateConfig - Delete DeleteConfig + ReadOnly bool + Query QueryConfig + Insert InsertConfig + Update UpdateConfig + Delete DeleteConfig } type trval struct { - query struct { + readOnly bool + query struct { limit string fil *Exp filNU bool cols map[string]struct{} - disable struct { - funcs bool - } + disable struct{ funcs bool } + block bool } insert struct { @@ -59,6 +64,7 @@ type trval struct { cols map[string]struct{} psmap map[string]string pslist []string + block bool } update struct { @@ -67,12 +73,14 @@ type trval struct { cols map[string]struct{} psmap map[string]string pslist []string + block bool } delete struct { fil *Exp filNU bool cols map[string]struct{} + block bool } } diff --git a/core/internal/qcode/qcode.go b/core/internal/qcode/qcode.go index f5147aa..3cd3128 100644 --- a/core/internal/qcode/qcode.go +++ b/core/internal/qcode/qcode.go @@ -207,7 +207,7 @@ func NewFilter() *Exp { func (com *Compiler) AddRole(role, table string, trc TRConfig) error { var err error - trv := &trval{} + trv := &trval{readOnly: trc.ReadOnly} // query config trv.query.fil, trv.query.filNU, err = compileFilter(trc.Query.Filters) @@ -219,6 +219,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error { } trv.query.cols = listToMap(trc.Query.Columns) trv.query.disable.funcs = trc.Query.DisableFunctions + trv.query.block = trc.Query.Block // insert config trv.insert.fil, trv.insert.filNU, err = compileFilter(trc.Insert.Filters) @@ -228,6 +229,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error { trv.insert.cols = listToMap(trc.Insert.Columns) trv.insert.psmap = parsePresets(trc.Insert.Presets) trv.insert.pslist = mapToList(trv.insert.psmap) + trv.insert.block = trc.Insert.Block // update config trv.update.fil, trv.update.filNU, err = compileFilter(trc.Update.Filters) @@ -237,6 +239,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error { trv.update.cols = listToMap(trc.Update.Columns) trv.update.psmap = parsePresets(trc.Update.Presets) trv.update.pslist = mapToList(trv.update.psmap) + trv.update.block = trc.Update.Block // delete config trv.delete.fil, trv.delete.filNU, err = compileFilter(trc.Delete.Filters) @@ -244,6 +247,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error { return err } trv.delete.cols = listToMap(trc.Delete.Columns) + trv.delete.block = trc.Delete.Block singular := flect.Singularize(table) plural := flect.Pluralize(table) @@ -330,6 +334,28 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error { trv := com.getRole(role, field.Name) + switch action { + case QTQuery: + if trv.query.block { + continue + } + + case QTInsert: + if trv.insert.block || trv.readOnly { + return fmt.Errorf("insert blocked: %s", field.Name) + } + + case QTUpdate: + if trv.update.block || trv.readOnly { + return fmt.Errorf("update blocked: %s", field.Name) + } + + case QTDelete: + if trv.delete.block || trv.readOnly { + return fmt.Errorf("delete blocked: %s", field.Name) + } + } + selects = append(selects, Select{ ID: id, ParentID: parentID,