blob: 8f458349e54b4300d176e617017d5bdaa91d7ed1 [file] [log] [blame]
Ed Tanous9140a672017-04-24 17:01:32 -07001#include "token_authorization_middleware.hpp"
Ed Tanousf9273472017-02-28 16:05:13 -08002#include <crow/app.h>
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07003#include "gmock/gmock.h"
Ed Tanous1ff48782017-04-18 12:45:08 -07004#include "gtest/gtest.h"
Ed Tanousf9273472017-02-28 16:05:13 -08005
Ed Tanous8041f312017-04-03 09:47:01 -07006using namespace crow;
7using namespace std;
8
Ed Tanous1e94fa42017-04-03 13:41:19 -07009// Tests that static urls are correctly passed
10TEST(TokenAuthentication, TestBasicReject) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070011 App<crow::PersistentData::Middleware, crow::TokenAuthorization::Middleware>
12 app;
Ed Tanous1e94fa42017-04-03 13:41:19 -070013 decltype(app)::server_t server(&app, "127.0.0.1", 45451);
Ed Tanouse0d918b2018-03-27 17:41:04 -070014 CROW_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; });
Ed Tanous1e94fa42017-04-03 13:41:19 -070015 auto _ = async(launch::async, [&] { server.run(); });
16 asio::io_service is;
17 std::string sendmsg;
Ed Tanousf9273472017-02-28 16:05:13 -080018
Ed Tanous1e94fa42017-04-03 13:41:19 -070019 static char buf[2048];
Ed Tanousf9273472017-02-28 16:05:13 -080020
Ed Tanous1e94fa42017-04-03 13:41:19 -070021 // Homepage should be passed with no credentials
22 sendmsg = "GET /\r\n\r\n";
23 {
Ed Tanous8041f312017-04-03 09:47:01 -070024 asio::ip::tcp::socket c(is);
Ed Tanous1e94fa42017-04-03 13:41:19 -070025 c.connect(asio::ip::tcp::endpoint(
26 asio::ip::address::from_string("127.0.0.1"), 45451));
Ed Tanous8041f312017-04-03 09:47:01 -070027 c.send(asio::buffer(sendmsg));
Ed Tanous7d3dba42017-04-05 13:04:39 -070028 c.receive(asio::buffer(buf, 2048));
Ed Tanous8041f312017-04-03 09:47:01 -070029 c.close();
Ed Tanous1e94fa42017-04-03 13:41:19 -070030 EXPECT_EQ("200", std::string(buf + 9, buf + 12));
31 }
32
33 // static should be passed with no credentials
34 sendmsg = "GET /static/index.html\r\n\r\n";
35 {
36 asio::ip::tcp::socket c(is);
37 c.connect(asio::ip::tcp::endpoint(
38 asio::ip::address::from_string("127.0.0.1"), 45451));
39 c.send(asio::buffer(sendmsg));
40 c.receive(asio::buffer(buf, 2048));
41 c.close();
42 EXPECT_EQ("404", std::string(buf + 9, buf + 12));
43 }
44
45 server.stop();
46}
47
48// Tests that Base64 basic strings work
49TEST(TokenAuthentication, TestRejectedResource) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070050 App<crow::PersistentData::Middleware, crow::TokenAuthorization::Middleware>
51 app;
Ed Tanous1e94fa42017-04-03 13:41:19 -070052 app.bindaddr("127.0.0.1").port(45451);
Ed Tanouse0d918b2018-03-27 17:41:04 -070053 CROW_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; });
Ed Tanous1e94fa42017-04-03 13:41:19 -070054 auto _ = async(launch::async, [&] { app.run(); });
55
56 asio::io_service is;
57 static char buf[2048];
58
59 // Other resources should not be passed
60 std::string sendmsg = "GET /foo\r\n\r\n";
61 asio::ip::tcp::socket c(is);
62 for (int i = 0; i < 200; i++) {
63 try {
64 c.connect(asio::ip::tcp::endpoint(
65 asio::ip::address::from_string("127.0.0.1"), 45451));
66 } catch (std::exception e) {
67 // do nothing
68 }
69 }
70 c.send(asio::buffer(sendmsg));
Ed Tanous7d3dba42017-04-05 13:04:39 -070071 c.receive(asio::buffer(buf, 2048));
Ed Tanous1e94fa42017-04-03 13:41:19 -070072 c.close();
73 EXPECT_EQ("401", std::string(buf + 9, buf + 12));
74
75 app.stop();
76}
77
78// Tests that Base64 basic strings work
79TEST(TokenAuthentication, TestGetLoginUrl) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070080 App<crow::PersistentData::Middleware, crow::TokenAuthorization::Middleware>
81 app;
Ed Tanous1e94fa42017-04-03 13:41:19 -070082 app.bindaddr("127.0.0.1").port(45451);
Ed Tanouse0d918b2018-03-27 17:41:04 -070083 CROW_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; });
Ed Tanous1e94fa42017-04-03 13:41:19 -070084 auto _ = async(launch::async, [&] { app.run(); });
85
86 asio::io_service is;
87 static char buf[2048];
88
89 // Other resources should not be passed
90 std::string sendmsg = "GET /login\r\n\r\n";
91 asio::ip::tcp::socket c(is);
92 for (int i = 0; i < 200; i++) {
93 try {
94 c.connect(asio::ip::tcp::endpoint(
95 asio::ip::address::from_string("127.0.0.1"), 45451));
96 } catch (std::exception e) {
97 // do nothing
98 }
99 }
100 c.send(asio::buffer(sendmsg));
Ed Tanous7d3dba42017-04-05 13:04:39 -0700101 c.receive(asio::buffer(buf, 2048));
Ed Tanous1e94fa42017-04-03 13:41:19 -0700102 c.close();
103 EXPECT_EQ("401", std::string(buf + 9, buf + 12));
104
105 app.stop();
106}
107
108// Tests boundary conditions on login
109TEST(TokenAuthentication, TestPostBadLoginUrl) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700110 App<crow::PersistentData::Middleware, crow::TokenAuthorization::Middleware>
111 app;
Ed Tanous1e94fa42017-04-03 13:41:19 -0700112 app.bindaddr("127.0.0.1").port(45451);
Ed Tanouse0d918b2018-03-27 17:41:04 -0700113 CROW_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; });
Ed Tanous1e94fa42017-04-03 13:41:19 -0700114 auto _ = async(launch::async, [&] { app.run(); });
115
116 asio::io_service is;
117 std::array<char, 2048> buf;
118 std::string sendmsg;
119
120 auto send_to_localhost = [&is, &buf](std::string sendmsg) {
121 asio::ip::tcp::socket c(is);
122 c.connect(asio::ip::tcp::endpoint(
123 asio::ip::address::from_string("127.0.0.1"), 45451));
124 c.send(asio::buffer(sendmsg));
Ed Tanous7d3dba42017-04-05 13:04:39 -0700125 c.receive(asio::buffer(buf));
Ed Tanous1e94fa42017-04-03 13:41:19 -0700126 c.close();
127 };
128
129 {
130 // Retry a couple of times waiting for the server to come up
131 asio::ip::tcp::socket c(is);
132 for (int i = 0; i < 200; i++) {
133 try {
134 c.connect(asio::ip::tcp::endpoint(
135 asio::ip::address::from_string("127.0.0.1"), 45451));
136 c.close();
137 break;
138 } catch (std::exception e) {
139 // do nothing. We expect this to fail while the server is starting up
140 }
141 }
142 }
143
144 // Test blank login credentials
145 sendmsg = "POST /login\r\nContent-Length:0\r\n\r\n\r\n";
146 {
147 send_to_localhost(sendmsg);
148 auto return_code = std::string(&buf[9], &buf[12]);
149 EXPECT_EQ("400", return_code);
150 }
151
152 // Test wrong login credentials
153 sendmsg =
154 "POST /login\r\nContent-Length:38\r\n\r\n{\"username\": \"foo\", "
155 "\"password\": \"bar\"}\r\n";
156 {
157 send_to_localhost(sendmsg);
158 auto return_code = std::string(&buf[9], &buf[12]);
159 EXPECT_EQ("401", return_code);
160 // TODO(ed) need to test more here. Response string?
161 }
162
163 // Test only sending a username
164 sendmsg =
165 "POST /login\r\nContent-Length:19\r\n\r\n{\"username\": \"foo\"}\r\n";
166 {
167 send_to_localhost(sendmsg);
168 auto return_code = std::string(&buf[9], &buf[12]);
169 EXPECT_EQ("400", return_code);
170 }
171
172 // Test only sending a password
173 sendmsg =
174 "POST /login\r\nContent-Length:19\r\n\r\n{\"password\": \"foo\"}\r\n";
175 {
176 send_to_localhost(sendmsg);
177 auto return_code = std::string(&buf[9], &buf[12]);
178 EXPECT_EQ("400", return_code);
179 }
180
181 app.stop();
182}
183
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700184// Test class that allows login for a fixed password.
185class KnownLoginAuthenticator {
186 public:
187 inline bool authenticate(const std::string& username,
188 const std::string& password) {
189 return (username == "dude") && (password == "foo");
190 }
191};
192
Ed Tanous1e94fa42017-04-03 13:41:19 -0700193TEST(TokenAuthentication, TestSuccessfulLogin) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700194 App<crow::PersistentData::Middleware, crow::TokenAuthorization::Middleware>
195 app;
Ed Tanous1e94fa42017-04-03 13:41:19 -0700196 app.bindaddr("127.0.0.1").port(45451);
Ed Tanouse0d918b2018-03-27 17:41:04 -0700197 CROW_ROUTE(app, "/")([]() { return boost::beast::http::status::ok; });
Ed Tanous1e94fa42017-04-03 13:41:19 -0700198 auto _ = async(launch::async, [&] { app.run(); });
199
200 asio::io_service is;
201 std::array<char, 2048> buf;
202 std::string sendmsg;
203
204 auto send_to_localhost = [&is, &buf](std::string sendmsg) {
205 asio::ip::tcp::socket c(is);
206 c.connect(asio::ip::tcp::endpoint(
207 asio::ip::address::from_string("127.0.0.1"), 45451));
208 c.send(asio::buffer(sendmsg));
Ed Tanous7d3dba42017-04-05 13:04:39 -0700209 c.receive(asio::buffer(buf));
Ed Tanous1e94fa42017-04-03 13:41:19 -0700210 c.close();
211 };
212
213 {
214 // Retry a couple of times waiting for the server to come up
215 asio::ip::tcp::socket c(is);
216 for (int i = 0; i < 200; i++) {
217 try {
218 c.connect(asio::ip::tcp::endpoint(
219 asio::ip::address::from_string("127.0.0.1"), 45451));
220 c.close();
221 break;
222 } catch (std::exception e) {
223 // do nothing. We expect this to fail while the server is starting up
224 }
225 }
226 }
227
228 // Test correct login credentials
229 sendmsg =
230 "POST /login\r\nContent-Length:40\r\n\r\n{\"username\": \"dude\", "
Ed Tanousf3d847c2017-06-12 16:01:42 -0700231 "\"password\": \"foo\"}\r\n";
Ed Tanous1e94fa42017-04-03 13:41:19 -0700232 {
233 send_to_localhost(sendmsg);
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700234 std::string response(std::begin(buf), std::end(buf));
235 // This is a routine to split strings until a newline is hit
236 // TODO(ed) this should really use the HTTP parser
237 std::vector<std::string> headers;
238 std::string::size_type pos = 0;
239 std::string::size_type prev = 0;
240 int content_length = 0;
241 std::string content_encoding("");
242 while ((pos = response.find("\r\n", prev)) != std::string::npos) {
243 auto this_string = response.substr(prev, pos - prev);
244 if (this_string == "") {
245 prev = pos + 2;
246 break;
247 }
248
249 headers.push_back(this_string);
250 prev = pos + 2;
251 }
252 EXPECT_EQ(headers[0], "HTTP/1.1 200 OK");
253 EXPECT_THAT(headers, testing::Contains("Content-Type: application/json"));
254 auto http_content = response.substr(prev);
Ed Tanous1e94fa42017-04-03 13:41:19 -0700255 }
256
Ed Tanous1e94fa42017-04-03 13:41:19 -0700257 // Try to use those login credentials to access a resource
258 sendmsg =
259 "GET /\r\nAuthorization: token\r\n\r\n{\"username\": \"dude\", "
260 "\"password\": \"dude\"}\r\n";
261 {
262 send_to_localhost(sendmsg);
263 auto return_code = std::string(&buf[9], &buf[12]);
264 EXPECT_EQ("200", return_code);
265 }
266
267 app.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700268}