MXS-1354: Add JSON serialization of users
The users can now be dumped and loaded as JSON objects. This allows easier parsing and handling of users while still retaining the possibility to manually edit the output. Added tests for dumping and loading the JSON form users. Also fixed a deadlock in Users::remove() where the same lock was acquired twice and a faulty test case where failed authentication was expected to work.
This commit is contained in:
parent
253d6d211f
commit
3aebe0f91e
@ -21,6 +21,7 @@
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/listener.h>
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/jansson.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
@ -28,6 +29,7 @@ MXS_BEGIN_DECLS
|
||||
/** User account types */
|
||||
enum account_type
|
||||
{
|
||||
ACCOUNT_UNKNOWN,
|
||||
ACCOUNT_BASIC, /**< Allows read-only access */
|
||||
ACCOUNT_ADMIN /**< Allows complete access */
|
||||
};
|
||||
@ -126,6 +128,26 @@ bool users_promote(USERS* users, const char* user);
|
||||
*/
|
||||
bool users_demote(USERS* users, const char* user);
|
||||
|
||||
/**
|
||||
* Dump users as JSON
|
||||
*
|
||||
* The resulting JSON can be loaded later to restore the users.
|
||||
*
|
||||
* @param users Users to dump
|
||||
*
|
||||
* @return JSON form of the users that can be used for serialization
|
||||
*/
|
||||
json_t* users_to_json(USERS *users);
|
||||
|
||||
/**
|
||||
* Load users from JSON
|
||||
*
|
||||
* @param json JSON to load
|
||||
*
|
||||
* @return The loaded users
|
||||
*/
|
||||
USERS* users_from_json(json_t* json);
|
||||
|
||||
/**
|
||||
* @brief Default user loading function
|
||||
*
|
||||
|
@ -56,7 +56,7 @@ static int test1()
|
||||
ss_info_dassert(rv, "Fetch valid user must not return NULL");
|
||||
rv = users_auth(users, "username", "newauth");
|
||||
mxs_log_flush_sync();
|
||||
ss_info_dassert(rv, "Fetch valid user must not return NULL");
|
||||
ss_info_dassert(rv == 0, "Fetch invalid user must return NULL");
|
||||
|
||||
ss_dfprintf(stderr, "\t..done\nAdd another user");
|
||||
rv = users_add(users, "username2", "authorisation2", ACCOUNT_ADMIN);
|
||||
@ -66,6 +66,15 @@ static int test1()
|
||||
rv = users_delete(users, "username");
|
||||
mxs_log_flush_sync();
|
||||
ss_info_dassert(rv, "Should delete just one user");
|
||||
|
||||
ss_dfprintf(stderr, "\t..done\nDump users table.");
|
||||
json_t* dump = users_to_json(users);
|
||||
ss_info_dassert(dump, "Users should be dumped");
|
||||
USERS* loaded_users = users_from_json(dump);
|
||||
ss_info_dassert(dump, "Users should be loaded");
|
||||
rv = users_auth(loaded_users, "username2", "authorisation2");
|
||||
ss_info_dassert(rv, "Loaded users should contain users");
|
||||
|
||||
ss_dfprintf(stderr, "\t..done\nFree user table.");
|
||||
users_free(users);
|
||||
mxs_log_flush_sync();
|
||||
|
@ -20,10 +20,48 @@
|
||||
#include <maxscale/users.h>
|
||||
#include <maxscale/authenticator.h>
|
||||
#include <maxscale/spinlock.hh>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/jansson.hh>
|
||||
|
||||
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():
|
||||
@ -68,10 +106,11 @@ public:
|
||||
{
|
||||
mxs::SpinLockGuard guard(m_lock);
|
||||
bool rval = false;
|
||||
UserMap::iterator it = m_data.find(user);
|
||||
|
||||
if (get(user))
|
||||
if (it != m_data.end())
|
||||
{
|
||||
m_data.erase(user);
|
||||
m_data.erase(it);
|
||||
rval = true;
|
||||
}
|
||||
|
||||
@ -161,9 +200,61 @@ public:
|
||||
return m_data.size() > 0;
|
||||
}
|
||||
|
||||
json_t* to_json() const
|
||||
{
|
||||
json_t* arr = json_array();
|
||||
mxs::SpinLockGuard guard(m_lock);
|
||||
|
||||
for (UserMap::const_iterator it = m_data.begin(); it != m_data.end(); it++)
|
||||
{
|
||||
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_PASSWORD, json_string(it->second.password.c_str()));
|
||||
json_array_append_new(arr, obj);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
static Users* from_json(json_t* json)
|
||||
{
|
||||
Users* u = reinterpret_cast<Users*>(users_alloc());
|
||||
u->load_json(json);
|
||||
return u;
|
||||
}
|
||||
|
||||
private:
|
||||
void load_json(json_t* json)
|
||||
{
|
||||
// This function is always called in a single-threaded context
|
||||
size_t i;
|
||||
json_t* value;
|
||||
|
||||
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* password = json_object_get(value, CN_PASSWORD);
|
||||
|
||||
if (name && json_is_string(name) &&
|
||||
type && json_is_string(type) &&
|
||||
password && json_is_string(password) &&
|
||||
json_to_account_type(type) != ACCOUNT_UNKNOWN)
|
||||
{
|
||||
add(json_string_value(name), json_string_value(password),
|
||||
json_to_account_type(type));
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Corrupt JSON value in users file: %s", mxs::json_dump(value).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mxs::SpinLock m_lock;
|
||||
UserMap m_data;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@ -193,6 +284,17 @@ bool users_delete(USERS *users, const char *user)
|
||||
return u->remove(user);
|
||||
}
|
||||
|
||||
json_t* users_to_json(USERS *users)
|
||||
{
|
||||
Users* u = reinterpret_cast<Users*>(users);
|
||||
return u->to_json();
|
||||
}
|
||||
|
||||
USERS* users_from_json(json_t* json)
|
||||
{
|
||||
return reinterpret_cast<USERS*>(Users::from_json(json));
|
||||
}
|
||||
|
||||
bool users_find(USERS* users, const char* user)
|
||||
{
|
||||
Users* u = reinterpret_cast<Users*>(users);
|
||||
|
Loading…
x
Reference in New Issue
Block a user