github.com/lianghucheng/zrddz@v0.0.0-20200923083010-c71f680932e2/src/gopkg.in/mgo.v2/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  	"crypto/tls"
    31  	"flag"
    32  	"fmt"
    33  	"io/ioutil"
    34  	"net"
    35  	"net/url"
    36  	"os"
    37  	"runtime"
    38  	"sync"
    39  	"time"
    40  
    41  	. "gopkg.in/check.v1"
    42  	"gopkg.in/mgo.v2"
    43  )
    44  
    45  func (s *S) TestAuthLoginDatabase(c *C) {
    46  	// Test both with a normal database and with an authenticated shard.
    47  	for _, addr := range []string{"localhost:40002", "localhost:40203"} {
    48  		session, err := mgo.Dial(addr)
    49  		c.Assert(err, IsNil)
    50  		defer session.Close()
    51  
    52  		coll := session.DB("mydb").C("mycoll")
    53  		err = coll.Insert(M{"n": 1})
    54  		c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
    55  
    56  		admindb := session.DB("admin")
    57  
    58  		err = admindb.Login("root", "wrong")
    59  		c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
    60  
    61  		err = admindb.Login("root", "rapadura")
    62  		c.Assert(err, IsNil)
    63  
    64  		err = coll.Insert(M{"n": 1})
    65  		c.Assert(err, IsNil)
    66  	}
    67  }
    68  
    69  func (s *S) TestAuthLoginSession(c *C) {
    70  	// Test both with a normal database and with an authenticated shard.
    71  	for _, addr := range []string{"localhost:40002", "localhost:40203"} {
    72  		session, err := mgo.Dial(addr)
    73  		c.Assert(err, IsNil)
    74  		defer session.Close()
    75  
    76  		coll := session.DB("mydb").C("mycoll")
    77  		err = coll.Insert(M{"n": 1})
    78  		c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
    79  
    80  		cred := mgo.Credential{
    81  			Username: "root",
    82  			Password: "wrong",
    83  		}
    84  		err = session.Login(&cred)
    85  		c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
    86  
    87  		cred.Password = "rapadura"
    88  
    89  		err = session.Login(&cred)
    90  		c.Assert(err, IsNil)
    91  
    92  		err = coll.Insert(M{"n": 1})
    93  		c.Assert(err, IsNil)
    94  	}
    95  }
    96  
    97  func (s *S) TestAuthLoginLogout(c *C) {
    98  	// Test both with a normal database and with an authenticated shard.
    99  	for _, addr := range []string{"localhost:40002", "localhost:40203"} {
   100  		session, err := mgo.Dial(addr)
   101  		c.Assert(err, IsNil)
   102  		defer session.Close()
   103  
   104  		admindb := session.DB("admin")
   105  		err = admindb.Login("root", "rapadura")
   106  		c.Assert(err, IsNil)
   107  
   108  		admindb.Logout()
   109  
   110  		coll := session.DB("mydb").C("mycoll")
   111  		err = coll.Insert(M{"n": 1})
   112  		c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
   113  
   114  		// Must have dropped auth from the session too.
   115  		session = session.Copy()
   116  		defer session.Close()
   117  
   118  		coll = session.DB("mydb").C("mycoll")
   119  		err = coll.Insert(M{"n": 1})
   120  		c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
   121  	}
   122  }
   123  
   124  func (s *S) TestAuthLoginLogoutAll(c *C) {
   125  	session, err := mgo.Dial("localhost:40002")
   126  	c.Assert(err, IsNil)
   127  	defer session.Close()
   128  
   129  	admindb := session.DB("admin")
   130  	err = admindb.Login("root", "rapadura")
   131  	c.Assert(err, IsNil)
   132  
   133  	session.LogoutAll()
   134  
   135  	coll := session.DB("mydb").C("mycoll")
   136  	err = coll.Insert(M{"n": 1})
   137  	c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
   138  
   139  	// Must have dropped auth from the session too.
   140  	session = session.Copy()
   141  	defer session.Close()
   142  
   143  	coll = session.DB("mydb").C("mycoll")
   144  	err = coll.Insert(M{"n": 1})
   145  	c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
   146  }
   147  
   148  func (s *S) TestAuthUpsertUserErrors(c *C) {
   149  	session, err := mgo.Dial("localhost:40002")
   150  	c.Assert(err, IsNil)
   151  	defer session.Close()
   152  
   153  	admindb := session.DB("admin")
   154  	err = admindb.Login("root", "rapadura")
   155  	c.Assert(err, IsNil)
   156  
   157  	mydb := session.DB("mydb")
   158  
   159  	err = mydb.UpsertUser(&mgo.User{})
   160  	c.Assert(err, ErrorMatches, "user has no Username")
   161  
   162  	err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", UserSource: "source"})
   163  	c.Assert(err, ErrorMatches, "user has both Password/PasswordHash and UserSource set")
   164  
   165  	err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", OtherDBRoles: map[string][]mgo.Role{"db": nil}})
   166  	c.Assert(err, ErrorMatches, "user with OtherDBRoles is only supported in the admin or \\$external databases")
   167  }
   168  
   169  func (s *S) TestAuthUpsertUser(c *C) {
   170  	if !s.versionAtLeast(2, 4) {
   171  		c.Skip("UpsertUser only works on 2.4+")
   172  	}
   173  	session, err := mgo.Dial("localhost:40002")
   174  	c.Assert(err, IsNil)
   175  	defer session.Close()
   176  
   177  	admindb := session.DB("admin")
   178  	err = admindb.Login("root", "rapadura")
   179  	c.Assert(err, IsNil)
   180  
   181  	mydb := session.DB("mydb")
   182  
   183  	ruser := &mgo.User{
   184  		Username: "myruser",
   185  		Password: "mypass",
   186  		Roles:    []mgo.Role{mgo.RoleRead},
   187  	}
   188  	rwuser := &mgo.User{
   189  		Username: "myrwuser",
   190  		Password: "mypass",
   191  		Roles:    []mgo.Role{mgo.RoleReadWrite},
   192  	}
   193  
   194  	err = mydb.UpsertUser(ruser)
   195  	c.Assert(err, IsNil)
   196  	err = mydb.UpsertUser(rwuser)
   197  	c.Assert(err, IsNil)
   198  
   199  	err = mydb.Login("myruser", "mypass")
   200  	c.Assert(err, IsNil)
   201  
   202  	admindb.Logout()
   203  
   204  	coll := session.DB("mydb").C("mycoll")
   205  	err = coll.Insert(M{"n": 1})
   206  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   207  
   208  	err = mydb.Login("myrwuser", "mypass")
   209  	c.Assert(err, IsNil)
   210  
   211  	err = coll.Insert(M{"n": 1})
   212  	c.Assert(err, IsNil)
   213  
   214  	myotherdb := session.DB("myotherdb")
   215  
   216  	err = admindb.Login("root", "rapadura")
   217  	c.Assert(err, IsNil)
   218  
   219  	// Test UserSource.
   220  	rwuserother := &mgo.User{
   221  		Username:   "myrwuser",
   222  		UserSource: "mydb",
   223  		Roles:      []mgo.Role{mgo.RoleRead},
   224  	}
   225  
   226  	err = myotherdb.UpsertUser(rwuserother)
   227  	if s.versionAtLeast(2, 6) {
   228  		c.Assert(err, ErrorMatches, `MongoDB 2.6\+ does not support the UserSource setting`)
   229  		return
   230  	}
   231  	c.Assert(err, IsNil)
   232  
   233  	admindb.Logout()
   234  
   235  	// Test indirection via UserSource: we can't write to it, because
   236  	// the roles for myrwuser are different there.
   237  	othercoll := myotherdb.C("myothercoll")
   238  	err = othercoll.Insert(M{"n": 1})
   239  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   240  
   241  	// Reading works, though.
   242  	err = othercoll.Find(nil).One(nil)
   243  	c.Assert(err, Equals, mgo.ErrNotFound)
   244  
   245  	// Can't login directly into the database using UserSource, though.
   246  	err = myotherdb.Login("myrwuser", "mypass")
   247  	c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
   248  }
   249  
   250  func (s *S) TestAuthUpsertUserOtherDBRoles(c *C) {
   251  	if !s.versionAtLeast(2, 4) {
   252  		c.Skip("UpsertUser only works on 2.4+")
   253  	}
   254  	session, err := mgo.Dial("localhost:40002")
   255  	c.Assert(err, IsNil)
   256  	defer session.Close()
   257  
   258  	admindb := session.DB("admin")
   259  	err = admindb.Login("root", "rapadura")
   260  	c.Assert(err, IsNil)
   261  
   262  	ruser := &mgo.User{
   263  		Username:     "myruser",
   264  		Password:     "mypass",
   265  		OtherDBRoles: map[string][]mgo.Role{"mydb": []mgo.Role{mgo.RoleRead}},
   266  	}
   267  
   268  	err = admindb.UpsertUser(ruser)
   269  	c.Assert(err, IsNil)
   270  	defer admindb.RemoveUser("myruser")
   271  
   272  	admindb.Logout()
   273  	err = admindb.Login("myruser", "mypass")
   274  
   275  	coll := session.DB("mydb").C("mycoll")
   276  	err = coll.Insert(M{"n": 1})
   277  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   278  
   279  	err = coll.Find(nil).One(nil)
   280  	c.Assert(err, Equals, mgo.ErrNotFound)
   281  }
   282  
   283  func (s *S) TestAuthUpsertUserUpdates(c *C) {
   284  	if !s.versionAtLeast(2, 4) {
   285  		c.Skip("UpsertUser only works on 2.4+")
   286  	}
   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  
   297  	// Insert a user that can read.
   298  	user := &mgo.User{
   299  		Username: "myruser",
   300  		Password: "mypass",
   301  		Roles:    []mgo.Role{mgo.RoleRead},
   302  	}
   303  	err = mydb.UpsertUser(user)
   304  	c.Assert(err, IsNil)
   305  
   306  	// Now update the user password.
   307  	user = &mgo.User{
   308  		Username: "myruser",
   309  		Password: "mynewpass",
   310  	}
   311  	err = mydb.UpsertUser(user)
   312  	c.Assert(err, IsNil)
   313  
   314  	// Login with the new user.
   315  	usession, err := mgo.Dial("myruser:mynewpass@localhost:40002/mydb")
   316  	c.Assert(err, IsNil)
   317  	defer usession.Close()
   318  
   319  	// Can read, but not write.
   320  	err = usession.DB("mydb").C("mycoll").Find(nil).One(nil)
   321  	c.Assert(err, Equals, mgo.ErrNotFound)
   322  	err = usession.DB("mydb").C("mycoll").Insert(M{"ok": 1})
   323  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   324  
   325  	// Update the user role.
   326  	user = &mgo.User{
   327  		Username: "myruser",
   328  		Roles:    []mgo.Role{mgo.RoleReadWrite},
   329  	}
   330  	err = mydb.UpsertUser(user)
   331  	c.Assert(err, IsNil)
   332  
   333  	// Dial again to ensure the password hasn't changed.
   334  	usession, err = mgo.Dial("myruser:mynewpass@localhost:40002/mydb")
   335  	c.Assert(err, IsNil)
   336  	defer usession.Close()
   337  
   338  	// Now it can write.
   339  	err = usession.DB("mydb").C("mycoll").Insert(M{"ok": 1})
   340  	c.Assert(err, IsNil)
   341  }
   342  
   343  func (s *S) TestAuthAddUser(c *C) {
   344  	session, err := mgo.Dial("localhost:40002")
   345  	c.Assert(err, IsNil)
   346  	defer session.Close()
   347  
   348  	admindb := session.DB("admin")
   349  	err = admindb.Login("root", "rapadura")
   350  	c.Assert(err, IsNil)
   351  
   352  	mydb := session.DB("mydb")
   353  	err = mydb.AddUser("myruser", "mypass", true)
   354  	c.Assert(err, IsNil)
   355  	err = mydb.AddUser("mywuser", "mypass", false)
   356  	c.Assert(err, IsNil)
   357  
   358  	err = mydb.Login("myruser", "mypass")
   359  	c.Assert(err, IsNil)
   360  
   361  	admindb.Logout()
   362  
   363  	coll := session.DB("mydb").C("mycoll")
   364  	err = coll.Insert(M{"n": 1})
   365  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   366  
   367  	err = mydb.Login("mywuser", "mypass")
   368  	c.Assert(err, IsNil)
   369  
   370  	err = coll.Insert(M{"n": 1})
   371  	c.Assert(err, IsNil)
   372  }
   373  
   374  func (s *S) TestAuthAddUserReplaces(c *C) {
   375  	session, err := mgo.Dial("localhost:40002")
   376  	c.Assert(err, IsNil)
   377  	defer session.Close()
   378  
   379  	admindb := session.DB("admin")
   380  	err = admindb.Login("root", "rapadura")
   381  	c.Assert(err, IsNil)
   382  
   383  	mydb := session.DB("mydb")
   384  	err = mydb.AddUser("myuser", "myoldpass", false)
   385  	c.Assert(err, IsNil)
   386  	err = mydb.AddUser("myuser", "mynewpass", true)
   387  	c.Assert(err, IsNil)
   388  
   389  	admindb.Logout()
   390  
   391  	err = mydb.Login("myuser", "myoldpass")
   392  	c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
   393  	err = mydb.Login("myuser", "mynewpass")
   394  	c.Assert(err, IsNil)
   395  
   396  	// ReadOnly flag was changed too.
   397  	err = mydb.C("mycoll").Insert(M{"n": 1})
   398  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   399  }
   400  
   401  func (s *S) TestAuthRemoveUser(c *C) {
   402  	session, err := mgo.Dial("localhost:40002")
   403  	c.Assert(err, IsNil)
   404  	defer session.Close()
   405  
   406  	admindb := session.DB("admin")
   407  	err = admindb.Login("root", "rapadura")
   408  	c.Assert(err, IsNil)
   409  
   410  	mydb := session.DB("mydb")
   411  	err = mydb.AddUser("myuser", "mypass", true)
   412  	c.Assert(err, IsNil)
   413  	err = mydb.RemoveUser("myuser")
   414  	c.Assert(err, IsNil)
   415  	err = mydb.RemoveUser("myuser")
   416  	c.Assert(err, Equals, mgo.ErrNotFound)
   417  
   418  	err = mydb.Login("myuser", "mypass")
   419  	c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
   420  }
   421  
   422  func (s *S) TestAuthLoginTwiceDoesNothing(c *C) {
   423  	session, err := mgo.Dial("localhost:40002")
   424  	c.Assert(err, IsNil)
   425  	defer session.Close()
   426  
   427  	admindb := session.DB("admin")
   428  	err = admindb.Login("root", "rapadura")
   429  	c.Assert(err, IsNil)
   430  
   431  	oldStats := mgo.GetStats()
   432  
   433  	err = admindb.Login("root", "rapadura")
   434  	c.Assert(err, IsNil)
   435  
   436  	newStats := mgo.GetStats()
   437  	c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
   438  }
   439  
   440  func (s *S) TestAuthLoginLogoutLoginDoesNothing(c *C) {
   441  	session, err := mgo.Dial("localhost:40002")
   442  	c.Assert(err, IsNil)
   443  	defer session.Close()
   444  
   445  	admindb := session.DB("admin")
   446  	err = admindb.Login("root", "rapadura")
   447  	c.Assert(err, IsNil)
   448  
   449  	oldStats := mgo.GetStats()
   450  
   451  	admindb.Logout()
   452  	err = admindb.Login("root", "rapadura")
   453  	c.Assert(err, IsNil)
   454  
   455  	newStats := mgo.GetStats()
   456  	c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
   457  }
   458  
   459  func (s *S) TestAuthLoginSwitchUser(c *C) {
   460  	session, err := mgo.Dial("localhost:40002")
   461  	c.Assert(err, IsNil)
   462  	defer session.Close()
   463  
   464  	admindb := session.DB("admin")
   465  	err = admindb.Login("root", "rapadura")
   466  	c.Assert(err, IsNil)
   467  
   468  	coll := session.DB("mydb").C("mycoll")
   469  	err = coll.Insert(M{"n": 1})
   470  	c.Assert(err, IsNil)
   471  
   472  	err = admindb.Login("reader", "rapadura")
   473  	c.Assert(err, IsNil)
   474  
   475  	// Can't write.
   476  	err = coll.Insert(M{"n": 1})
   477  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   478  
   479  	// But can read.
   480  	result := struct{ N int }{}
   481  	err = coll.Find(nil).One(&result)
   482  	c.Assert(err, IsNil)
   483  	c.Assert(result.N, Equals, 1)
   484  }
   485  
   486  func (s *S) TestAuthLoginChangePassword(c *C) {
   487  	session, err := mgo.Dial("localhost:40002")
   488  	c.Assert(err, IsNil)
   489  	defer session.Close()
   490  
   491  	admindb := session.DB("admin")
   492  	err = admindb.Login("root", "rapadura")
   493  	c.Assert(err, IsNil)
   494  
   495  	mydb := session.DB("mydb")
   496  	err = mydb.AddUser("myuser", "myoldpass", false)
   497  	c.Assert(err, IsNil)
   498  
   499  	err = mydb.Login("myuser", "myoldpass")
   500  	c.Assert(err, IsNil)
   501  
   502  	err = mydb.AddUser("myuser", "mynewpass", true)
   503  	c.Assert(err, IsNil)
   504  
   505  	err = mydb.Login("myuser", "mynewpass")
   506  	c.Assert(err, IsNil)
   507  
   508  	admindb.Logout()
   509  
   510  	// The second login must be in effect, which means read-only.
   511  	err = mydb.C("mycoll").Insert(M{"n": 1})
   512  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   513  }
   514  
   515  func (s *S) TestAuthLoginCachingWithSessionRefresh(c *C) {
   516  	session, err := mgo.Dial("localhost:40002")
   517  	c.Assert(err, IsNil)
   518  	defer session.Close()
   519  
   520  	admindb := session.DB("admin")
   521  	err = admindb.Login("root", "rapadura")
   522  	c.Assert(err, IsNil)
   523  
   524  	session.Refresh()
   525  
   526  	coll := session.DB("mydb").C("mycoll")
   527  	err = coll.Insert(M{"n": 1})
   528  	c.Assert(err, IsNil)
   529  }
   530  
   531  func (s *S) TestAuthLoginCachingWithSessionCopy(c *C) {
   532  	session, err := mgo.Dial("localhost:40002")
   533  	c.Assert(err, IsNil)
   534  	defer session.Close()
   535  
   536  	admindb := session.DB("admin")
   537  	err = admindb.Login("root", "rapadura")
   538  	c.Assert(err, IsNil)
   539  
   540  	session = session.Copy()
   541  	defer session.Close()
   542  
   543  	coll := session.DB("mydb").C("mycoll")
   544  	err = coll.Insert(M{"n": 1})
   545  	c.Assert(err, IsNil)
   546  }
   547  
   548  func (s *S) TestAuthLoginCachingWithSessionClone(c *C) {
   549  	session, err := mgo.Dial("localhost:40002")
   550  	c.Assert(err, IsNil)
   551  	defer session.Close()
   552  
   553  	admindb := session.DB("admin")
   554  	err = admindb.Login("root", "rapadura")
   555  	c.Assert(err, IsNil)
   556  
   557  	session = session.Clone()
   558  	defer session.Close()
   559  
   560  	coll := session.DB("mydb").C("mycoll")
   561  	err = coll.Insert(M{"n": 1})
   562  	c.Assert(err, IsNil)
   563  }
   564  
   565  func (s *S) TestAuthLoginCachingWithNewSession(c *C) {
   566  	session, err := mgo.Dial("localhost:40002")
   567  	c.Assert(err, IsNil)
   568  	defer session.Close()
   569  
   570  	admindb := session.DB("admin")
   571  	err = admindb.Login("root", "rapadura")
   572  	c.Assert(err, IsNil)
   573  
   574  	session = session.New()
   575  	defer session.Close()
   576  
   577  	coll := session.DB("mydb").C("mycoll")
   578  	err = coll.Insert(M{"n": 1})
   579  	c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*")
   580  }
   581  
   582  func (s *S) TestAuthLoginCachingAcrossPool(c *C) {
   583  	// Logins are cached even when the conenction goes back
   584  	// into the pool.
   585  
   586  	session, err := mgo.Dial("localhost:40002")
   587  	c.Assert(err, IsNil)
   588  	defer session.Close()
   589  
   590  	admindb := session.DB("admin")
   591  	err = admindb.Login("root", "rapadura")
   592  	c.Assert(err, IsNil)
   593  
   594  	// Add another user to test the logout case at the same time.
   595  	mydb := session.DB("mydb")
   596  	err = mydb.AddUser("myuser", "mypass", false)
   597  	c.Assert(err, IsNil)
   598  
   599  	err = mydb.Login("myuser", "mypass")
   600  	c.Assert(err, IsNil)
   601  
   602  	// Logout root explicitly, to test both cases.
   603  	admindb.Logout()
   604  
   605  	// Give socket back to pool.
   606  	session.Refresh()
   607  
   608  	// Brand new session, should use socket from the pool.
   609  	other := session.New()
   610  	defer other.Close()
   611  
   612  	oldStats := mgo.GetStats()
   613  
   614  	err = other.DB("admin").Login("root", "rapadura")
   615  	c.Assert(err, IsNil)
   616  	err = other.DB("mydb").Login("myuser", "mypass")
   617  	c.Assert(err, IsNil)
   618  
   619  	// Both logins were cached, so no ops.
   620  	newStats := mgo.GetStats()
   621  	c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
   622  
   623  	// And they actually worked.
   624  	err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
   625  	c.Assert(err, IsNil)
   626  
   627  	other.DB("admin").Logout()
   628  
   629  	err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
   630  	c.Assert(err, IsNil)
   631  }
   632  
   633  func (s *S) TestAuthLoginCachingAcrossPoolWithLogout(c *C) {
   634  	// Now verify that logouts are properly flushed if they
   635  	// are not revalidated after leaving the pool.
   636  
   637  	session, err := mgo.Dial("localhost:40002")
   638  	c.Assert(err, IsNil)
   639  	defer session.Close()
   640  
   641  	admindb := session.DB("admin")
   642  	err = admindb.Login("root", "rapadura")
   643  	c.Assert(err, IsNil)
   644  
   645  	// Add another user to test the logout case at the same time.
   646  	mydb := session.DB("mydb")
   647  	err = mydb.AddUser("myuser", "mypass", true)
   648  	c.Assert(err, IsNil)
   649  
   650  	err = mydb.Login("myuser", "mypass")
   651  	c.Assert(err, IsNil)
   652  
   653  	// Just some data to query later.
   654  	err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   655  	c.Assert(err, IsNil)
   656  
   657  	// Give socket back to pool.
   658  	session.Refresh()
   659  
   660  	// Brand new session, should use socket from the pool.
   661  	other := session.New()
   662  	defer other.Close()
   663  
   664  	oldStats := mgo.GetStats()
   665  
   666  	err = other.DB("mydb").Login("myuser", "mypass")
   667  	c.Assert(err, IsNil)
   668  
   669  	// Login was cached, so no ops.
   670  	newStats := mgo.GetStats()
   671  	c.Assert(newStats.SentOps, Equals, oldStats.SentOps)
   672  
   673  	// Can't write, since root has been implicitly logged out
   674  	// when the collection went into the pool, and not revalidated.
   675  	err = other.DB("mydb").C("mycoll").Insert(M{"n": 1})
   676  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   677  
   678  	// But can read due to the revalidated myuser login.
   679  	result := struct{ N int }{}
   680  	err = other.DB("mydb").C("mycoll").Find(nil).One(&result)
   681  	c.Assert(err, IsNil)
   682  	c.Assert(result.N, Equals, 1)
   683  }
   684  
   685  func (s *S) TestAuthEventual(c *C) {
   686  	// Eventual sessions don't keep sockets around, so they are
   687  	// an interesting test case.
   688  	session, err := mgo.Dial("localhost:40002")
   689  	c.Assert(err, IsNil)
   690  	defer session.Close()
   691  
   692  	admindb := session.DB("admin")
   693  	err = admindb.Login("root", "rapadura")
   694  	c.Assert(err, IsNil)
   695  
   696  	err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   697  	c.Assert(err, IsNil)
   698  
   699  	var wg sync.WaitGroup
   700  	wg.Add(20)
   701  
   702  	for i := 0; i != 10; i++ {
   703  		go func() {
   704  			defer wg.Done()
   705  			var result struct{ N int }
   706  			err := session.DB("mydb").C("mycoll").Find(nil).One(&result)
   707  			c.Assert(err, IsNil)
   708  			c.Assert(result.N, Equals, 1)
   709  		}()
   710  	}
   711  
   712  	for i := 0; i != 10; i++ {
   713  		go func() {
   714  			defer wg.Done()
   715  			err := session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   716  			c.Assert(err, IsNil)
   717  		}()
   718  	}
   719  
   720  	wg.Wait()
   721  }
   722  
   723  func (s *S) TestAuthURL(c *C) {
   724  	session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/")
   725  	c.Assert(err, IsNil)
   726  	defer session.Close()
   727  
   728  	err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   729  	c.Assert(err, IsNil)
   730  }
   731  
   732  func (s *S) TestAuthURLWrongCredentials(c *C) {
   733  	session, err := mgo.Dial("mongodb://root:wrong@localhost:40002/")
   734  	if session != nil {
   735  		session.Close()
   736  	}
   737  	c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.")
   738  	c.Assert(session, IsNil)
   739  }
   740  
   741  func (s *S) TestAuthURLWithNewSession(c *C) {
   742  	// When authentication is in the URL, the new session will
   743  	// actually carry it on as well, even if logged out explicitly.
   744  	session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/")
   745  	c.Assert(err, IsNil)
   746  	defer session.Close()
   747  
   748  	session.DB("admin").Logout()
   749  
   750  	// Do it twice to ensure it passes the needed data on.
   751  	session = session.New()
   752  	defer session.Close()
   753  	session = session.New()
   754  	defer session.Close()
   755  
   756  	err = session.DB("mydb").C("mycoll").Insert(M{"n": 1})
   757  	c.Assert(err, IsNil)
   758  }
   759  
   760  func (s *S) TestAuthURLWithDatabase(c *C) {
   761  	session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002")
   762  	c.Assert(err, IsNil)
   763  	defer session.Close()
   764  
   765  	mydb := session.DB("mydb")
   766  	err = mydb.AddUser("myruser", "mypass", true)
   767  	c.Assert(err, IsNil)
   768  
   769  	// Test once with database, and once with source.
   770  	for i := 0; i < 2; i++ {
   771  		var url string
   772  		if i == 0 {
   773  			url = "mongodb://myruser:mypass@localhost:40002/mydb"
   774  		} else {
   775  			url = "mongodb://myruser:mypass@localhost:40002/admin?authSource=mydb"
   776  		}
   777  		usession, err := mgo.Dial(url)
   778  		c.Assert(err, IsNil)
   779  		defer usession.Close()
   780  
   781  		ucoll := usession.DB("mydb").C("mycoll")
   782  		err = ucoll.FindId(0).One(nil)
   783  		c.Assert(err, Equals, mgo.ErrNotFound)
   784  		err = ucoll.Insert(M{"n": 1})
   785  		c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   786  	}
   787  }
   788  
   789  func (s *S) TestDefaultDatabase(c *C) {
   790  	tests := []struct{ url, db string }{
   791  		{"mongodb://root:rapadura@localhost:40002", "test"},
   792  		{"mongodb://root:rapadura@localhost:40002/admin", "admin"},
   793  		{"mongodb://localhost:40001", "test"},
   794  		{"mongodb://localhost:40001/", "test"},
   795  		{"mongodb://localhost:40001/mydb", "mydb"},
   796  	}
   797  
   798  	for _, test := range tests {
   799  		session, err := mgo.Dial(test.url)
   800  		c.Assert(err, IsNil)
   801  		defer session.Close()
   802  
   803  		c.Logf("test: %#v", test)
   804  		c.Assert(session.DB("").Name, Equals, test.db)
   805  
   806  		scopy := session.Copy()
   807  		c.Check(scopy.DB("").Name, Equals, test.db)
   808  		scopy.Close()
   809  	}
   810  }
   811  
   812  func (s *S) TestAuthDirect(c *C) {
   813  	// Direct connections must work to the master and slaves.
   814  	for _, port := range []string{"40031", "40032", "40033"} {
   815  		url := fmt.Sprintf("mongodb://root:rapadura@localhost:%s/?connect=direct", port)
   816  		session, err := mgo.Dial(url)
   817  		c.Assert(err, IsNil)
   818  		defer session.Close()
   819  
   820  		session.SetMode(mgo.Monotonic, true)
   821  
   822  		var result struct{}
   823  		err = session.DB("mydb").C("mycoll").Find(nil).One(&result)
   824  		c.Assert(err, Equals, mgo.ErrNotFound)
   825  	}
   826  }
   827  
   828  func (s *S) TestAuthDirectWithLogin(c *C) {
   829  	// Direct connections must work to the master and slaves.
   830  	for _, port := range []string{"40031", "40032", "40033"} {
   831  		url := fmt.Sprintf("mongodb://localhost:%s/?connect=direct", port)
   832  		session, err := mgo.Dial(url)
   833  		c.Assert(err, IsNil)
   834  		defer session.Close()
   835  
   836  		session.SetMode(mgo.Monotonic, true)
   837  		session.SetSyncTimeout(3 * time.Second)
   838  
   839  		err = session.DB("admin").Login("root", "rapadura")
   840  		c.Assert(err, IsNil)
   841  
   842  		var result struct{}
   843  		err = session.DB("mydb").C("mycoll").Find(nil).One(&result)
   844  		c.Assert(err, Equals, mgo.ErrNotFound)
   845  	}
   846  }
   847  
   848  func (s *S) TestAuthScramSha1Cred(c *C) {
   849  	if !s.versionAtLeast(2, 7, 7) {
   850  		c.Skip("SCRAM-SHA-1 tests depend on 2.7.7")
   851  	}
   852  	cred := &mgo.Credential{
   853  		Username:  "root",
   854  		Password:  "rapadura",
   855  		Mechanism: "SCRAM-SHA-1",
   856  		Source:    "admin",
   857  	}
   858  	host := "localhost:40002"
   859  	c.Logf("Connecting to %s...", host)
   860  	session, err := mgo.Dial(host)
   861  	c.Assert(err, IsNil)
   862  	defer session.Close()
   863  
   864  	mycoll := session.DB("admin").C("mycoll")
   865  
   866  	c.Logf("Connected! Testing the need for authentication...")
   867  	err = mycoll.Find(nil).One(nil)
   868  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   869  
   870  	c.Logf("Authenticating...")
   871  	err = session.Login(cred)
   872  	c.Assert(err, IsNil)
   873  	c.Logf("Authenticated!")
   874  
   875  	c.Logf("Connected! Testing the need for authentication...")
   876  	err = mycoll.Find(nil).One(nil)
   877  	c.Assert(err, Equals, mgo.ErrNotFound)
   878  }
   879  
   880  func (s *S) TestAuthScramSha1URL(c *C) {
   881  	if !s.versionAtLeast(2, 7, 7) {
   882  		c.Skip("SCRAM-SHA-1 tests depend on 2.7.7")
   883  	}
   884  	host := "localhost:40002"
   885  	c.Logf("Connecting to %s...", host)
   886  	session, err := mgo.Dial(fmt.Sprintf("root:rapadura@%s?authMechanism=SCRAM-SHA-1", host))
   887  	c.Assert(err, IsNil)
   888  	defer session.Close()
   889  
   890  	mycoll := session.DB("admin").C("mycoll")
   891  
   892  	c.Logf("Connected! Testing the need for authentication...")
   893  	err = mycoll.Find(nil).One(nil)
   894  	c.Assert(err, Equals, mgo.ErrNotFound)
   895  }
   896  
   897  func (s *S) TestAuthX509Cred(c *C) {
   898  	session, err := mgo.Dial("localhost:40001")
   899  	c.Assert(err, IsNil)
   900  	defer session.Close()
   901  	binfo, err := session.BuildInfo()
   902  	c.Assert(err, IsNil)
   903  	if binfo.OpenSSLVersion == "" {
   904  		c.Skip("server does not support SSL")
   905  	}
   906  
   907  	clientCertPEM, err := ioutil.ReadFile("harness/certs/client.pem")
   908  	c.Assert(err, IsNil)
   909  
   910  	clientCert, err := tls.X509KeyPair(clientCertPEM, clientCertPEM)
   911  	c.Assert(err, IsNil)
   912  
   913  	tlsConfig := &tls.Config{
   914  		// Isolating tests to client certs, don't care about server validation.
   915  		InsecureSkipVerify: true,
   916  		Certificates:       []tls.Certificate{clientCert},
   917  	}
   918  
   919  	var host = "localhost:40003"
   920  	c.Logf("Connecting to %s...", host)
   921  	session, err = mgo.DialWithInfo(&mgo.DialInfo{
   922  		Addrs: []string{host},
   923  		DialServer: func(addr *mgo.ServerAddr) (net.Conn, error) {
   924  			return tls.Dial("tcp", addr.String(), tlsConfig)
   925  		},
   926  	})
   927  	c.Assert(err, IsNil)
   928  	defer session.Close()
   929  
   930  	err = session.Login(&mgo.Credential{Username: "root", Password: "rapadura"})
   931  	c.Assert(err, IsNil)
   932  
   933  	// This needs to be kept in sync with client.pem
   934  	x509Subject := "CN=localhost,OU=Client,O=MGO,L=MGO,ST=MGO,C=GO"
   935  
   936  	externalDB := session.DB("$external")
   937  	var x509User mgo.User = mgo.User{
   938  		Username:     x509Subject,
   939  		OtherDBRoles: map[string][]mgo.Role{"admin": []mgo.Role{mgo.RoleRoot}},
   940  	}
   941  	err = externalDB.UpsertUser(&x509User)
   942  	c.Assert(err, IsNil)
   943  
   944  	session.LogoutAll()
   945  
   946  	c.Logf("Connected! Ensuring authentication is required...")
   947  	names, err := session.DatabaseNames()
   948  	c.Assert(err, ErrorMatches, "not authorized .*")
   949  
   950  	cred := &mgo.Credential{
   951  		Username:  x509Subject,
   952  		Mechanism: "MONGODB-X509",
   953  		Source:    "$external",
   954  	}
   955  
   956  	c.Logf("Authenticating...")
   957  	err = session.Login(cred)
   958  	c.Assert(err, IsNil)
   959  	c.Logf("Authenticated!")
   960  
   961  	names, err = session.DatabaseNames()
   962  	c.Assert(err, IsNil)
   963  	c.Assert(len(names) > 0, Equals, true)
   964  }
   965  
   966  var (
   967  	plainFlag = flag.String("plain", "", "Host to test PLAIN authentication against (depends on custom environment)")
   968  	plainUser = "einstein"
   969  	plainPass = "password"
   970  )
   971  
   972  func (s *S) TestAuthPlainCred(c *C) {
   973  	if *plainFlag == "" {
   974  		c.Skip("no -plain")
   975  	}
   976  	cred := &mgo.Credential{
   977  		Username:  plainUser,
   978  		Password:  plainPass,
   979  		Source:    "$external",
   980  		Mechanism: "PLAIN",
   981  	}
   982  	c.Logf("Connecting to %s...", *plainFlag)
   983  	session, err := mgo.Dial(*plainFlag)
   984  	c.Assert(err, IsNil)
   985  	defer session.Close()
   986  
   987  	records := session.DB("records").C("records")
   988  
   989  	c.Logf("Connected! Testing the need for authentication...")
   990  	err = records.Find(nil).One(nil)
   991  	c.Assert(err, ErrorMatches, "unauthorized|not authorized .*")
   992  
   993  	c.Logf("Authenticating...")
   994  	err = session.Login(cred)
   995  	c.Assert(err, IsNil)
   996  	c.Logf("Authenticated!")
   997  
   998  	c.Logf("Connected! Testing the need for authentication...")
   999  	err = records.Find(nil).One(nil)
  1000  	c.Assert(err, Equals, mgo.ErrNotFound)
  1001  }
  1002  
  1003  func (s *S) TestAuthPlainURL(c *C) {
  1004  	if *plainFlag == "" {
  1005  		c.Skip("no -plain")
  1006  	}
  1007  	c.Logf("Connecting to %s...", *plainFlag)
  1008  	session, err := mgo.Dial(fmt.Sprintf("%s:%s@%s?authMechanism=PLAIN", url.QueryEscape(plainUser), url.QueryEscape(plainPass), *plainFlag))
  1009  	c.Assert(err, IsNil)
  1010  	defer session.Close()
  1011  
  1012  	c.Logf("Connected! Testing the need for authentication...")
  1013  	err = session.DB("records").C("records").Find(nil).One(nil)
  1014  	c.Assert(err, Equals, mgo.ErrNotFound)
  1015  }
  1016  
  1017  var (
  1018  	kerberosFlag = flag.Bool("kerberos", false, "Test Kerberos authentication (depends on custom environment)")
  1019  	kerberosHost = "ldaptest.10gen.cc"
  1020  	kerberosUser = "drivers@LDAPTEST.10GEN.CC"
  1021  
  1022  	winKerberosPasswordEnv = "MGO_KERBEROS_PASSWORD"
  1023  )
  1024  
  1025  // Kerberos has its own suite because it talks to a remote server
  1026  // that is prepared to authenticate against a kerberos deployment.
  1027  type KerberosSuite struct{}
  1028  
  1029  var _ = Suite(&KerberosSuite{})
  1030  
  1031  func (kerberosSuite *KerberosSuite) SetUpSuite(c *C) {
  1032  	mgo.SetDebug(true)
  1033  	mgo.SetStats(true)
  1034  }
  1035  
  1036  func (kerberosSuite *KerberosSuite) TearDownSuite(c *C) {
  1037  	mgo.SetDebug(false)
  1038  	mgo.SetStats(false)
  1039  }
  1040  
  1041  func (kerberosSuite *KerberosSuite) SetUpTest(c *C) {
  1042  	mgo.SetLogger((*cLogger)(c))
  1043  	mgo.ResetStats()
  1044  }
  1045  
  1046  func (kerberosSuite *KerberosSuite) TearDownTest(c *C) {
  1047  	mgo.SetLogger(nil)
  1048  }
  1049  
  1050  func (kerberosSuite *KerberosSuite) TestAuthKerberosCred(c *C) {
  1051  	if !*kerberosFlag {
  1052  		c.Skip("no -kerberos")
  1053  	}
  1054  	cred := &mgo.Credential{
  1055  		Username:  kerberosUser,
  1056  		Mechanism: "GSSAPI",
  1057  	}
  1058  	windowsAppendPasswordToCredential(cred)
  1059  	c.Logf("Connecting to %s...", kerberosHost)
  1060  	session, err := mgo.Dial(kerberosHost)
  1061  	c.Assert(err, IsNil)
  1062  	defer session.Close()
  1063  
  1064  	c.Logf("Connected! Testing the need for authentication...")
  1065  	n, err := session.DB("kerberos").C("test").Find(M{}).Count()
  1066  	c.Assert(err, ErrorMatches, ".*authorized.*")
  1067  
  1068  	c.Logf("Authenticating...")
  1069  	err = session.Login(cred)
  1070  	c.Assert(err, IsNil)
  1071  	c.Logf("Authenticated!")
  1072  
  1073  	n, err = session.DB("kerberos").C("test").Find(M{}).Count()
  1074  	c.Assert(err, IsNil)
  1075  	c.Assert(n, Equals, 1)
  1076  }
  1077  
  1078  func (kerberosSuite *KerberosSuite) TestAuthKerberosURL(c *C) {
  1079  	if !*kerberosFlag {
  1080  		c.Skip("no -kerberos")
  1081  	}
  1082  	c.Logf("Connecting to %s...", kerberosHost)
  1083  	connectUri := url.QueryEscape(kerberosUser) + "@" + kerberosHost + "?authMechanism=GSSAPI"
  1084  	if runtime.GOOS == "windows" {
  1085  		connectUri = url.QueryEscape(kerberosUser) + ":" + url.QueryEscape(getWindowsKerberosPassword()) + "@" + kerberosHost + "?authMechanism=GSSAPI"
  1086  	}
  1087  	session, err := mgo.Dial(connectUri)
  1088  	c.Assert(err, IsNil)
  1089  	defer session.Close()
  1090  	n, err := session.DB("kerberos").C("test").Find(M{}).Count()
  1091  	c.Assert(err, IsNil)
  1092  	c.Assert(n, Equals, 1)
  1093  }
  1094  
  1095  func (kerberosSuite *KerberosSuite) TestAuthKerberosServiceName(c *C) {
  1096  	if !*kerberosFlag {
  1097  		c.Skip("no -kerberos")
  1098  	}
  1099  
  1100  	wrongServiceName := "wrong"
  1101  	rightServiceName := "mongodb"
  1102  
  1103  	cred := &mgo.Credential{
  1104  		Username:  kerberosUser,
  1105  		Mechanism: "GSSAPI",
  1106  		Service:   wrongServiceName,
  1107  	}
  1108  	windowsAppendPasswordToCredential(cred)
  1109  
  1110  	c.Logf("Connecting to %s...", kerberosHost)
  1111  	session, err := mgo.Dial(kerberosHost)
  1112  	c.Assert(err, IsNil)
  1113  	defer session.Close()
  1114  
  1115  	c.Logf("Authenticating with incorrect service name...")
  1116  	err = session.Login(cred)
  1117  	c.Assert(err, ErrorMatches, ".*@LDAPTEST.10GEN.CC not found.*")
  1118  
  1119  	cred.Service = rightServiceName
  1120  	c.Logf("Authenticating with correct service name...")
  1121  	err = session.Login(cred)
  1122  	c.Assert(err, IsNil)
  1123  	c.Logf("Authenticated!")
  1124  
  1125  	n, err := session.DB("kerberos").C("test").Find(M{}).Count()
  1126  	c.Assert(err, IsNil)
  1127  	c.Assert(n, Equals, 1)
  1128  }
  1129  
  1130  func (kerberosSuite *KerberosSuite) TestAuthKerberosServiceHost(c *C) {
  1131  	if !*kerberosFlag {
  1132  		c.Skip("no -kerberos")
  1133  	}
  1134  
  1135  	wrongServiceHost := "eggs.bacon.tk"
  1136  	rightServiceHost := kerberosHost
  1137  
  1138  	cred := &mgo.Credential{
  1139  		Username:    kerberosUser,
  1140  		Mechanism:   "GSSAPI",
  1141  		ServiceHost: wrongServiceHost,
  1142  	}
  1143  	windowsAppendPasswordToCredential(cred)
  1144  
  1145  	c.Logf("Connecting to %s...", kerberosHost)
  1146  	session, err := mgo.Dial(kerberosHost)
  1147  	c.Assert(err, IsNil)
  1148  	defer session.Close()
  1149  
  1150  	c.Logf("Authenticating with incorrect service host...")
  1151  	err = session.Login(cred)
  1152  	c.Assert(err, ErrorMatches, ".*@LDAPTEST.10GEN.CC not found.*")
  1153  
  1154  	cred.ServiceHost = rightServiceHost
  1155  	c.Logf("Authenticating with correct service host...")
  1156  	err = session.Login(cred)
  1157  	c.Assert(err, IsNil)
  1158  	c.Logf("Authenticated!")
  1159  
  1160  	n, err := session.DB("kerberos").C("test").Find(M{}).Count()
  1161  	c.Assert(err, IsNil)
  1162  	c.Assert(n, Equals, 1)
  1163  }
  1164  
  1165  // No kinit on SSPI-style Kerberos, so we need to provide a password. In order
  1166  // to avoid inlining password, require it to be set as an environment variable,
  1167  // for instance: `SET MGO_KERBEROS_PASSWORD=this_isnt_the_password`
  1168  func getWindowsKerberosPassword() string {
  1169  	pw := os.Getenv(winKerberosPasswordEnv)
  1170  	if pw == "" {
  1171  		panic(fmt.Sprintf("Need to set %v environment variable to run Kerberos tests on Windows", winKerberosPasswordEnv))
  1172  	}
  1173  	return pw
  1174  }
  1175  
  1176  func windowsAppendPasswordToCredential(cred *mgo.Credential) {
  1177  	if runtime.GOOS == "windows" {
  1178  		cred.Password = getWindowsKerberosPassword()
  1179  	}
  1180  }