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