Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rgw: implement get/put object tags for S3 #13753

Merged
merged 5 commits into from Jun 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/rgw/CMakeLists.txt
Expand Up @@ -111,6 +111,8 @@ set(rgw_a_srcs
rgw_rest_user.cc
rgw_role.cc
rgw_swift_auth.cc
rgw_tag.cc
rgw_tag_s3.cc
rgw_tools.cc
rgw_usage.cc
rgw_user.cc
Expand Down
1 change: 1 addition & 0 deletions src/rgw/rgw_auth_s3.cc
Expand Up @@ -38,6 +38,7 @@ static const auto signed_subresources = {
"response-content-language",
"response-content-type",
"response-expires",
"tagging",
"torrent",
"uploadId",
"uploads",
Expand Down
5 changes: 4 additions & 1 deletion src/rgw/rgw_common.cc
Expand Up @@ -73,6 +73,7 @@ rgw_http_errors rgw_http_s3_errors({
{ ERR_TOO_MANY_BUCKETS, {400, "TooManyBuckets" }},
{ ERR_MALFORMED_XML, {400, "MalformedXML" }},
{ ERR_AMZ_CONTENT_SHA256_MISMATCH, {400, "XAmzContentSHA256Mismatch" }},
{ ERR_INVALID_TAG, {400, "InvalidTag"}},
{ ERR_LENGTH_REQUIRED, {411, "MissingContentLength" }},
{ EACCES, {403, "AccessDenied" }},
{ EPERM, {403, "AccessDenied" }},
Expand All @@ -94,6 +95,7 @@ rgw_http_errors rgw_http_s3_errors({
{ ERR_USER_EXIST, {409, "UserAlreadyExists" }},
{ ERR_EMAIL_EXIST, {409, "EmailExists" }},
{ ERR_KEY_EXIST, {409, "KeyExists"}},
{ ERR_TAG_CONFLICT, {409, "OperationAborted"}},
{ ERR_INVALID_SECRET_KEY, {400, "InvalidSecretKey"}},
{ ERR_INVALID_KEY_TYPE, {400, "InvalidKeyType"}},
{ ERR_INVALID_CAP, {400, "InvalidCapability"}},
Expand Down Expand Up @@ -945,7 +947,8 @@ void RGWHTTPArgs::append(const string& name, const string& val)
(name.compare("versioning") == 0) ||
(name.compare("website") == 0) ||
(name.compare("requestPayment") == 0) ||
(name.compare("torrent") == 0)) {
(name.compare("torrent") == 0) ||
(name.compare("tagging") == 0)) {
sub_resources[name] = val;
} else if (name[0] == 'r') { // root of all evil
if ((name.compare("response-content-type") == 0) ||
Expand Down
9 changes: 7 additions & 2 deletions src/rgw/rgw_common.h
Expand Up @@ -49,6 +49,7 @@ using ceph::crypto::MD5;
#define RGW_AMZ_PREFIX "x-amz-"
#define RGW_AMZ_META_PREFIX RGW_AMZ_PREFIX "meta-"
#define RGW_AMZ_WEBSITE_REDIRECT_LOCATION RGW_AMZ_PREFIX "website-redirect-location"
#define RGW_AMZ_TAG_COUNT RGW_AMZ_PREFIX "tagging-count"

#define RGW_SYS_PARAM_PREFIX "rgwx-"

Expand Down Expand Up @@ -78,6 +79,7 @@ using ceph::crypto::MD5;

#define RGW_ATTR_PG_VER RGW_ATTR_PREFIX "pg_ver"
#define RGW_ATTR_SOURCE_ZONE RGW_ATTR_PREFIX "source_zone"
#define RGW_ATTR_TAGS RGW_ATTR_PREFIX RGW_AMZ_PREFIX "tagging"

#define RGW_ATTR_TEMPURL_KEY1 RGW_ATTR_META_PREFIX "temp-url-key"
#define RGW_ATTR_TEMPURL_KEY2 RGW_ATTR_META_PREFIX "temp-url-key-2"
Expand Down Expand Up @@ -206,6 +208,8 @@ using ceph::crypto::MD5;
#define ERR_DELETE_CONFLICT 2206
#define ERR_NO_SUCH_BUCKET_POLICY 2207
#define ERR_INVALID_LOCATION_CONSTRAINT 2208
#define ERR_TAG_CONFLICT 2209
#define ERR_INVALID_TAG 2210

#define ERR_BUSY_RESHARDING 2300

Expand Down Expand Up @@ -472,7 +476,9 @@ enum RGWOpType {
RGW_OP_PUT_BUCKET_POLICY,
RGW_OP_GET_BUCKET_POLICY,
RGW_OP_DELETE_BUCKET_POLICY,

RGW_OP_PUT_OBJ_TAGGING,
RGW_OP_GET_OBJ_TAGGING,
RGW_OP_DELETE_OBJ_TAGGING,
/* rgw specific */
RGW_OP_ADMIN_SET_METADATA,
RGW_OP_GET_OBJ_LAYOUT,
Expand Down Expand Up @@ -2221,7 +2227,6 @@ extern std::string url_decode(const boost::string_view& src_str,
extern void url_encode(const std::string& src,
string& dst);
extern std::string url_encode(const std::string& src);

/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
extern void calc_hmac_sha1(const char *key, int key_len,
const char *msg, int msg_len, char *dest);
Expand Down
24 changes: 24 additions & 0 deletions src/rgw/rgw_iam_policy.cc
Expand Up @@ -411,6 +411,8 @@ static const actpair actpairs[] =
{ "s3:DeleteBucketWebsite", s3DeleteBucketWebsite },
{ "s3:DeleteObject", s3DeleteObject },
{ "s3:DeleteObjectVersion", s3DeleteObjectVersion },
{ "s3:DeleteObjectTagging", s3DeleteObjectTagging },
{ "s3:DeleteObjectVersionTagging", s3DeleteObjectVersionTagging },
{ "s3:DeleteReplicationConfiguration", s3DeleteReplicationConfiguration },
{ "s3:GetAccelerateConfiguration", s3GetAccelerateConfiguration },
{ "s3:GetBucketAcl", s3GetBucketAcl },
Expand All @@ -430,6 +432,8 @@ static const actpair actpairs[] =
{ "s3:GetObjectVersionAcl", s3GetObjectVersionAcl },
{ "s3:GetObjectVersion", s3GetObjectVersion },
{ "s3:GetObjectVersionTorrent", s3GetObjectVersionTorrent },
{ "s3:GetObjectTagging", s3GetObjectTagging },
{ "s3:GetObjectVersionTagging", s3GetObjectVersionTagging},
{ "s3:GetReplicationConfiguration", s3GetReplicationConfiguration },
{ "s3:ListAllMyBuckets", s3ListAllMyBuckets },
{ "s3:ListBucketMultiPartUploads", s3ListBucketMultiPartUploads },
Expand All @@ -450,6 +454,8 @@ static const actpair actpairs[] =
{ "s3:PutObjectAcl", s3PutObjectAcl },
{ "s3:PutObject", s3PutObject },
{ "s3:PutObjectVersionAcl", s3PutObjectVersionAcl },
{ "s3:PutObjectTagging", s3PutObjectTagging },
{ "s3:PutObjectVersionTagging", s3PutObjectVersionTagging },
{ "s3:PutReplicationConfiguration", s3PutReplicationConfiguration },
{ "s3:RestoreObject", s3RestoreObject }};

Expand Down Expand Up @@ -1356,6 +1362,24 @@ const char* action_bit_string(uint64_t action) {

case s3DeleteReplicationConfiguration:
return "s3:DeleteReplicationConfiguration";

case s3PutObjectTagging:
return "s3:PutObjectTagging";

case s3PutObjectVersionTagging:
return "s3:PutObjectVersionTagging";

case s3GetObjectTagging:
return "s3:GetObjectTagging";

case s3GetObjectVersionTagging:
return "s3:GetObjectVersionTagging";

case s3DeleteObjectTagging:
return "s3:DeleteObjectTagging";

case s3DeleteObjectVersionTagging:
return "s3:DeleteObjectVersionTagging";
}
return "s3Invalid";
}
Expand Down
14 changes: 13 additions & 1 deletion src/rgw/rgw_iam_policy.h
Expand Up @@ -91,7 +91,13 @@ static constexpr std::uint64_t s3PutLifecycleConfiguration = 1ULL << 44;
static constexpr std::uint64_t s3PutReplicationConfiguration = 1ULL << 45;
static constexpr std::uint64_t s3GetReplicationConfiguration = 1ULL << 46;
static constexpr std::uint64_t s3DeleteReplicationConfiguration = 1ULL << 47;
static constexpr std::uint64_t s3Count = 48;
static constexpr std::uint64_t s3GetObjectTagging = 1ULL << 48;
static constexpr std::uint64_t s3PutObjectTagging = 1ULL << 49;
static constexpr std::uint64_t s3DeleteObjectTagging = 1ULL << 50;
static constexpr std::uint64_t s3GetObjectVersionTagging = 1ULL << 51;
static constexpr std::uint64_t s3PutObjectVersionTagging = 1ULL << 52;
static constexpr std::uint64_t s3DeleteObjectVersionTagging = 1ULL << 53;
static constexpr std::uint64_t s3Count = 54;
static constexpr std::uint64_t s3All = (1ULL << s3Count) - 1;

namespace {
Expand All @@ -101,6 +107,8 @@ inline int op_to_perm(std::uint64_t op) {
case s3GetObjectTorrent:
case s3GetObjectVersion:
case s3GetObjectVersionTorrent:
case s3GetObjectTagging:
case s3GetObjectVersionTagging:
case s3ListAllMyBuckets:
case s3ListBucket:
case s3ListBucketMultiPartUploads:
Expand All @@ -114,6 +122,10 @@ inline int op_to_perm(std::uint64_t op) {
case s3DeleteObject:
case s3DeleteObjectVersion:
case s3PutObject:
case s3PutObjectTagging:
case s3PutObjectVersionTagging:
case s3DeleteObjectTagging:
case s3DeleteObjectVersionTagging:
case s3RestoreObject:
return RGW_PERM_WRITE;

Expand Down
98 changes: 98 additions & 0 deletions src/rgw/rgw_op.cc
Expand Up @@ -40,6 +40,7 @@
#include "rgw_client_io.h"
#include "rgw_compression.h"
#include "rgw_role.h"
#include "rgw_tag_s3.h"
#include "cls/lock/cls_lock_client.h"
#include "cls/rgw/cls_rgw_client.h"

Expand Down Expand Up @@ -690,6 +691,102 @@ int RGWOp::verify_op_mask()
return 0;
}

int RGWGetObjTags::verify_permission()
{
if (!verify_object_permission(s,
s->object.instance.empty() ?
rgw::IAM::s3GetObjectTagging:
rgw::IAM::s3GetObjectVersionTagging))
return -EACCES;

return 0;
}

void RGWGetObjTags::pre_exec(){
rgw_bucket_object_pre_exec(s);
}

void RGWGetObjTags::execute()
{
rgw_obj obj;
map<string,bufferlist> attrs;

obj = rgw_obj(s->bucket, s->object);

store->set_atomic(s->obj_ctx, obj);

op_ret = get_obj_attrs(store, s, obj, attrs);
auto tags = attrs.find(RGW_ATTR_TAGS);
if(tags != attrs.end()){
has_tags = true;
tags_bl.append(tags->second);
}
send_response_data(tags_bl);
}

int RGWPutObjTags::verify_permission()
{
if (!verify_object_permission(s,
s->object.instance.empty() ?
rgw::IAM::s3PutObjectTagging:
rgw::IAM::s3PutObjectVersionTagging))
return -EACCES;
return 0;
}

void RGWPutObjTags::execute()
{

op_ret = get_params();
if (op_ret < 0)
return;


if (s->object.empty()){
op_ret= -EINVAL; // we only support tagging on existing objects
return;
}

rgw_obj obj;
obj = rgw_obj(s->bucket, s->object);
store->set_atomic(s->obj_ctx, obj);
op_ret = modify_obj_attr(store, s, obj, RGW_ATTR_TAGS, tags_bl);
if (op_ret == -ECANCELED){
op_ret = -ERR_TAG_CONFLICT;
}
}

void RGWDeleteObjTags::pre_exec(){
rgw_bucket_object_pre_exec(s);
}


int RGWDeleteObjTags::verify_permission(){

if (!s->object.empty()){
if (!verify_object_permission(s,
s->object.instance.empty() ?
rgw::IAM::s3DeleteObjectTagging:
rgw::IAM::s3DeleteObjectVersionTagging))
return -EACCES;
}
return 0;
}

void RGWDeleteObjTags::execute() {
if (s->object.empty())
return;

rgw_obj obj;
obj = rgw_obj(s->bucket, s->object);
store->set_atomic(s->obj_ctx, obj);
map <string, bufferlist> attrs;
map <string, bufferlist> rmattr;
bufferlist bl;
rmattr[RGW_ATTR_TAGS] = bl;
op_ret = store->set_attrs(s->obj_ctx, s->bucket_info, obj, attrs, &rmattr);
}

int RGWOp::do_aws4_auth_completion()
{
ldout(s->cct, 5) << "NOTICE: call to do_aws4_auth_completion" << dendl;
Expand Down Expand Up @@ -3375,6 +3472,7 @@ void RGWPutObj::execute()
populate_with_generic_attrs(s, attrs);
rgw_get_request_metadata(s->cct, s->info, attrs);
encode_delete_at_attr(delete_at, attrs);
encode_obj_tags_attr(obj_tags.get(), attrs);

/* Add a custom metadata to expose the information whether an object
* is an SLO or not. Appending the attribute must be performed AFTER
Expand Down
59 changes: 59 additions & 0 deletions src/rgw/rgw_op.h
Expand Up @@ -41,6 +41,7 @@

#include "rgw_lc.h"
#include "rgw_torrent.h"
#include "rgw_tag.h"

#include "include/assert.h"

Expand Down Expand Up @@ -326,6 +327,49 @@ class RGWGetObj_Filter : public RGWGetDataCB
}
};

class RGWGetObjTags : public RGWOp {
protected:
bufferlist tags_bl;
bool has_tags{false};
public:
int verify_permission();
void execute();
void pre_exec();

virtual void send_response_data(bufferlist& bl) = 0;
virtual const string name() noexcept override { return "get_obj_tags"; }
virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
Copy link
Contributor

@chardan chardan Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noexcept? (...then again, at some point the decoration starts to feel excessive, doesn't it? ;->)

RGWOpType get_type() { return RGW_OP_GET_OBJ_TAGGING; }

};

class RGWPutObjTags : public RGWOp {
protected:
bufferlist tags_bl;
public:
int verify_permission();
void execute();

virtual void send_response() = 0;
virtual int get_params() = 0;
virtual const string name() { return "put_obj_tags"; }
virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
RGWOpType get_type() { return RGW_OP_PUT_OBJ_TAGGING; }

};

class RGWDeleteObjTags: public RGWOp {
public:
void pre_exec();
int verify_permission();
void execute();

virtual void send_response() = 0;
virtual const string name() { return "delete_obj_tags"; }
virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; }
RGWOpType get_type() { return RGW_OP_DELETE_OBJ_TAGGING;}
};

class RGWBulkDelete : public RGWOp {
public:
struct acct_path_t {
Expand Down Expand Up @@ -931,6 +975,7 @@ class RGWPutObj : public RGWOp {
string etag;
bool chunked_upload;
RGWAccessControlPolicy policy;
std::unique_ptr <RGWObjTags> obj_tags;
const char *dlo_manifest;
RGWSLOInfo *slo_info;
map<string, bufferlist> attrs;
Expand Down Expand Up @@ -1881,6 +1926,20 @@ static inline void encode_delete_at_attr(boost::optional<ceph::real_time> delete
attrs[RGW_ATTR_DELETE_AT] = delatbl;
} /* encode_delete_at_attr */

static inline void encode_obj_tags_attr(RGWObjTags* obj_tags, map<string, bufferlist>& attrs)
{
if (obj_tags == nullptr){
// we assume the user submitted a tag format which we couldn't parse since
// this wouldn't be parsed later by get/put obj tags, lets delete if the
// attr was populated
return;
}

bufferlist tagsbl;
obj_tags->encode(tagsbl);
attrs[RGW_ATTR_TAGS] = tagsbl;
}

static inline int encode_dlo_manifest_attr(const char * const dlo_manifest,
map<string, bufferlist>& attrs)
{
Expand Down
4 changes: 3 additions & 1 deletion src/rgw/rgw_rest.cc
Expand Up @@ -1941,7 +1941,9 @@ int RGWHandler_REST::read_permissions(RGWOp* op_obj)
only_bucket = true;
break;
case OP_DELETE:
only_bucket = true;
if (!s->info.args.exists("tagging")){
only_bucket = true;
}
break;
case OP_OPTIONS:
only_bucket = true;
Expand Down