/
testapp_sasl.cc
268 lines (219 loc) · 8.07 KB
/
testapp_sasl.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* Copyright 2016-Present Couchbase, Inc.
*
* Use of this software is governed by the Business Source License included
* in the file licenses/BSL-Couchbase.txt. As of the Change Date specified
* in that file, in accordance with the Business Source License, use of this
* software will be governed by the Apache License, Version 2.0, included in
* the file licenses/APL2.txt.
*/
#include "testapp.h"
#include "testapp_client_test.h"
#include <boost/filesystem.hpp>
#include <algorithm>
using namespace std::string_literals;
class SaslTest : public TestappClientTest {
public:
/**
* Create a vector containing all of the supported mechanisms we
* need to test.
*/
SaslTest() {
mechanisms.emplace_back("PLAIN");
mechanisms.emplace_back("SCRAM-SHA1");
mechanisms.emplace_back("SCRAM-SHA256");
mechanisms.emplace_back("SCRAM-SHA512");
}
void SetUp() override {
adminConnection->createBucket(bucket1, "", BucketType::Memcached);
adminConnection->createBucket(bucket2, "", BucketType::Memcached);
const auto dbname =
boost::filesystem::path{mcd_env->getTestDir()} / bucket3;
const auto config = "dbname="s + dbname.generic_string();
adminConnection->createBucket(bucket3, config, BucketType::Couchbase);
}
void TearDown() override {
adminConnection->deleteBucket(bucket1);
adminConnection->deleteBucket(bucket2);
adminConnection->deleteBucket(bucket3);
}
protected:
void testMixStartingFrom(const std::string& mechanism) {
MemcachedConnection& conn = getConnection();
for (const auto& mech : mechanisms) {
conn.reconnect();
conn.authenticate(bucket1, password1, mechanism);
conn.authenticate(bucket2, password2, mech);
}
}
void testIllegalLogin(const std::string& user, const std::string& mech) {
MemcachedConnection& conn = getConnection();
try {
conn.authenticate(user, "wtf", mech);
FAIL() << "incorrect authentication should fail for user \"" << user
<< "\" with mech \"" << mech << "\"";
} catch (const ConnectionError& e) {
EXPECT_TRUE(e.isAuthError()) << e.what();
}
conn.reconnect();
}
void testUnknownUser(const std::string& mech) {
testIllegalLogin("wtf", mech);
}
void testWrongPassword(const std::string& mech) {
testIllegalLogin("@admin", mech);
}
/**
* Update the list of supported authentication mechanisms
*
* @param mechanisms The new set of supported mechanisms
* @param ssl Is the list for ssl connections or not
*/
void setSupportedMechanisms(const std::string& mechanisms, bool ssl) {
std::string key{"sasl_mechanisms"};
if (ssl) {
key.insert(0, "ssl_");
}
memcached_cfg[key] = mechanisms;
reconfigure();
}
bool isSupported(const std::string mechanism) {
auto& conn = getConnection();
const auto mechs = conn.getSaslMechanisms();
if (mechs.find(mechanism) == std::string::npos) {
std::cerr << "Skipping test due to missing server support for "
<< mechanism << std::endl;
return false;
}
return true;
}
std::vector<std::string> mechanisms;
const std::string bucket1{"bucket-1"};
const std::string password1{"1S|=,%#x1"};
const std::string bucket2{"bucket-2"};
const std::string password2{"secret"};
const std::string bucket3{"bucket-3"};
};
INSTANTIATE_TEST_SUITE_P(TransportProtocols,
SaslTest,
::testing::Values(TransportProtocols::McbpPlain,
TransportProtocols::McbpSsl),
::testing::PrintToStringParamName());
TEST_P(SaslTest, SinglePLAIN) {
MemcachedConnection& conn = getConnection();
conn.authenticate(bucket1, password1, "PLAIN");
}
TEST_P(SaslTest, SingleSCRAM_SHA1) {
MemcachedConnection& conn = getConnection();
conn.authenticate(bucket1, password1, "SCRAM-SHA1");
}
TEST_P(SaslTest, SingleSCRAM_SHA256) {
MemcachedConnection& conn = getConnection();
conn.authenticate(bucket1, password1, "SCRAM-SHA256");
}
TEST_P(SaslTest, SingleSCRAM_SHA512) {
MemcachedConnection& conn = getConnection();
conn.authenticate(bucket1, password1, "SCRAM-SHA512");
}
TEST_P(SaslTest, UnknownUserPlain) {
testUnknownUser("PLAIN");
}
TEST_P(SaslTest, UnknownUserSCRAM_SHA1) {
testUnknownUser("SCRAM-SHA1");
}
TEST_P(SaslTest, UnknownUserSCRAM_SHA256) {
testUnknownUser("SCRAM-SHA256");
}
TEST_P(SaslTest, UnknownUserSCRAM_SHA512) {
testUnknownUser("SCRAM-SHA512");
}
TEST_P(SaslTest, IncorrectPlain) {
testWrongPassword("PLAIN");
}
TEST_P(SaslTest, IncorrectSCRAM_SHA1) {
testWrongPassword("SCRAM-SHA1");
}
TEST_P(SaslTest, IncorrectSCRAM_SHA256) {
testWrongPassword("SCRAM-SHA256");
}
TEST_P(SaslTest, IncorrectSCRAM_SHA512) {
testWrongPassword("SCRAM-SHA512");
}
TEST_P(SaslTest, TestSaslMixFrom_PLAIN) {
testMixStartingFrom("PLAIN");
}
TEST_P(SaslTest, TestSaslMixFrom_SCRAM_SHA1) {
testMixStartingFrom("SCRAM-SHA1");
}
TEST_P(SaslTest, TestSaslMixFrom_SCRAM_SHA256) {
testMixStartingFrom("SCRAM-SHA256");
}
TEST_P(SaslTest, TestSaslMixFrom_SCRAM_SHA512) {
testMixStartingFrom("SCRAM-SHA512");
}
TEST_P(SaslTest, TestDisablePLAIN) {
auto& conn = getConnection();
const auto before = conn.getSaslMechanisms();
auto& c = connectionMap.getConnection(!conn.isSsl(), conn.getFamily());
c.reconnect();
const auto otherMechs = c.getSaslMechanisms();
setSupportedMechanisms("SCRAM-SHA1", conn.isSsl());
c.reconnect();
conn.reconnect();
// We should only support SCRAM-SHA1
EXPECT_EQ("SCRAM-SHA1", conn.getSaslMechanisms());
EXPECT_EQ(otherMechs, c.getSaslMechanisms());
// It should not be possible to select any other mechanisms:
for (const auto& mech : mechanisms) {
// get a fresh connection
conn.reconnect();
if (mech == "SCRAM-SHA1") {
// This should work
conn.authenticate(bucket1, password1, mech);
} else {
// All other should fail
try {
conn.authenticate(bucket1, password1, mech);
FAIL() << "Mechanism " << mech << " should be disabled";
} catch (const ConnectionError& e) {
EXPECT_TRUE(e.isAuthError());
}
}
}
// verify that we didn't change the setting for the other connection
c.reconnect();
// And PLAIN auth should work
c.authenticate(bucket1, password1, "PLAIN");
// Restore the sasl mechanisms
setSupportedMechanisms(before, conn.isSsl());
}
// Pretend we're a collection aware client
TEST_P(SaslTest, CollectionsConnectionSetup) {
auto& conn = getConnection();
// Hello
BinprotHelloCommand helloCmd("Collections");
helloCmd.enableFeature(cb::mcbp::Feature::Collections);
helloCmd.enableFeature(cb::mcbp::Feature::XERROR);
helloCmd.enableFeature(cb::mcbp::Feature::SELECT_BUCKET);
const auto helloRsp = BinprotHelloResponse(conn.execute(helloCmd));
ASSERT_TRUE(helloRsp.isSuccess());
// Get err map
const auto errMapRsp = conn.execute(BinprotGetErrorMapCommand{});
// Get SASL mechs
const auto mechs = conn.getSaslMechanisms();
// Do a SASL auth
EXPECT_NO_THROW(conn.authenticate(bucket3, password1, mechs));
// Select the bucket
EXPECT_NO_THROW(conn.selectBucket(bucket3));
// Do a get
BinprotGetCommand getCmd;
getCmd.setOp(cb::mcbp::ClientOpcode::Get);
getCmd.setKey(std::string{"\0key", 4});
getCmd.setVBucket(Vbid(0));
auto auto_retry_tmpfail = conn.getAutoRetryTmpfail();
conn.setAutoRetryTmpfail(true);
const auto getRsp = BinprotGetResponse(conn.execute(getCmd));
conn.setAutoRetryTmpfail(auto_retry_tmpfail);
EXPECT_EQ(cb::mcbp::Status::NotMyVbucket, getRsp.getStatus());
}