blob: 68efe8f5b41e12f9f0a0dbcaf33db104fc5e8d0d [file] [log] [blame]
Ed Tanousf9273472017-02-28 16:05:13 -08001#include "token_authorization_middleware.hpp"
2#include <crow/app.h>
3#include "gtest/gtest.h"
Ed Tanousb4a7bfa2017-04-04 17:23:00 -07004#include "gmock/gmock.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) {
11 App<crow::TokenAuthorizationMiddleware> app;
12 decltype(app)::server_t server(&app, "127.0.0.1", 45451);
13 CROW_ROUTE(app, "/")([]() { return 200; });
14 auto _ = async(launch::async, [&] { server.run(); });
15 asio::io_service is;
16 std::string sendmsg;
Ed Tanousf9273472017-02-28 16:05:13 -080017
Ed Tanous1e94fa42017-04-03 13:41:19 -070018 static char buf[2048];
Ed Tanousf9273472017-02-28 16:05:13 -080019
Ed Tanous1e94fa42017-04-03 13:41:19 -070020 // Homepage should be passed with no credentials
21 sendmsg = "GET /\r\n\r\n";
22 {
Ed Tanous8041f312017-04-03 09:47:01 -070023 asio::ip::tcp::socket c(is);
Ed Tanous1e94fa42017-04-03 13:41:19 -070024 c.connect(asio::ip::tcp::endpoint(
25 asio::ip::address::from_string("127.0.0.1"), 45451));
Ed Tanous8041f312017-04-03 09:47:01 -070026 c.send(asio::buffer(sendmsg));
27 auto received_count = c.receive(asio::buffer(buf, 2048));
28 c.close();
Ed Tanous1e94fa42017-04-03 13:41:19 -070029 EXPECT_EQ("200", std::string(buf + 9, buf + 12));
30 }
31
32 // static should be passed with no credentials
33 sendmsg = "GET /static/index.html\r\n\r\n";
34 {
35 asio::ip::tcp::socket c(is);
36 c.connect(asio::ip::tcp::endpoint(
37 asio::ip::address::from_string("127.0.0.1"), 45451));
38 c.send(asio::buffer(sendmsg));
39 c.receive(asio::buffer(buf, 2048));
40 c.close();
41 EXPECT_EQ("404", std::string(buf + 9, buf + 12));
42 }
43
44 server.stop();
45}
46
47// Tests that Base64 basic strings work
48TEST(TokenAuthentication, TestRejectedResource) {
49 App<crow::TokenAuthorizationMiddleware> app;
50 app.bindaddr("127.0.0.1").port(45451);
51 CROW_ROUTE(app, "/")([]() { return 200; });
52 auto _ = async(launch::async, [&] { app.run(); });
53
54 asio::io_service is;
55 static char buf[2048];
56
57 // Other resources should not be passed
58 std::string sendmsg = "GET /foo\r\n\r\n";
59 asio::ip::tcp::socket c(is);
60 for (int i = 0; i < 200; i++) {
61 try {
62 c.connect(asio::ip::tcp::endpoint(
63 asio::ip::address::from_string("127.0.0.1"), 45451));
64 } catch (std::exception e) {
65 // do nothing
66 }
67 }
68 c.send(asio::buffer(sendmsg));
69 auto received_count = c.receive(asio::buffer(buf, 2048));
70 c.close();
71 EXPECT_EQ("401", std::string(buf + 9, buf + 12));
72
73 app.stop();
74}
75
76// Tests that Base64 basic strings work
77TEST(TokenAuthentication, TestGetLoginUrl) {
78 App<crow::TokenAuthorizationMiddleware> app;
79 app.bindaddr("127.0.0.1").port(45451);
80 CROW_ROUTE(app, "/")([]() { return 200; });
81 auto _ = async(launch::async, [&] { app.run(); });
82
83 asio::io_service is;
84 static char buf[2048];
85
86 // Other resources should not be passed
87 std::string sendmsg = "GET /login\r\n\r\n";
88 asio::ip::tcp::socket c(is);
89 for (int i = 0; i < 200; i++) {
90 try {
91 c.connect(asio::ip::tcp::endpoint(
92 asio::ip::address::from_string("127.0.0.1"), 45451));
93 } catch (std::exception e) {
94 // do nothing
95 }
96 }
97 c.send(asio::buffer(sendmsg));
98 auto received_count = c.receive(asio::buffer(buf, 2048));
99 c.close();
100 EXPECT_EQ("401", std::string(buf + 9, buf + 12));
101
102 app.stop();
103}
104
105// Tests boundary conditions on login
106TEST(TokenAuthentication, TestPostBadLoginUrl) {
107 App<crow::TokenAuthorizationMiddleware> app;
108 app.bindaddr("127.0.0.1").port(45451);
109 CROW_ROUTE(app, "/")([]() { return 200; });
110 auto _ = async(launch::async, [&] { app.run(); });
111
112 asio::io_service is;
113 std::array<char, 2048> buf;
114 std::string sendmsg;
115
116 auto send_to_localhost = [&is, &buf](std::string sendmsg) {
117 asio::ip::tcp::socket c(is);
118 c.connect(asio::ip::tcp::endpoint(
119 asio::ip::address::from_string("127.0.0.1"), 45451));
120 c.send(asio::buffer(sendmsg));
121 auto received_count = c.receive(asio::buffer(buf));
122 c.close();
123 };
124
125 {
126 // Retry a couple of times waiting for the server to come up
127 asio::ip::tcp::socket c(is);
128 for (int i = 0; i < 200; i++) {
129 try {
130 c.connect(asio::ip::tcp::endpoint(
131 asio::ip::address::from_string("127.0.0.1"), 45451));
132 c.close();
133 break;
134 } catch (std::exception e) {
135 // do nothing. We expect this to fail while the server is starting up
136 }
137 }
138 }
139
140 // Test blank login credentials
141 sendmsg = "POST /login\r\nContent-Length:0\r\n\r\n\r\n";
142 {
143 send_to_localhost(sendmsg);
144 auto return_code = std::string(&buf[9], &buf[12]);
145 EXPECT_EQ("400", return_code);
146 }
147
148 // Test wrong login credentials
149 sendmsg =
150 "POST /login\r\nContent-Length:38\r\n\r\n{\"username\": \"foo\", "
151 "\"password\": \"bar\"}\r\n";
152 {
153 send_to_localhost(sendmsg);
154 auto return_code = std::string(&buf[9], &buf[12]);
155 EXPECT_EQ("401", return_code);
156 // TODO(ed) need to test more here. Response string?
157 }
158
159 // Test only sending a username
160 sendmsg =
161 "POST /login\r\nContent-Length:19\r\n\r\n{\"username\": \"foo\"}\r\n";
162 {
163 send_to_localhost(sendmsg);
164 auto return_code = std::string(&buf[9], &buf[12]);
165 EXPECT_EQ("400", return_code);
166 }
167
168 // Test only sending a password
169 sendmsg =
170 "POST /login\r\nContent-Length:19\r\n\r\n{\"password\": \"foo\"}\r\n";
171 {
172 send_to_localhost(sendmsg);
173 auto return_code = std::string(&buf[9], &buf[12]);
174 EXPECT_EQ("400", return_code);
175 }
176
177 app.stop();
178}
179
180// Tests boundary conditions on login
181TEST(TokenAuthentication, TestSuccessfulLogin) {
182 App<crow::TokenAuthorizationMiddleware> app;
183 app.bindaddr("127.0.0.1").port(45451);
184 CROW_ROUTE(app, "/")([]() { return 200; });
185 auto _ = async(launch::async, [&] { app.run(); });
186
187 asio::io_service is;
188 std::array<char, 2048> buf;
189 std::string sendmsg;
190
191 auto send_to_localhost = [&is, &buf](std::string sendmsg) {
192 asio::ip::tcp::socket c(is);
193 c.connect(asio::ip::tcp::endpoint(
194 asio::ip::address::from_string("127.0.0.1"), 45451));
195 c.send(asio::buffer(sendmsg));
196 auto received_count = c.receive(asio::buffer(buf));
197 c.close();
198 };
199
200 {
201 // Retry a couple of times waiting for the server to come up
202 asio::ip::tcp::socket c(is);
203 for (int i = 0; i < 200; i++) {
204 try {
205 c.connect(asio::ip::tcp::endpoint(
206 asio::ip::address::from_string("127.0.0.1"), 45451));
207 c.close();
208 break;
209 } catch (std::exception e) {
210 // do nothing. We expect this to fail while the server is starting up
211 }
212 }
213 }
214
215 // Test correct login credentials
216 sendmsg =
217 "POST /login\r\nContent-Length:40\r\n\r\n{\"username\": \"dude\", "
218 "\"password\": \"dude\"}\r\n";
219 {
220 send_to_localhost(sendmsg);
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700221 std::string response(std::begin(buf), std::end(buf));
222 // This is a routine to split strings until a newline is hit
223 // TODO(ed) this should really use the HTTP parser
224 std::vector<std::string> headers;
225 std::string::size_type pos = 0;
226 std::string::size_type prev = 0;
227 int content_length = 0;
228 std::string content_encoding("");
229 while ((pos = response.find("\r\n", prev)) != std::string::npos) {
230 auto this_string = response.substr(prev, pos - prev);
231 if (this_string == "") {
232 prev = pos + 2;
233 break;
234 }
235
236 headers.push_back(this_string);
237 prev = pos + 2;
238 }
239 EXPECT_EQ(headers[0], "HTTP/1.1 200 OK");
240 EXPECT_THAT(headers, testing::Contains("Content-Type: application/json"));
241 auto http_content = response.substr(prev);
Ed Tanous1e94fa42017-04-03 13:41:19 -0700242 }
243
244
Ed Tanousb4a7bfa2017-04-04 17:23:00 -0700245
Ed Tanous1e94fa42017-04-03 13:41:19 -0700246 // Try to use those login credentials to access a resource
247 sendmsg =
248 "GET /\r\nAuthorization: token\r\n\r\n{\"username\": \"dude\", "
249 "\"password\": \"dude\"}\r\n";
250 {
251 send_to_localhost(sendmsg);
252 auto return_code = std::string(&buf[9], &buf[12]);
253 EXPECT_EQ("200", return_code);
254 }
255
256 app.stop();
Ed Tanous8041f312017-04-03 09:47:01 -0700257}