Begin CLI
Parse args using getopt_long() in main(). Begin implementation of a CLI.
This commit is contained in:
parent
10f00aeb45
commit
cfee2bf1d8
@ -42,11 +42,12 @@ std::vector<char> Authenticator::pbkdf5(std::string password, const std::vector<
|
|||||||
unsigned char hash[32];
|
unsigned char hash[32];
|
||||||
const EVP_MD *sha256 = EVP_sha256();
|
const EVP_MD *sha256 = EVP_sha256();
|
||||||
const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data());
|
const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data());
|
||||||
int ret = PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash);
|
int ret =
|
||||||
|
PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash);
|
||||||
if(ret != 1)
|
if(ret != 1)
|
||||||
{
|
{
|
||||||
Logger::error() << "Authenticator: pbkdf5: Failed to create hash";
|
Logger::error() << "Authenticator: pbkdf5: Failed to create hash";
|
||||||
return { };
|
return {};
|
||||||
}
|
}
|
||||||
std::vector<char> result;
|
std::vector<char> result;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
#include "database/userdao.h"
|
#include "database/userdao.h"
|
||||||
|
|
||||||
|
#define AUTH_DEFAULT_SALT_SIZE 32
|
||||||
enum AuthenticationError
|
enum AuthenticationError
|
||||||
{
|
{
|
||||||
UserNotFound,
|
UserNotFound,
|
||||||
|
184
cli.cpp
Normal file
184
cli.cpp
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
#include "cli.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "random.h"
|
||||||
|
#include "authenticator.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
CLI::CLI(Config &config, Database &db)
|
||||||
|
{
|
||||||
|
this->db = &db;
|
||||||
|
this->conf = &config;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::user_add(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
std::string username = args.at(0);
|
||||||
|
std::string password = args.at(1);
|
||||||
|
|
||||||
|
auto userDao = db->createUserDao();
|
||||||
|
|
||||||
|
Permissions perms = this->conf->handlersConfig.anon_permissions;
|
||||||
|
int p = perms.getPermissions();
|
||||||
|
p |= PERM_CAN_CREATE | PERM_CAN_SEARCH | PERM_CAN_EDIT;
|
||||||
|
Permissions newPermissions = Permissions{p};
|
||||||
|
|
||||||
|
Random r;
|
||||||
|
User user;
|
||||||
|
user.enabled = true;
|
||||||
|
user.login = username;
|
||||||
|
user.salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE);
|
||||||
|
user.permissions = newPermissions;
|
||||||
|
|
||||||
|
Authenticator auth{*userDao};
|
||||||
|
std::vector<char> hashResult = auth.hash(password, user.salt);
|
||||||
|
if(hashResult.empty())
|
||||||
|
{
|
||||||
|
std::cout << "Error during hashing - Got empty hash";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
user.password = hashResult;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
userDao->save(user);
|
||||||
|
}
|
||||||
|
catch(std::runtime_error &e)
|
||||||
|
{
|
||||||
|
std::cout << "Exception: " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::user_change_pw(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
std::string username = args.at(0);
|
||||||
|
std::string password = args.at(1);
|
||||||
|
|
||||||
|
auto userDao = db->createUserDao();
|
||||||
|
|
||||||
|
auto user = userDao->find(username);
|
||||||
|
if(user)
|
||||||
|
{
|
||||||
|
Random r;
|
||||||
|
Authenticator auth{*userDao};
|
||||||
|
user->salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE);
|
||||||
|
user->password = auth.hash(password, user->sault);
|
||||||
|
if(user->password.empty())
|
||||||
|
{
|
||||||
|
std::cout << "Error during hashing - Got empty hash";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
userDao->save(*user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "User not found" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::user_rename(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::user_set_perms(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::user_list(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::user_show(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::cli_help(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
std::string command;
|
||||||
|
if(args.size() > 0)
|
||||||
|
command = args[0];
|
||||||
|
for(struct cmd &cmd : cmds)
|
||||||
|
{
|
||||||
|
if(command != "" && cmd.name != command)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << cmd.name << " - " << cmd.helptext << std::endl;
|
||||||
|
for(struct cmd &subCmd : cmd.subCommands)
|
||||||
|
{
|
||||||
|
std::cout << "\t" << subCmd.name << " " << subCmd.helptext << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::processCommand(const std::vector<CLI::cmd> &commands, std::string cmd, const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
auto c = std::find_if(commands.begin(), commands.end(), [&cmd](const struct CLI::cmd &a) { return a.name == cmd; });
|
||||||
|
if(c == commands.end())
|
||||||
|
{
|
||||||
|
std::cout << "No such command" << std::endl;
|
||||||
|
return cli_help({});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!c->subCommands.empty() && args.size() >= c->required_args)
|
||||||
|
{
|
||||||
|
std::string newcmd = args[0];
|
||||||
|
std::vector<std::string> newargs = args;
|
||||||
|
newargs.erase(newargs.begin());
|
||||||
|
processCommand(c->subCommands, newcmd, newargs);
|
||||||
|
}
|
||||||
|
if(args.size() < c->required_args)
|
||||||
|
{
|
||||||
|
std::cout << "not enough parameters passed" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return c->func(this, args);
|
||||||
|
}
|
||||||
|
catch(std::runtime_error &e)
|
||||||
|
{
|
||||||
|
std::cout << "Exception: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLI::processCommand(std::string cmd, const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
return processCommand(this->cmds, cmd, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLI::startInteractive()
|
||||||
|
{
|
||||||
|
std::cout << "qswiki cli" << std::endl;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
std::string input;
|
||||||
|
std::cout << "> ";
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
|
||||||
|
std::vector<std::string> splitted = utils::split(input, "\\s+");
|
||||||
|
if(splitted.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string cmd = splitted[0];
|
||||||
|
splitted.erase(splitted.begin());
|
||||||
|
if(!processCommand(cmd, splitted))
|
||||||
|
{
|
||||||
|
std::cout << "Command failed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
}
|
63
cli.h
Normal file
63
cli.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef CLI_H
|
||||||
|
#define CLI_H
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "database/database.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
class CLI
|
||||||
|
{
|
||||||
|
struct cmd
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::string helptext;
|
||||||
|
unsigned int required_args;
|
||||||
|
std::vector<cmd> subCommands;
|
||||||
|
std::function<bool(CLI *, const std::vector<std::string> &)> func;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Database *db;
|
||||||
|
Config *conf;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool cli_help(const std::vector<std::string> &args);
|
||||||
|
bool user_add(const std::vector<std::string> &args);
|
||||||
|
bool user_change_pw(const std::vector<std::string> &args);
|
||||||
|
bool user_rename(const std::vector<std::string> &args);
|
||||||
|
bool user_set_perms(const std::vector<std::string> &args);
|
||||||
|
bool user_list(const std::vector<std::string> &args);
|
||||||
|
bool user_show(const std::vector<std::string> &args);
|
||||||
|
|
||||||
|
std::vector<struct cmd> cmds{
|
||||||
|
{{"user",
|
||||||
|
"user operations on the database",
|
||||||
|
1,
|
||||||
|
{{{"add", "[user] [password] - creates a user", 2, {}, &CLI::user_add},
|
||||||
|
{"changepw", "[user] [password] - changes the password of user", 2, {}, &CLI::user_change_pw},
|
||||||
|
{"rename", "[user] [new name] - renames a user", 2, {}, &CLI::user_rename},
|
||||||
|
{"setperms", "[user] [perms] - sets the permissions of the user", 2, {}, &CLI::user_set_perms},
|
||||||
|
{"list", "- lists users", 2, {}, &CLI::user_list},
|
||||||
|
{"show", "[user] - show detailed information about user", 2, {}, &CLI::user_show}}},
|
||||||
|
&CLI::cli_help},
|
||||||
|
{"exit",
|
||||||
|
"exit cli",
|
||||||
|
0,
|
||||||
|
{},
|
||||||
|
[](CLI *, const std::vector<std::string> &args) -> bool
|
||||||
|
{
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
return true;
|
||||||
|
}},
|
||||||
|
{"help", "print this help", 0, {}, &CLI::cli_help}}};
|
||||||
|
|
||||||
|
bool processCommand(const std::vector<CLI::cmd> &commands, std::string cmd, const std::vector<std::string> &args);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CLI(Config &config, Database &d);
|
||||||
|
void startInteractive();
|
||||||
|
bool processCommand(std::string cmd, const std::vector<std::string> &args);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CLI_H
|
@ -15,19 +15,20 @@ Response HandlerUserSettings::handleRequest(const Request &r)
|
|||||||
|
|
||||||
if(newpassword != newpasswordconfirm)
|
if(newpassword != newpasswordconfirm)
|
||||||
{
|
{
|
||||||
//TODO: is not nice, users has to hit the back button...
|
// TODO: is not nice, users has to hit the back button...
|
||||||
return this->errorResponse("Passwords don't match", "The entered new passwords don't match");
|
return this->errorResponse("Passwords don't match", "The entered new passwords don't match");
|
||||||
}
|
}
|
||||||
auto userDao = this->database->createUserDao();
|
auto userDao = this->database->createUserDao();
|
||||||
Authenticator authenticator(*userDao);
|
Authenticator authenticator(*userDao);
|
||||||
|
|
||||||
std::variant<User, AuthenticationError> authresult = authenticator.authenticate(this->userSession->user.login, oldpassword);
|
std::variant<User, AuthenticationError> authresult =
|
||||||
|
authenticator.authenticate(this->userSession->user.login, oldpassword);
|
||||||
if(std::holds_alternative<AuthenticationError>(authresult))
|
if(std::holds_alternative<AuthenticationError>(authresult))
|
||||||
{
|
{
|
||||||
return this->errorResponse("Invalid current password", "The old password you entered is invalid");
|
return this->errorResponse("Invalid current password", "The old password you entered is invalid");
|
||||||
}
|
}
|
||||||
Random r;
|
Random r;
|
||||||
std::vector<char> salt = r.getRandom(23);
|
std::vector<char> salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE);
|
||||||
User user = std::get<User>(authresult);
|
User user = std::get<User>(authresult);
|
||||||
user.salt = salt;
|
user.salt = salt;
|
||||||
user.password = authenticator.hash(newpassword, user.salt);
|
user.password = authenticator.hash(newpassword, user.salt);
|
||||||
|
50
qswiki.cpp
50
qswiki.cpp
@ -25,6 +25,7 @@ SOFTWARE.
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <getopt.h>
|
||||||
#include "gateway/gatewayinterface.h"
|
#include "gateway/gatewayinterface.h"
|
||||||
#include "gateway/gatewayfactory.h"
|
#include "gateway/gatewayfactory.h"
|
||||||
#include "handlers/handlerfactory.h"
|
#include "handlers/handlerfactory.h"
|
||||||
@ -37,6 +38,7 @@ SOFTWARE.
|
|||||||
#include "requestworker.h"
|
#include "requestworker.h"
|
||||||
#include "cache/fscache.h"
|
#include "cache/fscache.h"
|
||||||
#include "sandbox/sandboxfactory.h"
|
#include "sandbox/sandboxfactory.h"
|
||||||
|
#include "cli.h"
|
||||||
void sigterm_handler(int arg)
|
void sigterm_handler(int arg)
|
||||||
{
|
{
|
||||||
// TODO: proper shutdown.
|
// TODO: proper shutdown.
|
||||||
@ -56,6 +58,10 @@ void setup_signal_handlers()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define OPT_PRINT_VERSION 23
|
||||||
|
|
||||||
|
static struct option long_options[] = {{"cli", no_argument, 0, 'c'}, {"version", no_argument, 0, OPT_PRINT_VERSION}};
|
||||||
|
|
||||||
std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
|
std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -63,13 +69,48 @@ std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
|
|||||||
|
|
||||||
return std::make_unique<FsCache>(path);
|
return std::make_unique<FsCache>(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_version_string()
|
||||||
|
{
|
||||||
|
return "master";
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
char *configfilepath = NULL;
|
||||||
|
int option;
|
||||||
|
int option_index;
|
||||||
|
bool cli_mode = false;
|
||||||
|
|
||||||
if(geteuid() == 0)
|
if(geteuid() == 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Do not run this as root!" << std::endl;
|
std::cerr << "Do not run this as root!" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while((option = getopt_long(argc, argv, "cv", long_options, &option_index)) != -1)
|
||||||
|
{
|
||||||
|
switch(option)
|
||||||
|
{
|
||||||
|
case 'c':
|
||||||
|
cli_mode = true;
|
||||||
|
break;
|
||||||
|
case OPT_PRINT_VERSION:
|
||||||
|
std::cout << get_version_string() << std::endl;
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(optind == argc)
|
||||||
|
{
|
||||||
|
std::cerr << "Missing config path" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
configfilepath = argv[optind++];
|
||||||
|
|
||||||
auto sandbox = createSandbox();
|
auto sandbox = createSandbox();
|
||||||
// TODO: do we want to keep it mandatory or configurable?
|
// TODO: do we want to keep it mandatory or configurable?
|
||||||
if(!sandbox->supported())
|
if(!sandbox->supported())
|
||||||
@ -82,7 +123,7 @@ int main(int argc, char **argv)
|
|||||||
std::cerr << "no path to config file provided" << std::endl;
|
std::cerr << "no path to config file provided" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
std::string configpath = std::filesystem::absolute(argv[1]).string();
|
std::string configpath = std::filesystem::absolute(configfilepath).string();
|
||||||
if(!sandbox->enableForInit())
|
if(!sandbox->enableForInit())
|
||||||
{
|
{
|
||||||
Logger::error() << "Sandboxing for init mode could not be activated.";
|
Logger::error() << "Sandboxing for init mode could not be activated.";
|
||||||
@ -114,6 +155,13 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
auto database = createDatabase(config);
|
auto database = createDatabase(config);
|
||||||
|
|
||||||
|
if(cli_mode)
|
||||||
|
{
|
||||||
|
CLI cli(config, *database);
|
||||||
|
cli.startInteractive();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: quite ugly, anon-handling must be rethought
|
// TODO: quite ugly, anon-handling must be rethought
|
||||||
auto userdao = database->createUserDao();
|
auto userdao = database->createUserDao();
|
||||||
std::optional<User> anon = userdao->find(config.handlersConfig.anon_username);
|
std::optional<User> anon = userdao->find(config.handlersConfig.anon_username);
|
||||||
|
Loading…
Reference in New Issue
Block a user