94 lines
3.8 KiB
C++
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
|