Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for SCRAM-SHA-256 authentication #282

Open
wants to merge 6 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ install:
- go get gopkg.in/yaml.v2
- go get gopkg.in/tomb.v2
- go get golang.org/x/lint/golint
- go get github.com/xdg-go/scram

before_script:
- golint ./... | grep -v 'ID' | cat
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implement
MongoDB 4.0 is currently experimental - we would happily accept PRs to help improve support!

## Changes
* Adds support for SCRAM-SHA-256 authentication mechanism
* Fixes attempting to authenticate before every query ([details](https://github.com/go-mgo/mgo/issues/254))
* Removes bulk update / delete batch size limitations ([details](https://github.com/go-mgo/mgo/issues/288))
* Adds native support for `time.Duration` marshalling ([details](https://github.com/go-mgo/mgo/pull/373))
Expand Down
39 changes: 10 additions & 29 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ package mgo

import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"errors"
"fmt"
Expand Down Expand Up @@ -274,9 +273,14 @@ func (socket *mongoSocket) loginPlain(cred Credential) error {
func (socket *mongoSocket) loginSASL(cred Credential) error {
var sasl saslStepper
var err error
if cred.Mechanism == "SCRAM-SHA-1" {
// SCRAM is handled without external libraries.
sasl = saslNewScram(cred)
if cred.Mechanism == "SCRAM-SHA-1" || cred.Mechanism == "SCRAM-SHA-256" {
// SCRAM is handled with github.com/xdg-go/scram.
var method *scram.Method
method, err = scram.NewMethod(cred.Mechanism)
if err != nil {
return err
}
sasl, err = scram.NewClient(method, cred.Username, cred.Password)
} else if len(cred.ServiceHost) > 0 {
sasl, err = saslNew(cred, cred.ServiceHost)
} else {
Expand Down Expand Up @@ -317,7 +321,7 @@ func (socket *mongoSocket) loginSASL(cred Credential) error {
if err != nil {
return err
}
if done && res.Done {
if done {
socket.dropAuth(cred.Source)
socket.creds = append(socket.creds, cred)
break
Expand All @@ -343,35 +347,12 @@ func (socket *mongoSocket) loginSASL(cred Credential) error {
if err != nil {
return err
}
if done && res.Done {
socket.dropAuth(cred.Source)
socket.creds = append(socket.creds, cred)
break
}

}

return nil
}

func saslNewScram(cred Credential) *saslScram {
credsum := md5.New()
credsum.Write([]byte(cred.Username + ":mongo:" + cred.Password))
client := scram.NewClient(sha1.New, cred.Username, hex.EncodeToString(credsum.Sum(nil)))
return &saslScram{cred: cred, client: client}
}

type saslScram struct {
cred Credential
client *scram.Client
}

func (s *saslScram) Close() {}

func (s *saslScram) Step(serverData []byte) (clientData []byte, done bool, err error) {
more := s.client.Step(serverData)
return s.client.Out(), !more, s.client.Err()
}

func (socket *mongoSocket) loginRun(db string, query, result interface{}, f func() error) error {
var mutex sync.Mutex
var replyErr error
Expand Down
49 changes: 49 additions & 0 deletions auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,55 @@ func (s *S) TestAuthScramSha1URL(c *C) {
c.Assert(err, Equals, mgo.ErrNotFound)
}

func (s *S) TestAuthScramSha256Cred(c *C) {
if !s.versionAtLeast(4, 0, 0) {
c.Skip("SCRAM-SHA-256 tests depend on 4.0.0")
}
cred := &mgo.Credential{
Username: "root",
Password: "rapadura",
Mechanism: "SCRAM-SHA-256",
Source: "admin",
}
host := "localhost:40002"
c.Logf("Connecting to %s...", host)
session, err := mgo.Dial(host)
c.Assert(err, IsNil)
defer session.Close()

mycoll := session.DB("admin").C("mycoll")

c.Logf("Connected! Testing the need for authentication...")
err = mycoll.Find(nil).One(nil)
c.Assert(err, ErrorMatches, "unauthorized|not authorized .*|.* requires authentication")

c.Logf("Authenticating...")
err = session.Login(cred)
c.Assert(err, IsNil)
c.Logf("Authenticated!")

c.Logf("Connected! Testing the need for authentication...")
err = mycoll.Find(nil).One(nil)
c.Assert(err, Equals, mgo.ErrNotFound)
}

func (s *S) TestAuthScramSha256URL(c *C) {
if !s.versionAtLeast(4, 0, 0) {
c.Skip("SCRAM-SHA-256 tests depend on 4.0.0")
}
host := "localhost:40002"
c.Logf("Connecting to %s...", host)
session, err := mgo.Dial(fmt.Sprintf("root:rapadura@%s?authMechanism=SCRAM-SHA-256", host))
c.Assert(err, IsNil)
defer session.Close()

mycoll := session.DB("admin").C("mycoll")

c.Logf("Connected! Testing the need for authentication...")
err = mycoll.Find(nil).One(nil)
c.Assert(err, Equals, mgo.ErrNotFound)
}

func (s *S) TestAuthX509Cred(c *C) {
session, err := mgo.Dial("localhost:40001")
c.Assert(err, IsNil)
Expand Down
Loading