From 661fed0dbbf54f389b8b447c147fc9e3d5ddad83 Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Sat, 5 Nov 2016 17:43:30 -0500 Subject: [PATCH 1/4] Set default transaction mode to read only with --readonly flag --- pkg/client/client.go | 22 ++++++++++++++++++++++ pkg/command/options.go | 1 + 2 files changed, 23 insertions(+) diff --git a/pkg/client/client.go b/pkg/client/client.go index 223c06b..d85f24c 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -225,7 +225,29 @@ func (client *Client) Query(query string) (*Result, error) { return res, err } +func (client *Client) SetReadOnlyMode() error { + var value string + if err := client.db.Get(&value, "SHOW default_transaction_read_only;"); err != nil { + return err + } + + if value == "off" { + _, err = client.db.Exec("SET default_transaction_read_only=on;") + return err + } + + return nil +} + func (client *Client) query(query string, args ...interface{}) (*Result, error) { + // We're going to force-set transaction mode on every query. + // This is needed so that default mode could not be changed by user. + if command.Opts.ReadOnly { + if err := client.SetReadOnlyMode(); err != nil { + return nil, err + } + } + action := strings.ToLower(strings.Split(query, " ")[0]) if action == "update" || action == "delete" { res, err := client.db.Exec(query, args...) diff --git a/pkg/command/options.go b/pkg/command/options.go index d08c2c6..b2c8435 100644 --- a/pkg/command/options.go +++ b/pkg/command/options.go @@ -24,6 +24,7 @@ type Options struct { SkipOpen bool `short:"s" long:"skip-open" description:"Skip browser open on start"` Sessions bool `long:"sessions" description:"Enable multiple database sessions" default:"false"` Prefix string `long:"prefix" description:"Add a url prefix"` + ReadOnly bool `long:"readonly" description:"Run database connection in readonly mode"` } var Opts Options From 2d2bd1d0b1bf91b2325a568641c1d99b0e21e5d2 Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Sat, 5 Nov 2016 17:51:34 -0500 Subject: [PATCH 2/4] Add test to verify readonly mode --- pkg/client/client.go | 2 +- pkg/client/client_test.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index d85f24c..5c3e274 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -232,7 +232,7 @@ func (client *Client) SetReadOnlyMode() error { } if value == "off" { - _, err = client.db.Exec("SET default_transaction_read_only=on;") + _, err := client.db.Exec("SET default_transaction_read_only=on;") return err } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 1ded3f7..e6b3041 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -333,6 +333,18 @@ func test_HistoryUniqueness(t *testing.T) { assert.Equal(t, "SELECT * FROM books WHERE id = 1", client.History[0].Query) } +func test_ReadOnlyMode(t *testing.T) { + url := fmt.Sprintf("postgres://%s@%s:%s/%s?sslmode=disable", serverUser, serverHost, serverPort, serverDatabase) + client, _ := NewFromUrl(url, nil) + + err := client.SetReadOnlyMode() + assert.Equal(t, nil, err) + + _, err = client.Query("CREATE TABLE foobar(id integer);") + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "in a read-only transaction") +} + func TestAll(t *testing.T) { if onWindows() { // Dont have access to windows machines at the moment... @@ -362,6 +374,7 @@ func TestAll(t *testing.T) { test_ResultCsv(t) test_History(t) test_HistoryError(t) + test_ReadOnlyMode(t) teardownClient() teardown() From 8f90ec4173846f0263ee8146d670a921f5b37593 Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Sat, 5 Nov 2016 17:52:18 -0500 Subject: [PATCH 3/4] Add missing history uniqueness test --- pkg/client/client_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index e6b3041..c03b7b6 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -373,6 +373,7 @@ func TestAll(t *testing.T) { test_TableRowsOrderEscape(t) test_ResultCsv(t) test_History(t) + test_HistoryUniqueness(t) test_HistoryError(t) test_ReadOnlyMode(t) From d9cb536518b188e81762bb71099e7ddf85e9f9bd Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Sat, 5 Nov 2016 20:50:56 -0500 Subject: [PATCH 4/4] Add security warning --- main.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/main.go b/main.go index c3cd91e..444a908 100644 --- a/main.go +++ b/main.go @@ -64,6 +64,15 @@ func initOptions() { os.Exit(0) } + if options.ReadOnly { + msg := `------------------------------------------------------ +SECURITY WARNING: You are running pgweb in read-only mode. +This mode is designed for environments where users could potentially delete / change data. +For proper read-only access please follow postgresql role management documentation. +------------------------------------------------------` + fmt.Println(msg) + } + printVersion() }