162 lines
5.5 KiB
C++
162 lines
5.5 KiB
C++
// Copyright (c) Vitaliy Filippov, 2019+
|
|
// License: VNPL-1.1 (see README.md for details)
|
|
|
|
#include "cli.h"
|
|
#include "cluster_client.h"
|
|
#include "str_util.h"
|
|
|
|
// Create/update/delete a user
|
|
struct cli_modify_user_t
|
|
{
|
|
cli_tool_t *parent;
|
|
|
|
std::string user_name;
|
|
std::string user_type;
|
|
json11::Json groups;
|
|
bool del = false;
|
|
|
|
int state = 0;
|
|
cli_result_t result;
|
|
etcd_kv_t kv;
|
|
json11::Json::object new_cfg;
|
|
|
|
bool is_done()
|
|
{
|
|
return state == 100;
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
if (state == 1)
|
|
goto resume_1;
|
|
else if (state == 2)
|
|
goto resume_2;
|
|
if (user_type != "client" && user_type != "admin")
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Unknown user type: "+user_type };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (groups.is_string())
|
|
{
|
|
groups = groups == "" ? std::vector<std::string>() : explode(",", groups.string_value(), true);
|
|
}
|
|
else if (groups.is_array())
|
|
{
|
|
for (auto & gr: groups.array_items())
|
|
{
|
|
if (!gr.is_string())
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Group names must be strings" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (!groups.is_null())
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Group names must be strings" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (user_name == "")
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "User name must not be empty" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
parent->etcd_txn(json11::Json::object {
|
|
{ "success", json11::Json::array { json11::Json::object {
|
|
{ "request_range", json11::Json::object {
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
|
} },
|
|
} } }
|
|
});
|
|
state = 1;
|
|
resume_1:
|
|
if (parent->waiting > 0)
|
|
return;
|
|
if (parent->etcd_err.err)
|
|
{
|
|
result = parent->etcd_err;
|
|
state = 100;
|
|
return;
|
|
}
|
|
kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][0]["response_range"]["kvs"][0]);
|
|
while (true)
|
|
{
|
|
new_cfg = kv.value.object_items();
|
|
if (!kv.mod_revision && del)
|
|
{
|
|
result = (cli_result_t){ .err = ENOENT, .text = "User "+user_name+" does not exist" };
|
|
state = 100;
|
|
break;
|
|
}
|
|
if (!groups.is_null())
|
|
new_cfg["groups"] = groups;
|
|
if (user_type != "")
|
|
new_cfg["type"] = user_type;
|
|
if (!new_cfg["type"].is_string())
|
|
new_cfg["type"] = "client";
|
|
parent->etcd_txn(json11::Json::object {
|
|
{ "compare", json11::Json::array { json11::Json::object {
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
|
{ "target", kv.mod_revision ? "MOD" : "VERSION" },
|
|
{ kv.mod_revision ? "mod_revision" : "version", kv.mod_revision },
|
|
} } },
|
|
{ "success", json11::Json::array {
|
|
del ? json11::Json::object { { "request_delete_range", json11::Json::object {
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
|
} } } : json11::Json::object { { "request_put", json11::Json::object {
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
|
{ "value", base64_encode(json11::Json(new_cfg).dump()) }
|
|
} } },
|
|
} },
|
|
{ "failure", json11::Json::array { json11::Json::object {
|
|
{ "request_range", json11::Json::object {
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
|
} },
|
|
} } },
|
|
});
|
|
state = 2;
|
|
resume_2:
|
|
if (parent->waiting > 0)
|
|
return;
|
|
if (parent->etcd_err.err)
|
|
{
|
|
result = parent->etcd_err;
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (parent->etcd_result["succeeded"].bool_value())
|
|
break;
|
|
kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][0]["response_range"]["kvs"][0]);
|
|
}
|
|
state = 100;
|
|
result.text = del ? "User "+user_name+" removed" : "User "+user_name+" modified";
|
|
new_cfg["name"] = user_name;
|
|
result.data = del ? json11::Json::object{ { "ok", true } } : new_cfg;
|
|
}
|
|
};
|
|
|
|
std::function<bool(cli_result_t &)> cli_tool_t::start_modify_user(json11::Json cfg)
|
|
{
|
|
auto creator = new cli_modify_user_t();
|
|
creator->parent = this;
|
|
creator->user_name = cfg["name"].string_value();
|
|
creator->user_type = cfg["type"].string_value();
|
|
creator->groups = cfg["groups"];
|
|
creator->del = cfg["remove"].bool_value();
|
|
return [creator](cli_result_t & result)
|
|
{
|
|
creator->loop();
|
|
if (creator->is_done())
|
|
{
|
|
result = creator->result;
|
|
delete creator;
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
}
|