]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include <gtest/gtest.h> | |
5 | #include <gmock/gmock.h> | |
6 | #include "common/ceph_context.h" | |
7 | #include "rgw/rgw_common.h" | |
f67539c2 | 8 | #define FORTEST_VIRTUAL virtual |
9f95a23c TL |
9 | #include "rgw/rgw_kms.cc" |
10 | ||
11 | using ::testing::_; | |
12 | using ::testing::Action; | |
13 | using ::testing::ActionInterface; | |
14 | using ::testing::MakeAction; | |
f67539c2 | 15 | using ::testing::StrEq; |
9f95a23c TL |
16 | |
17 | ||
18 | class MockTransitSecretEngine : public TransitSecretEngine { | |
19 | ||
20 | public: | |
f67539c2 | 21 | MockTransitSecretEngine(CephContext *cct, EngineParmMap parms) : TransitSecretEngine(cct, parms){} |
9f95a23c | 22 | |
f67539c2 | 23 | MOCK_METHOD(int, send_request, (const char *method, std::string_view infix, std::string_view key_id, const std::string& postdata, bufferlist &bl), (override)); |
9f95a23c TL |
24 | |
25 | }; | |
26 | ||
27 | class MockKvSecretEngine : public KvSecretEngine { | |
28 | ||
29 | public: | |
f67539c2 | 30 | MockKvSecretEngine(CephContext *cct, EngineParmMap parms) : KvSecretEngine(cct, parms){} |
9f95a23c | 31 | |
f67539c2 | 32 | MOCK_METHOD(int, send_request, (const char *method, std::string_view infix, std::string_view key_id, const std::string& postdata, bufferlist &bl), (override)); |
9f95a23c TL |
33 | |
34 | }; | |
35 | ||
36 | class TestSSEKMS : public ::testing::Test { | |
37 | ||
38 | protected: | |
39 | CephContext *cct; | |
f67539c2 | 40 | MockTransitSecretEngine* old_engine; |
9f95a23c | 41 | MockKvSecretEngine* kv_engine; |
f67539c2 | 42 | MockTransitSecretEngine* transit_engine; |
9f95a23c TL |
43 | |
44 | void SetUp() override { | |
f67539c2 | 45 | EngineParmMap old_parms, kv_parms, new_parms; |
9f95a23c | 46 | cct = (new CephContext(CEPH_ENTITY_TYPE_ANY))->get(); |
f67539c2 TL |
47 | old_parms["compat"] = "2"; |
48 | old_engine = new MockTransitSecretEngine(cct, std::move(old_parms)); | |
49 | kv_engine = new MockKvSecretEngine(cct, std::move(kv_parms)); | |
50 | new_parms["compat"] = "1"; | |
51 | transit_engine = new MockTransitSecretEngine(cct, std::move(new_parms)); | |
9f95a23c TL |
52 | } |
53 | ||
54 | void TearDown() { | |
f67539c2 | 55 | delete old_engine; |
9f95a23c | 56 | delete kv_engine; |
f67539c2 | 57 | delete transit_engine; |
9f95a23c TL |
58 | } |
59 | ||
60 | }; | |
61 | ||
62 | ||
63 | TEST_F(TestSSEKMS, vault_token_file_unset) | |
64 | { | |
65 | cct->_conf.set_val("rgw_crypt_vault_auth", "token"); | |
f67539c2 TL |
66 | EngineParmMap old_parms, kv_parms; |
67 | TransitSecretEngine te(cct, std::move(old_parms)); | |
68 | KvSecretEngine kv(cct, std::move(kv_parms)); | |
9f95a23c | 69 | |
f67539c2 | 70 | std::string_view key_id("my_key"); |
9f95a23c TL |
71 | std::string actual_key; |
72 | ||
73 | ASSERT_EQ(te.get_key(key_id, actual_key), -EINVAL); | |
74 | ASSERT_EQ(kv.get_key(key_id, actual_key), -EINVAL); | |
75 | } | |
76 | ||
77 | ||
78 | TEST_F(TestSSEKMS, non_existent_vault_token_file) | |
79 | { | |
80 | cct->_conf.set_val("rgw_crypt_vault_auth", "token"); | |
81 | cct->_conf.set_val("rgw_crypt_vault_token_file", "/nonexistent/file"); | |
f67539c2 TL |
82 | EngineParmMap old_parms, kv_parms; |
83 | TransitSecretEngine te(cct, std::move(old_parms)); | |
84 | KvSecretEngine kv(cct, std::move(kv_parms)); | |
9f95a23c | 85 | |
f67539c2 | 86 | std::string_view key_id("my_key/1"); |
9f95a23c TL |
87 | std::string actual_key; |
88 | ||
89 | ASSERT_EQ(te.get_key(key_id, actual_key), -ENOENT); | |
90 | ASSERT_EQ(kv.get_key(key_id, actual_key), -ENOENT); | |
91 | } | |
92 | ||
93 | ||
f67539c2 TL |
94 | typedef int SendRequestMethod(const char *, |
95 | std::string_view, std::string_view, | |
96 | const std::string &, bufferlist &); | |
9f95a23c TL |
97 | |
98 | class SetPointedValueAction : public ActionInterface<SendRequestMethod> { | |
99 | public: | |
f67539c2 | 100 | std::string json; |
9f95a23c TL |
101 | |
102 | SetPointedValueAction(std::string json){ | |
103 | this->json = json; | |
104 | } | |
105 | ||
f67539c2 TL |
106 | int Perform(const ::std::tuple<const char *, std::string_view, std::string_view, const std::string &, bufferlist &>& args) override { |
107 | // const char *method = ::std::get<0>(args); | |
108 | // std::string_view infix = ::std::get<1>(args); | |
109 | // std::string_view key_id = ::std::get<2>(args); | |
110 | // const std::string& postdata = ::std::get<3>(args); | |
111 | bufferlist& bl = ::std::get<4>(args); | |
112 | ||
113 | // std::cout << "method = " << method << " infix = " << infix << " key_id = " << key_id | |
114 | // << " postdata = " << postdata | |
115 | // << " => json = " << json | |
116 | // << std::endl; | |
117 | ||
118 | bl.append(json); | |
119 | // note: in the bufferlist, the string is not | |
120 | // necessarily 0 terminated at this point. Logic in | |
121 | // rgw_kms.cc must handle this (by appending a 0.) | |
9f95a23c TL |
122 | return 0; |
123 | } | |
124 | }; | |
125 | ||
126 | Action<SendRequestMethod> SetPointedValue(std::string json) { | |
127 | return MakeAction(new SetPointedValueAction(json)); | |
128 | } | |
129 | ||
130 | ||
131 | TEST_F(TestSSEKMS, test_transit_key_version_extraction){ | |
132 | string json = R"({"data": {"keys": {"6": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})"; | |
f67539c2 | 133 | EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("1/2/3/4/5/6"), StrEq(""), _)).WillOnce(SetPointedValue(json)); |
9f95a23c TL |
134 | |
135 | std::string actual_key; | |
136 | std::string tests[11] {"/", "my_key/", "my_key", "", "my_key/a", "my_key/1a", | |
137 | "my_key/a1", "my_key/1a1", "my_key/1/a", "1", "my_key/1/" | |
138 | }; | |
139 | ||
140 | int res; | |
141 | for (const auto &test: tests) { | |
f67539c2 | 142 | res = old_engine->get_key(std::string_view(test), actual_key); |
9f95a23c TL |
143 | ASSERT_EQ(res, -EINVAL); |
144 | } | |
145 | ||
f67539c2 | 146 | res = old_engine->get_key(std::string_view("1/2/3/4/5/6"), actual_key); |
9f95a23c TL |
147 | ASSERT_EQ(res, 0); |
148 | ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg=")); | |
149 | } | |
150 | ||
151 | ||
152 | TEST_F(TestSSEKMS, test_transit_backend){ | |
153 | ||
f67539c2 | 154 | std::string_view my_key("my_key/1"); |
9f95a23c TL |
155 | std::string actual_key; |
156 | ||
157 | // Mocks the expected return Value from Vault Server using custom Argument Action | |
158 | string json = R"({"data": {"keys": {"1": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})"; | |
f67539c2 | 159 | EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("my_key/1"), StrEq(""), _)).WillOnce(SetPointedValue(json)); |
9f95a23c | 160 | |
f67539c2 | 161 | int res = old_engine->get_key(my_key, actual_key); |
9f95a23c TL |
162 | |
163 | ASSERT_EQ(res, 0); | |
164 | ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg=")); | |
165 | } | |
166 | ||
167 | ||
f67539c2 TL |
168 | TEST_F(TestSSEKMS, test_transit_makekey){ |
169 | ||
170 | std::string_view my_key("my_key"); | |
171 | std::string actual_key; | |
172 | map<string, bufferlist> attrs; | |
173 | ||
174 | // Mocks the expected return Value from Vault Server using custom Argument Action | |
175 | string post_json = R"({"data": {"ciphertext": "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ","key_version": 1,"plaintext": "3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="}})"; | |
176 | EXPECT_CALL(*transit_engine, send_request(StrEq("POST"), StrEq("/datakey/plaintext/"), StrEq("my_key"), _, _)) | |
177 | .WillOnce(SetPointedValue(post_json)); | |
178 | ||
179 | set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, R"({"aws:s3:arn": "fred"})"); | |
180 | set_attr(attrs, RGW_ATTR_CRYPT_KEYID, my_key); | |
181 | ||
182 | int res = transit_engine->make_actual_key(attrs, actual_key); | |
183 | std::string cipher_text { get_str_attribute(attrs,RGW_ATTR_CRYPT_DATAKEY) }; | |
184 | ||
185 | ASSERT_EQ(res, 0); | |
186 | ASSERT_EQ(actual_key, from_base64("3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g=")); | |
187 | ASSERT_EQ(cipher_text, "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ"); | |
188 | } | |
189 | ||
190 | TEST_F(TestSSEKMS, test_transit_reconstitutekey){ | |
191 | ||
192 | std::string_view my_key("my_key"); | |
193 | std::string actual_key; | |
194 | map<string, bufferlist> attrs; | |
195 | ||
196 | // Mocks the expected return Value from Vault Server using custom Argument Action | |
197 | set_attr(attrs, RGW_ATTR_CRYPT_DATAKEY, "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ"); | |
198 | string post_json = R"({"data": {"key_version": 1,"plaintext": "3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="}})"; | |
199 | EXPECT_CALL(*transit_engine, send_request(StrEq("POST"), StrEq("/decrypt/"), StrEq("my_key"), _, _)) | |
200 | .WillOnce(SetPointedValue(post_json)); | |
201 | ||
202 | set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, R"({"aws:s3:arn": "fred"})"); | |
203 | set_attr(attrs, RGW_ATTR_CRYPT_KEYID, my_key); | |
204 | ||
205 | int res = transit_engine->reconstitute_actual_key(attrs, actual_key); | |
206 | ||
207 | ASSERT_EQ(res, 0); | |
208 | ASSERT_EQ(actual_key, from_base64("3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g=")); | |
209 | } | |
210 | ||
9f95a23c TL |
211 | TEST_F(TestSSEKMS, test_kv_backend){ |
212 | ||
f67539c2 | 213 | std::string_view my_key("my_key"); |
9f95a23c TL |
214 | std::string actual_key; |
215 | ||
216 | // Mocks the expected return value from Vault Server using custom Argument Action | |
217 | string json = R"({"data": {"data": {"key": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})"; | |
f67539c2 TL |
218 | EXPECT_CALL(*kv_engine, send_request(StrEq("GET"), StrEq(""), StrEq("my_key"), StrEq(""), _)) |
219 | .WillOnce(SetPointedValue(json)); | |
9f95a23c TL |
220 | |
221 | int res = kv_engine->get_key(my_key, actual_key); | |
222 | ||
223 | ASSERT_EQ(res, 0); | |
224 | ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg=")); | |
225 | } | |
226 | ||
227 | ||
228 | TEST_F(TestSSEKMS, concat_url) | |
229 | { | |
230 | // Each test has 3 strings: | |
231 | // * the base URL | |
232 | // * the path we want to concatenate | |
233 | // * the exepected final URL | |
234 | std::string tests[9][3] ={ | |
235 | {"", "", ""}, | |
236 | {"", "bar", "/bar"}, | |
237 | {"", "/bar", "/bar"}, | |
238 | {"foo", "", "foo"}, | |
239 | {"foo", "bar", "foo/bar"}, | |
240 | {"foo", "/bar", "foo/bar"}, | |
241 | {"foo/", "", "foo/"}, | |
242 | {"foo/", "bar", "foo/bar"}, | |
243 | {"foo/", "/bar", "foo/bar"}, | |
244 | }; | |
245 | for (const auto &test: tests) { | |
246 | std::string url(test[0]), path(test[1]), expected(test[2]); | |
247 | concat_url(url, path); | |
248 | ASSERT_EQ(url, expected); | |
249 | } | |
250 | } | |
251 | ||
252 | ||
f67539c2 TL |
253 | TEST_F(TestSSEKMS, string_ends_maybe_slash) |
254 | { | |
255 | struct { std::string hay, needle; bool expected; } tests[] ={ | |
256 | {"jack here", "fred", false}, | |
257 | {"here is a fred", "fred", true}, | |
258 | {"and a fred/", "fred", true}, | |
259 | {"no fred here", "fred", false}, | |
260 | {"double fred//", "fred", true}, | |
261 | }; | |
262 | for (const auto &test: tests) { | |
263 | bool expected { string_ends_maybe_slash(test.hay, test.needle) }; | |
264 | ASSERT_EQ(expected, test.expected); | |
265 | } | |
266 | } | |
267 | ||
268 | ||
9f95a23c TL |
269 | TEST_F(TestSSEKMS, test_transit_backend_empty_response) |
270 | { | |
f67539c2 | 271 | std::string_view my_key("/key/nonexistent/1"); |
9f95a23c TL |
272 | std::string actual_key; |
273 | ||
274 | // Mocks the expected return Value from Vault Server using custom Argument Action | |
275 | string json = R"({"errors": ["version does not exist or cannot be found"]})"; | |
f67539c2 | 276 | EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("/key/nonexistent/1"), StrEq(""), _)).WillOnce(SetPointedValue(json)); |
9f95a23c | 277 | |
f67539c2 | 278 | int res = old_engine->get_key(my_key, actual_key); |
9f95a23c TL |
279 | |
280 | ASSERT_EQ(res, -EINVAL); | |
281 | ASSERT_EQ(actual_key, from_base64("")); | |
282 | } |