| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 1 | #include <boost/algorithm/string/predicate.hpp> | 
| Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 2 | #include <random> | 
| Ed Tanous | f927347 | 2017-02-28 16:05:13 -0800 | [diff] [blame] | 3 | #include <unordered_map> | 
| Ed Tanous | f927347 | 2017-02-28 16:05:13 -0800 | [diff] [blame] | 4 |  | 
| Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 5 | #include <crow/logging.h> | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 6 | #include <base64.hpp> | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 7 | #include <token_authorization_middleware.hpp> | 
| Ed Tanous | f927347 | 2017-02-28 16:05:13 -0800 | [diff] [blame] | 8 |  | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 9 | namespace crow { | 
 | 10 |  | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 11 | using random_bytes_engine = | 
 | 12 |     std::independent_bits_engine<std::default_random_engine, CHAR_BIT, | 
 | 13 |                                  unsigned char>; | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 14 |  | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 15 | TokenAuthorizationMiddleware::TokenAuthorizationMiddleware() | 
 | 16 |     : auth_token2(""){ | 
| Ed Tanous | f927347 | 2017-02-28 16:05:13 -0800 | [diff] [blame] | 17 |  | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 18 |       }; | 
 | 19 |  | 
 | 20 | void TokenAuthorizationMiddleware::before_handle(crow::request& req, | 
 | 21 |                                                  response& res, context& ctx) { | 
| Ed Tanous | 9992332 | 2017-03-03 14:21:24 -0800 | [diff] [blame] | 22 |   auto return_unauthorized = [&req, &res]() { | 
 | 23 |     res.code = 401; | 
 | 24 |     res.end(); | 
 | 25 |   }; | 
| Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 26 |  | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 27 |   LOG(DEBUG) << "Token Auth Got route " << req.url; | 
| Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 28 |  | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 29 |   if (req.url == "/" || boost::starts_with(req.url, "/static/")) { | 
 | 30 |     // TODO this is total hackery to allow the login page to work before the | 
 | 31 |     // user is authenticated.  Also, it will be quite slow for all pages instead | 
 | 32 |     // of a one time hit for the whitelist entries.  Ideally, this should be | 
 | 33 |     // done | 
 | 34 |     // in the url router handler, with tagged routes for the whitelist entries. | 
| Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 35 |     // Another option would be to whitelist a minimal | 
| Ed Tanous | 9b65f1f | 2017-03-07 15:17:13 -0800 | [diff] [blame] | 36 |     return; | 
 | 37 |   } | 
 | 38 |  | 
| Ed Tanous | 9992332 | 2017-03-03 14:21:24 -0800 | [diff] [blame] | 39 |   if (req.url == "/login") { | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 40 |     if (req.method != HTTPMethod::POST) { | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 41 |       return_unauthorized(); | 
 | 42 |       return; | 
 | 43 |     } else { | 
 | 44 |       auto login_credentials = crow::json::load(req.body); | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 45 |       if (!login_credentials) { | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 46 |         return_unauthorized(); | 
 | 47 |         return; | 
 | 48 |       } | 
 | 49 |       auto username = login_credentials["username"].s(); | 
 | 50 |       auto password = login_credentials["password"].s(); | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 51 |  | 
| Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 52 |       // TODO(ed) pull real passwords from PAM | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 53 |       if (username == "dude" && password == "dude") { | 
 | 54 |         // TODO(ed) the RNG should be initialized at start, not every time we | 
 | 55 |         // want a token | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 56 |         std::random_device rand; | 
 | 57 |         random_bytes_engine rbe; | 
 | 58 |         std::string token('a', 20); | 
 | 59 |         std::generate(begin(token), end(token), std::ref(rbe)); | 
 | 60 |         std::string encoded_token; | 
 | 61 |         base64::base64_encode(token, encoded_token); | 
 | 62 |         ctx.auth_token = encoded_token; | 
 | 63 |         this->auth_token2 = encoded_token; | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 64 |         crow::json::wvalue x; | 
 | 65 |         auto auth_token = ctx.auth_token; | 
 | 66 |         x["token"] = auth_token; | 
 | 67 |  | 
 | 68 |         res.write(json::dump(x)); | 
 | 69 |         res.end(); | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 70 |       } else { | 
 | 71 |         return_unauthorized(); | 
 | 72 |         return; | 
 | 73 |       } | 
 | 74 |     } | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 75 |  | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 76 |   } else if (req.url == "/logout") { | 
 | 77 |     this->auth_token2 = ""; | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 78 |     res.code = 200; | 
 | 79 |     res.end(); | 
 | 80 |   } else {  // Normal, non login, non static file request | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 81 |     // Check to make sure we're logged in | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 82 |     if (this->auth_token2.empty()) { | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 83 |       return_unauthorized(); | 
 | 84 |       return; | 
 | 85 |     } | 
 | 86 |     // Check for an authorization header, reject if not present | 
 | 87 |     if (req.headers.count("Authorization") != 1) { | 
 | 88 |       return_unauthorized(); | 
 | 89 |       return; | 
 | 90 |     } | 
 | 91 |  | 
 | 92 |     std::string auth_header = req.get_header_value("Authorization"); | 
 | 93 |     // If the user is attempting any kind of auth other than token, reject | 
 | 94 |     if (!boost::starts_with(auth_header, "Token ")) { | 
 | 95 |       return_unauthorized(); | 
 | 96 |       return; | 
 | 97 |     } | 
 | 98 |  | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 99 |     // TODO(ed), use span here instead of constructing a new string | 
 | 100 |     if (auth_header.substr(6) != this->auth_token2) { | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 101 |       return_unauthorized(); | 
 | 102 |       return; | 
 | 103 |     } | 
| Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 104 |     // else let the request continue unharmed | 
| Ed Tanous | 9992332 | 2017-03-03 14:21:24 -0800 | [diff] [blame] | 105 |   } | 
 | 106 | } | 
| Ed Tanous | f927347 | 2017-02-28 16:05:13 -0800 | [diff] [blame] | 107 |  | 
| Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame^] | 108 | void TokenAuthorizationMiddleware::after_handle(request& req, response& res, | 
 | 109 |                                                 context& ctx) {} | 
| Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 110 | } |