forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add explicit username and password option for Mongodb (elastic#2889)
We previously only had the option of specifying a user/pass in the URL string, which is problematic because it results in the password being indexed in Elasticsearch (elastic#2888). This adds the option to specify a username/password at the module configuration. To make this happen, I had to copy some unexported functions from the mgo driver. (cherry picked from commit 6c7dfe4)
- Loading branch information
Showing
9 changed files
with
288 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package mongodb | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
"strings" | ||
|
||
mgo "gopkg.in/mgo.v2" | ||
) | ||
|
||
/* | ||
* Functions copied from the mgo driver to help with parsing the URL. | ||
* | ||
* http://bazaar.launchpad.net/+branch/mgo/v2/view/head:/session.go#L382 | ||
*/ | ||
|
||
type urlInfo struct { | ||
addrs []string | ||
user string | ||
pass string | ||
db string | ||
options map[string]string | ||
} | ||
|
||
func parseURL(s string) (*urlInfo, error) { | ||
if strings.HasPrefix(s, "mongodb://") { | ||
s = s[10:] | ||
} | ||
info := &urlInfo{options: make(map[string]string)} | ||
if c := strings.Index(s, "?"); c != -1 { | ||
for _, pair := range strings.FieldsFunc(s[c+1:], isOptSep) { | ||
l := strings.SplitN(pair, "=", 2) | ||
if len(l) != 2 || l[0] == "" || l[1] == "" { | ||
return nil, errors.New("connection option must be key=value: " + pair) | ||
} | ||
info.options[l[0]] = l[1] | ||
} | ||
s = s[:c] | ||
} | ||
if c := strings.Index(s, "@"); c != -1 { | ||
pair := strings.SplitN(s[:c], ":", 2) | ||
if len(pair) > 2 || pair[0] == "" { | ||
return nil, errors.New("credentials must be provided as user:pass@host") | ||
} | ||
var err error | ||
info.user, err = url.QueryUnescape(pair[0]) | ||
if err != nil { | ||
return nil, fmt.Errorf("cannot unescape username in URL: %q", pair[0]) | ||
} | ||
if len(pair) > 1 { | ||
info.pass, err = url.QueryUnescape(pair[1]) | ||
if err != nil { | ||
return nil, fmt.Errorf("cannot unescape password in URL") | ||
} | ||
} | ||
s = s[c+1:] | ||
} | ||
if c := strings.Index(s, "/"); c != -1 { | ||
info.db = s[c+1:] | ||
s = s[:c] | ||
} | ||
info.addrs = strings.Split(s, ",") | ||
return info, nil | ||
} | ||
|
||
func isOptSep(c rune) bool { | ||
return c == ';' || c == '&' | ||
} | ||
|
||
// ParseURL parses the given URL and returns a DialInfo structure ready | ||
// to be passed to DialWithInfo | ||
func ParseURL(host, username, pass string) (*mgo.DialInfo, error) { | ||
uinfo, err := parseURL(host) | ||
if err != nil { | ||
return nil, err | ||
} | ||
direct := false | ||
mechanism := "" | ||
service := "" | ||
source := "" | ||
for k, v := range uinfo.options { | ||
switch k { | ||
case "authSource": | ||
source = v | ||
case "authMechanism": | ||
mechanism = v | ||
case "gssapiServiceName": | ||
service = v | ||
case "connect": | ||
if v == "direct" { | ||
direct = true | ||
break | ||
} | ||
if v == "replicaSet" { | ||
break | ||
} | ||
fallthrough | ||
default: | ||
return nil, errors.New("unsupported connection URL option: " + k + "=" + v) | ||
} | ||
} | ||
|
||
info := &mgo.DialInfo{ | ||
Addrs: uinfo.addrs, | ||
Direct: direct, | ||
Database: uinfo.db, | ||
Username: uinfo.user, | ||
Password: uinfo.pass, | ||
Mechanism: mechanism, | ||
Service: service, | ||
Source: source, | ||
} | ||
|
||
if len(username) > 0 { | ||
info.Username = username | ||
} | ||
if len(pass) > 0 { | ||
info.Password = pass | ||
} | ||
|
||
return info, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package mongodb | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestParseURL(t *testing.T) { | ||
tests := []struct { | ||
Name string | ||
URL string | ||
Username string | ||
Password string | ||
ExpectedAddr string | ||
ExpectedUsername string | ||
ExpectedPassword string | ||
}{ | ||
{ | ||
Name: "basic test", | ||
URL: "localhost:40001", | ||
Username: "user", | ||
Password: "secret", | ||
|
||
ExpectedAddr: "localhost:40001", | ||
ExpectedUsername: "user", | ||
ExpectedPassword: "secret", | ||
}, | ||
{ | ||
Name: "with schema", | ||
URL: "mongodb://localhost:40001", | ||
Username: "user", | ||
Password: "secret", | ||
|
||
ExpectedAddr: "localhost:40001", | ||
ExpectedUsername: "user", | ||
ExpectedPassword: "secret", | ||
}, | ||
{ | ||
Name: "user password in url", | ||
URL: "mongodb://user:secret@localhost:40001", | ||
Username: "", | ||
Password: "", | ||
|
||
ExpectedAddr: "localhost:40001", | ||
ExpectedUsername: "user", | ||
ExpectedPassword: "secret", | ||
}, | ||
{ | ||
Name: "user password overwride", | ||
URL: "mongodb://user:secret@localhost:40001", | ||
Username: "anotheruser", | ||
Password: "anotherpass", | ||
|
||
ExpectedAddr: "localhost:40001", | ||
ExpectedUsername: "anotheruser", | ||
ExpectedPassword: "anotherpass", | ||
}, | ||
{ | ||
Name: "with options", | ||
URL: "mongodb://localhost:40001?connect=direct&authSource=me", | ||
Username: "anotheruser", | ||
Password: "anotherpass", | ||
|
||
ExpectedAddr: "localhost:40001", | ||
ExpectedUsername: "anotheruser", | ||
ExpectedPassword: "anotherpass", | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
info, err := ParseURL(test.URL, test.Username, test.Password) | ||
assert.NoError(t, err, test.Name) | ||
assert.Equal(t, info.Addrs[0], test.ExpectedAddr, test.Name) | ||
assert.Equal(t, info.Username, test.ExpectedUsername, test.Name) | ||
assert.Equal(t, info.Password, test.ExpectedPassword, test.Name) | ||
} | ||
} |
Oops, something went wrong.