22
22
#include < boost/beast/core/detail/base64.hpp>
23
23
#include < algorithm>
24
24
#include < cstdlib>
25
+ #include < ctime>
25
26
#include < functional>
26
27
#include < memory>
27
28
#include < thread>
32
33
#include < mink_err_codes.h>
33
34
#include " jrpc.h"
34
35
#include < gdt.pb.enums_only.h>
36
+ #include < vector>
35
37
36
38
37
39
// boost beast/asio
@@ -41,13 +43,13 @@ namespace websocket = beast::websocket;
41
43
namespace net = boost::asio;
42
44
namespace ssl = boost::asio::ssl;
43
45
namespace base64 = boost::beast::detail::base64;
46
+ namespace stdc = std::chrono;
44
47
using tcp = boost::asio::ip::tcp;
45
48
using Jrpc = json_rpc::JsonRpc;
46
- namespace chrono = std::chrono;
47
- using usr_info_t = std::tuple<std::string,
48
- int ,
49
- WebSocketBase *,
50
- std::time_t >;
49
+ using usr_info_t = std::tuple<std::string, // username
50
+ int , // user flags
51
+ WebSocketBase *, // connection pointer
52
+ uint64_t >; // last timestamp
51
53
52
54
// using wss = websocket::stream<beast::ssl_stream<beast::tcp_stream>>;
53
55
class WebSocketBase ;
@@ -93,12 +95,19 @@ class EVUserCB: public gdt::GDTCallbackMethod {
93
95
void fail (beast::error_code ec, char const *what);
94
96
// bool user_auth_prepare(boost::string_view &auth_str, int type);
95
97
// std::tuple<std::string, std::string, bool, int> user_auth(boost::string_view &auth_hdr);
96
- std::tuple<int , std::string, std::string, bool , int > user_auth_jrpc (const std::string &crdt);
98
+ std::tuple<int , std::string, std::string, int , int > user_auth_jrpc (const std::string &crdt);
97
99
98
- #ifdef ENABLE_WS_SINGLE_SESSION
99
100
/* ******************************/
100
101
/* List of authenticated users */
101
102
/* ******************************/
103
+ struct UserBanInfo {
104
+ std::string username;
105
+ int attemtps;
106
+ uint64_t ts_msec;
107
+ uint64_t ts_banned_until;
108
+ bool banned;
109
+ };
110
+
102
111
class UserList {
103
112
public:
104
113
UserList () = default ;
@@ -108,6 +117,9 @@ class UserList {
108
117
109
118
usr_info_t exists (const std::string &u);
110
119
bool add (const usr_info_t &u);
120
+ UserBanInfo *add_attempt (const std::string &u);
121
+ UserBanInfo *get_banned (const std::string &u);
122
+ void lift_ban (const std::string &u);
111
123
void remove (const std::string &u, const uint64_t &ts);
112
124
void remove_all ();
113
125
void process_all (const std::function<void (const usr_info_t &)> &f);
@@ -116,13 +128,13 @@ class UserList {
116
128
private:
117
129
std::mutex m;
118
130
std::vector<usr_info_t > users;
131
+ std::map<std::string, UserBanInfo> ban_lst;
119
132
};
120
133
121
134
/* ************/
122
135
/* User list */
123
136
/* ************/
124
137
extern UserList USERS;
125
- #endif
126
138
127
139
/* ****************/
128
140
/* WebSocketBase */
@@ -138,12 +150,13 @@ class WebSocketBase {
138
150
USERS.remove (std::get<0 >(usr_info_), std::get<3 >(usr_info_));
139
151
}
140
152
141
- usr_info_t usr_info_;
142
153
#endif
143
154
virtual beast::flat_buffer &get_buffer () = 0;
144
155
virtual std::mutex &get_mtx () = 0;
145
156
virtual void async_buffer_send (const std::string &d) = 0;
146
157
virtual void do_close () = 0;
158
+
159
+ usr_info_t usr_info_ = {" " , 0 , nullptr , 0 };
147
160
};
148
161
149
162
/* *********************************/
@@ -302,6 +315,8 @@ class WebSocketSession : public WebSocketBase {
302
315
int id = 0 ;
303
316
// request timeout
304
317
int req_tmt = 2000 ;
318
+ // daemon
319
+ auto dd = static_cast <JsonRpcdDescriptor*>(mink::CURRENT_DAEMON);
305
320
// verify if json is a valid json rpc data
306
321
try {
307
322
jrpc.verify (true );
@@ -322,17 +337,82 @@ class WebSocketSession : public WebSocketBase {
322
337
const std::string &crdts = jrpc.get_auth_crdts ();
323
338
324
339
// user auth info
325
- std::tuple<int , std::string, std::string, bool , int > ua;
340
+ std::tuple<int , std::string, std::string, int , int > ua;
326
341
// connect with DB
327
342
ua = user_auth_jrpc (crdts);
328
- if (!std::get<3 >(ua))
343
+
344
+ /* *************************************/
345
+ /* tuple index [3] = user auth status */
346
+ /* *************************************/
347
+ // -1 = invalid user
348
+ // 0 = user found, invalid password
349
+ // 1 = user found and authenticated
350
+ if (std::get<3 >(ua) == -1 )
351
+ throw AuthException (mink::error::EC_AUTH_UNKNOWN_USER);
352
+
353
+ /* *************/
354
+ /* user found */
355
+ /* *************/
356
+ // get unix timestamp (part of user tuple)
357
+ auto ts_now = stdc::system_clock::now ().time_since_epoch ();
358
+ // now ts msec
359
+ uint64_t now_msec = stdc::duration_cast<stdc::milliseconds>(ts_now).count ();
360
+
361
+ // invalid password check
362
+ if (std::get<3 >(ua) == 0 ){
363
+ // find user
364
+ UserBanInfo *bi = USERS.get_banned (std::get<1 >(ua));
365
+
366
+ // add to list if not found
367
+ if (!bi)
368
+ bi = USERS.add_attempt (std::get<1 >(ua));
369
+
370
+ // if found, inc attempts
371
+ else
372
+ ++bi->attemtps ;
373
+
374
+ // check if banned
375
+ if (bi->banned ){
376
+ // check if ban can be lifted
377
+ if (now_msec - bi->ts_msec > bi->ts_banned_until ){
378
+ std::cout << " Ban lifted" << std::endl;
379
+ USERS.lift_ban (bi->username );
380
+ bi = nullptr ;
381
+
382
+ }else {
383
+ // too many failed attempts
384
+ throw AuthException (mink::error::EC_AUTH_USER_BANNED);
385
+ }
386
+
387
+ // check if ban needs to be set
388
+ }else {
389
+ if (bi->attemtps >= dd->dparams .get_pval <int >(6 )){
390
+ bi->banned = true ;
391
+ bi->ts_banned_until = now_msec + (dd->dparams .get_pval <int >(7 ) * 60 * 1000 );
392
+ // user is now banned
393
+ throw AuthException (mink::error::EC_AUTH_USER_BANNED);
394
+ }
395
+ }
396
+
397
+ // invalid password
329
398
throw AuthException (mink::error::EC_AUTH_FAILED);
330
399
331
- // save session credentials
332
- set_credentials (std::get<0 >(ua),
333
- std::get<1 >(ua),
334
- std::get<2 >(ua),
335
- std::get<4 >(ua));
400
+ // password ok, check if user was banned
401
+ }else {
402
+ // find
403
+ UserBanInfo *bi = USERS.get_banned (std::get<1 >(ua));
404
+ // user found, check if ban can be lifted
405
+ if (bi && bi->banned ){
406
+ if (bi->ts_banned_until <= now_msec){
407
+ USERS.lift_ban (bi->username );
408
+
409
+ // ban can't be lifted just yet
410
+ }else {
411
+ throw AuthException (mink::error::EC_AUTH_USER_BANNED);
412
+ }
413
+ }
414
+ }
415
+
336
416
#ifdef ENABLE_WS_SINGLE_SESSION
337
417
if (USERS.count () > 0 ) {
338
418
// new user is admin, logout other users
@@ -369,16 +449,24 @@ class WebSocketSession : public WebSocketBase {
369
449
}
370
450
}
371
451
}
372
- // get unix timestamp (part of user tuple)
373
- auto ts_now = chrono::system_clock::now ();
452
+ #endif
453
+
454
+
455
+ // save session credentials
456
+ set_credentials (std::get<0 >(ua),
457
+ std::get<1 >(ua),
458
+ std::get<2 >(ua),
459
+ std::get<4 >(ua));
460
+
374
461
// add to user list
375
462
auto new_usr = std::make_tuple (std::get<1 >(ua),
376
463
std::get<4 >(ua),
377
464
this ,
378
- ts_now. time_since_epoch (). count () );
465
+ now_msec );
379
466
USERS.add (new_usr);
467
+
468
+ // set current user for this connection
380
469
usr_info_ = new_usr;
381
- #endif
382
470
383
471
// generate response
384
472
auto j_res = json_rpc::JsonRpc::gen_response (id);
0 commit comments