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