From db8fa5d3ae06e92c8d9534f7ddd24f3e02d9d0f2 Mon Sep 17 00:00:00 2001 From: Riyaz Faizullabhoy Date: Tue, 10 May 2016 15:12:06 -0700 Subject: [PATCH] Use server and signer users during non-bootstrap connections, bump gorethink Signed-off-by: Riyaz Faizullabhoy --- Godeps/Godeps.json | 16 +++---- cmd/notary-server/config.go | 6 ++- cmd/notary-signer/config.go | 8 +++- const.go | 4 ++ server/storage/rethinkdb.go | 3 +- signer/keydbstore/rethink_keydbstore.go | 4 +- storage/rethinkdb/rethinkdb.go | 23 +++++++++- .../dancannon/gorethink.v2/CHANGELOG.md | 16 +++++++ .../gopkg.in/dancannon/gorethink.v2/README.md | 42 ++++++++++++++--- .../dancannon/gorethink.v2/cluster.go | 45 ++++++++++--------- .../dancannon/gorethink.v2/connection.go | 2 + vendor/gopkg.in/dancannon/gorethink.v2/doc.go | 2 +- .../dancannon/gorethink.v2/encoding/cache.go | 7 ++- .../gopkg.in/dancannon/gorethink.v2/query.go | 4 ++ .../dancannon/gorethink.v2/query_table.go | 2 + .../dancannon/gorethink.v2/query_test.go | 14 ++++++ .../gorethink.v2/query_transformation.go | 21 +++++++++ .../dancannon/gorethink.v2/query_write.go | 1 + .../dancannon/gorethink.v2/session_test.go | 22 +++++++++ 19 files changed, 196 insertions(+), 46 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index ee193b4bd1..696400753c 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -182,23 +182,23 @@ }, { "ImportPath": "gopkg.in/dancannon/gorethink.v2", - "Comment": "v2.0.0", - "Rev": "ad28ba0b1cbf0aa5bbae1ea71941564cc08abfe4" + "Comment": "v2.0.2", + "Rev": "3742792da4bc279ccd6d807f24687009cbeda860" }, { "ImportPath": "gopkg.in/dancannon/gorethink.v2/encoding", - "Comment": "v2.0.0", - "Rev": "ad28ba0b1cbf0aa5bbae1ea71941564cc08abfe4" + "Comment": "v2.0.2", + "Rev": "3742792da4bc279ccd6d807f24687009cbeda860" }, { "ImportPath": "gopkg.in/dancannon/gorethink.v2/ql2", - "Comment": "v2.0.0", - "Rev": "ad28ba0b1cbf0aa5bbae1ea71941564cc08abfe4" + "Comment": "v2.0.2", + "Rev": "3742792da4bc279ccd6d807f24687009cbeda860" }, { "ImportPath": "gopkg.in/dancannon/gorethink.v2/types", - "Comment": "v2.0.0", - "Rev": "ad28ba0b1cbf0aa5bbae1ea71941564cc08abfe4" + "Comment": "v2.0.2", + "Rev": "3742792da4bc279ccd6d807f24687009cbeda860" }, { "ImportPath": "github.com/denisenkom/go-mssqldb", diff --git a/cmd/notary-server/config.go b/cmd/notary-server/config.go index c875b06a6f..3cf4ae96ff 100644 --- a/cmd/notary-server/config.go +++ b/cmd/notary-server/config.go @@ -94,7 +94,11 @@ func getStore(configuration *viper.Viper, hRegister healthRegister) ( CertFile: storeConfig.Cert, KeyFile: storeConfig.Key, } - sess, err = rethinkdb.Connection(tlsOpts, storeConfig.Source) + if doBootstrap { + sess, err = rethinkdb.AdminConnection(tlsOpts, storeConfig.Source) + } else { + sess, err = rethinkdb.UserConnection(tlsOpts, storeConfig.Source, notary.NotaryServerUser) + } if err != nil { return nil, fmt.Errorf("Error starting %s driver: %s", backend, err.Error()) } diff --git a/cmd/notary-signer/config.go b/cmd/notary-signer/config.go index 41c049f5b5..2f47f531dc 100644 --- a/cmd/notary-signer/config.go +++ b/cmd/notary-signer/config.go @@ -127,9 +127,13 @@ func setUpCryptoservices(configuration *viper.Viper, allowedBackends []string) ( CertFile: storeConfig.Cert, KeyFile: storeConfig.Key, } - sess, err = rethinkdb.Connection(tlsOpts, storeConfig.Source) + if doBootstrap { + sess, err = rethinkdb.AdminConnection(tlsOpts, storeConfig.Source) + } else { + sess, err = rethinkdb.UserConnection(tlsOpts, storeConfig.Source, notary.NotarySignerUser) + } if err != nil { - return nil, err + return nil, fmt.Errorf("Error starting %s driver: %s", backend, err.Error()) } s := keydbstore.NewRethinkDBKeyStore(storeConfig.DBName, passphraseRetriever, defaultAlias, sess) health.RegisterPeriodicFunc("DB operational", s.CheckHealth, time.Minute) diff --git a/const.go b/const.go index 5fcd3234d0..a5f0828994 100644 --- a/const.go +++ b/const.go @@ -56,6 +56,10 @@ const ( MemoryBackend = "memory" SQLiteBackend = "sqlite3" RethinkDBBackend = "rethinkdb" + + // Users for the notaryserver and notarysigner databases, respectively + NotaryServerUser = "server" + NotarySignerUser = "signer" ) // NotaryDefaultExpiries is the construct used to configure the default expiry times of diff --git a/server/storage/rethinkdb.go b/server/storage/rethinkdb.go index ad9217ce95..568ed39c5d 100644 --- a/server/storage/rethinkdb.go +++ b/server/storage/rethinkdb.go @@ -7,6 +7,7 @@ import ( "sort" "time" + "github.com/docker/notary" "github.com/docker/notary/storage/rethinkdb" "github.com/docker/notary/tuf/data" "gopkg.in/dancannon/gorethink.v2" @@ -270,7 +271,7 @@ func (rdb RethinkDB) Bootstrap() error { }); err != nil { return err } - return rethinkdb.CreateAndGrantDBUser(rdb.sess, rdb.dbName, "server", "") + return rethinkdb.CreateAndGrantDBUser(rdb.sess, rdb.dbName, notary.NotaryServerUser, "") } // CheckHealth is currently a noop diff --git a/signer/keydbstore/rethink_keydbstore.go b/signer/keydbstore/rethink_keydbstore.go index 2c326324ac..074aaa3c3d 100644 --- a/signer/keydbstore/rethink_keydbstore.go +++ b/signer/keydbstore/rethink_keydbstore.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/docker/notary" "github.com/docker/notary/passphrase" "github.com/docker/notary/storage/rethinkdb" "github.com/docker/notary/trustmanager" @@ -243,8 +244,7 @@ func (rdb RethinkDBKeyStore) Bootstrap() error { }); err != nil { return err } - - return rethinkdb.CreateAndGrantDBUser(rdb.sess, rdb.dbName, "signer", "") + return rethinkdb.CreateAndGrantDBUser(rdb.sess, rdb.dbName, notary.NotarySignerUser, "") } // CheckHealth verifies that DB exists and is query-able diff --git a/storage/rethinkdb/rethinkdb.go b/storage/rethinkdb/rethinkdb.go index 8e7b942069..b4f91fa456 100644 --- a/storage/rethinkdb/rethinkdb.go +++ b/storage/rethinkdb/rethinkdb.go @@ -3,6 +3,7 @@ package rethinkdb import ( "time" + "github.com/Sirupsen/logrus" "github.com/docker/go-connections/tlsconfig" "gopkg.in/dancannon/gorethink.v2" ) @@ -17,9 +18,10 @@ type Timing struct { DeletedAt time.Time `gorethink:"deleted_at"` } -// Connection sets up a RethinkDB connection to the host (`host:port` format) +// AdminConnection sets up an admin RethinkDB connection to the host (`host:port` format) // using the CA .pem file provided at path `caFile` -func Connection(tlsOpts tlsconfig.Options, host string) (*gorethink.Session, error) { +func AdminConnection(tlsOpts tlsconfig.Options, host string) (*gorethink.Session, error) { + logrus.Debugf("attempting to connect admin to host %s", host) t, err := tlsconfig.Client(tlsOpts) if err != nil { return nil, err @@ -31,3 +33,20 @@ func Connection(tlsOpts tlsconfig.Options, host string) (*gorethink.Session, err }, ) } + +// UserConnection sets up a user RethinkDB connection to the host (`host:port` format) +// using the CA .pem file provided at path `caFile`, using the provided username. +func UserConnection(tlsOpts tlsconfig.Options, host, username string) (*gorethink.Session, error) { + logrus.Debugf("attempting to connect user %s to host %s", username, host) + t, err := tlsconfig.Client(tlsOpts) + if err != nil { + return nil, err + } + return gorethink.Connect( + gorethink.ConnectOpts{ + Address: host, + TLSConfig: t, + Username: username, + }, + ) +} diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/CHANGELOG.md b/vendor/gopkg.in/dancannon/gorethink.v2/CHANGELOG.md index 1fc67ed28d..be0dd0f971 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/CHANGELOG.md +++ b/vendor/gopkg.in/dancannon/gorethink.v2/CHANGELOG.md @@ -2,6 +2,22 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## v2.0.2 - 2016-04-18 + +### Fixed + - Fixed issue which prevented anonymous `time.Time` values from being encoded when used in a struct. + - Fixed panic when attempting to run a query with a nil session + +## v2.0.1 - 2016-04-14 + +### Added + - Added `UnionWithOpts` term which allows `Union` to be called with optional arguments (such as `Interleave`) + - Added `IncludeOffsets` and `IncludeTypes` optional arguments to `ChangesOpts` + - Added `Conflict` optional argument to `InsertOpts` + +### Fixed + - Fixed error when connecting to database as non-admin user, please note that `DiscoverHosts` will not work with user authentication at this time due to the fact that RethinkDB restricts access to the required system tables. + ## v2.0.0 - 2016-04-13 ### Changed diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/README.md b/vendor/gopkg.in/dancannon/gorethink.v2/README.md index 7d9bac5fb8..60ccbf2ac9 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/README.md +++ b/vendor/gopkg.in/dancannon/gorethink.v2/README.md @@ -8,7 +8,7 @@ ![GoRethink Logo](https://raw.github.com/wiki/dancannon/gorethink/gopher-and-thinker-s.png "Golang Gopher and RethinkDB Thinker") -Current version: v2.0.0 (RethinkDB v2.3) +Current version: v2.0.2 (RethinkDB v2.3) Please note that this version of the driver only supports versions of RethinkDB using the v0.4 protocol (any versions of the driver older than RethinkDB 2.0 will not work). @@ -16,15 +16,16 @@ If you need any help you can find me on the [RethinkDB slack](http://slack.rethi ## Installation -```sh -go get -u github.com/dancannon/gorethink -``` - -Or (pinned to the v1.x.x tag) ``` go get gopkg.in/dancannon/gorethink.v2 ``` +(Or v1) + +```sh +go get gopkg.in/dancannon/gorethink.v1 +``` + ## Connection ### Basic Connection @@ -91,6 +92,35 @@ if err != nil { When `DiscoverHosts` is true any nodes are added to the cluster after the initial connection then the new node will be added to the pool of available nodes used by GoRethink. Unfortunately the canonical address of each server in the cluster **MUST** be set as otherwise clients will try to connect to the database nodes locally. For more information about how to set a RethinkDB servers canonical address set this page http://www.rethinkdb.com/docs/config-file/. +## User Authentication + +To login with a username and password you should first create a user, this can be done by writing to the `users` system table and then grant that user access to any tables or databases they need access to. This queries can also be executed in the RethinkDB admin console. + +```go +err := r.DB("rethinkdb").Table("users").Insert(map[string]string{ + "id": "john", + "password": "p455w0rd", +}).Exec(session) +... +err = r.DB("blog").Table("posts").Grant("john", map[string]bool{ + "read": true, + "write": true, +}).Exec(session) +... +``` + +Finally the username and password should be passed to `Connect` when creating your session, for example: + +```go +session, err := r.Connect(r.ConnectOpts{ + Address: "localhost:28015", + Database: "blog", + Username: "john", + Password: "p455w0rd", +}) +``` + +Please note that `DiscoverHosts` will not work with user authentication at this time due to the fact that RethinkDB restricts access to the required system tables. ## Query Functions diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/cluster.go b/vendor/gopkg.in/dancannon/gorethink.v2/cluster.go index f4945bb82e..76ae68304c 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/cluster.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/cluster.go @@ -222,28 +222,23 @@ func (c *Cluster) connectNodes(hosts []Host) { } defer conn.Close() - q, err := newQuery( - DB("rethinkdb").Table("server_status"), - map[string]interface{}{}, - c.opts, - ) - if err != nil { - Log.Warnf("Error building query: %s", err) - continue - } - - _, cursor, err := conn.Query(q) - if err != nil { - Log.Warnf("Error fetching cluster status: %s", err) - continue - } - - // TODO: connect to seed hosts using `.Server()` to get server ID. Need - // some way of making this backwards compatible - - // TODO: AFTER try to discover hosts - if c.opts.DiscoverHosts { + q, err := newQuery( + DB("rethinkdb").Table("server_status"), + map[string]interface{}{}, + c.opts, + ) + if err != nil { + Log.Warnf("Error building query: %s", err) + continue + } + + _, cursor, err := conn.Query(q) + if err != nil { + Log.Warnf("Error fetching cluster status: %s", err) + continue + } + var results []nodeStatus err = cursor.All(&results) if err != nil { @@ -265,7 +260,13 @@ func (c *Cluster) connectNodes(hosts []Host) { } } } else { - node, err := c.connectNode(host.String(), []Host{host}) + svrRsp, err := conn.Server() + if err != nil { + Log.Warnf("Error fetching server ID: %s", err) + continue + } + + node, err := c.connectNode(svrRsp.ID, []Host{host}) if err == nil { if _, ok := nodeSet[node.ID]; !ok { Log.WithFields(logrus.Fields{ diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/connection.go b/vendor/gopkg.in/dancannon/gorethink.v2/connection.go index 93b71d0d33..b4e58db001 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/connection.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/connection.go @@ -110,6 +110,8 @@ func (c *Connection) Query(q Query) (*Response, *Cursor, error) { // Add token if query is a START/NOREPLY_WAIT if q.Type == p.Query_START || q.Type == p.Query_NOREPLY_WAIT || q.Type == p.Query_SERVER_INFO { q.Token = c.nextToken() + } + if q.Type == p.Query_START || q.Type == p.Query_NOREPLY_WAIT { if c.opts.Database != "" { var err error q.Opts["db"], err = DB(c.opts.Database).build() diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/doc.go b/vendor/gopkg.in/dancannon/gorethink.v2/doc.go index daa47c46bb..b6dd8ac565 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/doc.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/doc.go @@ -1,6 +1,6 @@ // Package gorethink implements a Go driver for RethinkDB // -// Current version: v2.0.0 (RethinkDB v2.3) +// Current version: v2.0.2 (RethinkDB v2.3) // For more in depth information on how to use RethinkDB check out the API docs // at http://rethinkdb.com/api package gorethink diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/encoding/cache.go b/vendor/gopkg.in/dancannon/gorethink.v2/encoding/cache.go index 041497a1a1..bffb4cdcd8 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/encoding/cache.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/encoding/cache.go @@ -6,6 +6,7 @@ import ( "reflect" "sort" "sync" + "time" ) // A field represents a single field found in a struct. @@ -131,7 +132,7 @@ func typeFields(t reflect.Type) []field { } // Record found field and index sequence. - if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct || isPseudoType(ft) { tagged := name != "" if name == "" { name = sf.Name @@ -200,6 +201,10 @@ func typeFields(t reflect.Type) []field { return fields } +func isPseudoType(t reflect.Type) bool { + return t == reflect.TypeOf(time.Time{}) +} + // dominantField looks through the fields, all of which are known to // have the same name, to find the single field that dominates the // others using Go's embedding rules, modified by the presence of diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/query.go b/vendor/gopkg.in/dancannon/gorethink.v2/query.go index 1bd88a3691..8c04dbceb5 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/query.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/query.go @@ -242,6 +242,10 @@ func (t Term) Run(s *Session, optArgs ...RunOpts) (*Cursor, error) { opts = optArgs[0].toMap() } + if s == nil || !s.IsConnected() { + return nil, ErrConnectionClosed + } + q, err := s.newQuery(t, opts) if err != nil { return nil, err diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/query_table.go b/vendor/gopkg.in/dancannon/gorethink.v2/query_table.go index 2a7f4f3898..e9a7df5b84 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/query_table.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/query_table.go @@ -152,6 +152,8 @@ type ChangesOpts struct { Squash interface{} `gorethink:"squash,omitempty"` IncludeInitial interface{} `gorethink:"include_initial,omitempty"` IncludeStates interface{} `gorethink:"include_states,omitempty"` + IncludeOffsets interface{} `gorethink:"include_offsets,omitempty"` + IncludeTypes interface{} `gorethink:"include_types,omitempty"` ChangefeedQueueSize interface{} `gorethink:"changefeed_queue_size,omitempty"` } diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/query_test.go b/vendor/gopkg.in/dancannon/gorethink.v2/query_test.go index 486be1318e..9337250528 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/query_test.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/query_test.go @@ -65,3 +65,17 @@ func (s *RethinkSuite) TestQueryRunRawTime(c *test.C) { c.Assert(response["$reql_type$"], test.NotNil) c.Assert(response["$reql_type$"], test.Equals, "TIME") } + +func (s *RethinkSuite) TestQueryRunNil(c *test.C) { + res, err := Expr("Test").Run(nil) + c.Assert(res, test.IsNil) + c.Assert(err, test.NotNil) + c.Assert(err, test.Equals, ErrConnectionClosed) +} + +func (s *RethinkSuite) TestQueryRunNotConnected(c *test.C) { + res, err := Expr("Test").Run(&Session{}) + c.Assert(res, test.IsNil) + c.Assert(err, test.NotNil) + c.Assert(err, test.Equals, ErrConnectionClosed) +} \ No newline at end of file diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/query_transformation.go b/vendor/gopkg.in/dancannon/gorethink.v2/query_transformation.go index 4960f01284..56ebe2e7c3 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/query_transformation.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/query_transformation.go @@ -151,6 +151,15 @@ func (t Term) IsEmpty(args ...interface{}) Term { return constructMethodTerm(t, "IsEmpty", p.Term_IS_EMPTY, args, map[string]interface{}{}) } +// UnionOpts contains the optional arguments for the Slice term +type UnionOpts struct { + Interleave interface{} `gorethink:"interleave,omitempty"` +} + +func (o *UnionOpts) toMap() map[string]interface{} { + return optArgsToMap(o) +} + // Union concatenates two sequences. func Union(args ...interface{}) Term { return constructRootTerm("Union", p.Term_UNION, args, map[string]interface{}{}) @@ -161,6 +170,18 @@ func (t Term) Union(args ...interface{}) Term { return constructMethodTerm(t, "Union", p.Term_UNION, args, map[string]interface{}{}) } +// UnionWithOpts like Union concatenates two sequences however allows for optional +// arguments to be passed. +func UnionWithOpts(optArgs UnionOpts, args ...interface{}) Term { + return constructRootTerm("Union", p.Term_UNION, args, optArgs.toMap()) +} + +// UnionWithOpts like Union concatenates two sequences however allows for optional +// arguments to be passed. +func (t Term) UnionWithOpts(optArgs UnionOpts, args ...interface{}) Term { + return constructMethodTerm(t, "Union", p.Term_UNION, args, optArgs.toMap()) +} + // Sample selects a given number of elements from a sequence with uniform random // distribution. Selection is done without replacement. func (t Term) Sample(args ...interface{}) Term { diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/query_write.go b/vendor/gopkg.in/dancannon/gorethink.v2/query_write.go index b8fcd413ff..1adb779310 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/query_write.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/query_write.go @@ -30,6 +30,7 @@ type UpdateOpts struct { Durability interface{} `gorethink:"durability,omitempty"` ReturnChanges interface{} `gorethink:"return_changes,omitempty"` NotAtomic interface{} `gorethink:"non_atomic,omitempty"` + Conflict interface{} `gorethink:"conflict,omitempty"` } func (o *UpdateOpts) toMap() map[string]interface{} { diff --git a/vendor/gopkg.in/dancannon/gorethink.v2/session_test.go b/vendor/gopkg.in/dancannon/gorethink.v2/session_test.go index 59503823f9..53b27de1c1 100644 --- a/vendor/gopkg.in/dancannon/gorethink.v2/session_test.go +++ b/vendor/gopkg.in/dancannon/gorethink.v2/session_test.go @@ -129,3 +129,25 @@ func (s *RethinkSuite) TestSessionConnectDatabase(c *test.C) { c.Assert(err, test.NotNil) c.Assert(err.Error(), test.Equals, "gorethink: Database `test2` does not exist. in: \nr.Table(\"test2\")") } + +func (s *RethinkSuite) TestSessionConnectUsername(c *test.C) { + session, err := Connect(ConnectOpts{ + Address: url, + }) + c.Assert(err, test.IsNil) + + DB("rethinkdb").Table("users").Insert(map[string]string{ + "id": "gorethink_test", + "password": "password", + }).Exec(session) + + session, err = Connect(ConnectOpts{ + Address: url, + Username: "gorethink_test", + Password: "password", + }) + c.Assert(err, test.IsNil) + + _, err = Expr("Hello World").Run(session) + c.Assert(err, test.IsNil) +}