* Add phone call authentication method * Remove dynamic mem allocation from error handler * Add more error handling code * Move error handling to different file * Remove Socket IO dependency * cleanup in websocket code * Add rapidjson as cmake dependency * Added Dockerfiles as primary build system for packages * Changed policy in CMakeList to work with lower version of CMake * Fix opensuse builds * Link filesystem library in gcc 8.5 or older
188 lines
6.0 KiB
C++
188 lines
6.0 KiB
C++
#pragma once
|
|
|
|
#include <rublon/bits.hpp>
|
|
#include <rublon/core_handler.hpp>
|
|
#include <rublon/curl.hpp>
|
|
#include <rublon/error.hpp>
|
|
#include <rublon/json.hpp>
|
|
#include <rublon/memory.hpp>
|
|
#include <rublon/utils.hpp>
|
|
|
|
#include <rapidjson/prettywriter.h>
|
|
#include <rapidjson/rapidjson.h>
|
|
|
|
namespace rublon {
|
|
|
|
std::string exec(const char * cmd) {
|
|
std::array< char, 128 > buffer;
|
|
std::string result;
|
|
std::unique_ptr< FILE, decltype(&pclose) > pipe(popen(cmd, "r"), pclose);
|
|
if(!pipe) {
|
|
return "";
|
|
}
|
|
while(fgets(buffer.data(), static_cast< int >(buffer.size()), pipe.get()) != nullptr) {
|
|
result += buffer.data();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::map< std::string, std::string > getSSHDConfig() {
|
|
std::istringstream iss(exec("sshd -T"));
|
|
std::map< std::string, std::string > result;
|
|
for(std::string line; std::getline(iss, line);) {
|
|
auto first_token = line.substr(0, line.find(' '));
|
|
result[first_token] = line.substr(line.find(' ') + 1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class Status {
|
|
std::string_view _statusDirPath = "/var/lib/rublon";
|
|
std::string_view _statusFilePath = "/var/lib/rublon/install.json";
|
|
|
|
RapidJSONPMRStackAlloc< 4 * 1024 > _alloc;
|
|
Document _data;
|
|
bool _statusUpdated;
|
|
|
|
std::string_view _appVersionKey = "/appVer";
|
|
std::string_view _appTypeKey = "/type";
|
|
std::string_view _paramSystemName = "/params/os";
|
|
std::string_view _paramSSHDBase = "/params/sshd_config/";
|
|
|
|
public:
|
|
Status() : _alloc{}, _data{&_alloc}, _statusUpdated{false} {
|
|
if(not details::exists(_statusFilePath.data())) {
|
|
log(LogLevel::Info, "application first run, creating status file at %s", _statusFilePath.data());
|
|
details::mkdir(_statusDirPath.data());
|
|
details::touch(_statusFilePath.data());
|
|
}
|
|
|
|
std::ifstream ifs{_statusFilePath.data()};
|
|
if(!ifs.is_open()) {
|
|
/// TODO handle no file error
|
|
}
|
|
|
|
rapidjson::IStreamWrapper isw{ifs};
|
|
_data.ParseStream(isw);
|
|
}
|
|
|
|
void updateAppVersion(std::string_view newVersion) {
|
|
RapidJSONPMRStackAlloc< 128 > stackAlloc;
|
|
auto jsonPointer = JSONPointer{_appVersionKey.data(), &stackAlloc};
|
|
auto version = jsonPointer.Get(_data);
|
|
if(not version || version->GetString() != newVersion) {
|
|
markUpdated();
|
|
jsonPointer.Set(_data, Value{newVersion.data(), _data.GetAllocator()});
|
|
}
|
|
}
|
|
|
|
void updateSystemVersion(std::string_view system) {
|
|
RapidJSONPMRStackAlloc< 128 > stackAlloc;
|
|
auto jsonPointer = JSONPointer{_paramSystemName.data(), &stackAlloc};
|
|
auto version = jsonPointer.Get(_data);
|
|
if(not version || version->GetString() != system) {
|
|
markUpdated();
|
|
jsonPointer.Set(_data, Value{system.data(), _data.GetAllocator()});
|
|
}
|
|
}
|
|
|
|
void updateSSHDConfig() {
|
|
using namespace std::string_view_literals;
|
|
constexpr auto keys = make_array("authenticationmethods"sv,
|
|
"challengeresponseauthentication"sv,
|
|
"kbdinteractiveauthentication"sv,
|
|
"logingracetime"sv,
|
|
"maxauthtries"sv,
|
|
"passwordauthentication"sv,
|
|
"permitemptypasswords"sv,
|
|
"permitrootlogin"sv,
|
|
"pubkeyauthentication"sv,
|
|
"usepam"sv);
|
|
|
|
auto config = getSSHDConfig();
|
|
|
|
for(const auto key : keys) {
|
|
auto [currentPair, inserted] = config.try_emplace(std::string{key.data()}, "N/A");
|
|
auto & [currentKey, currentValue] = *currentPair;
|
|
|
|
const auto jsonPath = std::string{_paramSSHDBase.data()} + key.data();
|
|
|
|
RapidJSONPMRStackAlloc< 512 > stackAlloc;
|
|
auto jsonPointer = JSONPointer{jsonPath.c_str(), &stackAlloc};
|
|
|
|
auto oldValue = jsonPointer.Get(_data);
|
|
|
|
if(not oldValue || oldValue->GetString() != currentValue) {
|
|
_statusUpdated = true;
|
|
jsonPointer.Set(_data, Value{currentValue.c_str(), _data.GetAllocator()});
|
|
}
|
|
}
|
|
}
|
|
|
|
void markUpdated() {
|
|
_statusUpdated = true;
|
|
}
|
|
|
|
bool updated() const {
|
|
return _statusUpdated;
|
|
}
|
|
|
|
void save() {
|
|
if(updated()) {
|
|
memory::Monotonic_8k_HeapResource tmpResource;
|
|
RapidJSONPMRAlloc alloc{&tmpResource};
|
|
FileWriter s{_statusFilePath};
|
|
rapidjson::PrettyWriter< FileWriter, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc};
|
|
writer.SetIndent(' ', 2);
|
|
_data.Accept(writer);
|
|
}
|
|
}
|
|
|
|
std::string print() {
|
|
std::string result;
|
|
memory::Monotonic_8k_HeapResource tmpResource;
|
|
RapidJSONPMRAlloc alloc{&tmpResource};
|
|
StringWriter s{result};
|
|
rapidjson::PrettyWriter< StringWriter< std::string >, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc};
|
|
writer.SetIndent(' ', 2);
|
|
_data.Accept(writer);
|
|
|
|
return result;
|
|
}
|
|
|
|
Document & data() {
|
|
return _data;
|
|
}
|
|
};
|
|
|
|
class CheckApplication {
|
|
tl::expected< bool, Error > persistStatus(Status & status) const {
|
|
status.data().RemoveMember("systemToken");
|
|
status.save();
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
tl::expected< int, Error > call(const CoreHandler_t & coreHandler, std::string_view systemToken) const {
|
|
memory::Monotonic_1k_HeapResource mr;
|
|
RapidJSONPMRStackAlloc< 2048 > alloc{};
|
|
constexpr std::string_view api = "/api/app/init";
|
|
Status status;
|
|
|
|
const auto persist = [&](const auto /*ok*/) { return this->persistStatus(status); };
|
|
|
|
status.updateAppVersion(RUBLON_VERSION_STRING);
|
|
status.updateSystemVersion(details::osName(&mr));
|
|
status.updateSSHDConfig();
|
|
|
|
if(status.updated()) {
|
|
auto & alloc = status.data().GetAllocator();
|
|
status.data().AddMember("systemToken", Value{systemToken.data(), alloc}, alloc);
|
|
return coreHandler.request(alloc, api, status.data()).and_then(persist);
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
} // namespace rublon
|