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"
11 using boost::container::flat_set
;
12 using rgw::auth::Identity
;
13 using rgw::auth::Principal
;
18 CctCleaner(CephContext
* _cct
) : cct(_cct
) {}
28 class FakeIdentity
: public Identity
{
30 FakeIdentity() = default;
32 uint32_t get_perms_from_aclspec(const DoutPrefixProvider
* dpp
, const aclspec_t
& aclspec
) const override
{
36 bool is_admin_of(const rgw_user
& uid
) const override
{
40 bool is_owner_of(const rgw_user
& uid
) const override
{
44 virtual uint32_t get_perm_mask() const override
{
48 uint32_t get_identity_type() const override
{
52 string
get_acct_name() const override
{
56 string
get_subuser() const override
{
60 void to_str(std::ostream
& out
) const override
{
64 bool is_identity(const flat_set
<Principal
>& ids
) const override
{
69 class TestUser
: public sal::User
{
71 virtual std::unique_ptr
<User
> clone() override
{
72 return std::unique_ptr
<User
>(new TestUser(*this));
75 virtual int list_buckets(const DoutPrefixProvider
*dpp
, const string
&, const string
&, uint64_t, bool, sal::BucketList
&, optional_yield y
) override
{
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
{
83 virtual int read_attrs(const DoutPrefixProvider
*dpp
, optional_yield y
) override
{
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
{
91 virtual int read_stats_async(const DoutPrefixProvider
*dpp
, RGWGetUserStats_CB
*cb
) override
{
95 virtual int complete_flush_stats(const DoutPrefixProvider
*dpp
, optional_yield y
) override
{
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
{
103 virtual int trim_usage(const DoutPrefixProvider
*dpp
, uint64_t start_epoch
, uint64_t end_epoch
) override
{
107 virtual int load_user(const DoutPrefixProvider
*dpp
, optional_yield y
) override
{
111 virtual int store_user(const DoutPrefixProvider
* dpp
, optional_yield y
, bool exclusive
, RGWUserInfo
* old_info
) override
{
115 virtual int remove_user(const DoutPrefixProvider
* dpp
, optional_yield y
) override
{
118 virtual int merge_and_store_attrs(const DoutPrefixProvider
*dpp
, rgw::sal::Attrs
& attrs
, optional_yield y
) override
{
121 virtual ~TestUser() = default;
124 class TestAccounter
: public io::Accounter
, public io::BasicClient
{
128 virtual int init_env(CephContext
*cct
) override
{
133 ~TestAccounter() = default;
135 virtual void set_account(bool enabled
) override
{
138 virtual uint64_t get_bytes_sent() const override
{
142 virtual uint64_t get_bytes_received() const override
{
146 virtual RGWEnv
& get_env() noexcept override
{
150 virtual size_t complete_request() override
{
155 auto cct
= new CephContext(CEPH_ENTITY_TYPE_CLIENT
);
157 CctCleaner
cleaner(cct
);
159 TEST(TestRGWLua
, EmptyScript
)
161 const std::string script
;
165 req_state
s(cct
, &e
, id
);
167 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "", script
);
171 #define DEFINE_REQ_STATE RGWEnv e; req_state s(cct, &e, 0);
173 TEST(TestRGWLua
, SyntaxError
)
175 const std::string script
= R
"(
177 RGWDebugLog("missing
'end'")
182 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "", script
);
186 TEST(TestRGWLua
, Hello
)
188 const std::string script
= R
"(
189 RGWDebugLog("hello from lua
")
194 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "", script
);
198 TEST(TestRGWLua
, RGWDebugLogNumber
)
200 const std::string script
= R
"(
201 RGWDebugLog(1234567890)
206 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "", script
);
210 TEST(TestRGWLua
, RGWDebugNil
)
212 const std::string script
= R
"(
218 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "", script
);
222 TEST(TestRGWLua
, URI
)
224 const std::string script
= R
"(
225 RGWDebugLog(Request.DecodedURI)
226 assert(Request.DecodedURI == "http
://hello.world/")
230 s.decoded_uri = "http
://hello.world/";
232 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "", script
);
236 TEST(TestRGWLua
, Response
)
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)
246 s
.err
.http_ret
= 400;
248 s
.err
.err_code
= "Bad Request";
249 s
.err
.message
= "This is a bad request";
251 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
255 TEST(TestRGWLua
, SetResponse
)
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
")
264 s
.err
.message
= "this is a bad request";
266 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
270 TEST(TestRGWLua
, SetRGWId
)
272 const std::string script
= R
"(
273 assert(Request.RGWId == "foo
")
274 Request.RGWId = "bar
"
280 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
284 TEST(TestRGWLua
, InvalidField
)
286 const std::string script
= R
"(
287 RGWDebugLog(Request.Kaboom)
293 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "kaboom", script
);
297 TEST(TestRGWLua
, InvalidSubField
)
299 const std::string script
= R
"(
300 RGWDebugLog(Request.Error.Kaboom)
305 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "kaboom", script
);
309 TEST(TestRGWLua
, Bucket
)
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)
333 b
.tenant
= "mytenant";
335 b
.marker
= "mymarker";
336 b
.bucket_id
= "myid";
337 s
.bucket
.reset(new sal::RadosBucket(nullptr, b
));
339 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
343 TEST(TestRGWLua
, GenericAttributes
)
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
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";
362 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
366 TEST(TestRGWLua
, Environment
)
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
380 s
.env
.emplace("", "bar");
381 s
.env
.emplace("goodbye", "cruel world");
382 s
.env
.emplace("ka", "boom");
384 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
388 TEST(TestRGWLua
, Tags
)
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
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");
405 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
409 TEST(TestRGWLua
, TagsNotWriteable
)
411 const std::string script
= R
"(
412 Request.Tags["hello
"] = "goodbye
"
416 s
.tagset
.add_tag("hello", "world");
418 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
422 TEST(TestRGWLua
, Metadata
)
424 const std::string script
= R
"(
425 assert(#Request.HTTP.Metadata == 3)
426 for k, v in pairs(Request.HTTP.Metadata) do
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
")
440 s
.info
.x_meta_map
["hello"] = "world";
441 s
.info
.x_meta_map
["foo"] = "bar";
442 s
.info
.x_meta_map
["ka"] = "boom";
444 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
448 TEST(TestRGWLua
, Acl
)
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)
456 print("Grant User
.Tenant
: " .. g.User.Tenant)
457 print("Grant User
.Id
: " .. g.User.Id)
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
468 if k == "john$doe
" then
469 assert(v.Permission == 4)
470 elseif k == "jane$doe
" then
471 assert(v.Permission == 1)
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
);
499 TEST(TestRGWLua
, User
)
501 const std::string script
= R
"(
503 assert(Request.User.Id == "myid
")
504 assert(Request.User.Tenant == "mytenant
")
510 u
.tenant
= "mytenant";
512 s
.user
.reset(new sal::RadosUser(nullptr, u
));
514 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
519 TEST(TestRGWLua
, UseFunction
)
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)
528 print_owner(Request.ObjectOwner)
530 function print_acl(acl_type)
531 index = acl_type .. "ACL
"
534 print(acl_type .. "ACL Owner
")
535 print_owner(acl.Owner)
537 print("no
" .. acl_type .. " ACL in request
: " .. Request.Id)
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"));
559 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
563 TEST(TestRGWLua
, WithLib
)
565 const std::string script
= R
"(
566 expected_result = {"my
", "bucket
", "name
", "is
", "fish
"}
568 for p in string.gmatch(Request.Bucket.Name, "%a
+") do
569 assert(p == expected_result[i])
577 b
.name
= "my-bucket-name-is-fish";
578 s
.bucket
.reset(new sal::RadosBucket(nullptr, b
));
580 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
584 TEST(TestRGWLua
, NotAllowedInLib
)
586 const std::string script
= R
"(
587 os.clock() -- this should be ok
588 os.exit() -- this should fail (os.exit() is removed)
593 const auto rc
= lua::request::execute(nullptr, nullptr, nullptr, &s
, "put_obj", script
);
596 #include <sys/socket.h>
599 bool unix_socket_client_ended_ok
= false;
601 void unix_socket_client(const std::string
& path
) {
604 if ((fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
605 std::cout
<< "unix socket error: " << errno
<< std::endl
;
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);
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
;
623 while((rc
=read(fd
, buff
, sizeof(buff
))) > 0) {
624 std::cout
<< std::string(buff
, rc
);
625 unix_socket_client_ended_ok
= true;
629 TEST(TestRGWLua
, OpsLog
)
631 const std::string unix_socket_path
= "./aSocket.sock";
632 unlink(unix_socket_path
.c_str());
634 std::thread
unix_socket_thread(unix_socket_client
, unix_socket_path
);
636 const std::string script
= R
"(
637 if Request.Response.HTTPStatusCode == 200 then
638 assert(Request.Response.Message == "Life is great
")
640 assert(Request.Bucket)
641 assert(Request.Log() == 0)
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
));
651 s
.err
.http_ret
= 200;
653 s
.err
.err_code
= "200OK";
654 s
.err
.message
= "Life is great";
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());
667 s
.cct
->_conf
->rgw_ops_log_rados
= false;
669 s
.auth
.identity
= std::unique_ptr
<rgw::auth::Identity
>(
672 auto rc
= lua::request::execute(store
.get(), nullptr, olog
.get(), &s
, "put_obj", script
);
675 s
.err
.http_ret
= 400;
676 rc
= lua::request::execute(store
.get(), nullptr, olog
.get(), &s
, "put_obj", script
);
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
);