MXS-1354: Add user account types to REST API

The user accounts can now be created with a specific account type. This
allows read-only users to be created for the REST API.
This commit is contained in:
Markus Mäkelä 2017-08-16 06:29:46 +03:00
parent ec045b7ab6
commit 9d24a63c10
9 changed files with 108 additions and 119 deletions

View File

@ -18,6 +18,7 @@
#include <maxscale/cdefs.h>
#include <maxscale/dcb.h>
#include <maxscale/users.h>
MXS_BEGIN_DECLS
@ -73,13 +74,11 @@ typedef struct admin_session
void admin_users_init();
const char* admin_enable_linux_account(const char *uname);
const char *admin_enable_linux_admin_account(const char *uname);
const char* admin_enable_linux_account(const char *uname, enum account_type type);
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);
const char *admin_add_inet_admin_user(const char *uname, const char* password);
const char* admin_add_inet_user(const char *uname, const char *password, enum account_type type);
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

@ -65,8 +65,9 @@ MXS_BEGIN_DECLS
#define MXS_JSON_PTR_PARAM_SSL_CERT_VERIFY_DEPTH MXS_JSON_PTR_PARAMETERS "/ssl_cert_verify_depth"
/** Non-parameter JSON pointers */
#define MXS_JSON_PTR_MODULE "/data/attributes/module"
#define MXS_JSON_PTR_MODULE "/data/attributes/module"
#define MXS_JSON_PTR_PASSWORD "/data/attributes/password"
#define MXS_JSON_PTR_ACCOUNT "/data/attributes/account"
/**
* Common configuration parameters names
@ -75,6 +76,7 @@ MXS_BEGIN_DECLS
* For example CN_PASSWORD resolves to the static string "password". This means
* that the sizeof(CN_<name>) returns the actual size of that string.
*/
extern const char CN_ACCOUNT[];
extern const char CN_ADDRESS[];
extern const char CN_ARG_MAX[];
extern const char CN_ARG_MIN[];

View File

@ -179,4 +179,22 @@ void users_diagnostic(DCB* dcb, USERS* users);
*/
json_t* users_diagnostic_json(USERS* users);
/**
* Convert account_type to a string
*
* @param type Enum value
*
* @return String representation of @c type
*/
const char* account_type_to_str(enum account_type type);
/**
* Convert JSON value to account_type value
*
* @param json JSON value to convert
*
* @return Enum value of @c json
*/
enum account_type json_to_account_type(json_t* json);
MXS_END_DECLS

View File

@ -122,7 +122,7 @@ static const char* admin_remove_user(USERS *users, const char* fname, const char
return ADMIN_SUCCESS;
}
static json_t* admin_user_json_data(const char* host, const char* user, enum user_type user_type)
static json_t* admin_user_json_data(const char* host, const char* user, enum user_type user_type, enum account_type account)
{
ss_dassert(user_type != USER_TYPE_ALL);
const char* type = user_type == USER_TYPE_INET ? CN_INET : CN_UNIX;
@ -131,6 +131,10 @@ static json_t* admin_user_json_data(const char* host, const char* user, enum use
json_object_set_new(entry, CN_ID, json_string(user));
json_object_set_new(entry, CN_TYPE, json_string(type));
json_t* param = json_object();
json_object_set_new(param, CN_ACCOUNT, json_string(account_type_to_str(account)));
json_object_set_new(entry, CN_ATTRIBUTES, param);
std::string self = MXS_JSON_API_USERS;
self += type;
json_object_set_new(entry, CN_RELATIONSHIPS, mxs_json_self_link(host, self.c_str(), user));
@ -146,7 +150,9 @@ static void user_types_to_json(USERS* users, json_t* arr, const char* host, enum
json_array_foreach(json, index, value)
{
json_array_append_new(arr, admin_user_json_data(host, json_string_value(value), type));
const char* user = json_string_value(json_object_get(value, CN_NAME));
enum account_type account = json_to_account_type(json_object_get(value, CN_ACCOUNT));
json_array_append_new(arr, admin_user_json_data(host, user, type, account));
}
json_decref(json);
@ -170,11 +176,12 @@ static std::string path_from_type(enum user_type type)
json_t* admin_user_to_json(const char* host, const char* user, enum user_type type)
{
account_type account = admin_is_admin_user(user) ? ACCOUNT_ADMIN : ACCOUNT_BASIC;
std::string path = path_from_type(type);
path += "/";
path += user;
return mxs_json_resource(host, path.c_str(), admin_user_json_data(host, user, type));
return mxs_json_resource(host, path.c_str(), admin_user_json_data(host, user, type, account));
}
json_t* admin_all_users_to_json(const char* host, enum user_type type)
@ -333,21 +340,9 @@ static USERS *load_inet_users()
*
* @return NULL on success or an error string on failure.
*/
const char *admin_enable_linux_account(const char *uname)
const char *admin_enable_linux_account(const char *uname, enum account_type type)
{
return admin_add_user(&linux_users, LINUX_USERS_FILE_NAME, uname, NULL, ACCOUNT_BASIC);
}
/**
* Enable Linux account
*
* @param uname Name of Linux user
*
* @return NULL on success or an error string on failure.
*/
const char *admin_enable_linux_admin_account(const char *uname)
{
return admin_add_user(&linux_users, LINUX_USERS_FILE_NAME, uname, NULL, ACCOUNT_ADMIN);
return admin_add_user(&linux_users, LINUX_USERS_FILE_NAME, uname, NULL, type);
}
/**
@ -403,13 +398,6 @@ void mxs_crypt(const char* password, const char* salt, char* output)
#endif
}
static const char* add_inet_user(const char *uname, const char* password, account_type type)
{
char cpassword[MXS_CRYPT_SIZE];
mxs_crypt(password, ADMIN_SALT, cpassword);
return admin_add_user(&inet_users, INET_USERS_FILE_NAME, uname, cpassword, type);
}
/**
* Add insecure remote (network) basic user.
*
@ -418,22 +406,11 @@ static const char* add_inet_user(const char *uname, const char* password, accoun
*
* @return NULL on success or an error string on failure.
*/
const char *admin_add_inet_user(const char *uname, const char* password)
const char *admin_add_inet_user(const char *uname, const char* password, enum account_type type)
{
return add_inet_user(uname, password, ACCOUNT_BASIC);
}
/**
* Add insecure remote (network) admin user.
*
* @param uname Name of the new user.
* @param password Password of the new user.
*
* @return NULL on success or an error string on failure.
*/
const char *admin_add_inet_admin_user(const char *uname, const char* password)
{
return add_inet_user(uname, password, ACCOUNT_ADMIN);
char cpassword[MXS_CRYPT_SIZE];
mxs_crypt(password, ADMIN_SALT, cpassword);
return admin_add_user(&inet_users, INET_USERS_FILE_NAME, uname, cpassword, type);
}
/**

View File

@ -54,6 +54,7 @@
using std::set;
using std::string;
const char CN_ACCOUNT[] = "account";
const char CN_ADDRESS[] = "address";
const char CN_ARG_MAX[] = "arg_max";
const char CN_ARG_MIN[] = "arg_min";

View File

@ -23,11 +23,12 @@
#include <algorithm>
#include <maxscale/atomic.h>
#include <maxscale/jansson.hh>
#include <maxscale/json_api.h>
#include <maxscale/paths.h>
#include <maxscale/platform.h>
#include <maxscale/spinlock.h>
#include <maxscale/jansson.hh>
#include <maxscale/json_api.h>
#include <maxscale/users.h>
#include "maxscale/config.h"
#include "maxscale/monitor.h"
@ -1723,6 +1724,7 @@ bool validate_user_json(json_t* json)
json_t* id = mxs_json_pointer(json, MXS_JSON_PTR_ID);
json_t* type = mxs_json_pointer(json, MXS_JSON_PTR_TYPE);
json_t* password = mxs_json_pointer(json, MXS_JSON_PTR_PASSWORD);
json_t* account = mxs_json_pointer(json, MXS_JSON_PTR_ACCOUNT);
if (!id)
{
@ -1740,6 +1742,18 @@ bool validate_user_json(json_t* json)
{
runtime_error("The '%s' field is not a string", MXS_JSON_PTR_TYPE);
}
else if (!account)
{
runtime_error("Request body does not define the '%s' field", MXS_JSON_PTR_ACCOUNT);
}
else if (!json_is_string(account))
{
runtime_error("The '%s' field is not a string", MXS_JSON_PTR_ACCOUNT);
}
else if (json_to_account_type(account) == ACCOUNT_UNKNOWN)
{
runtime_error("The '%s' field is not a valid account value", MXS_JSON_PTR_ACCOUNT);
}
else
{
if (strcmp(json_string_value(type), CN_INET) == 0)
@ -1780,14 +1794,15 @@ bool runtime_create_user_from_json(json_t* json)
const char* user = json_string_value(mxs_json_pointer(json, MXS_JSON_PTR_ID));
const char* password = json_string_value(mxs_json_pointer(json, MXS_JSON_PTR_PASSWORD));
string strtype = json_string_value(mxs_json_pointer(json, MXS_JSON_PTR_TYPE));
account_type type = json_to_account_type(mxs_json_pointer(json, MXS_JSON_PTR_ACCOUNT));
const char* err = NULL;
if (strtype == CN_INET && (err = admin_add_inet_user(user, password)) == ADMIN_SUCCESS)
if (strtype == CN_INET && (err = admin_add_inet_user(user, password, type)) == ADMIN_SUCCESS)
{
MXS_NOTICE("Create network user '%s'", user);
rval = true;
}
else if (strtype == CN_UNIX && (err = admin_enable_linux_account(user)) == ADMIN_SUCCESS)
else if (strtype == CN_UNIX && (err = admin_enable_linux_account(user, type)) == ADMIN_SUCCESS)
{
MXS_NOTICE("Enabled account '%s'", user);
rval = true;

View File

@ -11,20 +11,7 @@
* Public License.
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 20-08-2014 Mark Riddoch Initial implementation
* 23-05-2016 Massimiliano Pinto admin_add_user and admin_remove_user
* no longer accept password parameter
*
* @endverbatim
*/
// To ensure that ss_info_assert asserts also when builing in non-debug mode.
// To ensure that ss_info_assert asserts also when building in non-debug mode.
#if !defined(SS_DEBUG)
#define SS_DEBUG
#endif
@ -38,6 +25,7 @@
#include <maxscale/adminusers.h>
#include <maxscale/alloc.h>
#include <maxscale/utils.h>
#include <maxscale/users.h>
/**
* test1 default user
@ -76,13 +64,13 @@ test2()
{
const char *err;
if ((err = admin_enable_linux_account("user0")) != NULL)
if ((err = admin_enable_linux_account("user0", ACCOUNT_ADMIN)) != NULL)
{
fprintf(stderr, "admin_add_user: test 2.1 (add user) failed, %s.\n", err);
return 1;
}
if (admin_enable_linux_account("user0") == NULL)
if (admin_enable_linux_account("user0", ACCOUNT_ADMIN) == NULL)
{
fprintf(stderr, "admin_add_user: test 2.2 (add user) failed, duplicate.\n");
@ -98,7 +86,7 @@ test2()
}
/* Add the user back, for test5. */
if ((err = admin_enable_linux_account("user0")) != NULL)
if ((err = admin_enable_linux_account("user0", ACCOUNT_ADMIN)) != NULL)
{
fprintf(stderr, "admin_add_user: test 2.4 (add user) failed, %s.\n", err);
@ -122,7 +110,7 @@ test3()
{
const char *err;
if ((err = admin_enable_linux_account("user1")) != NULL)
if ((err = admin_enable_linux_account("user1", ACCOUNT_ADMIN)) != NULL)
{
fprintf(stderr, "admin_add_user: test 3.1 (add user) failed, %s.\n", err);
@ -180,7 +168,7 @@ test4()
for (i = 1; i < n_users; i++)
{
sprintf(user, "user%d", i);
if ((err = admin_enable_linux_account(user)) != NULL)
if ((err = admin_enable_linux_account(user, ACCOUNT_ADMIN)) != NULL)
{
fprintf(stderr, "admin_add_user: test 4.1 (add user) failed, %s.\n", err);
@ -224,7 +212,7 @@ test5()
{
const char *err;
if ((err = admin_enable_linux_account("user")) != NULL)
if ((err = admin_enable_linux_account("user", ACCOUNT_ADMIN)) != NULL)
{
fprintf(stderr, "admin_add_user: test 5.1 (add user) failed, %s.\n", err);
@ -259,11 +247,7 @@ main(int argc, char **argv)
unlink(buf);
// admin_verify() should be removed, since the maxadmin authentication has
// been changed completely. However, telnetd still uses that admin_verify()
// so I'll leave the function there for now. But telnetd should be dropped,
// since it cannot use the same user file as maxadmin does.
// result += test1();
result += test1();
result += test2();
result += test3();
result += test4();

View File

@ -30,41 +30,6 @@ namespace
static const char STR_BASIC[] = "basic";
static const char STR_ADMIN[] = "admin";
static const char* account_type_to_str(account_type type)
{
switch (type)
{
case ACCOUNT_BASIC:
return STR_BASIC;
case ACCOUNT_ADMIN:
return STR_ADMIN;
default:
MXS_ERROR("Unknown enum account_type value: %d", (int)type);
ss_dassert(!true);
return "unknown";
}
}
static account_type json_to_account_type(json_t* json)
{
std::string str = json_string_value(json);
if (str == STR_BASIC)
{
return ACCOUNT_BASIC;
}
else if (str == STR_ADMIN)
{
return ACCOUNT_ADMIN;
}
MXS_ERROR("Unknown account type string: %s", str.c_str());
ss_dassert(!true);
return ACCOUNT_UNKNOWN;
}
struct UserInfo
{
UserInfo():
@ -180,7 +145,10 @@ public:
for (UserMap::const_iterator it = m_data.begin(); it != m_data.end(); it++)
{
json_array_append_new(rval, json_string(it->first.c_str()));
json_t* obj = json_object();
json_object_set_new(obj, CN_NAME, json_string(it->first.c_str()));
json_object_set_new(obj, CN_ACCOUNT, json_string(account_type_to_str(it->second.permissions)));
json_array_append_new(rval, obj);
}
return rval;
@ -217,7 +185,7 @@ public:
{
json_t* obj = json_object();
json_object_set_new(obj, CN_NAME, json_string(it->first.c_str()));
json_object_set_new(obj, CN_TYPE, json_string(account_type_to_str(it->second.permissions)));
json_object_set_new(obj, CN_ACCOUNT, json_string(account_type_to_str(it->second.permissions)));
json_object_set_new(obj, CN_PASSWORD, json_string(it->second.password.c_str()));
json_array_append_new(arr, obj);
}
@ -248,7 +216,7 @@ private:
json_array_foreach(json, i, value)
{
json_t* name = json_object_get(value, CN_NAME);
json_t* type = json_object_get(value, CN_TYPE);
json_t* type = json_object_get(value, CN_ACCOUNT);
json_t* password = json_object_get(value, CN_PASSWORD);
if (name && json_is_string(name) &&
@ -381,3 +349,34 @@ int users_default_loadusers(SERV_LISTENER *port)
port->users = users_alloc();
return MXS_AUTH_LOADUSERS_OK;
}
const char* account_type_to_str(enum account_type type)
{
switch (type)
{
case ACCOUNT_BASIC:
return STR_BASIC;
case ACCOUNT_ADMIN:
return STR_ADMIN;
default:
return "unknown";
}
}
enum account_type json_to_account_type(json_t* json)
{
std::string str = json_string_value(json);
if (str == STR_BASIC)
{
return ACCOUNT_BASIC;
}
else if (str == STR_ADMIN)
{
return ACCOUNT_ADMIN;
}
return ACCOUNT_UNKNOWN;
}

View File

@ -2268,10 +2268,7 @@ static void do_inet_add_user(DCB *dcb, char *user, char *password, enum account_
return;
}
const char* (*f)(const char*, const char*) = type == ACCOUNT_ADMIN ?
admin_add_inet_admin_user : admin_add_inet_user;
if ((err = f(user, password)) == NULL)
if ((err = admin_add_inet_user(user, password, type)) == NULL)
{
dcb_printf(dcb, "Account %s for remote (network) usage has been successfully added.\n", user);
}
@ -2596,10 +2593,7 @@ static void do_enable_account(DCB *dcb, char *user, enum account_type type)
return;
}
const char* (*f)(const char*) = type == ACCOUNT_ADMIN ?
admin_enable_linux_admin_account : admin_enable_linux_account;
if ((err = f(user)) == NULL)
if ((err = admin_enable_linux_account(user, type)) == NULL)
{
dcb_printf(dcb, "The Linux user %s has successfully been enabled.\n", user);
}