Skip to content

Commit b2b190d

Browse files
committed
Merge branch 'wl10718-extended-auth'
# Conflicts: # include/mysql_xapi.h
2 parents 563e1ae + 3dcd00f commit b2b190d

File tree

12 files changed

+535
-30
lines changed

12 files changed

+535
-30
lines changed

cdk/include/mysql/cdk/data_source.h

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -152,33 +152,50 @@ class Protocol_options
152152
class TCPIP::Options
153153
: public ds::Options<Protocol_options>
154154
{
155+
private:
156+
157+
#ifdef WITH_SSL
158+
cdk::connection::TLS::Options m_tls_options;
159+
#endif
160+
bool m_auth_method_set;
161+
auth_method_t m_auth_method;
162+
155163
public:
156164

157-
Options()
165+
Options() : m_auth_method_set(false),
166+
m_auth_method(auth_method_t::MYSQL41)
158167
{
159-
m_auth_method = auth_method_t::MYSQL41;
160168
}
161169

162170
Options(const string &usr, const std::string *pwd =NULL)
163-
: ds::Options<Protocol_options>(usr, pwd)
171+
: ds::Options<Protocol_options>(usr, pwd),
172+
m_auth_method_set(false),
173+
m_auth_method(auth_method_t::MYSQL41)
164174
{
165175
/*
166176
We don't know if the connection will be over SSL.
167177
Guessing unencrypted connection and MYSQL41 auth.
168178
*/
169-
m_auth_method = auth_method_t::MYSQL41;
170179
}
171180

172181
#ifdef WITH_SSL
173182

174183
void set_tls(const cdk::connection::TLS::Options& options)
175184
{
176185
m_tls_options = options;
177-
// Use PLAIN auth for SSL connections
178-
if (options.ssl_mode() == cdk::connection::TLS::Options::SSL_MODE::DISABLED)
179-
m_auth_method = auth_method_t::MYSQL41;
180-
else
181-
m_auth_method = auth_method_t::PLAIN;
186+
187+
/*
188+
The authentication method should not be changed
189+
if the user code set it specifically
190+
*/
191+
if (!m_auth_method_set)
192+
{
193+
// Use PLAIN auth for SSL connections
194+
if (options.ssl_mode() == cdk::connection::TLS::Options::SSL_MODE::DISABLED)
195+
m_auth_method = auth_method_t::MYSQL41;
196+
else
197+
m_auth_method = auth_method_t::PLAIN;
198+
}
182199
}
183200

184201
const cdk::connection::TLS::Options& get_tls() const
@@ -191,16 +208,9 @@ class TCPIP::Options
191208
void set_auth_method(auth_method_t auth_method)
192209
{
193210
m_auth_method = auth_method;
211+
m_auth_method_set = true;
194212
}
195213

196-
private:
197-
198-
#ifdef WITH_SSL
199-
cdk::connection::TLS::Options m_tls_options;
200-
#endif
201-
auth_method_t m_auth_method;
202-
203-
204214
auth_method_t auth_method() const
205215
{
206216
return m_auth_method;

devapi/session.cc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,25 @@ void set_ssl_mode(cdk::connection::TLS::Options &tls_opt,
195195
#endif //WITH_SSL
196196

197197

198+
cdk::ds::mysqlx::Protocol_options::auth_method_t get_auth_method(const std::string &name)
199+
{
200+
#define map_auth(x) { #x, cdk::ds::mysqlx::Protocol_options::x },
201+
static std::map<std::string, cdk::ds::mysqlx::Protocol_options::auth_method_t>
202+
auth_methods{ AUTH_METHODS(map_auth) };
203+
204+
try {
205+
return auth_methods.at(name);
206+
}
207+
catch (const std::out_of_range&)
208+
{
209+
std::string msg = "Invalid auth value: " + std::string(name);
210+
throw_error(msg.c_str());
211+
// Quiet compiler warnings
212+
return cdk::ds::mysqlx::Protocol_options::MYSQL41;
213+
}
214+
}
215+
216+
198217
struct Host_sources : public cdk::ds::Multi_source
199218
{
200219

@@ -342,6 +361,13 @@ struct URI_parser
342361
" without TLS support."
343362
);
344363
#endif
364+
} else if (lc_key == "auth")
365+
{
366+
std::string auth;
367+
auth.resize(val.size());
368+
std::transform(val.begin(), val.end(), auth.begin(), ::toupper);
369+
370+
set_auth_method(get_auth_method(auth));
345371
} else
346372
{
347373
std::stringstream err;
@@ -456,6 +482,30 @@ Session::Session(SessionSettings settings)
456482
opt.set_tls(opt_ssl);
457483
#endif
458484

485+
/*
486+
Setting Authentication method after SSL options
487+
to override the defaults if necessary
488+
*/
489+
if (settings.has_option(SessionOption::AUTH))
490+
{
491+
AuthMethod am =
492+
AuthMethod(
493+
settings.find(SessionOption::AUTH).get<unsigned>());
494+
495+
switch(am)
496+
{
497+
case AuthMethod::PLAIN:
498+
opt.set_auth_method(cdk::ds::mysqlx::Protocol_options::PLAIN);
499+
break;
500+
case AuthMethod::MYSQL41:
501+
opt.set_auth_method(cdk::ds::mysqlx::Protocol_options::MYSQL41);
502+
break;
503+
case AuthMethod::EXTERNAL:
504+
opt.set_auth_method(cdk::ds::mysqlx::Protocol_options::EXTERNAL);
505+
break;
506+
}
507+
}
508+
459509
Host_sources source;
460510

461511
std::string host = "localhost";

devapi/tests/session-t.cc

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,117 @@ TEST_F(Sess, trx)
281281
cout << "Done!" << endl;
282282
}
283283

284+
TEST_F(Sess, auth_method)
285+
{
286+
287+
SKIP_IF_NO_XPLUGIN;
288+
289+
auto check_user = [](mysqlx::Session &sess)
290+
{
291+
SqlResult res = sess.sql("SELECT CURRENT_USER()").execute();
292+
auto row = res.fetchOne();
293+
string str = row[0];
294+
cout << "User: " << str << endl;
295+
};
296+
297+
{
298+
mysqlx::Session sess(SessionOption::PORT, get_port(),
299+
SessionOption::USER, get_user(),
300+
SessionOption::PWD, get_password() ? get_password() : nullptr,
301+
SessionOption::SSL_MODE, SSLMode::DISABLED,
302+
SessionOption::AUTH, AuthMethod::MYSQL41
303+
);
304+
check_user(sess);
305+
}
306+
307+
{
308+
// This will throw because of plain auth without SSL
309+
EXPECT_THROW(mysqlx::Session sess(SessionOption::PORT, get_port(),
310+
SessionOption::USER, get_user(),
311+
SessionOption::PWD, get_password() ? get_password() : nullptr,
312+
SessionOption::SSL_MODE, SSLMode::DISABLED,
313+
SessionOption::AUTH, AuthMethod::PLAIN),
314+
Error);
315+
}
316+
317+
{
318+
mysqlx::Session sess(SessionOption::PORT, get_port(),
319+
SessionOption::USER, get_user(),
320+
SessionOption::PWD, get_password() ? get_password() : nullptr,
321+
SessionOption::SSL_MODE, SSLMode::REQUIRED,
322+
SessionOption::AUTH, AuthMethod::PLAIN
323+
);
324+
check_user(sess);
325+
}
326+
327+
{
328+
mysqlx::Session sess(SessionOption::PORT, get_port(),
329+
SessionOption::USER, get_user(),
330+
SessionOption::PWD, get_password() ? get_password() : nullptr,
331+
SessionOption::SSL_MODE, SSLMode::REQUIRED,
332+
SessionOption::AUTH, AuthMethod::MYSQL41
333+
);
334+
check_user(sess);
335+
}
336+
337+
std::stringstream uri;
338+
uri << "mysqlx://" << get_user();
339+
if (get_password() && *get_password())
340+
uri << ":" << get_password();
341+
uri << "@" << "localhost:" << get_port();
342+
343+
{
344+
std::stringstream str;
345+
str << uri.str() << "/?ssl-mode=disabled&auth=mysql41";
346+
mysqlx::Session sess(str.str());
347+
check_user(sess);
348+
}
349+
350+
{
351+
std::stringstream str;
352+
str << uri.str() << "/?ssl-mode=disabled&auth=plain";
353+
EXPECT_THROW(mysqlx::Session sess(str.str()), Error);
354+
}
355+
356+
{
357+
std::stringstream str;
358+
str << uri.str() << "/?ssl-mode=required&auth=plain";
359+
mysqlx::Session sess(str.str());
360+
check_user(sess);
361+
}
362+
363+
{
364+
std::stringstream str;
365+
str << uri.str() << "/?ssl-mode=required&auth=mysql41";
366+
mysqlx::Session sess(str.str());
367+
check_user(sess);
368+
}
369+
}
370+
371+
TEST_F(Sess, auth_external)
372+
{
373+
374+
SKIP_IF_NO_XPLUGIN;
375+
376+
// This will throw because of EXTERNAL is not supported
377+
EXPECT_THROW(mysqlx::Session sess(SessionOption::PORT, get_port(),
378+
SessionOption::USER, get_user(),
379+
SessionOption::PWD, get_password() ? get_password() : nullptr,
380+
SessionOption::SSL_MODE, SSLMode::DISABLED,
381+
SessionOption::AUTH, AuthMethod::PLAIN),
382+
Error);
383+
384+
std::stringstream uri;
385+
uri << "mysqlx://" << get_user();
386+
if (get_password() && *get_password())
387+
uri << ":" << get_password();
388+
uri << "@" << "localhost:" << get_port();
389+
390+
uri << "/?ssl-mode=required&auth=external";
391+
EXPECT_THROW(mysqlx::Session sess(uri.str()),
392+
Error);
393+
}
394+
284395

285396
TEST_F(Sess, ssl_session)
286397
{

include/devapi/detail/settings.h

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ namespace internal {
1919
template <typename Traits>
2020
class Settings_detail
2121
{
22-
using Options = typename Traits::Options;
23-
using SSLMode = typename Traits::SSLMode;
22+
using Options = typename Traits::Options;
23+
using SSLMode = typename Traits::SSLMode;
24+
using AuthMethod = typename Traits::AuthMethod;
2425

2526
protected:
2627

@@ -34,6 +35,20 @@ class Settings_detail
3435
void do_add(Options opt, Value &&v);
3536
Value& find(Options opt);
3637

38+
/*
39+
Declare options that require specific type of value (mostly enumerations).
40+
For such options we do not accept setting them to arbitrary values. Instead
41+
an overload of opt_val() with appropriate type will be used to set value
42+
of the option.
43+
*/
44+
45+
#define OPT_VAL_TYPE(X) \
46+
X(SSL_MODE,SSLMode) \
47+
X(AUTH,AuthMethod)
48+
49+
#define CHECK_OPT(Opt,Type) \
50+
if (opt == Options::Opt) \
51+
throw Error(#Opt "setting requires value of type " #Type);
3752

3853
/*
3954
Store option value in Value object (with basic run-time type checks)
@@ -42,8 +57,7 @@ class Settings_detail
4257

4358
static Value opt_val(Options opt, Value &&val)
4459
{
45-
if (opt == Options::SSL_MODE)
46-
throw Error("SSL_MODE setting requires SessionSettings::SSLMode value.");
60+
OPT_VAL_TYPE(CHECK_OPT)
4761
return val;
4862
}
4963

@@ -59,8 +73,7 @@ class Settings_detail
5973
>
6074
static Value opt_val(Options opt, V &&val)
6175
{
62-
if (opt == Options::SSL_MODE)
63-
throw Error("SSL_MODE setting requires SessionSettings::SSLMode value.");
76+
OPT_VAL_TYPE(CHECK_OPT)
6477
return string(val);
6578
}
6679

@@ -71,6 +84,15 @@ class Settings_detail
7184
return unsigned(m);
7285
}
7386

87+
static Value opt_val(Options opt, AuthMethod m)
88+
{
89+
if (opt != Options::AUTH)
90+
throw Error("SessionSettings::AuthMethod value can only be used on AUTH setting.");
91+
return unsigned(m);
92+
}
93+
94+
95+
7496
/*
7597
TODO: Document it
7698
*/

0 commit comments

Comments
 (0)