Line data Source code
1 : #ifndef SOCIAL_NETWORK_MICROSERVICES_USERHANDLER_H
2 : #define SOCIAL_NETWORK_MICROSERVICES_USERHANDLER_H
3 :
4 : #include <bson/bson.h>
5 : #include <libmemcached/memcached.h>
6 : #include <libmemcached/util.h>
7 : #include <mongoc.h>
8 :
9 : #include <iomanip>
10 : #include <iostream>
11 : #include <jwt/jwt.hpp>
12 : #include <nlohmann/json.hpp>
13 : #include <random>
14 : #include <string>
15 :
16 : #include "../../gen-cpp/SocialGraphService.h"
17 : #include "../../gen-cpp/UserService.h"
18 : #include "../../gen-cpp/social_network_types.h"
19 : #include "../../third_party/PicoSHA2/picosha2.h"
20 : #include "../ClientPool.h"
21 : #include "../ThriftClient.h"
22 : #include "../logger.h"
23 : #include "../tracing.h"
24 :
25 : // Custom Epoch (January 1, 2018 Midnight GMT = 2018-01-01T00:00:00Z)
26 : #define CUSTOM_EPOCH 1514764800000
27 : #define MONGODB_TIMEOUT_MS 100
28 :
29 : namespace social_network {
30 :
31 : using std::chrono::duration_cast;
32 : using std::chrono::milliseconds;
33 : using std::chrono::seconds;
34 : using std::chrono::system_clock;
35 : using namespace jwt::params;
36 :
37 : static int64_t current_timestamp = -1;
38 : static int counter = 0;
39 :
40 0 : static int GetCounter(int64_t timestamp) {
41 0 : if (current_timestamp > timestamp) {
42 0 : LOG(fatal) << "Timestamps are not incremental.";
43 0 : exit(EXIT_FAILURE);
44 : }
45 0 : if (current_timestamp == timestamp) {
46 0 : return counter++;
47 : } else {
48 0 : current_timestamp = timestamp;
49 0 : counter = 0;
50 0 : return counter++;
51 : }
52 : }
53 :
54 1161 : std::string GenRandomString(const int len) {
55 : static const std::string alphanum =
56 : "0123456789"
57 : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
58 1161 : "abcdefghijklmnopqrstuvwxyz";
59 2323 : std::random_device rd;
60 1162 : std::mt19937 gen(rd());
61 : std::uniform_int_distribution<int> dist(
62 1162 : 0, static_cast<int>(alphanum.length() - 1));
63 1162 : std::string s;
64 38336 : for (int i = 0; i < len; ++i) {
65 37174 : s += alphanum[dist(gen)];
66 : }
67 2324 : return s;
68 : }
69 :
70 : class UserHandler : public UserServiceIf {
71 : public:
72 : UserHandler(std::mutex *, const std::string &, const std::string &,
73 : memcached_pool_st *, mongoc_client_pool_t *,
74 : ClientPool<ThriftClient<SocialGraphServiceClient>> *);
75 0 : ~UserHandler() override = default;
76 : void RegisterUser(int64_t, const std::string &, const std::string &,
77 : const std::string &, const std::string &,
78 : const std::map<std::string, std::string> &) override;
79 : void RegisterUserWithId(int64_t, const std::string &, const std::string &,
80 : const std::string &, const std::string &, int64_t,
81 : const std::map<std::string, std::string> &) override;
82 :
83 : void ComposeCreatorWithUserId(
84 : Creator &, int64_t, int64_t, const std::string &,
85 : const std::map<std::string, std::string> &) override;
86 : void ComposeCreatorWithUsername(
87 : Creator &, int64_t, const std::string &,
88 : const std::map<std::string, std::string> &) override;
89 : void Login(std::string &, int64_t, const std::string &, const std::string &,
90 : const std::map<std::string, std::string> &) override;
91 : int64_t GetUserId(int64_t, const std::string &,
92 : const std::map<std::string, std::string> &) override;
93 :
94 : private:
95 : std::string _machine_id;
96 : std::string _secret;
97 : std::mutex *_thread_lock;
98 : memcached_pool_st *_memcached_client_pool;
99 : mongoc_client_pool_t *_mongodb_client_pool;
100 : ClientPool<ThriftClient<SocialGraphServiceClient>> *_social_graph_client_pool;
101 : };
102 :
103 1 : UserHandler::UserHandler(std::mutex *thread_lock, const std::string &machine_id,
104 : const std::string &secret,
105 : memcached_pool_st *memcached_client_pool,
106 : mongoc_client_pool_t *mongodb_client_pool,
107 : ClientPool<ThriftClient<SocialGraphServiceClient>>
108 1 : *social_graph_client_pool) {
109 1 : _thread_lock = thread_lock;
110 1 : _machine_id = machine_id;
111 1 : _memcached_client_pool = memcached_client_pool;
112 1 : _mongodb_client_pool = mongodb_client_pool;
113 1 : _secret = secret;
114 1 : _social_graph_client_pool = social_graph_client_pool;
115 1 : }
116 :
117 1162 : void UserHandler::RegisterUserWithId(
118 : const int64_t req_id, const std::string &first_name,
119 : const std::string &last_name, const std::string &username,
120 : const std::string &password, const int64_t user_id,
121 : const std::map<std::string, std::string> &carrier) {
122 : // 记录收到注册请求
123 2324 : LOG(info) << "Received RegisterUserWithId request [req_id=" << req_id << ", username=" << username << ", user_id=" << user_id << "]";
124 : // Initialize a span
125 2324 : TextMapReader reader(carrier);
126 2324 : std::map<std::string, std::string> writer_text_map;
127 2324 : TextMapWriter writer(writer_text_map);
128 2324 : auto parent_span = opentracing::Tracer::Global()->Extract(reader);
129 2324 : auto span = opentracing::Tracer::Global()->StartSpan(
130 : "register_user_withid_server",
131 4648 : {opentracing::ChildOf(parent_span->get())});
132 1162 : opentracing::Tracer::Global()->Inject(span->context(), writer);
133 :
134 : // Store user info into mongodb
135 : mongoc_client_t *mongodb_client =
136 1162 : mongoc_client_pool_pop(_mongodb_client_pool);
137 1162 : if (!mongodb_client) {
138 0 : ServiceException se;
139 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
140 0 : se.message = "Failed to pop a client from MongoDB pool";
141 0 : throw se;
142 : }
143 : auto collection =
144 1162 : mongoc_client_get_collection(mongodb_client, "user", "user");
145 :
146 : // Check if the username has existed in the database
147 1162 : bson_t *query = bson_new();
148 1162 : BSON_APPEND_UTF8(query, "username", username.c_str());
149 : mongoc_cursor_t *cursor =
150 1162 : mongoc_collection_find_with_opts(collection, query, nullptr, nullptr);
151 : const bson_t *doc;
152 : bson_error_t error;
153 1162 : bool found = mongoc_cursor_next(cursor, &doc);
154 1162 : if (mongoc_cursor_error(cursor, &error)) {
155 0 : LOG(warning) << error.message;
156 0 : bson_destroy(query);
157 0 : mongoc_cursor_destroy(cursor);
158 0 : mongoc_collection_destroy(collection);
159 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
160 0 : ServiceException se;
161 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
162 0 : se.message = error.message;
163 0 : throw se;
164 1162 : } else if (found) {
165 0 : LOG(warning) << "User " << username << " already existed.";
166 0 : ServiceException se;
167 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
168 0 : se.message = "User " + username + " already existed";
169 0 : bson_destroy(query);
170 0 : mongoc_cursor_destroy(cursor);
171 0 : mongoc_collection_destroy(collection);
172 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
173 0 : throw se;
174 : } else {
175 1162 : bson_t *new_doc = bson_new();
176 1162 : BSON_APPEND_INT64(new_doc, "user_id", user_id);
177 1162 : BSON_APPEND_UTF8(new_doc, "first_name", first_name.c_str());
178 1162 : BSON_APPEND_UTF8(new_doc, "last_name", last_name.c_str());
179 1162 : BSON_APPEND_UTF8(new_doc, "username", username.c_str());
180 2324 : std::string salt = GenRandomString(32);
181 1162 : BSON_APPEND_UTF8(new_doc, "salt", salt.c_str());
182 2324 : std::string password_hashed = picosha2::hash256_hex_string(password + salt);
183 1162 : BSON_APPEND_UTF8(new_doc, "password", password_hashed.c_str());
184 :
185 : bson_error_t error;
186 2324 : auto user_insert_span = opentracing::Tracer::Global()->StartSpan(
187 4648 : "user_mongo_insert_cilent", {opentracing::ChildOf(&span->context())});
188 1162 : if (!mongoc_collection_insert_one(collection, new_doc, nullptr, nullptr,
189 : &error)) {
190 600 : LOG(error) << "Failed to insert user " << username
191 200 : << " to MongoDB: " << error.message;
192 400 : ServiceException se;
193 200 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
194 : se.message =
195 200 : "Failed to insert user " + username + " to MongoDB: " + error.message;
196 200 : bson_destroy(query);
197 200 : mongoc_cursor_destroy(cursor);
198 200 : mongoc_collection_destroy(collection);
199 200 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
200 200 : throw se;
201 : } else {
202 962 : LOG(debug) << "User: " << username << " registered";
203 : }
204 962 : user_insert_span->Finish();
205 962 : bson_destroy(new_doc);
206 : }
207 962 : bson_destroy(query);
208 962 : mongoc_cursor_destroy(cursor);
209 962 : mongoc_collection_destroy(collection);
210 962 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
211 :
212 962 : if (!found) {
213 962 : auto social_graph_client_wrapper = _social_graph_client_pool->Pop();
214 962 : if (!social_graph_client_wrapper) {
215 0 : ServiceException se;
216 0 : se.errorCode = ErrorCode::SE_THRIFT_CONN_ERROR;
217 0 : se.message = "Failed to connect to social-graph-service";
218 0 : throw se;
219 : }
220 962 : auto social_graph_client = social_graph_client_wrapper->GetClient();
221 : try {
222 962 : social_graph_client->InsertUser(req_id, user_id, writer_text_map);
223 0 : } catch (...) {
224 0 : _social_graph_client_pool->Remove(social_graph_client_wrapper);
225 0 : LOG(error) << "Failed to insert user to social-graph-client";
226 0 : throw;
227 : }
228 961 : _social_graph_client_pool->Keepalive(social_graph_client_wrapper);
229 : }
230 :
231 961 : span->Finish();
232 : // 注册成功后
233 1924 : LOG(info) << "RegisterUserWithId completed [req_id=" << req_id << ", username=" << username << ", user_id=" << user_id << "]";
234 962 : }
235 :
236 0 : void UserHandler::RegisterUser(
237 : const int64_t req_id, const std::string &first_name,
238 : const std::string &last_name, const std::string &username,
239 : const std::string &password,
240 : const std::map<std::string, std::string> &carrier) {
241 : // Initialize a span
242 0 : TextMapReader reader(carrier);
243 0 : std::map<std::string, std::string> writer_text_map;
244 0 : TextMapWriter writer(writer_text_map);
245 0 : auto parent_span = opentracing::Tracer::Global()->Extract(reader);
246 0 : auto span = opentracing::Tracer::Global()->StartSpan(
247 0 : "register_user_server", {opentracing::ChildOf(parent_span->get())});
248 0 : opentracing::Tracer::Global()->Inject(span->context(), writer);
249 :
250 : // Compose user_id
251 0 : _thread_lock->lock();
252 : int64_t timestamp =
253 0 : duration_cast<milliseconds>(system_clock::now().time_since_epoch())
254 0 : .count() -
255 0 : CUSTOM_EPOCH;
256 0 : int idx = GetCounter(timestamp);
257 0 : _thread_lock->unlock();
258 :
259 0 : std::stringstream sstream;
260 0 : sstream << std::hex << timestamp;
261 0 : std::string timestamp_hex(sstream.str());
262 0 : if (timestamp_hex.size() > 10) {
263 0 : timestamp_hex.erase(0, timestamp_hex.size() - 10);
264 0 : } else if (timestamp_hex.size() < 10) {
265 0 : timestamp_hex = std::string(10 - timestamp_hex.size(), '0') + timestamp_hex;
266 : }
267 : // Empty the sstream buffer.
268 0 : sstream.clear();
269 0 : sstream.str(std::string());
270 :
271 0 : sstream << std::hex << idx;
272 0 : std::string counter_hex(sstream.str());
273 :
274 0 : if (counter_hex.size() > 3) {
275 0 : counter_hex.erase(0, counter_hex.size() - 3);
276 0 : } else if (counter_hex.size() < 3) {
277 0 : counter_hex = std::string(3 - counter_hex.size(), '0') + counter_hex;
278 : }
279 0 : std::string user_id_str = _machine_id + timestamp_hex + counter_hex;
280 0 : int64_t user_id = stoul(user_id_str, nullptr, 16) & 0x7FFFFFFFFFFFFFFF;
281 : ;
282 0 : LOG(debug) << "The user_id of the request " << req_id << " is " << user_id;
283 :
284 : // Store user info into mongodb
285 : mongoc_client_t *mongodb_client =
286 0 : mongoc_client_pool_pop(_mongodb_client_pool);
287 0 : if (!mongodb_client) {
288 0 : ServiceException se;
289 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
290 0 : se.message = "Failed to pop a client from MongoDB pool";
291 0 : throw se;
292 : }
293 : auto collection =
294 0 : mongoc_client_get_collection(mongodb_client, "user", "user");
295 :
296 : // Check if the username has existed in the database
297 0 : bson_t *query = bson_new();
298 0 : BSON_APPEND_UTF8(query, "username", username.c_str());
299 : mongoc_cursor_t *cursor =
300 0 : mongoc_collection_find_with_opts(collection, query, nullptr, nullptr);
301 : const bson_t *doc;
302 : bson_error_t error;
303 0 : bool found = mongoc_cursor_next(cursor, &doc);
304 0 : if (mongoc_cursor_error(cursor, &error)) {
305 0 : LOG(error) << error.message;
306 0 : bson_destroy(query);
307 0 : mongoc_cursor_destroy(cursor);
308 0 : mongoc_collection_destroy(collection);
309 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
310 0 : ServiceException se;
311 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
312 0 : se.message = error.message;
313 0 : throw se;
314 0 : } else if (found) {
315 0 : LOG(warning) << "User " << username << " already existed.";
316 0 : ServiceException se;
317 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
318 0 : se.message = "User " + username + " already existed";
319 0 : bson_destroy(query);
320 0 : mongoc_cursor_destroy(cursor);
321 0 : mongoc_collection_destroy(collection);
322 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
323 0 : throw se;
324 : } else {
325 0 : bson_t *new_doc = bson_new();
326 0 : BSON_APPEND_INT64(new_doc, "user_id", user_id);
327 0 : BSON_APPEND_UTF8(new_doc, "first_name", first_name.c_str());
328 0 : BSON_APPEND_UTF8(new_doc, "last_name", last_name.c_str());
329 0 : BSON_APPEND_UTF8(new_doc, "username", username.c_str());
330 0 : std::string salt = GenRandomString(32);
331 0 : BSON_APPEND_UTF8(new_doc, "salt", salt.c_str());
332 0 : std::string password_hashed = picosha2::hash256_hex_string(password + salt);
333 0 : BSON_APPEND_UTF8(new_doc, "password", password_hashed.c_str());
334 :
335 0 : auto user_insert_span = opentracing::Tracer::Global()->StartSpan(
336 0 : "user_mongo_insert_client", {opentracing::ChildOf(&span->context())});
337 0 : if (!mongoc_collection_insert_one(collection, new_doc, nullptr, nullptr,
338 : &error)) {
339 0 : LOG(error) << "Failed to insert user " << username
340 0 : << " to MongoDB: " << error.message;
341 0 : ServiceException se;
342 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
343 : se.message =
344 0 : "Failed to insert user " + username + " to MongoDB: " + error.message;
345 0 : bson_destroy(query);
346 0 : mongoc_cursor_destroy(cursor);
347 0 : mongoc_collection_destroy(collection);
348 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
349 0 : throw se;
350 : } else {
351 0 : LOG(debug) << "User: " << username << " registered";
352 : }
353 0 : user_insert_span->Finish();
354 0 : bson_destroy(new_doc);
355 : }
356 0 : bson_destroy(query);
357 0 : mongoc_cursor_destroy(cursor);
358 0 : mongoc_collection_destroy(collection);
359 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
360 :
361 0 : if (!found) {
362 0 : auto social_graph_client_wrapper = _social_graph_client_pool->Pop();
363 0 : if (!social_graph_client_wrapper) {
364 0 : ServiceException se;
365 0 : se.errorCode = ErrorCode::SE_THRIFT_CONN_ERROR;
366 0 : se.message = "Failed to connect to social-graph-service";
367 0 : throw se;
368 : }
369 0 : auto social_graph_client = social_graph_client_wrapper->GetClient();
370 : try {
371 0 : social_graph_client->InsertUser(req_id, user_id, writer_text_map);
372 0 : } catch (...) {
373 0 : _social_graph_client_pool->Remove(social_graph_client_wrapper);
374 0 : LOG(error) << "Failed to insert user to social-graph-service";
375 0 : throw;
376 : }
377 :
378 0 : _social_graph_client_pool->Keepalive(social_graph_client_wrapper);
379 : }
380 :
381 0 : span->Finish();
382 0 : }
383 :
384 0 : void UserHandler::ComposeCreatorWithUsername(
385 : Creator &_return, const int64_t req_id, const std::string &username,
386 : const std::map<std::string, std::string> &carrier) {
387 0 : LOG(info) << "Received ComposeCreatorWithUsername request [req_id=" << req_id << ", username=" << username << "]";
388 0 : TextMapReader reader(carrier);
389 0 : std::map<std::string, std::string> writer_text_map;
390 0 : TextMapWriter writer(writer_text_map);
391 0 : auto parent_span = opentracing::Tracer::Global()->Extract(reader);
392 0 : auto span = opentracing::Tracer::Global()->StartSpan(
393 0 : "compose_creator_server", {opentracing::ChildOf(parent_span->get())});
394 0 : opentracing::Tracer::Global()->Inject(span->context(), writer);
395 :
396 : size_t user_id_size;
397 : uint32_t memcached_flags;
398 :
399 : memcached_return_t memcached_rc;
400 : memcached_st *memcached_client =
401 0 : memcached_pool_pop(_memcached_client_pool, true, &memcached_rc);
402 : char *user_id_mmc;
403 0 : if (memcached_client) {
404 0 : auto id_get_span = opentracing::Tracer::Global()->StartSpan(
405 0 : "user_mmc_get_client", {opentracing::ChildOf(&span->context())});
406 : user_id_mmc =
407 0 : memcached_get(memcached_client, (username + ":user_id").c_str(),
408 0 : (username + ":user_id").length(), &user_id_size,
409 0 : &memcached_flags, &memcached_rc);
410 0 : id_get_span->Finish();
411 0 : if (!user_id_mmc && memcached_rc != MEMCACHED_NOTFOUND) {
412 0 : ServiceException se;
413 0 : se.errorCode = ErrorCode::SE_MEMCACHED_ERROR;
414 0 : se.message = memcached_strerror(memcached_client, memcached_rc);
415 0 : memcached_pool_push(_memcached_client_pool, memcached_client);
416 0 : throw se;
417 : }
418 0 : memcached_pool_push(_memcached_client_pool, memcached_client);
419 : } else {
420 0 : LOG(warning) << "Failed to pop a client from memcached pool";
421 : }
422 :
423 0 : int64_t user_id = -1;
424 0 : bool cached = false;
425 0 : if (user_id_mmc) {
426 0 : cached = true;
427 0 : LOG(debug) << "Found user_id of username :" << username << " in Memcached";
428 0 : user_id = std::stoul(user_id_mmc);
429 0 : free(user_id_mmc);
430 : }
431 :
432 : // If not cached in memcached
433 : else {
434 0 : LOG(debug) << "user_id not cached in Memcached";
435 : mongoc_client_t *mongodb_client =
436 0 : mongoc_client_pool_pop(_mongodb_client_pool);
437 0 : if (!mongodb_client) {
438 0 : ServiceException se;
439 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
440 0 : se.message = "Failed to pop a client from MongoDB pool";
441 0 : throw se;
442 : }
443 : auto collection =
444 0 : mongoc_client_get_collection(mongodb_client, "user", "user");
445 0 : if (!collection) {
446 0 : ServiceException se;
447 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
448 0 : se.message = "Failed to create collection user from DB user";
449 0 : throw se;
450 : }
451 0 : bson_t *query = bson_new();
452 0 : BSON_APPEND_UTF8(query, "username", username.c_str());
453 :
454 0 : auto find_span = opentracing::Tracer::Global()->StartSpan(
455 0 : "user_mongo_find_client", {opentracing::ChildOf(&span->context())});
456 : mongoc_cursor_t *cursor =
457 0 : mongoc_collection_find_with_opts(collection, query, nullptr, nullptr);
458 : const bson_t *doc;
459 0 : bool found = mongoc_cursor_next(cursor, &doc);
460 0 : find_span->Finish();
461 0 : if (!found) {
462 : bson_error_t error;
463 0 : if (mongoc_cursor_error(cursor, &error)) {
464 0 : LOG(error) << error.message;
465 0 : bson_destroy(query);
466 0 : mongoc_cursor_destroy(cursor);
467 0 : mongoc_collection_destroy(collection);
468 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
469 0 : ServiceException se;
470 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
471 0 : se.message = error.message;
472 0 : throw se;
473 : } else {
474 0 : LOG(warning) << "User: " << username << " doesn't exist in MongoDB";
475 0 : bson_destroy(query);
476 0 : mongoc_cursor_destroy(cursor);
477 0 : mongoc_collection_destroy(collection);
478 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
479 0 : ServiceException se;
480 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
481 0 : se.message = "User: " + username + " is not registered";
482 0 : throw se;
483 : }
484 : } else {
485 0 : LOG(debug) << "User: " << username << " found in MongoDB";
486 : bson_iter_t iter;
487 0 : if (bson_iter_init_find(&iter, doc, "user_id")) {
488 0 : user_id = bson_iter_value(&iter)->value.v_int64;
489 : } else {
490 0 : LOG(error) << "user_id attribute of user " << username
491 0 : << " was not found in the User object";
492 0 : bson_destroy(query);
493 0 : mongoc_cursor_destroy(cursor);
494 0 : mongoc_collection_destroy(collection);
495 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
496 0 : ServiceException se;
497 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
498 0 : se.message = "user_id attribute of user: " + username +
499 0 : " was not found in the User object";
500 0 : throw se;
501 : }
502 : }
503 0 : bson_destroy(query);
504 0 : mongoc_cursor_destroy(cursor);
505 0 : mongoc_collection_destroy(collection);
506 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
507 : }
508 :
509 0 : Creator creator;
510 0 : creator.username = username;
511 0 : creator.user_id = user_id;
512 :
513 0 : if (user_id != -1) {
514 0 : _return = creator;
515 : }
516 :
517 : memcached_client =
518 0 : memcached_pool_pop(_memcached_client_pool, true, &memcached_rc);
519 0 : if (memcached_client) {
520 0 : if (user_id != -1 && !cached) {
521 0 : auto id_set_span = opentracing::Tracer::Global()->StartSpan(
522 0 : "user_mmc_set_cilent", {opentracing::ChildOf(&span->context())});
523 0 : std::string user_id_str = std::to_string(user_id);
524 : memcached_rc =
525 0 : memcached_set(memcached_client, (username + ":user_id").c_str(),
526 0 : (username + ":user_id").length(), user_id_str.c_str(),
527 : user_id_str.length(), static_cast<time_t>(0),
528 0 : static_cast<uint32_t>(0));
529 0 : id_set_span->Finish();
530 0 : if (memcached_rc != MEMCACHED_SUCCESS) {
531 0 : LOG(warning) << "Failed to set the user_id of user " << username
532 0 : << " to Memcached: "
533 0 : << memcached_strerror(memcached_client, memcached_rc);
534 : }
535 : }
536 0 : memcached_pool_push(_memcached_client_pool, memcached_client);
537 : } else {
538 0 : LOG(warning) << "Failed to pop a client from memcached pool";
539 : }
540 0 : span->Finish();
541 0 : LOG(info) << "ComposeCreatorWithUsername completed [req_id=" << req_id << ", username=" << username << "]";
542 0 : }
543 :
544 200 : void UserHandler::ComposeCreatorWithUserId(
545 : Creator &_return, int64_t req_id, int64_t user_id,
546 : const std::string &username,
547 : const std::map<std::string, std::string> &carrier) {
548 400 : LOG(info) << "Received ComposeCreatorWithUserId request [req_id=" << req_id << ", username=" << username << ", user_id=" << user_id << "]";
549 400 : TextMapReader reader(carrier);
550 400 : std::map<std::string, std::string> writer_text_map;
551 400 : TextMapWriter writer(writer_text_map);
552 400 : auto parent_span = opentracing::Tracer::Global()->Extract(reader);
553 400 : auto span = opentracing::Tracer::Global()->StartSpan(
554 800 : "compose_creator_server", {opentracing::ChildOf(parent_span->get())});
555 200 : opentracing::Tracer::Global()->Inject(span->context(), writer);
556 :
557 400 : Creator creator;
558 200 : creator.username = username;
559 200 : creator.user_id = user_id;
560 :
561 200 : _return = creator;
562 :
563 200 : span->Finish();
564 400 : LOG(info) << "ComposeCreatorWithUserId completed [req_id=" << req_id << ", username=" << username << ", user_id=" << user_id << "]";
565 200 : }
566 :
567 0 : void UserHandler::Login(std::string &_return, int64_t req_id,
568 : const std::string &username,
569 : const std::string &password,
570 : const std::map<std::string, std::string> &carrier) {
571 0 : LOG(info) << "Received Login request [req_id=" << req_id << ", username=" << username << "]";
572 0 : TextMapReader reader(carrier);
573 0 : std::map<std::string, std::string> writer_text_map;
574 0 : TextMapWriter writer(writer_text_map);
575 0 : auto parent_span = opentracing::Tracer::Global()->Extract(reader);
576 0 : auto span = opentracing::Tracer::Global()->StartSpan(
577 0 : "login_server", {opentracing::ChildOf(parent_span->get())});
578 0 : opentracing::Tracer::Global()->Inject(span->context(), writer);
579 :
580 : size_t login_size;
581 : uint32_t memcached_flags;
582 :
583 : memcached_return_t memcached_rc;
584 : memcached_st *memcached_client =
585 0 : memcached_pool_pop(_memcached_client_pool, true, &memcached_rc);
586 : char *login_mmc;
587 0 : if (!memcached_client) {
588 0 : LOG(warning) << "Failed to pop a client from memcached pool";
589 : } else {
590 0 : auto get_login_span = opentracing::Tracer::Global()->StartSpan(
591 0 : "user_mmc_get_client", {opentracing::ChildOf(&span->context())});
592 0 : login_mmc = memcached_get(memcached_client, (username + ":login").c_str(),
593 0 : (username + ":login").length(), &login_size,
594 0 : &memcached_flags, &memcached_rc);
595 0 : get_login_span->Finish();
596 0 : if (!login_mmc && memcached_rc != MEMCACHED_NOTFOUND) {
597 0 : LOG(warning) << "Memcached error: "
598 0 : << memcached_strerror(memcached_client, memcached_rc);
599 : }
600 0 : memcached_pool_push(_memcached_client_pool, memcached_client);
601 : }
602 :
603 0 : std::string password_stored;
604 0 : std::string salt_stored;
605 0 : int64_t user_id_stored = -1;
606 0 : bool cached = false;
607 0 : json login_json;
608 :
609 0 : if (login_mmc) {
610 : // If not cached in memcached
611 0 : LOG(debug) << "Found username: " << username << " in Memcached";
612 0 : login_json = json::parse(std::string(login_mmc, login_size));
613 0 : password_stored = login_json["password"];
614 0 : salt_stored = login_json["salt"];
615 0 : user_id_stored = login_json["user_id"];
616 0 : cached = true;
617 0 : free(login_mmc);
618 : }
619 :
620 : else {
621 : // If not cached in memcached
622 0 : LOG(debug) << "Username: " << username << " NOT cached in Memcached";
623 :
624 : mongoc_client_t *mongodb_client =
625 0 : mongoc_client_pool_pop(_mongodb_client_pool);
626 0 : if (!mongodb_client) {
627 0 : ServiceException se;
628 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
629 0 : se.message = "Failed to pop a client from MongoDB pool";
630 0 : throw se;
631 : }
632 : auto collection =
633 0 : mongoc_client_get_collection(mongodb_client, "user", "user");
634 0 : if (!collection) {
635 0 : ServiceException se;
636 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
637 0 : se.message = "Failed to create collection user from DB user";
638 0 : throw se;
639 : }
640 0 : bson_t *query = bson_new();
641 0 : BSON_APPEND_UTF8(query, "username", username.c_str());
642 :
643 0 : auto find_span = opentracing::Tracer::Global()->StartSpan(
644 0 : "user_mongo_find_client", {opentracing::ChildOf(&span->context())});
645 : mongoc_cursor_t *cursor =
646 0 : mongoc_collection_find_with_opts(collection, query, nullptr, nullptr);
647 : const bson_t *doc;
648 0 : bool found = mongoc_cursor_next(cursor, &doc);
649 0 : find_span->Finish();
650 :
651 : bson_error_t error;
652 0 : if (mongoc_cursor_error(cursor, &error)) {
653 0 : LOG(error) << error.message;
654 0 : bson_destroy(query);
655 0 : mongoc_cursor_destroy(cursor);
656 0 : mongoc_collection_destroy(collection);
657 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
658 0 : ServiceException se;
659 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
660 0 : se.message = error.message;
661 0 : throw se;
662 : }
663 :
664 0 : if (!found) {
665 0 : LOG(warning) << "User: " << username << " doesn't exist in MongoDB";
666 0 : bson_destroy(query);
667 0 : mongoc_cursor_destroy(cursor);
668 0 : mongoc_collection_destroy(collection);
669 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
670 0 : ServiceException se;
671 0 : se.errorCode = ErrorCode::SE_UNAUTHORIZED;
672 0 : se.message = "User: " + username + " is not registered";
673 0 : throw se;
674 : } else {
675 0 : LOG(debug) << "Username: " << username << " found in MongoDB";
676 : bson_iter_t iter_password;
677 : bson_iter_t iter_salt;
678 : bson_iter_t iter_user_id;
679 0 : if (bson_iter_init_find(&iter_password, doc, "password") &&
680 0 : bson_iter_init_find(&iter_salt, doc, "salt") &&
681 0 : bson_iter_init_find(&iter_user_id, doc, "user_id")) {
682 0 : password_stored = bson_iter_value(&iter_password)->value.v_utf8.str;
683 0 : salt_stored = bson_iter_value(&iter_salt)->value.v_utf8.str;
684 0 : user_id_stored = bson_iter_value(&iter_user_id)->value.v_int64;
685 0 : login_json["password"] = password_stored;
686 0 : login_json["salt"] = salt_stored;
687 0 : login_json["user_id"] = user_id_stored;
688 : } else {
689 0 : LOG(error) << "user: " << username << " entry is NOT complete";
690 0 : bson_destroy(query);
691 0 : mongoc_cursor_destroy(cursor);
692 0 : mongoc_collection_destroy(collection);
693 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
694 0 : ServiceException se;
695 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
696 0 : se.message = "user: " + username + " entry is NOT complete";
697 0 : throw se;
698 : }
699 0 : bson_destroy(query);
700 0 : mongoc_cursor_destroy(cursor);
701 0 : mongoc_collection_destroy(collection);
702 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
703 : }
704 : }
705 :
706 0 : if (user_id_stored != -1 && !salt_stored.empty() &&
707 0 : !password_stored.empty()) {
708 : bool auth =
709 0 : picosha2::hash256_hex_string(password + salt_stored) == password_stored;
710 0 : if (auth) {
711 0 : auto user_id_str = std::to_string(user_id_stored);
712 : auto timestamp_str = std::to_string(
713 0 : duration_cast<seconds>(system_clock::now().time_since_epoch())
714 0 : .count());
715 0 : jwt::jwt_object obj{algorithm("HS256"), secret(_secret),
716 0 : payload({{"user_id", user_id_str},
717 : {"username", username},
718 : {"timestamp", timestamp_str},
719 0 : {"ttl", "3600"}})};
720 0 : _return = obj.signature();
721 : } else {
722 0 : ServiceException se;
723 0 : se.errorCode = ErrorCode::SE_UNAUTHORIZED;
724 0 : se.message = "Incorrect username or password";
725 0 : throw se;
726 : }
727 : } else {
728 0 : ServiceException se;
729 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
730 0 : se.message = "Username: " + username + " incomplete login information.";
731 0 : throw se;
732 : }
733 :
734 0 : if (!cached) {
735 : memcached_client =
736 0 : memcached_pool_pop(_memcached_client_pool, true, &memcached_rc);
737 0 : if (!memcached_client) {
738 0 : LOG(warning) << "Failed to pop a client from memcached pool";
739 : } else {
740 0 : auto set_login_span = opentracing::Tracer::Global()->StartSpan(
741 0 : "user_mmc_set_client", {opentracing::ChildOf(&span->context())});
742 0 : std::string login_str = login_json.dump();
743 : memcached_rc =
744 0 : memcached_set(memcached_client, (username + ":login").c_str(),
745 0 : (username + ":login").length(), login_str.c_str(),
746 0 : login_str.length(), 0, 0);
747 0 : set_login_span->Finish();
748 0 : if (memcached_rc != MEMCACHED_SUCCESS) {
749 0 : LOG(warning) << "Failed to set the login info of user " << username
750 0 : << " to Memcached: "
751 0 : << memcached_strerror(memcached_client, memcached_rc);
752 : }
753 0 : memcached_pool_push(_memcached_client_pool, memcached_client);
754 : }
755 : }
756 0 : span->Finish();
757 0 : LOG(info) << "Login completed [req_id=" << req_id << ", username=" << username << "]";
758 0 : }
759 75152 : int64_t UserHandler::GetUserId(
760 : int64_t req_id, const std::string &username,
761 : const std::map<std::string, std::string> &carrier) {
762 150380 : LOG(info) << "Received GetUserId request [req_id=" << req_id << ", username=" << username << "]";
763 150484 : TextMapReader reader(carrier);
764 150481 : std::map<std::string, std::string> writer_text_map;
765 150466 : TextMapWriter writer(writer_text_map);
766 150465 : auto parent_span = opentracing::Tracer::Global()->Extract(reader);
767 150454 : auto span = opentracing::Tracer::Global()->StartSpan(
768 300967 : "get_user_id_server", {opentracing::ChildOf(parent_span->get())});
769 75224 : opentracing::Tracer::Global()->Inject(span->context(), writer);
770 :
771 : size_t user_id_size;
772 : uint32_t memcached_flags;
773 :
774 : memcached_return_t memcached_rc;
775 : memcached_st *memcached_client =
776 75233 : memcached_pool_pop(_memcached_client_pool, true, &memcached_rc);
777 : char *user_id_mmc;
778 75245 : if (memcached_client) {
779 150450 : auto id_get_span = opentracing::Tracer::Global()->StartSpan(
780 : "user_mmc_get_user_id_client",
781 300969 : {opentracing::ChildOf(&span->context())});
782 : user_id_mmc =
783 150462 : memcached_get(memcached_client, (username + ":user_id").c_str(),
784 150465 : (username + ":user_id").length(), &user_id_size,
785 75235 : &memcached_flags, &memcached_rc);
786 75244 : id_get_span->Finish();
787 75175 : if (!user_id_mmc && memcached_rc != MEMCACHED_NOTFOUND) {
788 0 : ServiceException se;
789 0 : se.errorCode = ErrorCode::SE_MEMCACHED_ERROR;
790 0 : se.message = memcached_strerror(memcached_client, memcached_rc);
791 0 : memcached_pool_push(_memcached_client_pool, memcached_client);
792 0 : throw se;
793 : }
794 75175 : memcached_pool_push(_memcached_client_pool, memcached_client);
795 : } else {
796 0 : LOG(warning) << "Failed to pop a client from memcached pool";
797 : }
798 :
799 75223 : int64_t user_id = -1;
800 75223 : bool cached = false;
801 75223 : if (user_id_mmc) {
802 74040 : cached = true;
803 74040 : LOG(debug) << "Found user_id of username :" << username << " in Memcached";
804 74027 : user_id = std::stoul(user_id_mmc);
805 74057 : free(user_id_mmc);
806 : } else {
807 : // If not cached in memcached
808 1183 : LOG(debug) << "user_id not cached in Memcached";
809 : mongoc_client_t *mongodb_client =
810 1183 : mongoc_client_pool_pop(_mongodb_client_pool);
811 1183 : if (!mongodb_client) {
812 0 : ServiceException se;
813 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
814 0 : se.message = "Failed to pop a client from MongoDB pool";
815 0 : throw se;
816 : }
817 : auto collection =
818 1183 : mongoc_client_get_collection(mongodb_client, "user", "user");
819 1183 : if (!collection) {
820 0 : ServiceException se;
821 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
822 0 : se.message = "Failed to create collection user from DB user";
823 0 : throw se;
824 : }
825 1183 : bson_t *query = bson_new();
826 1183 : BSON_APPEND_UTF8(query, "username", username.c_str());
827 :
828 2366 : auto find_span = opentracing::Tracer::Global()->StartSpan(
829 4732 : "user_mongo_find_client", {opentracing::ChildOf(&span->context())});
830 : mongoc_cursor_t *cursor =
831 1183 : mongoc_collection_find_with_opts(collection, query, nullptr, nullptr);
832 : const bson_t *doc;
833 1183 : bool found = mongoc_cursor_next(cursor, &doc);
834 1182 : find_span->Finish();
835 1182 : if (!found) {
836 : bson_error_t error;
837 0 : if (mongoc_cursor_error(cursor, &error)) {
838 0 : LOG(error) << error.message;
839 0 : bson_destroy(query);
840 0 : mongoc_cursor_destroy(cursor);
841 0 : mongoc_collection_destroy(collection);
842 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
843 0 : ServiceException se;
844 0 : se.errorCode = ErrorCode::SE_MONGODB_ERROR;
845 0 : se.message = error.message;
846 0 : throw se;
847 : } else {
848 0 : LOG(warning) << "User: " << username << " doesn't exist in MongoDB";
849 0 : bson_destroy(query);
850 0 : mongoc_cursor_destroy(cursor);
851 0 : mongoc_collection_destroy(collection);
852 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
853 0 : ServiceException se;
854 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
855 0 : se.message = "User: " + username + " is not registered";
856 0 : throw se;
857 : }
858 : } else {
859 1182 : LOG(debug) << "User: " << username << " found in MongoDB";
860 : bson_iter_t iter;
861 1183 : if (bson_iter_init_find(&iter, doc, "user_id")) {
862 1183 : user_id = bson_iter_value(&iter)->value.v_int64;
863 : } else {
864 0 : LOG(error) << "user_id attribute of user " << username
865 0 : << " was not found in the User object";
866 0 : bson_destroy(query);
867 0 : mongoc_cursor_destroy(cursor);
868 0 : mongoc_collection_destroy(collection);
869 0 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
870 0 : ServiceException se;
871 0 : se.errorCode = ErrorCode::SE_THRIFT_HANDLER_ERROR;
872 0 : se.message = "user_id attribute of user: " + username +
873 0 : " was not found in the User object";
874 0 : throw se;
875 : }
876 : }
877 1183 : bson_destroy(query);
878 1183 : mongoc_cursor_destroy(cursor);
879 1183 : mongoc_collection_destroy(collection);
880 1183 : mongoc_client_pool_push(_mongodb_client_pool, mongodb_client);
881 : }
882 :
883 75240 : if (!cached) {
884 : memcached_client =
885 1183 : memcached_pool_pop(_memcached_client_pool, true, &memcached_rc);
886 1183 : if (!memcached_client) {
887 0 : LOG(warning) << "Failed to pop a client from memcached pool";
888 : } else {
889 2366 : std::string user_id_str = std::to_string(user_id);
890 2366 : auto set_login_span = opentracing::Tracer::Global()->StartSpan(
891 4732 : "user_mmc_set_client", {opentracing::ChildOf(&span->context())});
892 : memcached_rc =
893 3549 : memcached_set(memcached_client, (username + ":user_id").c_str(),
894 2365 : (username + ":user_id").length(), user_id_str.c_str(),
895 1183 : user_id_str.length(), 0, 0);
896 1183 : set_login_span->Finish();
897 1183 : if (memcached_rc != MEMCACHED_SUCCESS) {
898 0 : LOG(warning) << "Failed to set the login info of user " << username
899 0 : << " to Memcached: "
900 0 : << memcached_strerror(memcached_client, memcached_rc);
901 : }
902 1183 : memcached_pool_push(_memcached_client_pool, memcached_client);
903 : }
904 : }
905 :
906 75240 : span->Finish();
907 150438 : LOG(info) << "GetUserId completed [req_id=" << req_id << ", username=" << username << "]";
908 150478 : return user_id;
909 : }
910 :
911 : /*
912 : * The following code which obtaines machine ID from machine's MAC address was
913 : * inspired from https://stackoverflow.com/a/16859693.
914 : *
915 : * MAC address is obtained from /sys/class/net/<netif>/address
916 : */
917 1 : u_int16_t HashMacAddressPid(const std::string &mac) {
918 1 : u_int16_t hash = 0;
919 2 : std::string mac_pid = mac + std::to_string(getpid());
920 19 : for (unsigned int i = 0; i < mac_pid.size(); i++) {
921 18 : hash += (mac[i] << ((i & 1) * 8));
922 : }
923 2 : return hash;
924 : }
925 :
926 1 : std::string GetMachineId(std::string &netif) {
927 2 : std::string mac_hash;
928 :
929 2 : std::string mac_addr_filename = "/sys/class/net/" + netif + "/address";
930 2 : std::ifstream mac_addr_file;
931 1 : mac_addr_file.open(mac_addr_filename);
932 1 : if (!mac_addr_file) {
933 0 : LOG(fatal) << "Cannot read MAC address from net interface " << netif;
934 0 : return "";
935 : }
936 2 : std::string mac;
937 1 : mac_addr_file >> mac;
938 1 : if (mac == "") {
939 0 : LOG(fatal) << "Cannot read MAC address from net interface " << netif;
940 0 : return "";
941 : }
942 1 : mac_addr_file.close();
943 :
944 2 : LOG(info) << "MAC address = " << mac;
945 :
946 2 : std::stringstream stream;
947 1 : stream << std::hex << HashMacAddressPid(mac);
948 1 : mac_hash = stream.str();
949 :
950 1 : if (mac_hash.size() > 3) {
951 1 : mac_hash.erase(0, mac_hash.size() - 3);
952 0 : } else if (mac_hash.size() < 3) {
953 0 : mac_hash = std::string(3 - mac_hash.size(), '0') + mac_hash;
954 : }
955 1 : return mac_hash;
956 : }
957 : } // namespace social_network
958 :
959 : #endif // SOCIAL_NETWORK_MICROSERVICES_USERHANDLER_H
|