162 lines
5.2 KiB
C++
162 lines
5.2 KiB
C++
#include "dps/app.hpp"
|
|
#include "dps/config.hpp"
|
|
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <openssl/evp.h>
|
|
#include <zlib.h>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace {
|
|
|
|
void set_env(const char *name, const char *value) {
|
|
#ifdef _WIN32
|
|
_putenv_s(name, value);
|
|
#else
|
|
setenv(name, value, 1);
|
|
#endif
|
|
}
|
|
|
|
void clear_env(const char *name) {
|
|
#ifdef _WIN32
|
|
_putenv_s(name, "");
|
|
#else
|
|
unsetenv(name);
|
|
#endif
|
|
}
|
|
|
|
std::vector<unsigned char> decode_base64(const std::string &text) {
|
|
std::string normalized = text;
|
|
while (normalized.size() % 4 != 0) normalized.push_back('=');
|
|
|
|
std::vector<unsigned char> decoded((normalized.size() / 4) * 3);
|
|
const int size = EVP_DecodeBlock(decoded.data(),
|
|
reinterpret_cast<const unsigned char *>(normalized.data()),
|
|
static_cast<int>(normalized.size()));
|
|
assert(size >= 0);
|
|
|
|
std::size_t padding = 0;
|
|
if (!normalized.empty() && normalized.back() == '=') ++padding;
|
|
if (normalized.size() > 1 && normalized[normalized.size() - 2] == '=') ++padding;
|
|
decoded.resize(static_cast<std::size_t>(size) - padding);
|
|
return decoded;
|
|
}
|
|
|
|
std::string gunzip_bytes(const std::vector<unsigned char> &compressed) {
|
|
z_stream stream{};
|
|
stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(compressed.data()));
|
|
stream.avail_in = static_cast<uInt>(compressed.size());
|
|
|
|
if (inflateInit2(&stream, 15 + 16) != Z_OK) std::abort();
|
|
|
|
std::string output;
|
|
std::vector<char> buffer(4096);
|
|
int ret = Z_OK;
|
|
while (ret == Z_OK) {
|
|
stream.next_out = reinterpret_cast<Bytef *>(buffer.data());
|
|
stream.avail_out = static_cast<uInt>(buffer.size());
|
|
ret = inflate(&stream, Z_NO_FLUSH);
|
|
output.append(buffer.data(), buffer.size() - stream.avail_out);
|
|
}
|
|
inflateEnd(&stream);
|
|
assert(ret == Z_STREAM_END);
|
|
return output;
|
|
}
|
|
|
|
void test_crypto() {
|
|
dps::LegacyCrypto crypto("unit-test-secret");
|
|
|
|
const std::string password = "P@ssw0rd!";
|
|
const std::string hashed = crypto.hash_password(password);
|
|
assert(!hashed.empty());
|
|
assert(hashed.rfind("$2", 0) == 0);
|
|
assert(crypto.verify_password(password, hashed));
|
|
assert(!crypto.verify_password(password, password));
|
|
|
|
const std::string token = crypto.issue_jwt("tester", 60);
|
|
assert(!token.empty());
|
|
const auto subject = crypto.validate_jwt(token);
|
|
assert(subject.has_value());
|
|
assert(subject.value() == "tester");
|
|
assert(!crypto.validate_jwt(token + "broken").has_value());
|
|
|
|
const std::string gzip_payload = crypto.gzip_base64("{\"hello\":\"world\"}");
|
|
assert(!gzip_payload.empty());
|
|
const std::string inflated = gunzip_bytes(decode_base64(gzip_payload));
|
|
assert(inflated == "{\"hello\":\"world\"}");
|
|
|
|
const std::string rsa_tool = crypto.rsa_private_encrypt_tool("abc123");
|
|
const std::string rsa_script = crypto.rsa_private_encrypt_script("xyz789");
|
|
assert(!rsa_tool.empty());
|
|
assert(!rsa_script.empty());
|
|
assert(rsa_tool != "abc123");
|
|
assert(rsa_script != "xyz789");
|
|
}
|
|
|
|
void test_config() {
|
|
const fs::path path = fs::temp_directory_path() / "dps-config-smoke.conf";
|
|
{
|
|
std::ofstream out(path);
|
|
out << "server.host=0.0.0.0\n";
|
|
out << "server.port=8651\n";
|
|
out << "server.trust_proxy=true\n";
|
|
out << "server.proxy_header=x-real-ip\n";
|
|
out << "database.host=127.0.0.1\n";
|
|
out << "database.name=rindro\n";
|
|
out << "database.user=root\n";
|
|
out << "database.password=test\n";
|
|
out << "database.ssl_mode=verify_ca\n";
|
|
out << "database.ssl_ca=C:/certs/demo-ca.pem\n";
|
|
out << "database.plugin_dir=C:/mariadb/plugin\n";
|
|
}
|
|
|
|
set_env("DPS_SERVER_HOST", "10.0.0.8");
|
|
set_env("DPS_SERVER_PROXY_HEADER", "x-forwarded-for");
|
|
set_env("DPS_DB_HOST", "192.168.0.10");
|
|
set_env("DPS_DB_USER", "tester");
|
|
set_env("DPS_DB_PASSWORD", "secret");
|
|
set_env("DPS_DB_NAME", "demo");
|
|
set_env("DPS_DB_SSL_MODE", "disable");
|
|
set_env("DPS_DB_SSL_CA", "D:/certs/dev-ca.pem");
|
|
set_env("DPS_DB_PLUGIN_DIR", "D:/mariadb/plugin");
|
|
|
|
const dps::AppConfig config = dps::load_config(path.string());
|
|
assert(config.server.host == "10.0.0.8");
|
|
assert(config.server.port == 8651);
|
|
assert(config.server.trust_proxy);
|
|
assert(config.server.proxy_header == "x-forwarded-for");
|
|
assert(config.database.host == "192.168.0.10");
|
|
assert(config.database.user == "tester");
|
|
assert(config.database.password == "secret");
|
|
assert(config.database.name == "demo");
|
|
assert(config.database.ssl_mode == "disable");
|
|
assert(config.database.ssl_ca == "D:/certs/dev-ca.pem");
|
|
assert(config.database.plugin_dir == "D:/mariadb/plugin");
|
|
|
|
clear_env("DPS_SERVER_HOST");
|
|
clear_env("DPS_SERVER_PROXY_HEADER");
|
|
clear_env("DPS_DB_HOST");
|
|
clear_env("DPS_DB_USER");
|
|
clear_env("DPS_DB_PASSWORD");
|
|
clear_env("DPS_DB_NAME");
|
|
clear_env("DPS_DB_SSL_MODE");
|
|
clear_env("DPS_DB_SSL_CA");
|
|
clear_env("DPS_DB_PLUGIN_DIR");
|
|
std::remove(path.string().c_str());
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
test_crypto();
|
|
test_config();
|
|
return 0;
|
|
}
|