diff --git a/Documentation/REST-API/Resources-User.md b/Documentation/REST-API/Resources-User.md index 67d0095ae..a64a5a85c 100644 --- a/Documentation/REST-API/Resources-User.md +++ b/Documentation/REST-API/Resources-User.md @@ -195,6 +195,10 @@ following fields. * `data.attributes.account` * Set to `admin` for administrative users and `basic` to read-only users +Only admin accounts can perform POST, PUT, DELETE and PATCH requests. If a basic +account performs one of the aforementioned request, the REST API will respond +with a `401 Unauthorized` error. + Here is an example request body defining the network user _my-user_ with the password _my-password_ that is allowed to execute only read-only operations. diff --git a/maxctrl/lib/create.js b/maxctrl/lib/create.js index 723f627e1..799f41093 100644 --- a/maxctrl/lib/create.js +++ b/maxctrl/lib/create.js @@ -354,7 +354,8 @@ exports.builder = function(yargs) { return yargs.epilog('The created user can be used with the MaxScale REST API as ' + 'well as the MaxAdmin network interface. By default the created ' + 'user will have read-only privileges. To make the user an ' + - 'administrative user, use the `--type=admin` option.') + 'administrative user, use the `--type=admin` option. ' + + 'Basic users can only perform `list` and `show` commands.') .usage('Usage: create user ') .group(['type'], 'Create user options:') .option('type', { diff --git a/server/core/listener.cc b/server/core/listener.cc index af7debc4d..b947b29b6 100644 --- a/server/core/listener.cc +++ b/server/core/listener.cc @@ -872,11 +872,6 @@ DCB* Listener::accept_one_dcb(int fd, const sockaddr_storage* addr, const char* } } - if (client_dcb) - { - mxb::atomic::add(&m_service->client_count, 1); - } - return client_dcb; } diff --git a/server/core/session.cc b/server/core/session.cc index 3325b754d..d7db7af7f 100644 --- a/server/core/session.cc +++ b/server/core/session.cc @@ -257,18 +257,7 @@ private: static void session_free(MXS_SESSION* session) { MXS_INFO("Stopped %s client session [%" PRIu64 "]", session->service->name(), session->ses_id); - Service* service = static_cast(session->service); - session_final_free(session); - bool should_destroy = !mxb::atomic::load(&service->active); - - if (mxb::atomic::add(&service->client_count, -1) == 1 && should_destroy) - { - // Destroy the service in the main routing worker thread - mxs::RoutingWorker* main_worker = mxs::RoutingWorker::get(mxs::RoutingWorker::MAIN); - main_worker->execute(std::unique_ptr(new ServiceDestroyTask(service)), - Worker::EXECUTE_AUTO); - } } static void session_final_free(MXS_SESSION* ses) @@ -1162,6 +1151,7 @@ Session::Session(const SListener& listener) mxb::atomic::add(&service->stats.n_current, 1, mxb::atomic::RELAXED); mxb_assert(service->stats.n_current >= 0); + mxb::atomic::add(&service->client_count, 1, mxb::atomic::RELAXED); } Session::~Session() @@ -1179,6 +1169,17 @@ Session::~Session() mxb::atomic::add(&service->stats.n_current, -1, mxb::atomic::RELAXED); mxb_assert(service->stats.n_current >= 0); + + bool should_destroy = !mxb::atomic::load(&service->active); + + if (mxb::atomic::add(&service->client_count, -1) == 1 && should_destroy) + { + // Destroy the service in the main routing worker thread + mxs::RoutingWorker* main_worker = mxs::RoutingWorker::get(mxs::RoutingWorker::MAIN); + main_worker->execute( + std::unique_ptr(new ServiceDestroyTask(static_cast(service))), + Worker::EXECUTE_AUTO); + } } void Session::set_client_dcb(DCB* dcb) diff --git a/server/modules/authenticator/MariaDBAuth/dbusers.cc b/server/modules/authenticator/MariaDBAuth/dbusers.cc index 634959cfa..d22628893 100644 --- a/server/modules/authenticator/MariaDBAuth/dbusers.cc +++ b/server/modules/authenticator/MariaDBAuth/dbusers.cc @@ -478,6 +478,10 @@ int validate_mysql_user(MYSQL_AUTH* instance, rval = MXS_AUTH_FAILED_DB; } } + else + { + rval = MXS_AUTH_FAILED_WRONG_PASSWORD; + } } return rval; diff --git a/server/modules/authenticator/MariaDBAuth/mysql_auth.cc b/server/modules/authenticator/MariaDBAuth/mysql_auth.cc index 181afae99..e9ff43e46 100644 --- a/server/modules/authenticator/MariaDBAuth/mysql_auth.cc +++ b/server/modules/authenticator/MariaDBAuth/mysql_auth.cc @@ -37,6 +37,8 @@ #include #include +#include + static void* mysql_auth_init(char** options); static bool mysql_auth_set_protocol_data(DCB* dcb, GWBUF* buf); static bool mysql_auth_is_client_ssl_capable(DCB* dcb); @@ -305,6 +307,48 @@ static GWBUF* gen_auth_switch_request_packet(MySQLProtocol* proto, MYSQL_session *bufdata = '\0'; return buffer; } + +static void log_auth_failure(DCB* dcb, int auth_ret) +{ + MYSQL_session* client_data = (MYSQL_session*)dcb->data; + std::ostringstream extra; + + if (auth_ret == MXS_AUTH_FAILED_DB) + { + extra << "Unknown database: " << client_data->db; + } + else if (auth_ret == MXS_AUTH_FAILED_WRONG_PASSWORD) + { + extra << "Wrong password."; + } + else + { + extra << "User not found."; + } + + std::ostringstream host; + host << "[" << dcb->remote << "]:" << dcb_get_port(dcb); + + std::ostringstream db; + + if (*client_data->db) + { + db << " to database '" << client_data->db << "'"; + } + + MXS_LOG_EVENT(maxscale::event::AUTHENTICATION_FAILURE, + "%s: login attempt for user '%s'@%s%s, authentication failed. %s", + dcb->service->name(), client_data->user, host.str().c_str(), + db.str().c_str(), extra.str().c_str()); + + if (is_localhost_address(&dcb->ip) && !dcb->service->localhost_match_wildcard_host) + { + MXS_NOTICE("If you have a wildcard grant that covers this address, " + "try adding 'localhost_match_wildcard_host=true' for " + "service '%s'. ", dcb->service->name()); + } +} + /** * @brief Authenticates a MySQL user who is a client to MaxScale. * @@ -368,34 +412,7 @@ static int mysql_auth_authenticate(DCB* dcb) } else if (dcb->service->log_auth_warnings) { - // The default failure is a `User not found` one - char extra[256] = "User not found."; - - if (auth_ret == MXS_AUTH_FAILED_DB) - { - snprintf(extra, sizeof(extra), "Unknown database: %s", client_data->db); - } - else if (auth_ret == MXS_AUTH_FAILED_WRONG_PASSWORD) - { - strcpy(extra, "Wrong password."); - } - - MXS_LOG_EVENT(maxscale::event::AUTHENTICATION_FAILURE, - "%s: login attempt for user '%s'@[%s]:%d, authentication failed. %s", - dcb->service->name(), - client_data->user, - dcb->remote, - dcb_get_port(dcb), - extra); - - if (is_localhost_address(&dcb->ip) - && !dcb->service->localhost_match_wildcard_host) - { - MXS_NOTICE("If you have a wildcard grant that covers this address, " - "try adding 'localhost_match_wildcard_host=true' for " - "service '%s'. ", - dcb->service->name()); - } + log_auth_failure(dcb, auth_ret); } /* let's free the auth_token now */