diff --git a/include/maxscale/adminusers.h.in b/include/maxscale/adminusers.h.in index 2a1b4cfba..d4cd60447 100644 --- a/include/maxscale/adminusers.h.in +++ b/include/maxscale/adminusers.h.in @@ -18,6 +18,7 @@ #include #include +#include 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); diff --git a/include/maxscale/config.h b/include/maxscale/config.h index 7715ca8d3..e96539d92 100644 --- a/include/maxscale/config.h +++ b/include/maxscale/config.h @@ -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_) 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[]; diff --git a/include/maxscale/users.h b/include/maxscale/users.h index 202d7adae..972134694 100644 --- a/include/maxscale/users.h +++ b/include/maxscale/users.h @@ -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 diff --git a/server/core/adminusers.cc b/server/core/adminusers.cc index 32458c475..575a6c139 100644 --- a/server/core/adminusers.cc +++ b/server/core/adminusers.cc @@ -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); } /** diff --git a/server/core/config.cc b/server/core/config.cc index 2a9410dec..9ac2f7d6e 100644 --- a/server/core/config.cc +++ b/server/core/config.cc @@ -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"; diff --git a/server/core/config_runtime.cc b/server/core/config_runtime.cc index 939e2f571..e7c57c2db 100644 --- a/server/core/config_runtime.cc +++ b/server/core/config_runtime.cc @@ -23,11 +23,12 @@ #include #include +#include +#include #include #include #include -#include -#include +#include #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; diff --git a/server/core/test/testadminusers.cc b/server/core/test/testadminusers.cc index e501c511e..886f243e6 100644 --- a/server/core/test/testadminusers.cc +++ b/server/core/test/testadminusers.cc @@ -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 #include #include +#include /** * 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(); diff --git a/server/core/users.cc b/server/core/users.cc index 20b174234..8025979a6 100644 --- a/server/core/users.cc +++ b/server/core/users.cc @@ -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; +} diff --git a/server/modules/routing/debugcli/debugcmd.c b/server/modules/routing/debugcli/debugcmd.c index 0238d2708..3f9b57737 100644 --- a/server/modules/routing/debugcli/debugcmd.c +++ b/server/modules/routing/debugcli/debugcmd.c @@ -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); }