diff --git a/include/maxscale/adminusers.h.in b/include/maxscale/adminusers.h.in index 93fb79893..2846c0e15 100644 --- a/include/maxscale/adminusers.h.in +++ b/include/maxscale/adminusers.h.in @@ -71,6 +71,7 @@ const char* admin_disable_linux_account(const char *uname); bool admin_linux_account_enabled(const char *uname); const char* admin_add_inet_user(const char *uname, const char *password, enum user_account_type type); +const char* admin_alter_inet_user(const char* uname, const char* password); const char* admin_remove_inet_user(const char* uname); bool admin_inet_user_exists(const char *uname); bool admin_verify_inet_user(const char *uname, const char *password); diff --git a/include/maxscale/users.h b/include/maxscale/users.h index be3d91106..e527c852b 100644 --- a/include/maxscale/users.h +++ b/include/maxscale/users.h @@ -97,6 +97,17 @@ bool users_auth(USERS* users, const char* user, const char* password); */ bool users_find(USERS* users, const char* user); +/** + * Change password for a user + * + * @param users The users table + * @param user User to alter + * @param password The new password for the user + * + * @return True if password was changed + */ +bool users_change_password(USERS* users, const char* user, const char* password); + /** * Check if user is an administrator * diff --git a/maxctrl/lib/alter.js b/maxctrl/lib/alter.js index c5c8b8aba..747efc171 100644 --- a/maxctrl/lib/alter.js +++ b/maxctrl/lib/alter.js @@ -192,10 +192,6 @@ exports.builder = function(yargs) { }, function(argv) { maxctrl(argv, function(host) { - if (argv.u == argv.name) { - return error('Cannot alter current user') - } - var user = { 'data': { 'id': argv.name, @@ -206,10 +202,7 @@ exports.builder = function(yargs) { } } - return getJson(host, 'users/inet/' + argv.name) - .then((res) => user.data.attributes.account = res.data.attributes.account) - .then(() => doRequest(host, 'users/inet/' + argv.name, null, {method: 'DELETE'})) - .then(() => doRequest(host, 'users/inet', null, {method: 'POST', body: user})) + return doRequest(host, 'users/inet/' + argv.name, null, {method: 'PATCH', body: user}) }) }) .usage('Usage: alter ') diff --git a/maxctrl/test/alter.js b/maxctrl/test/alter.js index 7cebf21a3..887d4dd96 100644 --- a/maxctrl/test/alter.js +++ b/maxctrl/test/alter.js @@ -195,11 +195,6 @@ describe("Alter Commands", function() { .should.be.rejected }) - it('rejects alteration to current user', function() { - return doCommand('-u bob -p bob alter user bob bob2') - .should.be.rejected - }) - it('creates user', function() { return verifyCommand('create user testuser test', 'users/inet/testuser') }) @@ -212,5 +207,13 @@ describe("Alter Commands", function() { return doCommand('destroy user testuser') }) + it('allows alteration to current user', function() { + return verifyCommand('create user bob bob --type=admin', 'users/inet/bob') + .then(() => doCommand('-u bob -p bob alter user bob bob2')) + .then(() => doCommand('-u bob -p bob2 alter user bob bob')) + .then(() => doCommand('-u bob -p bob list servers')) + .then(() => doCommand('-u bob -p bob destroy user bob')) + }) + after(stopMaxScale) }); diff --git a/server/core/adminusers.cc b/server/core/adminusers.cc index 4a571b42a..c50f46157 100644 --- a/server/core/adminusers.cc +++ b/server/core/adminusers.cc @@ -138,6 +138,29 @@ static const char* admin_add_user(USERS** pusers, return ADMIN_SUCCESS; } +static const char* admin_alter_user(USERS** pusers, + const char* fname, + const char* uname, + const char* password) +{ + if (*pusers == NULL) + { + *pusers = users_alloc(); + } + + if (!users_change_password(*pusers, uname, password)) + { + return ADMIN_ERR_USERNOTFOUND; + } + + if (!admin_dump_users(*pusers, fname)) + { + return ADMIN_ERR_FILEOPEN; + } + + return ADMIN_SUCCESS; +} + static const char* admin_remove_user(USERS* users, const char* fname, const char* uname) { if (!users_delete(users, uname)) @@ -437,6 +460,19 @@ const char* admin_add_inet_user(const char* uname, const char* password, enum us return admin_add_user(&inet_users, INET_USERS_FILE_NAME, uname, password, type); } +/** + * Alter network user. + * + * @param uname The user to alter + * @param password The new password + * + * @return NULL on success or an error string on failure. + */ +const char* admin_alter_inet_user(const char* uname, const char* password) +{ + return admin_alter_user(&inet_users, INET_USERS_FILE_NAME, uname, password); +} + /** * Remove insecure remote (network) user * diff --git a/server/core/config_runtime.cc b/server/core/config_runtime.cc index 3002da06d..369fb3118 100644 --- a/server/core/config_runtime.cc +++ b/server/core/config_runtime.cc @@ -2875,6 +2875,31 @@ bool runtime_remove_user(const char* id, enum user_type type) return rval; } +bool runtime_alter_user(const std::string& user, const std::string& type, json_t* json) +{ + bool rval = false; + const char* password = json_string_value(mxs_json_pointer(json, MXS_JSON_PTR_PASSWORD)); + + if (!password) + { + config_runtime_error("No password provided"); + } + else if (type != CN_INET) + { + config_runtime_error("Users of type '%s' cannot be altered", type.c_str()); + } + else if (const char* err = admin_alter_inet_user(user.c_str(), password)) + { + config_runtime_error("%s", err); + } + else + { + rval = true; + } + + return rval; +} + bool validate_maxscale_json(json_t* json) { bool rval = false; diff --git a/server/core/internal/config_runtime.hh b/server/core/internal/config_runtime.hh index f4db4132b..9afacaf73 100644 --- a/server/core/internal/config_runtime.hh +++ b/server/core/internal/config_runtime.hh @@ -386,6 +386,17 @@ bool runtime_create_user_from_json(json_t* json); */ bool runtime_remove_user(const char* id, enum user_type type); +/** + * @brief Alter admin user password + * + * @param user Username + * @param type Type of the user + * @param json JSON defining the new user + * + * @return True if the user was altered successfully + */ +bool runtime_alter_user(const std::string& user, const std::string& type, json_t* json); + /** * @brief Alter core MaxScale parameters from JSON * diff --git a/server/core/resource.cc b/server/core/resource.cc index d2657426a..66e2ae1b8 100644 --- a/server/core/resource.cc +++ b/server/core/resource.cc @@ -760,6 +760,7 @@ HttpResponse cb_monitor_wait(const HttpRequest& request) MonitorManager::debug_wait_one_tick(); return HttpResponse(MHD_HTTP_OK); } + HttpResponse cb_create_user(const HttpRequest& request) { mxb_assert(request.get_json()); @@ -772,6 +773,19 @@ HttpResponse cb_create_user(const HttpRequest& request) return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error()); } +HttpResponse cb_alter_user(const HttpRequest& request) +{ + auto user = request.last_uri_part(); + auto type = request.uri_part(1); + + if (runtime_alter_user(user, type, request.get_json())) + { + return HttpResponse(MHD_HTTP_NO_CONTENT); + } + + return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error()); +} + HttpResponse cb_delete_user(const HttpRequest& request) { string user = request.last_uri_part(); @@ -1044,6 +1058,7 @@ public: m_patch.push_back(SResource(new Resource(cb_alter_logs, 2, "maxscale", "logs"))); m_patch.push_back(SResource(new Resource(cb_alter_maxscale, 1, "maxscale"))); m_patch.push_back(SResource(new Resource(cb_alter_qc, 2, "maxscale", "query_classifier"))); + m_patch.push_back(SResource(new Resource(cb_alter_user, 3, "users", "inet", ":inetuser"))); /** Update resource relationships directly */ m_patch.push_back(SResource(new Resource(cb_alter_server_service_relationship, diff --git a/server/core/users.cc b/server/core/users.cc index dff6f4d49..b9b255377 100644 --- a/server/core/users.cc +++ b/server/core/users.cc @@ -327,6 +327,13 @@ bool users_find(USERS* users, const char* user) return u->get(user); } +bool users_change_password(USERS* users, const char* user, const char* password) +{ + Users* u = reinterpret_cast(users); + UserInfo info; + return u->get(user, &info) && u->remove(user) && u->add(user, password, info.permissions); +} + bool users_auth(USERS* users, const char* user, const char* password) { Users* u = reinterpret_cast(users);