// SPDX-FileCopyrightText: 2022 Enrique M.G. // // SPDX-License-Identifier: LGPL-2.1-or-later #include "libllxgvagate.hpp" #include "filedb.hpp" #include "http.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace lliurex; using namespace edupals; using namespace edupals::variant; using namespace std; namespace stdfs=std::experimental::filesystem; Gate::Gate() : Gate(nullptr) { } Gate::Gate(function cb) : log_cb(cb), server("http://127.0.0.1:5000"), auth_mode(Gate::Default) { //log(LOG_DEBUG,"Gate with effective uid:"+std::to_string(geteuid())); //load_config(); userdb = FileDB(LLX_GVA_GATE_USER_DB_PATH,LLX_GVA_GATE_USER_DB_MAGIC); tokendb = FileDB(LLX_GVA_GATE_TOKEN_DB_PATH,LLX_GVA_GATE_TOKEN_DB_MAGIC); shadowdb = FileDB(LLX_GVA_GATE_SHADOW_DB_PATH,LLX_GVA_GATE_SHADOW_DB_MAGIC); } Gate::~Gate() { //log(LOG_DEBUG,"Gate destructor\n"); } bool Gate::exists_db() { bool status = userdb.exists() and tokendb.exists() and shadowdb.exists(); return status; } bool Gate::open(bool noroot) { //TODO: think a strategy bool user = false; bool token = false; bool shadow = false; if (!userdb.exists()) { log(LOG_ERR,"User database does not exists\n"); } else { if (!userdb.is_open()) { user = userdb.open(noroot); } } if (!tokendb.exists()) { log(LOG_ERR,"Token database does not exists\n"); } else { if (!tokendb.is_open()) { token = tokendb.open(); } } if (!shadowdb.exists()) { log(LOG_ERR,"Shadow database does not exists\n"); } else { if (!shadowdb.is_open()) { shadow = shadowdb.open(noroot); } } if (noroot) { return user; } else { return user and token and shadow; } } void Gate::create_db() { //TODO: handle exceptions log(LOG_DEBUG,"Creating databases...\n"); // checking db dir first const stdfs::path dbdir {LLX_GVA_GATE_DB_PATH}; stdfs::create_directories(dbdir); // user db if (!userdb.exists()) { log(LOG_DEBUG,"Creating user database\n"); userdb.create(DBFormat::Bson,S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR); userdb.open(); userdb.lock_write(); Variant user_data = Variant::create_struct(); user_data["users"] = Variant::create_array(0); userdb.write(user_data); userdb.unlock(); } // token db if (!tokendb.exists()) { log(LOG_DEBUG,"Creating token database\n"); tokendb.create(DBFormat::Bson,S_IRUSR | S_IRGRP | S_IWUSR); tokendb.open(); tokendb.lock_write(); Variant token_data = Variant::create_struct(); token_data["machine_token"] = ""; tokendb.write(token_data); tokendb.unlock(); } // shadow db if (!shadowdb.exists()) { log(LOG_DEBUG,"Creating shadow database\n"); shadowdb.create(DBFormat::Bson,S_IRUSR | S_IRGRP | S_IWUSR); shadowdb.open(); shadowdb.lock_write(); Variant shadow_data = Variant::create_struct(); shadow_data["passwords"] = Variant::create_array(0); shadowdb.write(shadow_data); shadowdb.unlock(); } } string Gate::machine_token() { AutoLock lock(LockMode::Read,&tokendb); Variant data = tokendb.read(); if (!validate(data,Validator::TokenDatabase)) { log(LOG_ERR,"Bad token database\n"); throw exception::GateError("Bad token database\n",0); } return data["machine_token"].get_string(); } void Gate::update_db(Variant data) { AutoLock user_lock(LockMode::Write,&userdb); AutoLock token_lock(LockMode::Write,&tokendb); //std::this_thread::sleep_for(std::chrono::milliseconds(4000)); Variant token_data = Variant::create_struct(); token_data["machine_token"] = data["machine_token"]; tokendb.write(token_data); Variant user_data = userdb.read(); if (!validate(user_data,Validator::UserDatabase)) { log(LOG_ERR,"Bad user database\n"); throw exception::GateError("Bad user database\n",0); } string login = data["user"]["login"].get_string(); int32_t uid = data["user"]["uid"].get_int32(); Variant tmp = Variant::create_array(0); for (size_t n=0;n dollar; for (size_t n=0;n 2) { return key.substr(dollar[1]+1,dollar[2]-dollar[1]-1); } return ""; } int Gate::lookup_password(string user,string password) { AutoLock shadow_lock(LockMode::Read,&shadowdb); int status = Gate::UserNotFound; Variant database = shadowdb.read(); //Validate here for (size_t n=0;n cb) { this->log_cb = cb; } int Gate::authenticate(string user,string password,int mode) { if (mode == Gate::Default) { if (auth_mode == Gate::Default) { mode = Gate::All; } else { mode = auth_mode; } } int status = Gate::None; // remote authentication if (mode == Gate::Remote or mode == Gate::All) { http::Client client(this->server); http::Response response; try { log(LOG_INFO,"login post to:" + this->server + "\n"); response = client.post("api/v1/login",{ {"user",user},{"passwd",password}}); } catch(std::exception& e) { log(LOG_WARNING,"Post error:" + string(e.what())+ "\n"); status = Gate::Error; } if (status == Gate::None) { log(LOG_INFO,"server response: "+std::to_string(response.status)+"\n"); switch (response.status) { case 200: { Variant data; try { data = response.parse(); if (validate(data,Validator::Authenticate)) { update_db(data); update_shadow_db(user,password); status = Gate::Allowed; } else { log(LOG_ERR,"Bad Authenticate response\n"); status = Gate::Error; } } catch(std::exception& e) { log(LOG_ERR,"Failed to parse server response\n"); log(LOG_ERR,string(e.what()) + "\n"); // TODO: Should we report an Error after a 200 response? status = Gate::Error; } } break; case 401: status = Gate::Unauthorized; break; default: status = Gate::Unauthorized; } } } //local authentication through shadowdb if ((mode == Gate::All and status == Gate::Error) or mode == Gate::Local) { log(LOG_INFO,"Trying with local cache\n"); try { status = lookup_password(user,password); } catch(std::exception& e) { log(LOG_ERR,string(e.what()) + "\n"); status = Gate::Error; } } return status; } void Gate::log(int priority, string message) { if (log_cb) { log_cb(priority,message); } } bool Gate::validate(Variant data,Validator validator) { switch (validator) { case Validator::Groups: if (!data.is_array()) { return false; } for (size_t n=0;nserver = cfg["server"].get_string(); } if (cfg["auth_mode"].is_string()) { string mode = cfg["auth_mode"].get_string(); if (mode == "remote") { auth_mode = Gate::Remote; } if (mode == "local") { auth_mode = Gate::Local; } if (mode == "all") { auth_mode = Gate::All; } } } catch (std::exception& e) { log(LOG_WARNING,"Failed to parse config file\n"); } } }