111 lines
4.4 KiB
C++
Raw Normal View History

2022-08-28 17:37:09 +02:00
#pragma clang diagnostic push
#pragma ide diagnostic ignored "readability-make-member-function-const"
#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
#include <chrono>
#include <iomanip>
#include <botan/argon2.h>
#include <botan/uuid.h>
#include <botan/totp.h>
#if defined(BOTAN_HAS_SYSTEM_RNG)
#include <botan/system_rng.h>
#else
#include <botan/auto_rng.h>
#endif
#include <jwt-cpp/traits/kazuho-picojson/traits.h>
#include <jwt-cpp/jwt.h>
#include <curl/curl.h>
#include "controllers/controllers.h"
#include "db/db.h"
#include "dto/dto.h"
size_t payload_source(char* ptr, size_t size, size_t nmemb, void* userp) {
auto* ss = (std::stringstream*)userp;
return ss->readsome(ptr, (long)(size*nmemb));
}
namespace api {
#if defined(BOTAN_HAS_SYSTEM_RNG)
std::unique_ptr<Botan::RNG> auth::rng = std::make_unique<Botan::System_RNG>();
#else
std::unique_ptr<Botan::RNG> auth::rng = std::make_unique<Botan::AutoSeeded_RNG>();
#endif
bool auth::verify2fa(const db::User& user, uint32_t totp) {
size_t allowed_skew = db::User_getEnumTfaType(user) == db::tfaTypes::TOTP ? 0 : 10;
const auto& totp_secret = (const std::vector<uint8_t>&) user.getValueOfTfaSecret();
return Botan::TOTP(Botan::OctetString(totp_secret)).verify_totp(totp, std::chrono::system_clock::now(), allowed_skew);
}
void auth::send_mail(const db::User& user) {
std::stringstream ss;
std::time_t t = std::time(nullptr);
const auto& totp_secret = (const std::vector<uint8_t>&) user.getValueOfTfaSecret();
char totp[16];
std::snprintf(totp, 16, "%06d", Botan::TOTP(Botan::OctetString(totp_secret)).generate_totp(t));
ss.imbue(std::locale("en_US.utf8"));
ss << "Date: " << std::put_time(std::localtime(&t), "%a, %d %b %Y %T %z") << "\r\n";
ss << "To: " << user.getValueOfName() << "\r\n";
ss << "From: fileserver@mattv.de\r\n";
ss << "Message-ID: " << Botan::UUID(*rng).to_string() << "@mattv.de>\r\n";
ss << "Subject: Fileserver - EMail 2fa code\r\n";
ss << "Your code is: " << totp << "\r\n";
ss << "It is valid for 5 Minutes\r\n";
CURL* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_USERNAME, "no-reply@mattv.de");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "noreplyLONGPASS123");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.mattv.de:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
auto recp = curl_slist_append(nullptr, user.getValueOfName().c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recp);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, &payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &ss);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
curl_easy_perform(curl);
curl_slist_free_all(recp);
curl_easy_cleanup(curl);
}
std::string auth::get_token(const db::User& user) {
auto db = drogon::app().getDbClient();
db::MapperToken token_mapper(db);
const auto iat = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
const auto exp = iat + std::chrono::hours{24};
db::Token new_token;
new_token.setOwnerId(user.getValueOfId());
new_token.setExp(exp.count());
token_mapper.insert(new_token);
return jwt::create<jwt::traits::kazuho_picojson>()
.set_type("JWT")
.set_payload_claim("sub", picojson::value((int64_t)user.getValueOfId()))
.set_payload_claim("jti", picojson::value((int64_t)new_token.getValueOfId()))
.set_issued_at(std::chrono::system_clock::from_time_t(iat.count()))
.set_expires_at(std::chrono::system_clock::from_time_t(exp.count()))
.sign(jwt::algorithm::hs256{jwt_secret});
}
void auth::generate_root(db::User& user) {
db::MapperUser user_mapper(drogon::app().getDbClient());
auto node = fs::create_node("", user, false, std::nullopt, true);
user.setRootId(std::get<db::INode>(node).getValueOfId());
user_mapper.update(user);
}
void auth::revoke_all(const db::User& user) {
db::MapperToken token_mapper(drogon::app().getDbClient());
token_mapper.deleteBy(db::Criteria(db::Token::Cols::_owner_id, db::CompareOps::EQ, user.getValueOfId()));
}
}
#pragma clang diagnostic pop