1
2
3
4
5
6
7 package integration
8
9 import (
10 "context"
11 "fmt"
12 "os"
13 "testing"
14
15 "go.mongodb.org/mongo-driver/bson/bsontype"
16 "go.mongodb.org/mongo-driver/internal/integtest"
17 "go.mongodb.org/mongo-driver/mongo/description"
18 "go.mongodb.org/mongo-driver/mongo/options"
19 "go.mongodb.org/mongo-driver/mongo/writeconcern"
20 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
21 "go.mongodb.org/mongo-driver/x/mongo/driver"
22 )
23
24 type scramTestCase struct {
25 username string
26 password string
27 mechanisms []string
28 altPassword string
29 }
30
31 func TestSCRAM(t *testing.T) {
32 if os.Getenv("AUTH") != "auth" {
33 t.Skip("Skipping because authentication is required")
34 }
35
36 server, err := integtest.Topology(t).SelectServer(context.Background(), description.WriteSelector())
37 noerr(t, err)
38 serverConnection, err := server.Connection(context.Background())
39 noerr(t, err)
40 defer serverConnection.Close()
41
42 if !serverConnection.Description().WireVersion.Includes(7) {
43 t.Skip("Skipping because MongoDB 4.0 is needed for SCRAM-SHA-256")
44 }
45
46
47 var romanFour = "\u2163"
48 var romanNine = "\u2168"
49
50 testUsers := []scramTestCase{
51
52 {username: "sha1", password: "sha1", mechanisms: []string{"SCRAM-SHA-1"}},
53 {username: "sha256", password: "sha256", mechanisms: []string{"SCRAM-SHA-256"}},
54 {username: "both", password: "both", mechanisms: []string{"SCRAM-SHA-1", "SCRAM-SHA-256"}},
55
56 {username: "IX", password: "IX", mechanisms: []string{"SCRAM-SHA-256"}, altPassword: "I\u00ADX"},
57 {username: romanNine, password: romanFour, mechanisms: []string{"SCRAM-SHA-256"}, altPassword: "I\u00ADV"},
58 }
59
60
61
62 wc := writeconcern.New(writeconcern.WMajority())
63 collOne := integtest.ColName(t)
64 dropCollection(t, integtest.DBName(t), collOne)
65 insertDocs(t, integtest.DBName(t),
66 collOne, wc, bsoncore.BuildDocument(nil, bsoncore.AppendStringElement(nil, "name", "scram_test")),
67 )
68
69
70 err = createScramUsers(t, server, testUsers)
71 if err != nil {
72 t.Fatal(err)
73 }
74
75
76
77
78
79 for _, m := range []string{"SCRAM-SHA-1", "SCRAM-SHA-256", "negotiate"} {
80 for _, c := range testUsers {
81 t.Run(
82 fmt.Sprintf("%s %s", c.username, m),
83 func(t *testing.T) {
84 err := testScramUserAuthWithMech(t, c, m)
85 if m == "negotiate" || hasAuthMech(c.mechanisms, m) {
86 noerr(t, err)
87 } else {
88 autherr(t, err)
89 }
90 },
91 )
92 }
93 }
94
95
96
97 bogus := scramTestCase{username: "eliot", password: "trustno1"}
98 err = testScramUserAuthWithMech(t, bogus, "negotiate")
99 autherr(t, err)
100
101
102 for _, c := range testUsers {
103 if c.altPassword == "" {
104 continue
105 }
106 c.password = c.altPassword
107 t.Run(
108 fmt.Sprintf("%s alternate password", c.username),
109 func(t *testing.T) {
110 err := testScramUserAuthWithMech(t, c, "SCRAM-SHA-256")
111 noerr(t, err)
112 },
113 )
114 }
115
116 }
117
118 func hasAuthMech(mechs []string, m string) bool {
119 for _, v := range mechs {
120 if v == m {
121 return true
122 }
123 }
124 return false
125 }
126
127 func testScramUserAuthWithMech(t *testing.T, c scramTestCase, mech string) error {
128 t.Helper()
129 credential := options.Credential{
130 Username: c.username,
131 Password: c.password,
132 AuthSource: integtest.DBName(t),
133 }
134 switch mech {
135 case "negotiate":
136 credential.AuthMechanism = ""
137 default:
138 credential.AuthMechanism = mech
139 }
140 return runScramAuthTest(t, credential)
141 }
142
143 func runScramAuthTest(t *testing.T, credential options.Credential) error {
144 t.Helper()
145 topology := integtest.TopologyWithCredential(t, credential)
146 server, err := topology.SelectServer(context.Background(), description.WriteSelector())
147 noerr(t, err)
148
149 cmd := bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, "dbstats", 1))
150 _, err = runCommand(server, integtest.DBName(t), cmd)
151 return err
152 }
153
154 func createScramUsers(t *testing.T, s driver.Server, cases []scramTestCase) error {
155 db := integtest.DBName(t)
156 for _, c := range cases {
157 var values []bsoncore.Value
158 for _, v := range c.mechanisms {
159 values = append(values, bsoncore.Value{Type: bsontype.String, Data: bsoncore.AppendString(nil, v)})
160 }
161 newUserCmd := bsoncore.BuildDocumentFromElements(nil,
162 bsoncore.AppendStringElement(nil, "createUser", c.username),
163 bsoncore.AppendStringElement(nil, "pwd", c.password),
164 bsoncore.AppendArrayElement(nil, "roles", bsoncore.BuildArray(nil,
165 bsoncore.Value{Type: bsontype.EmbeddedDocument, Data: bsoncore.BuildDocumentFromElements(nil,
166 bsoncore.AppendStringElement(nil, "role", "readWrite"),
167 bsoncore.AppendStringElement(nil, "db", db),
168 )},
169 )),
170 bsoncore.AppendArrayElement(nil, "mechanisms", bsoncore.BuildArray(nil, values...)),
171 )
172 _, err := runCommand(s, db, newUserCmd)
173 if err != nil {
174 return fmt.Errorf("Couldn't create user '%s' on db '%s': %w", c.username, integtest.DBName(t), err)
175 }
176 }
177 return nil
178 }
179
View as plain text