Files
retoor 3c4334cec9 feat: implement MoloApp plans 01-15 with integration
Full C++23 framework tree: runtime, observability, security,
data, app, realtime, supervisor, clients, CLI, example plus
enterprise tenancy, audit, operability, governance,
supplychain. Postgres via docker compose for tests.
2026-10-23 18:20:04 +02:00

94 lines
3.8 KiB
C++

#include "App.hpp"
#include "../fw/Cookies.hpp"
#include "../fw/Data.hpp"
#include "../fw/Router.hpp"
#include "../fw/Validate.hpp"
namespace molo::example::app {
void register_security_demo(fw::Router& router, Services& services) {
fw::Auth* auth = services.auth.get();
fw::SessionStore* sessions = services.sessions.get();
fw::UploadStore* uploads = services.uploads.get();
std::string secret = services.cookie_secret;
router.get("/auth/me", "auth-me", [auth](fw::Request req) {
std::string user, password;
if (!fw::parse_basic_auth(req.headers.get("Authorization"), user,
password))
return fw::JsonResponse("{\"error\":\"basic auth required\"}", 401);
if (!auth->check(user, password))
return fw::JsonResponse("{\"error\":\"bad credentials\"}", 403);
return fw::JsonResponse(
"{\"user\":\"" + fw::json_escape(user) + "\"}", 200);
});
router.post("/auth/login", "auth-login", [auth](fw::Request req) {
auto input = fw::parse_json_object_flat(req.body);
if (input.empty()) input = fw::parse_form(req.body);
if (!input.count("username") || !input.count("password"))
return fw::JsonResponse("{\"error\":\"username and password required\"}",
400);
if (!auth->check(input["username"], input["password"]))
return fw::JsonResponse("{\"error\":\"bad credentials\"}", 403);
return fw::JsonResponse(
"{\"user\":\"" + fw::json_escape(input["username"]) + "\"}", 200);
});
router.get("/sessions/counter", "session-counter",
[sessions, secret](fw::Request req) {
std::string raw = req.cookie_or("session", "");
std::string id;
std::map<std::string, std::string> data;
bool fresh = false;
if (raw.empty() || !fw::cookie_verify(raw, secret, id) ||
!sessions->get(id, data)) {
id = sessions->create();
fresh = true;
data.clear();
}
long count = 0;
if (data.count("count")) {
try {
count = std::stol(data["count"]);
} catch (...) {
count = 0;
}
}
++count;
sessions->put(id, "count", std::to_string(count));
fw::Response res(200,
"{\"count\":" + std::to_string(count) +
",\"fresh\":" +
(fresh ? "true" : "false") + "}",
"application/json");
res.headers.set(
"Set-Cookie",
fw::make_set_cookie("session", fw::cookie_sign(id, secret),
"/", true, false, "Lax", 3600));
return res;
});
router.post("/uploads/file", "upload", [uploads](fw::Request req) {
std::string filename = req.query_or("filename", "upload.txt");
std::string content_type =
req.headers.get("Content-Type");
if (content_type.empty()) content_type = "text/plain";
fw::UploadInfo info;
std::string error;
if (!uploads->store(filename, content_type, req.body, info, error)) {
if (error == "file too large")
return fw::JsonResponse("{\"error\":\"" + error + "\"}", 413);
if (error == "content type not allowed")
return fw::JsonResponse("{\"error\":\"" + error + "\"}", 415);
return fw::JsonResponse("{\"error\":\"" + error + "\"}", 400);
}
return fw::JsonResponse("{\"file\":\"" + fw::json_escape(info.filename) +
"\",\"size\":" + std::to_string(info.size) +
"}",
201);
});
}
} // namespace molo::example::app