文档章节

S3奇淫技巧:一步完成上传及ACL设置

秦牧羊
 秦牧羊
发布于 2017/06/28 14:33
字数 1018
阅读 129
收藏 0

需求描述

用户A需要在上传文件到S3的同时设置允许用户test1可读,上传数据和设置ACL需要一步完成。其目的是尽量减少http请求次数,提高并发效率。

python用例

from boto.s3.connection import S3Connection
import boto
import os

endpoint = 's3.ceph.work'
bucket_name = 'test3'
access_key = ''
secret_key = ''
key_name = 'abc'

conn = boto.connect_s3(
    aws_access_key_id=access_key,
    aws_secret_access_key=secret_key,
    host=endpoint,
    is_secure=False,
    calling_format=boto.s3.connection.SubdomainCallingFormat(),
    validate_certs=False,
)

bucket = conn.get_bucket(bucket_name)
key_ = bucket.new_key(key_name)

#方法1,通常一般用这个方法
key_.set_contents_from_string('aa') #第一次http请求,PUT操作,上传数据
key_.add_user_grant('READ','test1') #第二次http请求,PUT操作,设置ACL

#方法2,设置http请求头
headers = {"x-amz-grant-read":"id=test1"} #这里切记要在id(type)后面加上"=",不然就踩坑了
key_.set_contents_from_string('aa', headers=headers) #一次http请求,PUT操作,上传数据同时设置Object的ACL

原理说明

方法2其实也是遵循了S3标准的操作,只需要在上传Object的HTTP请求header中加入"x-amz-acl"或“x-amz-grant-permission”,目前ceph已经实现了emailAddress、id、url三种类型认证方式,但是注意提交的value,一定要用 type=value的形式,下面介绍一下rgw中的源码具体实现。

S3中关于上传Object的具体标准请参考如下 http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUTacl.html

RGW的实现

第一步根据之前源码介绍的找入口方法,定位到以下函数

#src/rgw/rgw_rest_s3.cc
RGWOp *RGWHandler_REST_Obj_S3::op_put()
{
  if (is_acl_op()) {
    return new RGWPutACLs_ObjStore_S3;
  }
  if (s->init_state.src_bucket.empty())
    return new RGWPutObj_ObjStore_S3; #这里对应的是PUT方式上传Object
  else
    return new RGWCopyObj_ObjStore_S3;
}

再看一下这个类的定义如下

#src/rgw/rgw_rest_s3.cc
class RGWPutObj_ObjStore_S3 : public RGWPutObj_ObjStore {
public:
  RGWPutObj_ObjStore_S3() {}
  ~RGWPutObj_ObjStore_S3() {}

  int get_params(); #这里实现HTTP请求头的数据处理
  int get_data(bufferlist& bl);
  void send_response();

  int validate_aws4_single_chunk(char *chunk_str,
                                 char *chunk_data_str,
                                 unsigned int chunk_data_size,
                                 string chunk_signature);
  int validate_and_unwrap_available_aws4_chunked_data(bufferlist& bl_in,
                                                      bufferlist& bl_out);
};

在get_params的实现中通过create_s3_policy来实现对policy的构建

#src/rgw/rgw_rest_s3.cc

int RGWPutObj_ObjStore_S3::get_params()
{
  RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
  map<string, bufferlist> src_attrs;
  size_t pos;
  int ret;

  RGWAccessControlPolicy_S3 s3policy(s->cct);
  if (!s->length)
    return -ERR_LENGTH_REQUIRED;

  ret = create_s3_policy(s, store, s3policy, s->owner); #创建policy
  if (ret < 0)
    return ret;

  policy = s3policy;
  
  

再看一下create_s3_policy有两类s3policy的构建方法,一类是create_from_headers,另外一类是create_canned,我们这里是通过http header设置,所以选create_from_headers

#src/rgw/rgw_rest_s3.cc
static int create_s3_policy(struct req_state *s, RGWRados *store,
			    RGWAccessControlPolicy_S3& s3policy,
			    ACLOwner& owner)
{
  if (s->has_acl_header) {
    if (!s->canned_acl.empty())
      return -ERR_INVALID_REQUEST;

    return s3policy.create_from_headers(store, s->info.env, owner);
  }

  return s3policy.create_canned(owner, s->bucket_owner, s->canned_acl);
}

再看一下create_from_headers的实现,其基本流程是遍历s3_acl_header获得对应HTTP的ACL类型,最后通过parse_grantee_str生成对应的规则

#src/rgw/rgw_acl_s3.cc
static const s3_acl_header acl_header_perms[] = {
  {RGW_PERM_READ, "HTTP_X_AMZ_GRANT_READ"},
  {RGW_PERM_WRITE, "HTTP_X_AMZ_GRANT_WRITE"},
  {RGW_PERM_READ_ACP,"HTTP_X_AMZ_GRANT_READ_ACP"},
  {RGW_PERM_WRITE_ACP, "HTTP_X_AMZ_GRANT_WRITE_ACP"},
  {RGW_PERM_FULL_CONTROL, "HTTP_X_AMZ_GRANT_FULL_CONTROL"},
  {0, NULL}
};

int RGWAccessControlPolicy_S3::create_from_headers(RGWRados *store, RGWEnv *env, ACLOwner& _owner)
{
  std::list<ACLGrant> grants;
 
  for (const struct s3_acl_header *p = acl_header_perms; p->rgw_perm; p++) {
    if (parse_acl_header(store, env, p, grants) < 0) #根据匹配到的header生成对应acl
      return false;
  }

  RGWAccessControlList_S3& _acl = static_cast<RGWAccessControlList_S3 &>(acl);
  int r = _acl.create_from_grants(grants);

  owner = _owner;

  return r;
}


static int parse_acl_header(RGWRados *store, RGWEnv *env,
         const struct s3_acl_header *perm, std::list<ACLGrant>& _grants)
{
  std::list<string> grantees;
  std::string hacl_str;

  const char *hacl = get_acl_header(env, perm);
  if (hacl == NULL)
    return 0;

  hacl_str = hacl;
  get_str_list(hacl_str, ",", grantees);

  for (list<string>::iterator it = grantees.begin(); it != grantees.end(); ++it) {
    ACLGrant grant;
    int ret = parse_grantee_str(store, *it, perm, grant); #最终实现acl的匹配在这里面
    if (ret < 0)
      return ret;

    _grants.push_back(grant);
  }

  return 0;
}

static int parse_grantee_str(RGWRados *store, string& grantee_str,
        const struct s3_acl_header *perm, ACLGrant& grant)
{
  string id_type, id_val_quoted;
  int rgw_perm = perm->rgw_perm;
  int ret;

  RGWUserInfo info;

  ret = parse_key_value(grantee_str, id_type, id_val_quoted); #注意这里对header中传进来的type=value进行处理
  if (ret < 0)
    return ret;

  string id_val = rgw_trim_quotes(id_val_quoted);

  if (strcasecmp(id_type.c_str(), "emailAddress") == 0) {
    ret = rgw_get_user_info_by_email(store, id_val, info);
    if (ret < 0)
      return ret;

    grant.set_canon(info.user_id, info.display_name, rgw_perm);
  } else if (strcasecmp(id_type.c_str(), "id") == 0) {
    rgw_user user(id_val);
    ret = rgw_get_user_info_by_uid(store, user, info);
    if (ret < 0)
      return ret;

    grant.set_canon(info.user_id, info.display_name, rgw_perm);
  } else if (strcasecmp(id_type.c_str(), "uri") == 0) {
    ACLGroupTypeEnum gid = grant.uri_to_group(id_val);
    if (gid == ACL_GROUP_NONE)
      return -EINVAL;

    grant.set_group(gid, rgw_perm);
  } else {
    return -EINVAL;
  }

  return 0;
}

这里需要重点提到的一点就是在parse_grantee_str中parse_key_value方法,如果你传进来的header字段的value不包含"=",那么你就悲剧了。

#src/rgw/rgw_common.cc
int parse_key_value(string& in_str, const char *delim, string& key, string& val)
{
  if (delim == NULL)
    return -EINVAL;

  int pos = in_str.find(delim);
  if (pos < 0)
    return -EINVAL;

  trim_whitespace(in_str.substr(0, pos), key);
  pos++;

  trim_whitespace(in_str.substr(pos), val);

  return 0;
}

int parse_key_value(string& in_str, string& key, string& val)
{
  return parse_key_value(in_str, "=", key,val); #将header里的value按"="进行拆分
}

© 著作权归作者所有

共有 人打赏支持
秦牧羊
粉丝 73
博文 57
码字总数 28980
作品 0
广州
架构师
私信 提问
rgw object read and write

一、Get Object。 1、读取Object的主要处理流程。 RGWGetObj::execute() |创建RGWGetObjCB类实例,其中handledata()函数为回调函数,该函数会调用RGWGetObj::getdatacb()函数,而该函数最终会...

linuxhunter
2016/04/12
364
4
ossutil的过滤参数include/exclude

应用场景 某些用户在本地或者OSS上存放了不同类型的文件,比如jpg文件和pptx文件。用户希望在(批量)上传jpg文件的时候设置不同的meta和ACL,而(批量)上传pptx文件的时候设置另外的meta和...

zuozhao
06/06
0
0
全世界最短IE判定if(!+[1,])的解释

<script type="text/javascript">alert([1,2]);//相当于alert([1,2].toString()); --这在IE与非IE上都相同,都会弹出"1,2"alert([1,]);//相当于alert([1,].toString());--在非IE的标准浏览器......

大师兄
2015/06/11
0
0
Maven奇淫技巧

Maven奇淫技巧 命令行窗口指定settings.xml 命令如下:

xjt2016
2016/11/21
12
0
RGW S3 ACL解析

RGW ACL主要类关系图如下图所示: RGW ACL处理类关系图如下图所示: RGW ACL主要处理流程详细说明如下。 一、PUT ACL。 RGWPutACL::execute() |从HTTP请求数据流中解析出RGWAccessControlPol...

linuxhunter
2016/04/14
89
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS 安装PHP5和PHP7

安装PHP5 下载解压二进制包 [root@test-a src]# cd /usr/local/src/[root@test-a src]# wget http://cn2.php.net/distributions/php-5.6.32.tar.bz2[root@test-a src]# tar jxvf php-5.6......

野雪球
今天
4
0
windows上类似dnsmasq的软件Dual DHCP DNS Server

官网地址:http://dhcp-dns-server.sourceforge.net/官网定向的下载地址:https://sourceforge.net/projects/dhcp-dns-server/files/ 设置参考地址:http://blog.51cto.com/zhukeqiang/18264......

xueyuse0012
今天
3
0
LinkedHashMap源码解析

前言 HashMap中的元素时无序的,也就是说遍历HashMap的时候,顺序和放入的顺序是不一样的。 如果需要有序的Map,就可以采用LinkedHashMap. LinkedHashMap通过维护一个包含所有元素的双向链表,...

grace_233
今天
3
0
初识flask

文档 0.10.1版本 http://www.pythondoc.com/flask/index.html 1.0.2版本 https://dormousehole.readthedocs.io/en/latest/ 安装flask $ pip3 install flaskCollecting flask Downloading......

yimingkeji
昨天
5
0
Akka系统《sixteen》译

Actor是一个封装状态(state)和行为(behavior)的对象,它们只通过交换消息通信(放入收件人邮箱的邮件)。从某种意义上说,Actor是最严格的面向对象编程形式,但它更适合将他们视为人:在与Act...

woshixin
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部