blob: 2ea65a7eebf1d9dd5cd18dd8a39a1312eb2b1c67 [file] [log] [blame]
Ed Tanousba9f9a62017-10-11 16:40:35 -07001#pragma once
2
3#include <base64.hpp>
4#include <nlohmann/json.hpp>
5#include <pam_authenticate.hpp>
6#include <webassets.hpp>
7#include <random>
8#include <crow/app.h>
9#include <crow/http_request.h>
10#include <crow/http_response.h>
11#include <boost/container/flat_map.hpp>
12#include <boost/uuid/uuid.hpp>
13#include <boost/uuid/uuid_generators.hpp>
14#include <boost/uuid/uuid_io.hpp>
15
16namespace crow {
17
18namespace PersistentData {
19struct UserSession {
20 std::string unique_id;
21 std::string session_token;
22 std::string username;
23 std::string csrf_token;
24};
25
26void to_json(nlohmann::json& j, const UserSession& p) {
27 j = nlohmann::json{{"unique_id", p.unique_id},
28 {"session_token", p.session_token},
29 {"username", p.username},
30 {"csrf_token", p.csrf_token}};
31}
32
33void from_json(const nlohmann::json& j, UserSession& p) {
34 try {
35 p.unique_id = j.at("unique_id").get<std::string>();
36 p.session_token = j.at("session_token").get<std::string>();
37 p.username = j.at("username").get<std::string>();
38 p.csrf_token = j.at("csrf_token").get<std::string>();
39 } catch (std::out_of_range) {
40 // do nothing. Session API incompatibility, leave sessions empty
41 }
42}
43
44class Middleware {
45 using SessionStore = boost::container::flat_map<std::string, UserSession>;
46 // todo(ed) should read this from a fixed location somewhere, not CWD
47 static constexpr const char* filename = "bmcweb_persistent_data.json";
48 int json_revision = 1;
49
50 public:
51 struct context {
52 SessionStore* auth_tokens;
53 };
54
55 Middleware() { read_data(); }
56
57 void before_handle(crow::request& req, response& res, context& ctx) {
58 ctx.auth_tokens = &auth_tokens;
59 }
60
61 void after_handle(request& req, response& res, context& ctx) {}
62
63 // TODO(ed) this should really use protobuf, or some other serialization
64 // library, but adding another dependency is somewhat outside the scope of
65 // this application for the moment
66 void read_data() {
67 std::ifstream persistent_file(filename);
68 int file_revision = 0;
69 if (persistent_file.is_open()) {
70 // call with exceptions disabled
71 auto data = nlohmann::json::parse(persistent_file, nullptr, false);
72 if (!data.is_discarded()) {
73 file_revision = data["revision"].get<int>();
74 auth_tokens = data["sessions"].get<SessionStore>();
75 system_uuid = data["system_uuid"].get<std::string>();
76 }
77 }
78 bool need_write = false;
79
80 if (system_uuid.empty()) {
81 system_uuid = boost::uuids::to_string(boost::uuids::random_generator()());
82 need_write = true;
83 }
84 if (file_revision < json_revision) {
85 need_write = true;
86 }
87
88 if (need_write) {
89 write_data();
90 }
91 }
92
93 void write_data() {
94 std::ofstream persistent_file(filename);
95 nlohmann::json data;
96 data["sessions"] = auth_tokens;
97 data["system_uuid"] = system_uuid;
98 data["revision"] = json_revision;
99 persistent_file << data;
100 }
101
102 UserSession generate_user_session(const std::string& username) {
103 static constexpr std::array<char, 62> alphanum = {
104 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
105 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
106 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
107 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
108 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
109
110 // entropy: 30 characters, 62 possibilies. log2(62^30) = 178 bits of
111 // entropy. OWASP recommends at least 60
112 // https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Session_ID_Entropy
113 std::string session_token;
114 session_token.resize(20, '0');
115 std::uniform_int_distribution<int> dist(0, alphanum.size() - 1);
116 for (int i = 0; i < session_token.size(); ++i) {
117 session_token[i] = alphanum[dist(rd)];
118 }
119 // Only need csrf tokens for cookie based auth, token doesn't matter
120 std::string csrf_token;
121 csrf_token.resize(20, '0');
122 for (int i = 0; i < csrf_token.size(); ++i) {
123 csrf_token[i] = alphanum[dist(rd)];
124 }
125
126 std::string unique_id;
127 unique_id.resize(10, '0');
128 for (int i = 0; i < unique_id.size(); ++i) {
129 unique_id[i] = alphanum[dist(rd)];
130 }
131 UserSession session{unique_id, session_token, username, csrf_token};
132 auth_tokens.emplace(session_token, session);
133 write_data();
134 return session;
135 }
136
137 SessionStore auth_tokens;
138 std::string system_uuid;
139 std::random_device rd;
140};
141
142} // namespaec PersistentData
143} // namespace crow