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 | } |
Ed Tanous | 1ff4878 | 2017-04-18 12:45:08 -0700 | [diff] [blame] | 54 | if (!login_credentials.has("username") || |
| 55 | !login_credentials.has("password")) { |
Ed Tanous | 1e94fa4 | 2017-04-03 13:41:19 -0700 | [diff] [blame] | 56 | return_bad_request(); |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 57 | return; |
| 58 | } |
| 59 | auto username = login_credentials["username"].s(); |
| 60 | auto password = login_credentials["password"].s(); |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 61 | |
Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 62 | // TODO(ed) pull real passwords from PAM |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 63 | if (username == "dude" && password == "dude") { |
| 64 | // TODO(ed) the RNG should be initialized at start, not every time we |
| 65 | // want a token |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 66 | std::random_device rand; |
| 67 | random_bytes_engine rbe; |
| 68 | std::string token('a', 20); |
Ed Tanous | 1ff4878 | 2017-04-18 12:45:08 -0700 | [diff] [blame] | 69 | // TODO(ed) for some reason clang-tidy finds a divide by zero error in |
| 70 | // cstdlibc here |
Ed Tanous | 93f987d | 2017-04-17 17:52:36 -0700 | [diff] [blame] | 71 | // commented out for now. Needs investigation |
| 72 | std::generate(begin(token), end(token), std::ref(rbe)); // NOLINT |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 73 | std::string encoded_token; |
| 74 | base64::base64_encode(token, encoded_token); |
| 75 | ctx.auth_token = encoded_token; |
| 76 | this->auth_token2 = encoded_token; |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 77 | crow::json::wvalue x; |
| 78 | auto auth_token = ctx.auth_token; |
| 79 | x["token"] = auth_token; |
| 80 | |
| 81 | res.write(json::dump(x)); |
Ed Tanous | b4a7bfa | 2017-04-04 17:23:00 -0700 | [diff] [blame] | 82 | res.add_header("Content-Type", "application/json"); |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 83 | res.end(); |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 84 | } else { |
| 85 | return_unauthorized(); |
| 86 | return; |
| 87 | } |
| 88 | } |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 89 | |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 90 | } else if (req.url == "/logout") { |
| 91 | this->auth_token2 = ""; |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 92 | res.code = 200; |
| 93 | res.end(); |
| 94 | } else { // Normal, non login, non static file request |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 95 | // Check to make sure we're logged in |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 96 | if (this->auth_token2.empty()) { |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 97 | return_unauthorized(); |
| 98 | return; |
| 99 | } |
| 100 | // Check for an authorization header, reject if not present |
| 101 | if (req.headers.count("Authorization") != 1) { |
| 102 | return_unauthorized(); |
| 103 | return; |
| 104 | } |
| 105 | |
| 106 | std::string auth_header = req.get_header_value("Authorization"); |
| 107 | // If the user is attempting any kind of auth other than token, reject |
| 108 | if (!boost::starts_with(auth_header, "Token ")) { |
| 109 | return_unauthorized(); |
| 110 | return; |
| 111 | } |
| 112 | |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 113 | // TODO(ed), use span here instead of constructing a new string |
| 114 | if (auth_header.substr(6) != this->auth_token2) { |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 115 | return_unauthorized(); |
| 116 | return; |
| 117 | } |
Ed Tanous | 1ccd57c | 2017-03-21 13:15:58 -0700 | [diff] [blame] | 118 | // else let the request continue unharmed |
Ed Tanous | 9992332 | 2017-03-03 14:21:24 -0800 | [diff] [blame] | 119 | } |
| 120 | } |
Ed Tanous | f927347 | 2017-02-28 16:05:13 -0800 | [diff] [blame] | 121 | |
Ed Tanous | 8041f31 | 2017-04-03 09:47:01 -0700 | [diff] [blame] | 122 | void TokenAuthorizationMiddleware::after_handle(request& req, response& res, |
| 123 | context& ctx) {} |
Ed Tanous | c4771fb | 2017-03-13 13:39:49 -0700 | [diff] [blame] | 124 | } |