xref: /openbmc/bmcweb/test/http/utility_test.cpp (revision 7da1c588)
1 #include "bmcweb_config.h"
2 
3 #include "utility.hpp"
4 
5 #include <boost/url/error.hpp>
6 #include <boost/url/url.hpp>
7 #include <boost/url/url_view.hpp>
8 #include <nlohmann/json.hpp>
9 
10 #include <cstdint>
11 #include <ctime>
12 #include <functional>
13 #include <limits>
14 #include <string>
15 #include <string_view>
16 
17 #include <gtest/gtest.h> // IWYU pragma: keep
18 // IWYU pragma: no_include <gtest/gtest-message.h>
19 // IWYU pragma: no_include <gtest/gtest-test-part.h>
20 // IWYU pragma: no_include "gtest/gtest_pred_impl.h"
21 
22 namespace crow::utility
23 {
24 namespace
25 {
26 
27 using ::crow::black_magic::getParameterTag;
28 
29 TEST(Utility, Base64DecodeAuthString)
30 {
31     std::string authString("dXNlcm40bWU6cGFzc3cwcmQ=");
32     std::string result;
33     EXPECT_TRUE(base64Decode(authString, result));
34     EXPECT_EQ(result, "usern4me:passw0rd");
35 }
36 
37 TEST(Utility, Base64DecodeNonAscii)
38 {
39     std::string junkString("\xff\xee\xdd\xcc\x01\x11\x22\x33");
40     std::string result;
41     EXPECT_FALSE(base64Decode(junkString, result));
42 }
43 
44 TEST(Utility, Base64EncodeString)
45 {
46     using namespace std::string_literals;
47     std::string encoded;
48 
49     encoded = base64encode("");
50     EXPECT_EQ(encoded, "");
51 
52     encoded = base64encode("f");
53     EXPECT_EQ(encoded, "Zg==");
54 
55     encoded = base64encode("f0");
56     EXPECT_EQ(encoded, "ZjA=");
57 
58     encoded = base64encode("f0\0"s);
59     EXPECT_EQ(encoded, "ZjAA");
60 
61     encoded = base64encode("f0\0 "s);
62     EXPECT_EQ(encoded, "ZjAAIA==");
63 
64     encoded = base64encode("f0\0 B"s);
65     EXPECT_EQ(encoded, "ZjAAIEI=");
66 
67     encoded = base64encode("f0\0 Ba"s);
68     EXPECT_EQ(encoded, "ZjAAIEJh");
69 
70     encoded = base64encode("f0\0 Bar"s);
71     EXPECT_EQ(encoded, "ZjAAIEJhcg==");
72 }
73 
74 TEST(Utility, Base64EncodeDecodeString)
75 {
76     using namespace std::string_literals;
77     std::string data("Data fr\0m 90 reading a \nFile"s);
78     std::string encoded = base64encode(data);
79     std::string decoded;
80     EXPECT_TRUE(base64Decode(encoded, decoded));
81     EXPECT_EQ(data, decoded);
82 }
83 
84 TEST(Utility, UrlFromPieces)
85 {
86     boost::urls::url url = urlFromPieces("redfish", "v1", "foo");
87     EXPECT_EQ(url.buffer(), "/redfish/v1/foo");
88 
89     url = urlFromPieces("/", "badString");
90     EXPECT_EQ(url.buffer(), "/%2F/badString");
91 
92     url = urlFromPieces("bad?tring");
93     EXPECT_EQ(url.buffer(), "/bad%3Ftring");
94 
95     url = urlFromPieces("/", "bad&tring");
96     EXPECT_EQ(url.buffer(), "/%2F/bad&tring");
97 
98     EXPECT_EQ(std::string_view(url.data(), url.size()), "/%2F/bad&tring");
99 
100     url = urlFromPieces("my-user");
101     EXPECT_EQ(std::string_view(url.data(), url.size()), "/my-user");
102 
103     url = urlFromPieces("my_user");
104     EXPECT_EQ(std::string_view(url.data(), url.size()), "/my_user");
105 
106     url = urlFromPieces("my_93user");
107     EXPECT_EQ(std::string_view(url.data(), url.size()), "/my_93user");
108 
109     // The following characters will be converted to ASCII number
110     // `[{]}\|"<>/?#%^
111     url =
112         urlFromPieces("~1234567890-_=+qwertyuiopasdfghjklzxcvbnm;:',.!@$&*()");
113     EXPECT_EQ(std::string_view(url.data(), url.size()),
114               "/~1234567890-_=+qwertyuiopasdfghjklzxcvbnm;:',.!@$&*()");
115 }
116 
117 TEST(Utility, readUrlSegments)
118 {
119     boost::urls::result<boost::urls::url_view> parsed =
120         boost::urls::parse_relative_ref("/redfish/v1/Chassis#/Fans/0/Reading");
121 
122     EXPECT_TRUE(readUrlSegments(*parsed, "redfish", "v1", "Chassis"));
123 
124     EXPECT_FALSE(readUrlSegments(*parsed, "FOOBAR", "v1", "Chassis"));
125 
126     EXPECT_FALSE(readUrlSegments(*parsed, "redfish", "v1"));
127 
128     EXPECT_FALSE(
129         readUrlSegments(*parsed, "redfish", "v1", "Chassis", "FOOBAR"));
130 
131     std::string out1;
132     std::string out2;
133     std::string out3;
134     EXPECT_TRUE(readUrlSegments(*parsed, "redfish", "v1", std::ref(out1)));
135     EXPECT_EQ(out1, "Chassis");
136 
137     out1 = out2 = out3 = "";
138     EXPECT_TRUE(readUrlSegments(*parsed, std::ref(out1), std::ref(out2),
139                                 std::ref(out3)));
140     EXPECT_EQ(out1, "redfish");
141     EXPECT_EQ(out2, "v1");
142     EXPECT_EQ(out3, "Chassis");
143 
144     out1 = out2 = out3 = "";
145     EXPECT_TRUE(readUrlSegments(*parsed, "redfish", std::ref(out1), "Chassis"));
146     EXPECT_EQ(out1, "v1");
147 
148     out1 = out2 = out3 = "";
149     EXPECT_TRUE(readUrlSegments(*parsed, std::ref(out1), "v1", std::ref(out2)));
150     EXPECT_EQ(out1, "redfish");
151     EXPECT_EQ(out2, "Chassis");
152 
153     EXPECT_FALSE(readUrlSegments(*parsed, "too", "short"));
154 
155     EXPECT_FALSE(readUrlSegments(*parsed, "too", "long", "too", "long"));
156 
157     EXPECT_FALSE(
158         readUrlSegments(*parsed, std::ref(out1), "v2", std::ref(out2)));
159 
160     EXPECT_FALSE(readUrlSegments(*parsed, "redfish", std::ref(out1),
161                                  std::ref(out2), std::ref(out3)));
162 
163     parsed = boost::urls::parse_relative_ref("/absolute/url");
164     EXPECT_TRUE(readUrlSegments(*parsed, "absolute", "url"));
165 
166     parsed = boost::urls::parse_relative_ref("not/absolute/url");
167     EXPECT_FALSE(readUrlSegments(*parsed, "not", "absolute", "url"));
168 
169     parsed = boost::urls::parse_relative_ref("/excellent/path");
170 
171     EXPECT_TRUE(readUrlSegments(*parsed, "excellent", "path", OrMorePaths()));
172     EXPECT_TRUE(readUrlSegments(*parsed, "excellent", OrMorePaths()));
173     EXPECT_TRUE(readUrlSegments(*parsed, OrMorePaths()));
174 }
175 
176 TEST(Utility, ValidateAndSplitUrlPositive)
177 {
178     std::string host;
179     std::string urlProto;
180     uint16_t port = 0;
181     std::string path;
182     ASSERT_TRUE(validateAndSplitUrl("https://foo.com:18080/bar", urlProto, host,
183                                     port, path));
184     EXPECT_EQ(host, "foo.com");
185     EXPECT_EQ(urlProto, "https");
186     EXPECT_EQ(port, 18080);
187 
188     EXPECT_EQ(path, "/bar");
189 
190     // query string
191     ASSERT_TRUE(validateAndSplitUrl("https://foo.com:18080/bar?foobar=1",
192                                     urlProto, host, port, path));
193     EXPECT_EQ(path, "/bar?foobar=1");
194 
195     // fragment
196     ASSERT_TRUE(validateAndSplitUrl("https://foo.com:18080/bar#frag", urlProto,
197                                     host, port, path));
198     EXPECT_EQ(path, "/bar#frag");
199 
200     // Missing port
201     ASSERT_TRUE(
202         validateAndSplitUrl("https://foo.com/bar", urlProto, host, port, path));
203     EXPECT_EQ(port, 443);
204 
205     // Missing path defaults to "/"
206     ASSERT_TRUE(
207         validateAndSplitUrl("https://foo.com/", urlProto, host, port, path));
208     EXPECT_EQ(path, "/");
209 
210     // If http push eventing is allowed, allow http and pick a default port of
211     // 80, if it's not, parse should fail.
212     ASSERT_EQ(
213         validateAndSplitUrl("http://foo.com/bar", urlProto, host, port, path),
214         bmcwebInsecureEnableHttpPushStyleEventing);
215     if constexpr (bmcwebInsecureEnableHttpPushStyleEventing)
216     {
217         EXPECT_EQ(port, 80);
218     }
219 }
220 
221 TEST(Router, ParameterTagging)
222 {
223     EXPECT_EQ(6 * 6 + 6 * 3 + 2, getParameterTag("<uint><double><int>"));
224     EXPECT_EQ(1, getParameterTag("<int>"));
225     EXPECT_EQ(2, getParameterTag("<uint>"));
226     EXPECT_EQ(3, getParameterTag("<float>"));
227     EXPECT_EQ(3, getParameterTag("<double>"));
228     EXPECT_EQ(4, getParameterTag("<str>"));
229     EXPECT_EQ(4, getParameterTag("<string>"));
230     EXPECT_EQ(5, getParameterTag("<path>"));
231     EXPECT_EQ(6 * 6 + 6 + 1, getParameterTag("<int><int><int>"));
232     EXPECT_EQ(6 * 6 + 6 + 2, getParameterTag("<uint><int><int>"));
233     EXPECT_EQ(6 * 6 + 6 * 3 + 2, getParameterTag("<uint><double><int>"));
234 }
235 
236 TEST(URL, JsonEncoding)
237 {
238     std::string urlString = "/foo";
239     EXPECT_EQ(nlohmann::json(boost::urls::url(urlString)), urlString);
240     EXPECT_EQ(nlohmann::json(boost::urls::url_view(urlString)), urlString);
241 }
242 
243 TEST(AppendUrlFromPieces, PiecesAreAppendedViaDelimiters)
244 {
245     boost::urls::url url = urlFromPieces("redfish", "v1", "foo");
246     EXPECT_EQ(std::string_view(url.data(), url.size()), "/redfish/v1/foo");
247 
248     appendUrlPieces(url, "bar");
249     EXPECT_EQ(std::string_view(url.data(), url.size()), "/redfish/v1/foo/bar");
250 
251     appendUrlPieces(url, "/", "bad&tring");
252     EXPECT_EQ(std::string_view(url.data(), url.size()),
253               "/redfish/v1/foo/bar/%2F/bad&tring");
254 }
255 
256 } // namespace
257 } // namespace crow::utility
258