MXS-2574: Add PATCH for /users/inet endpoint

The alteration of user passwords is now done inside MaxScale. This
prevents the possibility of a user locking themselves out.
This commit is contained in:
Markus Mäkelä 2019-06-24 14:44:11 +03:00
parent 77671a2393
commit aac0ecc373
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
9 changed files with 115 additions and 13 deletions

View File

@ -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);

View File

@ -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
*

View File

@ -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 <command>')

View File

@ -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)
});

View File

@ -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
*

View File

@ -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;

View File

@ -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
*

View File

@ -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,

View File

@ -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*>(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*>(users);