1 /* check-sources:disable-copyright-check */
2 /* unit test the sproxyd protocol backend */
3 #include <sys/types.h>
4 #include <limits.h>
5 #include <stdio.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <errno.h>
12 #include <check.h>
13 #include <droplet.h>
14 
15 #include "toyctl.h"
16 #include "testutils.h"
17 #include "utest_main.h"
18 
19 static struct server_state* state = NULL;
20 static dpl_dict_t* profile = NULL;
21 static dpl_ctx_t* ctx = NULL;
22 
setup(void)23 static void setup(void)
24 {
25   int r;
26 
27   unsetenv("DPLDIR");
28   unsetenv("DPLPROFILE");
29   dpl_init();
30 
31   r = toyserver_start(NULL, &state);
32   dpl_assert_int_eq(r, 0);
33 
34   profile = dpl_dict_new(13);
35   dpl_assert_ptr_not_null(profile);
36   dpl_assert_int_eq(
37       DPL_SUCCESS, dpl_dict_add(profile, "host", toyserver_addrlist(state), 0));
38   dpl_assert_int_eq(DPL_SUCCESS,
39                     dpl_dict_add(profile, "backend", "sproxyd", 0));
40   dpl_assert_int_eq(DPL_SUCCESS,
41                     dpl_dict_add(profile, "base_path", "/proxy/chord", 0));
42   dpl_assert_int_eq(DPL_SUCCESS,
43                     dpl_dict_add(profile, "droplet_dir", "/never/seen", 0));
44   dpl_assert_int_eq(DPL_SUCCESS,
45                     dpl_dict_add(profile, "profile_name", "viral", 0));
46   /* need this to disable the event log, otherwise the droplet_dir needs to
47    * exist */
48   dpl_assert_int_eq(DPL_SUCCESS, dpl_dict_add(profile, "pricing_dir", "", 0));
49 }
50 
teardown(void)51 static void teardown(void)
52 {
53   if (ctx) dpl_ctx_free(ctx);
54 
55   dpl_dict_free(profile);
56   toyserver_stop(state);
57 }
58 
START_TEST(get_set_test)59 START_TEST(get_set_test)
60 {
61   dpl_status_t s;
62   char* expected_uri;
63   int r;
64   char id[41];
65   static const char data[]
66       = "Carles wolf yr Austin, chambray twee lo-fi iPhone brunch Neutra"
67         "slow-carb. Viral +1 kitsch fashion axe wolf.  Selvage flexitarian"
68         "ugh banjo Godard, jean shorts occupy Marfa fingerstache literally"
69         "whatever keffiyeh put a bird on it biodiesel brunch. Forage plaid"
70         "wolf kitsch Etsy. Literally ugh Carles, Intelligentsia sartorial";
71 
72   /* create a context with all defaults */
73   ctx = dpl_ctx_new_from_dict(profile);
74   dpl_assert_ptr_not_null(ctx);
75 
76   s = dpl_gen_random_key(ctx, DPL_STORAGE_CLASS_STANDARD, /*custom*/ NULL, id,
77                          sizeof(id));
78   dpl_assert_int_eq(DPL_SUCCESS, s);
79 
80   s = dpl_put_id(ctx, "foobucket", id,
81                  /*options*/ NULL, DPL_FTYPE_REG,
82                  /*condition*/ NULL,
83                  /*range*/ NULL,
84                  /*metadata*/ NULL,
85                  /*sysmd*/ NULL, data, sizeof(data) - 1);
86   dpl_assert_int_eq(DPL_SUCCESS, s);
87 
88   dpl_assert_str_eq(state->request.host, toyserver_addrlist(state));
89   expected_uri = strconcat("/proxy/chord/", id, (char*)NULL);
90   dpl_assert_str_eq(state->request.uri, expected_uri);
91   dpl_assert_str_eq(state->request.body, data);
92   dpl_assert_int_eq(state->reply.status, 200);
93 
94   //   s = dpl_get_id(ctx, "foobucket", id, /*options*/NULL, DPL_FTYPE_REG,
95   //   /*condition*/NULL, /*range*/NULL, char **data_bufp, unsigned int
96   //   *data_lenp, dpl_dict_t **metadatap, dpl_sysmd_t *sysmdp);
97   //   dpl_assert_int_eq(DPL_SUCCESS, s);
98 
99   free(expected_uri);
100 }
101 END_TEST
102 
send_authenticated_request(const char * username,const char * password,struct request * reqp)103 static dpl_status_t send_authenticated_request(const char* username,
104                                                const char* password,
105                                                struct request* reqp)
106 {
107   dpl_status_t s;
108   dpl_dict_t* prof2;
109   char id[41];
110   static const char data[]
111       = "Carles wolf yr Austin, chambray twee lo-fi iPhone brunch Neutra"
112         "slow-carb. Viral";
113 
114   prof2 = dpl_dict_dup(profile);
115   if (username)
116     dpl_assert_int_eq(DPL_SUCCESS,
117                       dpl_dict_add(prof2, "access_key", username, 0));
118   if (password)
119     dpl_assert_int_eq(DPL_SUCCESS,
120                       dpl_dict_add(prof2, "secret_key", password, 0));
121 
122   /* create an unauthenticated context */
123   ctx = dpl_ctx_new_from_dict(prof2);
124   dpl_assert_ptr_not_null(ctx);
125   dpl_dict_free(prof2);
126 
127   s = dpl_gen_random_key(ctx, DPL_STORAGE_CLASS_STANDARD, /*custom*/ NULL, id,
128                          sizeof(id));
129   dpl_assert_int_eq(DPL_SUCCESS, s);
130 
131   s = dpl_put_id(ctx, "foobucket", id,
132                  /*options*/ NULL, DPL_FTYPE_REG,
133                  /*condition*/ NULL,
134                  /*range*/ NULL,
135                  /*metadata*/ NULL,
136                  /*sysmd*/ NULL, data, sizeof(data) - 1);
137   if (reqp) *reqp = state->request; /* sample before disconnection */
138 
139   dpl_ctx_free(ctx);
140   ctx = NULL;
141 
142   return s;
143 }
144 
START_TEST(basic_auth_test)145 START_TEST(basic_auth_test)
146 {
147 #define TESTCASE(u, p, ehasa, eu, ep, es)            \
148   {                                                  \
149     dpl_status_t s;                                  \
150     struct request req;                              \
151     memset(&req, 0, sizeof(req));                    \
152     s = send_authenticated_request(u, p, &req);      \
153     dpl_assert_int_eq(req.has_authorization, ehasa); \
154     dpl_assert_str_eq(req.username, eu);             \
155     dpl_assert_str_eq(req.password, ep);             \
156     dpl_assert_int_eq(s, es);                        \
157   }
158 
159 
160   /* By default, server notices authentication but doesn't care */
161 
162   /* unauthenticated request succeeds */
163   TESTCASE(NULL, NULL,
164            /*has_authorization*/ 0, "", "", DPL_SUCCESS);
165 
166   /* authenticated request with random username & password succeeds */
167   TESTCASE("brooklyn", "sriracha",
168            /*has_authorization*/ 1, "brooklyn", "sriracha", DPL_SUCCESS);
169 
170   /* Server will now refuse unauthenticated requests
171    * and requests with the wrong username or password. */
172   state->config.require_basic_auth = 1;
173 
174   /* unauthenticated request fails */
175   TESTCASE(NULL, NULL,
176            /*has_authorization*/ 0, "", "", DPL_FAILURE);
177 
178   /* authenticated request with bad username fails */
179   TESTCASE("not" TOY_USERNAME, TOY_PASSWORD,
180            /*has_authorization*/ 1, "not" TOY_USERNAME, TOY_PASSWORD,
181            DPL_EPERM);
182 
183   /* authenticated request with bad password fails */
184   TESTCASE(TOY_USERNAME, "not" TOY_PASSWORD,
185            /*has_authorization*/ 1, TOY_USERNAME, "not" TOY_PASSWORD,
186            DPL_EPERM);
187 
188   /* authenticated request with good username and password succeeds */
189   TESTCASE(TOY_USERNAME, TOY_PASSWORD,
190            /*has_authorization*/ 1, TOY_USERNAME, TOY_PASSWORD, DPL_SUCCESS);
191 #undef TESTCASE
192 }
193 END_TEST
194 
sproxyd_suite()195 Suite* sproxyd_suite()
196 {
197   Suite* s = suite_create("sproxyd");
198   TCase* t = tcase_create("base");
199   tcase_add_checked_fixture(t, setup, teardown);
200   tcase_add_test(t, get_set_test);
201   tcase_add_test(t, basic_auth_test);
202   suite_add_tcase(s, t);
203   return s;
204 }
205