111 lines
4.4 KiB
C++
111 lines
4.4 KiB
C++
|
#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
|