#include <gmock/gmock.h>
#include "common/ceph_context.h"
#include "rgw/rgw_common.h"
+#define FORTEST_VIRTUAL virtual
#include "rgw/rgw_kms.cc"
using ::testing::_;
using ::testing::Action;
using ::testing::ActionInterface;
using ::testing::MakeAction;
+using ::testing::StrEq;
class MockTransitSecretEngine : public TransitSecretEngine {
public:
- MockTransitSecretEngine(CephContext *cct) : TransitSecretEngine(cct){}
+ MockTransitSecretEngine(CephContext *cct, EngineParmMap parms) : TransitSecretEngine(cct, parms){}
- MOCK_METHOD(int, send_request, (std::string_view key_id, JSONParser* parser), (override));
+ MOCK_METHOD(int, send_request, (const char *method, std::string_view infix, std::string_view key_id, const std::string& postdata, bufferlist &bl), (override));
};
class MockKvSecretEngine : public KvSecretEngine {
public:
- MockKvSecretEngine(CephContext *cct) : KvSecretEngine(cct){}
+ MockKvSecretEngine(CephContext *cct, EngineParmMap parms) : KvSecretEngine(cct, parms){}
- MOCK_METHOD(int, send_request, (std::string_view key_id, JSONParser* parser), (override));
+ MOCK_METHOD(int, send_request, (const char *method, std::string_view infix, std::string_view key_id, const std::string& postdata, bufferlist &bl), (override));
};
protected:
CephContext *cct;
- MockTransitSecretEngine* transit_engine;
+ MockTransitSecretEngine* old_engine;
MockKvSecretEngine* kv_engine;
+ MockTransitSecretEngine* transit_engine;
void SetUp() override {
+ EngineParmMap old_parms, kv_parms, new_parms;
cct = (new CephContext(CEPH_ENTITY_TYPE_ANY))->get();
- transit_engine = new MockTransitSecretEngine(cct);
- kv_engine = new MockKvSecretEngine(cct);
+ old_parms["compat"] = "2";
+ old_engine = new MockTransitSecretEngine(cct, std::move(old_parms));
+ kv_engine = new MockKvSecretEngine(cct, std::move(kv_parms));
+ new_parms["compat"] = "1";
+ transit_engine = new MockTransitSecretEngine(cct, std::move(new_parms));
}
void TearDown() {
- delete transit_engine;
+ delete old_engine;
delete kv_engine;
+ delete transit_engine;
}
};
TEST_F(TestSSEKMS, vault_token_file_unset)
{
cct->_conf.set_val("rgw_crypt_vault_auth", "token");
- TransitSecretEngine te(cct);
- KvSecretEngine kv(cct);
+ EngineParmMap old_parms, kv_parms;
+ TransitSecretEngine te(cct, std::move(old_parms));
+ KvSecretEngine kv(cct, std::move(kv_parms));
std::string_view key_id("my_key");
std::string actual_key;
{
cct->_conf.set_val("rgw_crypt_vault_auth", "token");
cct->_conf.set_val("rgw_crypt_vault_token_file", "/nonexistent/file");
- TransitSecretEngine te(cct);
- KvSecretEngine kv(cct);
+ EngineParmMap old_parms, kv_parms;
+ TransitSecretEngine te(cct, std::move(old_parms));
+ KvSecretEngine kv(cct, std::move(kv_parms));
std::string_view key_id("my_key/1");
std::string actual_key;
}
-typedef int SendRequestMethod(std::string_view, JSONParser*);
+typedef int SendRequestMethod(const char *,
+ std::string_view, std::string_view,
+ const std::string &, bufferlist &);
class SetPointedValueAction : public ActionInterface<SendRequestMethod> {
public:
- string json;
+ std::string json;
SetPointedValueAction(std::string json){
this->json = json;
}
- int Perform(const ::std::tuple<std::string_view, JSONParser*>& args) override {
- JSONParser* p = ::std::get<1>(args);
- JSONParser* parser = new JSONParser();
-
- parser->parse(json.c_str(), json.length());
- *p = *parser;
+ int Perform(const ::std::tuple<const char *, std::string_view, std::string_view, const std::string &, bufferlist &>& args) override {
+// const char *method = ::std::get<0>(args);
+// std::string_view infix = ::std::get<1>(args);
+// std::string_view key_id = ::std::get<2>(args);
+// const std::string& postdata = ::std::get<3>(args);
+ bufferlist& bl = ::std::get<4>(args);
+
+// std::cout << "method = " << method << " infix = " << infix << " key_id = " << key_id
+// << " postdata = " << postdata
+// << " => json = " << json
+// << std::endl;
+
+ bl.append(json);
+ // note: in the bufferlist, the string is not
+ // necessarily 0 terminated at this point. Logic in
+ // rgw_kms.cc must handle this (by appending a 0.)
return 0;
}
};
TEST_F(TestSSEKMS, test_transit_key_version_extraction){
string json = R"({"data": {"keys": {"6": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
- EXPECT_CALL(*transit_engine, send_request(_, _)).WillOnce(SetPointedValue(json));
+ EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("1/2/3/4/5/6"), StrEq(""), _)).WillOnce(SetPointedValue(json));
std::string actual_key;
std::string tests[11] {"/", "my_key/", "my_key", "", "my_key/a", "my_key/1a",
int res;
for (const auto &test: tests) {
- res = transit_engine->get_key(std::string_view(test), actual_key);
+ res = old_engine->get_key(std::string_view(test), actual_key);
ASSERT_EQ(res, -EINVAL);
}
- res = transit_engine->get_key(std::string_view("1/2/3/4/5/6"), actual_key);
+ res = old_engine->get_key(std::string_view("1/2/3/4/5/6"), actual_key);
ASSERT_EQ(res, 0);
ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
}
// Mocks the expected return Value from Vault Server using custom Argument Action
string json = R"({"data": {"keys": {"1": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
- EXPECT_CALL(*transit_engine, send_request(_, _)).WillOnce(SetPointedValue(json));
+ EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("my_key/1"), StrEq(""), _)).WillOnce(SetPointedValue(json));
- int res = transit_engine->get_key(my_key, actual_key);
+ int res = old_engine->get_key(my_key, actual_key);
ASSERT_EQ(res, 0);
ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
}
+TEST_F(TestSSEKMS, test_transit_makekey){
+
+ std::string_view my_key("my_key");
+ std::string actual_key;
+ map<string, bufferlist> attrs;
+
+ // Mocks the expected return Value from Vault Server using custom Argument Action
+ string post_json = R"({"data": {"ciphertext": "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ","key_version": 1,"plaintext": "3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="}})";
+ EXPECT_CALL(*transit_engine, send_request(StrEq("POST"), StrEq("/datakey/plaintext/"), StrEq("my_key"), _, _))
+ .WillOnce(SetPointedValue(post_json));
+
+ set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, R"({"aws:s3:arn": "fred"})");
+ set_attr(attrs, RGW_ATTR_CRYPT_KEYID, my_key);
+
+ int res = transit_engine->make_actual_key(attrs, actual_key);
+ std::string cipher_text { get_str_attribute(attrs,RGW_ATTR_CRYPT_DATAKEY) };
+
+ ASSERT_EQ(res, 0);
+ ASSERT_EQ(actual_key, from_base64("3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="));
+ ASSERT_EQ(cipher_text, "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ");
+}
+
+TEST_F(TestSSEKMS, test_transit_reconstitutekey){
+
+ std::string_view my_key("my_key");
+ std::string actual_key;
+ map<string, bufferlist> attrs;
+
+ // Mocks the expected return Value from Vault Server using custom Argument Action
+ set_attr(attrs, RGW_ATTR_CRYPT_DATAKEY, "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ");
+ string post_json = R"({"data": {"key_version": 1,"plaintext": "3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="}})";
+ EXPECT_CALL(*transit_engine, send_request(StrEq("POST"), StrEq("/decrypt/"), StrEq("my_key"), _, _))
+ .WillOnce(SetPointedValue(post_json));
+
+ set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, R"({"aws:s3:arn": "fred"})");
+ set_attr(attrs, RGW_ATTR_CRYPT_KEYID, my_key);
+
+ int res = transit_engine->reconstitute_actual_key(attrs, actual_key);
+
+ ASSERT_EQ(res, 0);
+ ASSERT_EQ(actual_key, from_base64("3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="));
+}
+
TEST_F(TestSSEKMS, test_kv_backend){
std::string_view my_key("my_key");
// Mocks the expected return value from Vault Server using custom Argument Action
string json = R"({"data": {"data": {"key": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
- EXPECT_CALL(*kv_engine, send_request(_, _)).WillOnce(SetPointedValue(json));
+ EXPECT_CALL(*kv_engine, send_request(StrEq("GET"), StrEq(""), StrEq("my_key"), StrEq(""), _))
+ .WillOnce(SetPointedValue(json));
int res = kv_engine->get_key(my_key, actual_key);
}
+TEST_F(TestSSEKMS, string_ends_maybe_slash)
+{
+ struct { std::string hay, needle; bool expected; } tests[] ={
+ {"jack here", "fred", false},
+ {"here is a fred", "fred", true},
+ {"and a fred/", "fred", true},
+ {"no fred here", "fred", false},
+ {"double fred//", "fred", true},
+ };
+ for (const auto &test: tests) {
+ bool expected { string_ends_maybe_slash(test.hay, test.needle) };
+ ASSERT_EQ(expected, test.expected);
+ }
+}
+
+
TEST_F(TestSSEKMS, test_transit_backend_empty_response)
{
std::string_view my_key("/key/nonexistent/1");
// Mocks the expected return Value from Vault Server using custom Argument Action
string json = R"({"errors": ["version does not exist or cannot be found"]})";
- EXPECT_CALL(*transit_engine, send_request(_, _)).WillOnce(SetPointedValue(json));
+ EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("/key/nonexistent/1"), StrEq(""), _)).WillOnce(SetPointedValue(json));
- int res = transit_engine->get_key(my_key, actual_key);
+ int res = old_engine->get_key(my_key, actual_key);
ASSERT_EQ(res, -EINVAL);
ASSERT_EQ(actual_key, from_base64(""));
}
-