github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/labix.org/v2/mgo/auth_test.go (about)

     1  // mgo - MongoDB driver for Go
     2  //
     3  // Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
     4  //
     5  // All rights reserved.
     6  //
     7  // Redistribution and use in source and binary forms, with or without
     8  // modification, are permitted provided that the following conditions are met:
     9  //
    10  // 1. Redistributions of source code must retain the above copyright notice, this
    11  //    list of conditions and the following disclaimer.
    12  // 2. Redistributions in binary form must reproduce the above copyright notice,
    13  //    this list of conditions and the following disclaimer in the documentation
    14  //    and/or other materials provided with the distribution.
    15  //
    16  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    17  // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    18  // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    19  // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    20  // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    21  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    22  // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    23  // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    24  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    25  // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    26  
    27  package mgo_test
    28  
    29  import (
    30  	"camlistore.org/third_party/labix.org/v2/mgo"
    31  	. "camlistore.org/third_party/launchpad.net/gocheck"
    32  	"fmt"
    33  	"sync"
    34  	"time"
    35  )
    36  
    37  func (s *S) TestAuthLogin(c *C) {
    38  	// Test both with a normal database and with an authenticated shard.
    39  	for _, addr := range []string{"localhost:40002", "localhost:40203"} {
    40  		session, err := mgo.Dial(addr)
    41  		c.Assert(err, IsNil)
    42  		defer session.Close()
    43  
    44  		coll := session.DB("mydb").C("mycoll")
    45  		err = coll.Insert(M{"n": 1})
    46  		c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
    47  
    48  		admindb := session.DB("admin")
    49  
    50  		err = admindb.Login("root", "wrong")
    51  		c.Assert(err, ErrorMatches, "auth fails")
    52  
    53  		err = admindb.Login("root", "rapadura")
    54  		c.Assert(err, IsNil)
    55  
    56  		err = coll.Insert(M{"n": 1})
    57  		c.Assert(err, IsNil)
    58  	}
    59  }
    60  
    61  func (s *S) TestAuthLoginLogout(c *C) {
    62  	// Test both with a normal database and with an authenticated shard.
    63  	for _, addr := range []string{"localhost:40002", "localhost:40203"} {
    64  		session, err := mgo.Dial(addr)
    65  		c.Assert(err, IsNil)
    66  		defer session.Close()
    67  
    68  		admindb := session.DB("admin")
    69  		err = admindb.Login("root", "rapadura")
    70  		c.Assert(err, IsNil)
    71  
    72  		admindb.Logout()
    73  
    74  		coll := session.DB("mydb").C("mycoll")
    75  		err = coll.Insert(M{"n": 1})
    76  		c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
    77  
    78  		// Must have dropped auth from the session too.
    79  		session = session.Copy()
    80  		defer session.Close()
    81  
    82  		coll = session.DB("mydb").C("mycoll")
    83  		err = coll.Insert(M{"n": 1})
    84  		c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
    85  	}
    86  }
    87  
    88  func (s *S) TestAuthLoginLogoutAll(c *C) {
    89  	session, err := mgo.Dial("localhost:40002")
    90  	c.Assert(err, IsNil)
    91  	defer session.Close()
    92  
    93  	admindb := session.DB("admin")
    94  	err = admindb.Login("root", "rapadura")
    95  	c.Assert(err, IsNil)
    96  
    97  	session.LogoutAll()
    98  
    99  	coll := session.DB("mydb").C("mycoll")
   100  	err = coll.Insert(M{"n": 1})
   101  	c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
   102  
   103  	// Must have dropped auth from the session too.
   104  	session = session.Copy()
   105  	defer session.Close()
   106  
   107  	coll = session.DB("mydb").C("mycoll")
   108  	err = coll.Insert(M{"n": 1})
   109  	c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
   110  }
   111  
   112  func (s *S) TestAuthUpsertUserErrors(c *C) {
   113  	session, err := mgo.Dial("localhost:40002")
   114  	c.Assert(err, IsNil)
   115  	defer session.Close()
   116  
   117  	admindb := session.DB("admin")
   118  	err = admindb.Login("root", "rapadura")
   119  	c.Assert(err, IsNil)
   120  
   121  	mydb := session.DB("mydb")
   122  
   123  	err = mydb.UpsertUser(&mgo.User{})
   124  	c.Assert(err, ErrorMatches, "user has no Username")
   125  
   126  	err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", UserSource: "source"})
   127  	c.Assert(err, ErrorMatches, "user has both Password/PasswordHash and UserSource set")
   128  
   129  	err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", OtherDBRoles: map[string][]mgo.Role{"db": nil}})
   130  	c.Assert(err, ErrorMatches, "user with OtherDBRoles is only supported in admin database")
   131  }
   132  
   133  func (s *S) TestAuthUpsertUser(c *C) {
   134  	if !s.versionAtLeast(2, 4) {
   135  		c.Skip("UpsertUser only works on 2.4+")
   136  	}
   137  	session, err := mgo.Dial("localhost:40002")
   138  	c.Assert(err, IsNil)
   139  	defer session.Close()
   140  
   141  	admindb := session.DB("admin")
   142  	err = admindb.Login("root", "rapadura")
   143  	c.Assert(err, IsNil)
   144  
   145  	mydb := session.DB("mydb")
   146  	myotherdb := session.DB("myotherdb")
   147  
   148  	ruser := &mgo.User{
   149  		Username: "myruser",
   150  		Password: "mypass",
   151  		Roles:    []mgo.Role{mgo.RoleRead},
   152  	}
   153  	rwuser := &mgo.User{
   154  		Username: "myrwuser",
   155  		Password: "mypass",
   156  		Roles:    []mgo.Role{mgo.RoleReadWrite},
   157  	}
   158  	rwuserother := &mgo.User{
   159  		Username:   "myrwuser",
   160  		UserSource: "mydb",
   161  		Roles:      []mgo.Role{mgo.RoleRead},
   162  	}
   163  
   164  	err = mydb.UpsertUser(ruser)
   165  	c.Assert(err, IsNil)
   166  	err = mydb.UpsertUser(rwuser)
   167  	c.Assert(err, IsNil)
   168  	err = myotherdb.UpsertUser(rwuserother)
   169  	c.Assert(err, IsNil)
   170  
   171  	err = mydb.Login("myruser", "mypass")
   172  	c.Assert(err, IsNil)
   173  
   174  	admindb.Logout()
   175  
   176  	coll := session.DB("mydb").C("mycoll")
   177  	err = coll.Insert(M{"n": 1})
   178  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   179  
   180  	err = mydb.Login("myrwuser", "mypass")
   181  	c.Assert(err, IsNil)
   182  
   183  	err = coll.Insert(M{"n": 1})
   184  	c.Assert(err, IsNil)
   185  
   186  	// Test indirection via UserSource: we can't write to it, because
   187  	// the roles for myrwuser are different there.
   188  	othercoll := myotherdb.C("myothercoll")
   189  	err = othercoll.Insert(M{"n": 1})
   190  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   191  
   192  	// Reading works, though.
   193  	err = othercoll.Find(nil).One(nil)
   194  	c.Assert(err, Equals, mgo.ErrNotFound)
   195  
   196  	// Can't login directly into the database using UserSource, though.
   197  	err = myotherdb.Login("myrwuser", "mypass")
   198  	c.Assert(err, ErrorMatches, "auth fails")
   199  }
   200  
   201  func (s *S) TestAuthUpserUserOtherDBRoles(c *C) {
   202  	if !s.versionAtLeast(2, 4) {
   203  		c.Skip("UpsertUser only works on 2.4+")
   204  	}
   205  	session, err := mgo.Dial("localhost:40002")
   206  	c.Assert(err, IsNil)
   207  	defer session.Close()
   208  
   209  	admindb := session.DB("admin")
   210  	err = admindb.Login("root", "rapadura")
   211  	c.Assert(err, IsNil)
   212  
   213  	ruser := &mgo.User{
   214  		Username:     "myruser",
   215  		Password:     "mypass",
   216  		OtherDBRoles: map[string][]mgo.Role{"mydb": []mgo.Role{mgo.RoleRead}},
   217  	}
   218  
   219  	err = admindb.UpsertUser(ruser)
   220  	c.Assert(err, IsNil)
   221  	defer admindb.RemoveUser("myruser")
   222  
   223  	admindb.Logout()
   224  	err = admindb.Login("myruser", "mypass")
   225  
   226  	coll := session.DB("mydb").C("mycoll")
   227  	err = coll.Insert(M{"n": 1})
   228  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   229  
   230  	err = coll.Find(nil).One(nil)
   231  	c.Assert(err, Equals, mgo.ErrNotFound)
   232  }
   233  
   234  func (s *S) TestAuthUpserUserUnsetFields(c *C) {
   235  	if !s.versionAtLeast(2, 4) {
   236  		c.Skip("UpsertUser only works on 2.4+")
   237  	}
   238  	session, err := mgo.Dial("localhost:40002")
   239  	c.Assert(err, IsNil)
   240  	defer session.Close()
   241  
   242  	admindb := session.DB("admin")
   243  	err = admindb.Login("root", "rapadura")
   244  	c.Assert(err, IsNil)
   245  
   246  	// Insert a user with most fields set.
   247  	user := &mgo.User{
   248  		Username:     "myruser",
   249  		Password:     "mypass",
   250  		Roles:        []mgo.Role{mgo.RoleRead},
   251  		OtherDBRoles: map[string][]mgo.Role{"mydb": []mgo.Role{mgo.RoleRead}},
   252  	}
   253  	err = admindb.UpsertUser(user)
   254  	c.Assert(err, IsNil)
   255  	defer admindb.RemoveUser("myruser")
   256  
   257  	// Now update the user with few things set.
   258  	user = &mgo.User{
   259  		Username:   "myruser",
   260  		UserSource: "mydb",
   261  	}
   262  	err = admindb.UpsertUser(user)
   263  	c.Assert(err, IsNil)
   264  
   265  	// Everything that was unset must have been dropped.
   266  	var userm M
   267  	err = admindb.C("system.users").Find(M{"user": "myruser"}).One(&userm)
   268  	c.Assert(err, IsNil)
   269  	delete(userm, "_id")
   270  	c.Assert(userm, DeepEquals, M{"user": "myruser", "userSource": "mydb", "roles": []interface{}{}})
   271  
   272  	// Now set password again...
   273  	user = &mgo.User{
   274  		Username: "myruser",
   275  		Password: "mypass",
   276  	}
   277  	err = admindb.UpsertUser(user)
   278  	c.Assert(err, IsNil)
   279  
   280  	// ... and assert that userSource has been dropped.
   281  	err = admindb.C("system.users").Find(M{"user": "myruser"}).One(&userm)
   282  	_, found := userm["userSource"]
   283  	c.Assert(found, Equals, false)
   284  }
   285  
   286  func (s *S) TestAuthAddUser(c *C) {
   287  	session, err := mgo.Dial("localhost:40002")
   288  	c.Assert(err, IsNil)
   289  	defer session.Close()
   290  
   291  	admindb := session.DB("admin")
   292  	err = admindb.Login("root", "rapadura")
   293  	c.Assert(err, IsNil)
   294  
   295  	mydb := session.DB("mydb")
   296  	err = mydb.AddUser("myruser", "mypass", true)
   297  	c.Assert(err, IsNil)
   298  	err = mydb.AddUser("mywuser", "mypass", false)
   299  	c.Assert(err, IsNil)
   300  
   301  	err = mydb.Login("myruser", "mypass")
   302  	c.Assert(err, IsNil)
   303  
   304  	admindb.Logout()
   305  
   306  	coll := session.DB("mydb").C("mycoll")
   307  	err = coll.Insert(M{"n": 1})
   308  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   309  
   310  	err = mydb.Login("mywuser", "mypass")
   311  	c.Assert(err, IsNil)
   312  
   313  	err = coll.Insert(M{"n": 1})
   314  	c.Assert(err, IsNil)
   315  }
   316  
   317  func (s *S) TestAuthAddUserReplaces(c *C) {
   318  	session, err := mgo.Dial("localhost:40002")
   319  	c.Assert(err, IsNil)
   320  	defer session.Close()
   321  
   322  	admindb := session.DB("admin")
   323  	err = admindb.Login("root", "rapadura")
   324  	c.Assert(err, IsNil)
   325  
   326  	mydb := session.DB("mydb")
   327  	err = mydb.AddUser("myuser", "myoldpass", false)
   328  	c.Assert(err, IsNil)
   329  	err = mydb.AddUser("myuser", "mynewpass", true)
   330  	c.Assert(err, IsNil)
   331  
   332  	admindb.Logout()
   333  
   334  	err = mydb.Login("myuser", "myoldpass")
   335  	c.Assert(err, ErrorMatches, "auth fails")
   336  	err = mydb.Login("myuser", "mynewpass")
   337  	c.Assert(err, IsNil)
   338  
   339  	// ReadOnly flag was changed too.
   340  	err = mydb.C("mycoll").Insert(M{"n": 1})
   341  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   342  }
   343  
   344  func (s *S) TestAuthRemoveUser(c *C) {
   345  	session, err := mgo.Dial("localhost:40002")
   346  	c.Assert(err, IsNil)
   347  	defer session.Close()
   348  
   349  	admindb := session.DB("admin")
   350  	err = admindb.Login("root", "rapadura")
   351  	c.Assert(err, IsNil)
   352  
   353  	mydb := session.DB("mydb")
   354  	err = mydb.AddUser("myuser", "mypass", true)
   355  	c.Assert(err, IsNil)
   356  	err = mydb.RemoveUser("myuser")
   357  	c.Assert(err, IsNil)
   358  
   359  	err = mydb.Login("myuser", "mypass")
   360  	c.Assert(err, ErrorMatches, "auth fails")
   361  }
   362  
   363  func (s *S) TestAuthLoginTwiceDoesNothing(c *C) {
   364  	session, err := mgo.Dial("localhost:40002")
   365  	c.Assert(err, IsNil)
   366  	defer session.Close()
   367  
   368  	admindb := session.DB("admin")
   369  	err = admindb.Login("root", "rapadura")
   370  	c.Assert(err, IsNil)
   371  
   372  	oldStats := mgo.GetStats()
   373  
   374  	err = admindb.Login("root", "rapadura")
   375  	c.Assert(err, IsNil)
   376  
   377  	newStats := mgo.GetStats()
   378  	c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
   379  }
   380  
   381  func (s *S) TestAuthLoginLogoutLoginDoesNothing(c *C) {
   382  	session, err := mgo.Dial("localhost:40002")
   383  	c.Assert(err, IsNil)
   384  	defer session.Close()
   385  
   386  	admindb := session.DB("admin")
   387  	err = admindb.Login("root", "rapadura")
   388  	c.Assert(err, IsNil)
   389  
   390  	oldStats := mgo.GetStats()
   391  
   392  	admindb.Logout()
   393  	err = admindb.Login("root", "rapadura")
   394  	c.Assert(err, IsNil)
   395  
   396  	newStats := mgo.GetStats()
   397  	c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
   398  }
   399  
   400  func (s *S) TestAuthLoginSwitchUser(c *C) {
   401  	session, err := mgo.Dial("localhost:40002")
   402  	c.Assert(err, IsNil)
   403  	defer session.Close()
   404  
   405  	admindb := session.DB("admin")
   406  	err = admindb.Login("root", "rapadura")
   407  	c.Assert(err, IsNil)
   408  
   409  	coll := session.DB("mydb").C("mycoll")
   410  	err = coll.Insert(M{"n": 1})
   411  	c.Assert(err, IsNil)
   412  
   413  	err = admindb.Login("reader", "rapadura")
   414  	c.Assert(err, IsNil)
   415  
   416  	// Can't write.
   417  	err = coll.Insert(M{"n": 1})
   418  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   419  
   420  	// But can read.
   421  	result := struct{ N int }{}
   422  	err = coll.Find(nil).One(&result)
   423  	c.Assert(err, IsNil)
   424  	c.Assert(result.N, Equals, 1)
   425  }
   426  
   427  func (s *S) TestAuthLoginChangePassword(c *C) {
   428  	session, err := mgo.Dial("localhost:40002")
   429  	c.Assert(err, IsNil)
   430  	defer session.Close()
   431  
   432  	admindb := session.DB("admin")
   433  	err = admindb.Login("root", "rapadura")
   434  	c.Assert(err, IsNil)
   435  
   436  	mydb := session.DB("mydb")
   437  	err = mydb.AddUser("myuser", "myoldpass", false)
   438  	c.Assert(err, IsNil)
   439  
   440  	err = mydb.Login("myuser", "myoldpass")
   441  	c.Assert(err, IsNil)
   442  
   443  	err = mydb.AddUser("myuser", "mynewpass", true)
   444  	c.Assert(err, IsNil)
   445  
   446  	err = mydb.Login("myuser", "mynewpass")
   447  	c.Assert(err, IsNil)
   448  
   449  	admindb.Logout()
   450  
   451  	// The second login must be in effect, which means read-only.
   452  	err = mydb.C("mycoll").Insert(M{"n": 1})
   453  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   454  }
   455  
   456  func (s *S) TestAuthLoginCachingWithSessionRefresh(c *C) {
   457  	session, err := mgo.Dial("localhost:40002")
   458  	c.Assert(err, IsNil)
   459  	defer session.Close()
   460  
   461  	admindb := session.DB("admin")
   462  	err = admindb.Login("root", "rapadura")
   463  	c.Assert(err, IsNil)
   464  
   465  	session.Refresh()
   466  
   467  	coll := session.DB("mydb").C("mycoll")
   468  	err = coll.Insert(M{"n": 1})
   469  	c.Assert(err, IsNil)
   470  }
   471  
   472  func (s *S) TestAuthLoginCachingWithSessionCopy(c *C) {
   473  	session, err := mgo.Dial("localhost:40002")
   474  	c.Assert(err, IsNil)
   475  	defer session.Close()
   476  
   477  	admindb := session.DB("admin")
   478  	err = admindb.Login("root", "rapadura")
   479  	c.Assert(err, IsNil)
   480  
   481  	session = session.Copy()
   482  	defer session.Close()
   483  
   484  	coll := session.DB("mydb").C("mycoll")
   485  	err = coll.Insert(M{"n": 1})
   486  	c.Assert(err, IsNil)
   487  }
   488  
   489  func (s *S) TestAuthLoginCachingWithSessionClone(c *C) {
   490  	session, err := mgo.Dial("localhost:40002")
   491  	c.Assert(err, IsNil)
   492  	defer session.Close()
   493  
   494  	admindb := session.DB("admin")
   495  	err = admindb.Login("root", "rapadura")
   496  	c.Assert(err, IsNil)
   497  
   498  	session = session.Clone()
   499  	defer session.Close()
   500  
   501  	coll := session.DB("mydb").C("mycoll")
   502  	err = coll.Insert(M{"n": 1})
   503  	c.Assert(err, IsNil)
   504  }
   505  
   506  func (s *S) TestAuthLoginCachingWithNewSession(c *C) {
   507  	session, err := mgo.Dial("localhost:40002")
   508  	c.Assert(err, IsNil)
   509  	defer session.Close()
   510  
   511  	admindb := session.DB("admin")
   512  	err = admindb.Login("root", "rapadura")
   513  	c.Assert(err, IsNil)
   514  
   515  	session = session.New()
   516  	defer session.Close()
   517  
   518  	coll := session.DB("mydb").C("mycoll")
   519  	err = coll.Insert(M{"n": 1})
   520  	c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized for .*")
   521  }
   522  
   523  func (s *S) TestAuthLoginCachingAcrossPool(c *C) {
   524  	// Logins are cached even when the conenction goes back
   525  	// into the pool.
   526  
   527  	session, err := mgo.Dial("localhost:40002")
   528  	c.Assert(err, IsNil)
   529  	defer session.Close()
   530  
   531  	admindb := session.DB("admin")
   532  	err = admindb.Login("root", "rapadura")
   533  	c.Assert(err, IsNil)
   534  
   535  	// Add another user to test the logout case at the same time.
   536  	mydb := session.DB("mydb")
   537  	err = mydb.AddUser("myuser", "mypass", false)
   538  	c.Assert(err, IsNil)
   539  
   540  	err = mydb.Login("myuser", "mypass")
   541  	c.Assert(err, IsNil)
   542  
   543  	// Logout root explicitly, to test both cases.
   544  	admindb.Logout()
   545  
   546  	// Give socket back to pool.
   547  	session.Refresh()
   548  
   549  	// Brand new session, should use socket from the pool.
   550  	other := session.New()
   551  	defer other.Close()
   552  
   553  	oldStats := mgo.GetStats()
   554  
   555  	err = other.DB("admin").Login("root", "rapadura")
   556  	c.Assert(err, IsNil)
   557  	err = other.DB("mydb").Login("myuser", "mypass")
   558  	c.Assert(err, IsNil)
   559  
   560  	// Both logins were cached, so no ops.
   561  	newStats := mgo.GetStats()
   562  	c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
   563  
   564  	// And they actually worked.
   565  	err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
   566  	c.Assert(err, IsNil)
   567  
   568  	other.DB("admin").Logout()
   569  
   570  	err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
   571  	c.Assert(err, IsNil)
   572  }
   573  
   574  func (s *S) TestAuthLoginCachingAcrossPoolWithLogout(c *C) {
   575  	// Now verify that logouts are properly flushed if they
   576  	// are not revalidated after leaving the pool.
   577  
   578  	session, err := mgo.Dial("localhost:40002")
   579  	c.Assert(err, IsNil)
   580  	defer session.Close()
   581  
   582  	admindb := session.DB("admin")
   583  	err = admindb.Login("root", "rapadura")
   584  	c.Assert(err, IsNil)
   585  
   586  	// Add another user to test the logout case at the same time.
   587  	mydb := session.DB("mydb")
   588  	err = mydb.AddUser("myuser", "mypass", true)
   589  	c.Assert(err, IsNil)
   590  
   591  	err = mydb.Login("myuser", "mypass")
   592  	c.Assert(err, IsNil)
   593  
   594  	// Just some data to query later.
   595  	err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   596  	c.Assert(err, IsNil)
   597  
   598  	// Give socket back to pool.
   599  	session.Refresh()
   600  
   601  	// Brand new session, should use socket from the pool.
   602  	other := session.New()
   603  	defer other.Close()
   604  
   605  	oldStats := mgo.GetStats()
   606  
   607  	err = other.DB("mydb").Login("myuser", "mypass")
   608  	c.Assert(err, IsNil)
   609  
   610  	// Login was cached, so no ops.
   611  	newStats := mgo.GetStats()
   612  	c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
   613  
   614  	// Can't write, since root has been implicitly logged out
   615  	// when the collection went into the pool, and not revalidated.
   616  	err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
   617  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   618  
   619  	// But can read due to the revalidated myuser login.
   620  	result := struct{ N int }{}
   621  	err = other.DB("mydb").C("mycoll").Find(nil).One(&result)
   622  	c.Assert(err, IsNil)
   623  	c.Assert(result.N, Equals, 1)
   624  }
   625  
   626  func (s *S) TestAuthEventual(c *C) {
   627  	// Eventual sessions don't keep sockets around, so they are
   628  	// an interesting test case.
   629  	session, err := mgo.Dial("localhost:40002")
   630  	c.Assert(err, IsNil)
   631  	defer session.Close()
   632  
   633  	admindb := session.DB("admin")
   634  	err = admindb.Login("root", "rapadura")
   635  	c.Assert(err, IsNil)
   636  
   637  	err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   638  	c.Assert(err, IsNil)
   639  
   640  	var wg sync.WaitGroup
   641  	wg.Add(20)
   642  
   643  	for i := 0; i != 10; i++ {
   644  		go func() {
   645  			defer wg.Done()
   646  			var result struct{ N int }
   647  			err := session.DB("mydb").C("mycoll").Find(nil).One(&result)
   648  			c.Assert(err, IsNil)
   649  			c.Assert(result.N, Equals, 1)
   650  		}()
   651  	}
   652  
   653  	for i := 0; i != 10; i++ {
   654  		go func() {
   655  			defer wg.Done()
   656  			err := session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   657  			c.Assert(err, IsNil)
   658  		}()
   659  	}
   660  
   661  	wg.Wait()
   662  }
   663  
   664  func (s *S) TestAuthURL(c *C) {
   665  	session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/")
   666  	c.Assert(err, IsNil)
   667  	defer session.Close()
   668  
   669  	err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   670  	c.Assert(err, IsNil)
   671  }
   672  
   673  func (s *S) TestAuthURLWrongCredentials(c *C) {
   674  	session, err := mgo.Dial("mongodb://root:wrong@localhost:40002/")
   675  	if session != nil {
   676  		session.Close()
   677  	}
   678  	c.Assert(err, ErrorMatches, "auth fails")
   679  	c.Assert(session, IsNil)
   680  }
   681  
   682  func (s *S) TestAuthURLWithNewSession(c *C) {
   683  	// When authentication is in the URL, the new session will
   684  	// actually carry it on as well, even if logged out explicitly.
   685  	session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/")
   686  	c.Assert(err, IsNil)
   687  	defer session.Close()
   688  
   689  	session.DB("admin").Logout()
   690  
   691  	// Do it twice to ensure it passes the needed data on.
   692  	session = session.New()
   693  	defer session.Close()
   694  	session = session.New()
   695  	defer session.Close()
   696  
   697  	err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   698  	c.Assert(err, IsNil)
   699  }
   700  
   701  func (s *S) TestAuthURLWithDatabase(c *C) {
   702  	session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002")
   703  	c.Assert(err, IsNil)
   704  	defer session.Close()
   705  
   706  	mydb := session.DB("mydb")
   707  	err = mydb.AddUser("myruser", "mypass", true)
   708  	c.Assert(err, IsNil)
   709  
   710  	usession, err := mgo.Dial("mongodb://myruser:mypass@localhost:40002/mydb")
   711  	c.Assert(err, IsNil)
   712  	defer usession.Close()
   713  
   714  	ucoll := usession.DB("mydb").C("mycoll")
   715  	err = ucoll.FindId(0).One(nil)
   716  	c.Assert(err, Equals, mgo.ErrNotFound)
   717  	err = ucoll.Insert(M{"n": 1})
   718  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   719  }
   720  
   721  func (s *S) TestDefaultDatabase(c *C) {
   722  	tests := []struct{ url, db string }{
   723  		{"mongodb://root:rapadura@localhost:40002", "test"},
   724  		{"mongodb://root:rapadura@localhost:40002/admin", "admin"},
   725  		{"mongodb://localhost:40001", "test"},
   726  		{"mongodb://localhost:40001/", "test"},
   727  		{"mongodb://localhost:40001/mydb", "mydb"},
   728  	}
   729  
   730  	for _, test := range tests {
   731  		session, err := mgo.Dial(test.url)
   732  		c.Assert(err, IsNil)
   733  		defer session.Close()
   734  
   735  		c.Logf("test: %#v", test)
   736  		c.Assert(session.DB("").Name, Equals, test.db)
   737  
   738  		scopy := session.Copy()
   739  		c.Check(scopy.DB("").Name, Equals, test.db)
   740  		scopy.Close()
   741  	}
   742  }
   743  
   744  func (s *S) TestAuthDirect(c *C) {
   745  	// Direct connections must work to the master and slaves.
   746  	for _, port := range []string{"40031", "40032", "40033"} {
   747  		url := fmt.Sprintf("mongodb://root:rapadura@localhost:%s/?connect=direct", port)
   748  		session, err := mgo.Dial(url)
   749  		c.Assert(err, IsNil)
   750  		defer session.Close()
   751  
   752  		session.SetMode(mgo.Monotonic, true)
   753  
   754  		var result struct{}
   755  		err = session.DB("mydb").C("mycoll").Find(nil).One(&result)
   756  		c.Assert(err, Equals, mgo.ErrNotFound)
   757  	}
   758  }
   759  
   760  func (s *S) TestAuthDirectWithLogin(c *C) {
   761  	// Direct connections must work to the master and slaves.
   762  	for _, port := range []string{"40031", "40032", "40033"} {
   763  		url := fmt.Sprintf("mongodb://localhost:%s/?connect=direct", port)
   764  		session, err := mgo.Dial(url)
   765  		c.Assert(err, IsNil)
   766  		defer session.Close()
   767  
   768  		session.SetMode(mgo.Monotonic, true)
   769  		session.SetSyncTimeout(3 * time.Second)
   770  
   771  		err = session.DB("admin").Login("root", "rapadura")
   772  		c.Assert(err, IsNil)
   773  
   774  		var result struct{}
   775  		err = session.DB("mydb").C("mycoll").Find(nil).One(&result)
   776  		c.Assert(err, Equals, mgo.ErrNotFound)
   777  	}
   778  }