]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/rgw/test_rgw_lua.cc
501144d5a34d25159dbacf00326de93e62ae1277
[ceph.git] / ceph / src / test / rgw / test_rgw_lua.cc
1 #include <gtest/gtest.h>
2 #include "common/ceph_context.h"
3 #include "rgw/rgw_common.h"
4 #include "rgw/rgw_auth.h"
5 #include "rgw/rgw_process.h"
6 #include "rgw/rgw_sal_rados.h"
7 #include "rgw/rgw_lua_request.h"
8
9 using namespace std;
10 using namespace rgw;
11 using boost::container::flat_set;
12 using rgw::auth::Identity;
13 using rgw::auth::Principal;
14
15 class CctCleaner {
16 CephContext* cct;
17 public:
18 CctCleaner(CephContext* _cct) : cct(_cct) {}
19 ~CctCleaner() {
20 #ifdef WITH_SEASTAR
21 delete cct;
22 #else
23 cct->put();
24 #endif
25 }
26 };
27
28 class FakeIdentity : public Identity {
29 public:
30 FakeIdentity() = default;
31
32 uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override {
33 return 0;
34 };
35
36 bool is_admin_of(const rgw_user& uid) const override {
37 return false;
38 }
39
40 bool is_owner_of(const rgw_user& uid) const override {
41 return false;
42 }
43
44 virtual uint32_t get_perm_mask() const override {
45 return 0;
46 }
47
48 uint32_t get_identity_type() const override {
49 return TYPE_RGW;
50 }
51
52 string get_acct_name() const override {
53 return "";
54 }
55
56 string get_subuser() const override {
57 return "";
58 }
59
60 void to_str(std::ostream& out) const override {
61 return;
62 }
63
64 bool is_identity(const flat_set<Principal>& ids) const override {
65 return false;
66 }
67 };
68
69 class TestUser : public sal::User {
70 public:
71 virtual std::unique_ptr<User> clone() override {
72 return std::unique_ptr<User>(new TestUser(*this));
73 }
74
75 virtual int list_buckets(const DoutPrefixProvider *dpp, const string&, const string&, uint64_t, bool, sal::BucketList&, optional_yield y) override {
76 return 0;
77 }
78
79 virtual int create_bucket(const DoutPrefixProvider* dpp, const rgw_bucket& b, const std::string& zonegroup_id, rgw_placement_rule& placement_rule, std::string& swift_ver_location, const RGWQuotaInfo* pquota_info, const RGWAccessControlPolicy& policy, sal::Attrs& attrs, RGWBucketInfo& info, obj_version& ep_objv, bool exclusive, bool obj_lock_enabled, bool* existed, req_info& req_info, std::unique_ptr<sal::Bucket>* bucket, optional_yield y) override {
80 return 0;
81 }
82
83 virtual int read_attrs(const DoutPrefixProvider *dpp, optional_yield y) override {
84 return 0;
85 }
86
87 virtual int read_stats(const DoutPrefixProvider *dpp, optional_yield y, RGWStorageStats* stats, ceph::real_time *last_stats_sync, ceph::real_time *last_stats_update) override {
88 return 0;
89 }
90
91 virtual int read_stats_async(const DoutPrefixProvider *dpp, RGWGetUserStats_CB *cb) override {
92 return 0;
93 }
94
95 virtual int complete_flush_stats(const DoutPrefixProvider *dpp, optional_yield y) override {
96 return 0;
97 }
98
99 virtual int read_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries, bool *is_truncated, RGWUsageIter& usage_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage) override {
100 return 0;
101 }
102
103 virtual int trim_usage(const DoutPrefixProvider *dpp, uint64_t start_epoch, uint64_t end_epoch) override {
104 return 0;
105 }
106
107 virtual int load_user(const DoutPrefixProvider *dpp, optional_yield y) override {
108 return 0;
109 }
110
111 virtual int store_user(const DoutPrefixProvider* dpp, optional_yield y, bool exclusive, RGWUserInfo* old_info) override {
112 return 0;
113 }
114
115 virtual int remove_user(const DoutPrefixProvider* dpp, optional_yield y) override {
116 return 0;
117 }
118 virtual int merge_and_store_attrs(const DoutPrefixProvider *dpp, rgw::sal::Attrs& attrs, optional_yield y) override {
119 return 0;
120 }
121 virtual ~TestUser() = default;
122 };
123
124 class TestAccounter : public io::Accounter, public io::BasicClient {
125 RGWEnv env;
126
127 protected:
128 virtual int init_env(CephContext *cct) override {
129 return 0;
130 }
131
132 public:
133 ~TestAccounter() = default;
134
135 virtual void set_account(bool enabled) override {
136 }
137
138 virtual uint64_t get_bytes_sent() const override {
139 return 0;
140 }
141
142 virtual uint64_t get_bytes_received() const override {
143 return 0;
144 }
145
146 virtual RGWEnv& get_env() noexcept override {
147 return env;
148 }
149
150 virtual size_t complete_request() override {
151 return 0;
152 }
153 };
154
155 auto cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
156
157 CctCleaner cleaner(cct);
158
159 TEST(TestRGWLua, EmptyScript)
160 {
161 const std::string script;
162
163 RGWEnv e;
164 uint64_t id = 0;
165 req_state s(cct, &e, id);
166
167 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
168 ASSERT_EQ(rc, 0);
169 }
170
171 #define DEFINE_REQ_STATE RGWEnv e; req_state s(cct, &e, 0);
172
173 TEST(TestRGWLua, SyntaxError)
174 {
175 const std::string script = R"(
176 if 3 < 5 then
177 RGWDebugLog("missing 'end'")
178 )";
179
180 DEFINE_REQ_STATE;
181
182 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
183 ASSERT_EQ(rc, -1);
184 }
185
186 TEST(TestRGWLua, Hello)
187 {
188 const std::string script = R"(
189 RGWDebugLog("hello from lua")
190 )";
191
192 DEFINE_REQ_STATE;
193
194 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
195 ASSERT_EQ(rc, 0);
196 }
197
198 TEST(TestRGWLua, RGWDebugLogNumber)
199 {
200 const std::string script = R"(
201 RGWDebugLog(1234567890)
202 )";
203
204 DEFINE_REQ_STATE;
205
206 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
207 ASSERT_EQ(rc, 0);
208 }
209
210 TEST(TestRGWLua, RGWDebugNil)
211 {
212 const std::string script = R"(
213 RGWDebugLog(nil)
214 )";
215
216 DEFINE_REQ_STATE;
217
218 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
219 ASSERT_EQ(rc, -1);
220 }
221
222 TEST(TestRGWLua, URI)
223 {
224 const std::string script = R"(
225 RGWDebugLog(Request.DecodedURI)
226 assert(Request.DecodedURI == "http://hello.world/")
227 )";
228
229 DEFINE_REQ_STATE;
230 s.decoded_uri = "http://hello.world/";
231
232 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "", script);
233 ASSERT_EQ(rc, 0);
234 }
235
236 TEST(TestRGWLua, Response)
237 {
238 const std::string script = R"(
239 assert(Request.Response.Message == "This is a bad request")
240 assert(Request.Response.HTTPStatus == "Bad Request")
241 assert(Request.Response.RGWCode == 4000)
242 assert(Request.Response.HTTPStatusCode == 400)
243 )";
244
245 DEFINE_REQ_STATE;
246 s.err.http_ret = 400;
247 s.err.ret = 4000;
248 s.err.err_code = "Bad Request";
249 s.err.message = "This is a bad request";
250
251 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
252 ASSERT_EQ(rc, 0);
253 }
254
255 TEST(TestRGWLua, SetResponse)
256 {
257 const std::string script = R"(
258 assert(Request.Response.Message == "this is a bad request")
259 Request.Response.Message = "this is a good request"
260 assert(Request.Response.Message == "this is a good request")
261 )";
262
263 DEFINE_REQ_STATE;
264 s.err.message = "this is a bad request";
265
266 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
267 ASSERT_EQ(rc, 0);
268 }
269
270 TEST(TestRGWLua, SetRGWId)
271 {
272 const std::string script = R"(
273 assert(Request.RGWId == "foo")
274 Request.RGWId = "bar"
275 )";
276
277 DEFINE_REQ_STATE;
278 s.host_id = "foo";
279
280 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
281 ASSERT_NE(rc, 0);
282 }
283
284 TEST(TestRGWLua, InvalidField)
285 {
286 const std::string script = R"(
287 RGWDebugLog(Request.Kaboom)
288 )";
289
290 DEFINE_REQ_STATE;
291 s.host_id = "foo";
292
293 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "kaboom", script);
294 ASSERT_EQ(rc, -1);
295 }
296
297 TEST(TestRGWLua, InvalidSubField)
298 {
299 const std::string script = R"(
300 RGWDebugLog(Request.Error.Kaboom)
301 )";
302
303 DEFINE_REQ_STATE;
304
305 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "kaboom", script);
306 ASSERT_EQ(rc, -1);
307 }
308
309 TEST(TestRGWLua, Bucket)
310 {
311 const std::string script = R"(
312 assert(Request.Bucket)
313 RGWDebugLog("Bucket Id: " .. Request.Bucket.Id)
314 assert(Request.Bucket.Marker == "mymarker")
315 assert(Request.Bucket.Name == "myname")
316 assert(Request.Bucket.Tenant == "mytenant")
317 assert(Request.Bucket.Count == 0)
318 assert(Request.Bucket.Size == 0)
319 assert(Request.Bucket.ZoneGroupId)
320 assert(Request.Bucket.CreationTime)
321 assert(Request.Bucket.MTime)
322 assert(Request.Bucket.Quota.MaxSize == -1)
323 assert(Request.Bucket.Quota.MaxObjects == -1)
324 assert(tostring(Request.Bucket.Quota.Enabled))
325 assert(tostring(Request.Bucket.Quota.Rounded))
326 assert(Request.Bucket.User.Id)
327 assert(Request.Bucket.User.Tenant)
328 )";
329
330 DEFINE_REQ_STATE;
331
332 rgw_bucket b;
333 b.tenant = "mytenant";
334 b.name = "myname";
335 b.marker = "mymarker";
336 b.bucket_id = "myid";
337 s.bucket.reset(new sal::RadosBucket(nullptr, b));
338
339 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
340 ASSERT_EQ(rc, 0);
341 }
342
343 TEST(TestRGWLua, GenericAttributes)
344 {
345 const std::string script = R"(
346 assert(Request.GenericAttributes["hello"] == "world")
347 assert(Request.GenericAttributes["foo"] == "bar")
348 assert(Request.GenericAttributes["kaboom"] == nil)
349 assert(#Request.GenericAttributes == 4)
350 for k, v in pairs(Request.GenericAttributes) do
351 assert(k)
352 assert(v)
353 end
354 )";
355
356 DEFINE_REQ_STATE;
357 s.generic_attrs["hello"] = "world";
358 s.generic_attrs["foo"] = "bar";
359 s.generic_attrs["goodbye"] = "cruel world";
360 s.generic_attrs["ka"] = "boom";
361
362 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
363 ASSERT_EQ(rc, 0);
364 }
365
366 TEST(TestRGWLua, Environment)
367 {
368 const std::string script = R"(
369 assert(Request.Environment[""] == "bar")
370 assert(Request.Environment["goodbye"] == "cruel world")
371 assert(Request.Environment["ka"] == "boom")
372 assert(#Request.Environment == 3, #Request.Environment)
373 for k, v in pairs(Request.Environment) do
374 assert(k)
375 assert(v)
376 end
377 )";
378
379 DEFINE_REQ_STATE;
380 s.env.emplace("", "bar");
381 s.env.emplace("goodbye", "cruel world");
382 s.env.emplace("ka", "boom");
383
384 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
385 ASSERT_EQ(rc, 0);
386 }
387
388 TEST(TestRGWLua, Tags)
389 {
390 const std::string script = R"(
391 assert(#Request.Tags == 4)
392 assert(Request.Tags["foo"] == "bar")
393 for k, v in pairs(Request.Tags) do
394 assert(k)
395 assert(v)
396 end
397 )";
398
399 DEFINE_REQ_STATE;
400 s.tagset.add_tag("hello", "world");
401 s.tagset.add_tag("foo", "bar");
402 s.tagset.add_tag("goodbye", "cruel world");
403 s.tagset.add_tag("ka", "boom");
404
405 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
406 ASSERT_EQ(rc, 0);
407 }
408
409 TEST(TestRGWLua, TagsNotWriteable)
410 {
411 const std::string script = R"(
412 Request.Tags["hello"] = "goodbye"
413 )";
414
415 DEFINE_REQ_STATE;
416 s.tagset.add_tag("hello", "world");
417
418 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
419 ASSERT_NE(rc, 0);
420 }
421
422 TEST(TestRGWLua, Metadata)
423 {
424 const std::string script = R"(
425 assert(#Request.HTTP.Metadata == 3)
426 for k, v in pairs(Request.HTTP.Metadata) do
427 assert(k)
428 assert(v)
429 end
430 assert(Request.HTTP.Metadata["hello"] == "world")
431 assert(Request.HTTP.Metadata["kaboom"] == nil)
432 Request.HTTP.Metadata["hello"] = "goodbye"
433 Request.HTTP.Metadata["kaboom"] = "boom"
434 assert(#Request.HTTP.Metadata == 4)
435 assert(Request.HTTP.Metadata["hello"] == "goodbye")
436 assert(Request.HTTP.Metadata["kaboom"] == "boom")
437 )";
438
439 DEFINE_REQ_STATE;
440 s.info.x_meta_map["hello"] = "world";
441 s.info.x_meta_map["foo"] = "bar";
442 s.info.x_meta_map["ka"] = "boom";
443
444 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
445 ASSERT_EQ(rc, 0);
446 }
447
448 TEST(TestRGWLua, Acl)
449 {
450 const std::string script = R"(
451 function print_grant(g)
452 print("Grant Type: " .. g.Type)
453 print("Grant Group Type: " .. g.GroupType)
454 print("Grant Referer: " .. g.Referer)
455 if (g.User) then
456 print("Grant User.Tenant: " .. g.User.Tenant)
457 print("Grant User.Id: " .. g.User.Id)
458 end
459 end
460
461 assert(Request.UserAcl.Owner.DisplayName == "jack black", Request.UserAcl.Owner.DisplayName)
462 assert(Request.UserAcl.Owner.User.Id == "black", Request.UserAcl.Owner.User.Id)
463 assert(Request.UserAcl.Owner.User.Tenant == "jack", Request.UserAcl.Owner.User.Tenant)
464 assert(#Request.UserAcl.Grants == 5)
465 print_grant(Request.UserAcl.Grants[""])
466 for k, v in pairs(Request.UserAcl.Grants) do
467 print_grant(v)
468 if k == "john$doe" then
469 assert(v.Permission == 4)
470 elseif k == "jane$doe" then
471 assert(v.Permission == 1)
472 else
473 assert(false)
474 end
475 end
476 )";
477
478 DEFINE_REQ_STATE;
479 ACLOwner owner;
480 owner.set_id(rgw_user("jack", "black"));
481 owner.set_name("jack black");
482 s.user_acl.reset(new RGWAccessControlPolicy(cct));
483 s.user_acl->set_owner(owner);
484 ACLGrant grant1, grant2, grant3, grant4, grant5;
485 grant1.set_canon(rgw_user("jane", "doe"), "her grant", 1);
486 grant2.set_group(ACL_GROUP_ALL_USERS ,2);
487 grant3.set_referer("http://localhost/ref2", 3);
488 grant4.set_canon(rgw_user("john", "doe"), "his grant", 4);
489 grant5.set_group(ACL_GROUP_AUTHENTICATED_USERS, 5);
490 s.user_acl->get_acl().add_grant(&grant1);
491 s.user_acl->get_acl().add_grant(&grant2);
492 s.user_acl->get_acl().add_grant(&grant3);
493 s.user_acl->get_acl().add_grant(&grant4);
494 s.user_acl->get_acl().add_grant(&grant5);
495 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
496 ASSERT_EQ(rc, 0);
497 }
498
499 TEST(TestRGWLua, User)
500 {
501 const std::string script = R"(
502 assert(Request.User)
503 assert(Request.User.Id == "myid")
504 assert(Request.User.Tenant == "mytenant")
505 )";
506
507 DEFINE_REQ_STATE;
508
509 rgw_user u;
510 u.tenant = "mytenant";
511 u.id = "myid";
512 s.user.reset(new sal::RadosUser(nullptr, u));
513
514 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
515 ASSERT_EQ(rc, 0);
516 }
517
518
519 TEST(TestRGWLua, UseFunction)
520 {
521 const std::string script = R"(
522 function print_owner(owner)
523 print("Owner Dispaly Name: " .. owner.DisplayName)
524 print("Owner Id: " .. owner.User.Id)
525 print("Owner Tenanet: " .. owner.User.Tenant)
526 end
527
528 print_owner(Request.ObjectOwner)
529
530 function print_acl(acl_type)
531 index = acl_type .. "ACL"
532 acl = Request[index]
533 if acl then
534 print(acl_type .. "ACL Owner")
535 print_owner(acl.Owner)
536 else
537 print("no " .. acl_type .. " ACL in request: " .. Request.Id)
538 end
539 end
540
541 print_acl("User")
542 print_acl("Bucket")
543 print_acl("Object")
544 )";
545
546 DEFINE_REQ_STATE;
547 s.owner.set_name("user two");
548 s.owner.set_id(rgw_user("tenant2", "user2"));
549 s.user_acl.reset(new RGWAccessControlPolicy());
550 s.user_acl->get_owner().set_name("user three");
551 s.user_acl->get_owner().set_id(rgw_user("tenant3", "user3"));
552 s.bucket_acl.reset(new RGWAccessControlPolicy());
553 s.bucket_acl->get_owner().set_name("user four");
554 s.bucket_acl->get_owner().set_id(rgw_user("tenant4", "user4"));
555 s.object_acl.reset(new RGWAccessControlPolicy());
556 s.object_acl->get_owner().set_name("user five");
557 s.object_acl->get_owner().set_id(rgw_user("tenant5", "user5"));
558
559 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
560 ASSERT_EQ(rc, 0);
561 }
562
563 TEST(TestRGWLua, WithLib)
564 {
565 const std::string script = R"(
566 expected_result = {"my", "bucket", "name", "is", "fish"}
567 i = 1
568 for p in string.gmatch(Request.Bucket.Name, "%a+") do
569 assert(p == expected_result[i])
570 i = i + 1
571 end
572 )";
573
574 DEFINE_REQ_STATE;
575
576 rgw_bucket b;
577 b.name = "my-bucket-name-is-fish";
578 s.bucket.reset(new sal::RadosBucket(nullptr, b));
579
580 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
581 ASSERT_EQ(rc, 0);
582 }
583
584 TEST(TestRGWLua, NotAllowedInLib)
585 {
586 const std::string script = R"(
587 os.clock() -- this should be ok
588 os.exit() -- this should fail (os.exit() is removed)
589 )";
590
591 DEFINE_REQ_STATE;
592
593 const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
594 ASSERT_NE(rc, 0);
595 }
596 #include <sys/socket.h>
597 #include <stdlib.h>
598
599 bool unix_socket_client_ended_ok = false;
600
601 void unix_socket_client(const std::string& path) {
602 int fd;
603 // create the socket
604 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
605 std::cout << "unix socket error: " << errno << std::endl;
606 return;
607 }
608 // set the path
609 struct sockaddr_un addr;
610 memset(&addr, 0, sizeof(addr));
611 addr.sun_family = AF_UNIX;
612 strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path)-1);
613
614 // let the socket be created by the "rgw" side
615 std::this_thread::sleep_for(std::chrono::seconds(2));
616 if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
617 std::cout << "unix socket connect error: " << errno << std::endl;
618 return;
619 }
620
621 char buff[256];
622 int rc;
623 while((rc=read(fd, buff, sizeof(buff))) > 0) {
624 std::cout << std::string(buff, rc);
625 unix_socket_client_ended_ok = true;
626 }
627 }
628
629 TEST(TestRGWLua, OpsLog)
630 {
631 const std::string unix_socket_path = "./aSocket.sock";
632 unlink(unix_socket_path.c_str());
633
634 std::thread unix_socket_thread(unix_socket_client, unix_socket_path);
635
636 const std::string script = R"(
637 if Request.Response.HTTPStatusCode == 200 then
638 assert(Request.Response.Message == "Life is great")
639 else
640 assert(Request.Bucket)
641 assert(Request.Log() == 0)
642 end
643 )";
644
645 auto store = std::unique_ptr<sal::RadosStore>(new sal::RadosStore);
646 store->setRados(new RGWRados);
647 auto olog = std::unique_ptr<OpsLogSocket>(new OpsLogSocket(cct, 1024));
648 ASSERT_TRUE(olog->init(unix_socket_path));
649
650 DEFINE_REQ_STATE;
651 s.err.http_ret = 200;
652 s.err.ret = 0;
653 s.err.err_code = "200OK";
654 s.err.message = "Life is great";
655 rgw_bucket b;
656 b.tenant = "tenant";
657 b.name = "name";
658 b.marker = "marker";
659 b.bucket_id = "id";
660 s.bucket.reset(new sal::RadosBucket(nullptr, b));
661 s.bucket_name = "name";
662 s.enable_ops_log = true;
663 s.enable_usage_log = false;
664 s.user.reset(new TestUser());
665 TestAccounter ac;
666 s.cio = &ac;
667 s.cct->_conf->rgw_ops_log_rados = false;
668
669 s.auth.identity = std::unique_ptr<rgw::auth::Identity>(
670 new FakeIdentity());
671
672 auto rc = lua::request::execute(store.get(), nullptr, olog.get(), &s, "put_obj", script);
673 EXPECT_EQ(rc, 0);
674
675 s.err.http_ret = 400;
676 rc = lua::request::execute(store.get(), nullptr, olog.get(), &s, "put_obj", script);
677 EXPECT_EQ(rc, 0);
678
679 // give the socket client time to read
680 std::this_thread::sleep_for(std::chrono::seconds(5));
681 unix_socket_thread.detach(); // read is stuck there, so we cannot join
682 EXPECT_TRUE(unix_socket_client_ended_ok);
683 }
684