* with the legacy or S3 buckets.
*/
void rgw_make_bucket_entry_name(const string& tenant_name, const string& bucket_name, string& bucket_entry) {
- if (tenant_name.empty()) {
+ if (bucket_name.empty()) {
+ bucket_entry.clear();
+ } else if (tenant_name.empty()) {
bucket_entry = bucket_name;
} else {
bucket_entry = tenant_name + "/" + bucket_name;
string rgw_make_bucket_entry_name(const string& tenant_name, const string& bucket_name) {
string bucket_entry;
- if (tenant_name.empty()) {
+ if (bucket_name.empty()) {
+ bucket_entry.clear();
+ } else if (tenant_name.empty()) {
bucket_entry = bucket_name;
} else {
bucket_entry = tenant_name + "/" + bucket_name;
* Tenants are separated from buckets in URLs by a colon in S3.
* This function is not to be used on Swift URLs, not even for COPY arguments.
*/
-void rgw_parse_url_bucket(const string &bucket,
+void rgw_parse_url_bucket(const string &bucket, const string& auth_tenant,
string &tenant_name, string &bucket_name) {
+
int pos = bucket.find(':');
if (pos >= 0) {
+ /*
+ * N.B.: We allow ":bucket" syntax with explicit empty tenant in order
+ * to refer to the legacy tenant, in case users in new named tenants
+ * want to access old global buckets.
+ */
tenant_name = bucket.substr(0, pos);
+ bucket_name = bucket.substr(pos + 1);
} else {
- tenant_name.clear();
+ tenant_name = auth_tenant;
+ bucket_name = bucket;
}
- bucket_name = bucket.substr(pos + 1);
}
/**
string& bucket_entry);
extern string rgw_make_bucket_entry_name(const string& tenant_name,
const string& bucket_name);
-extern void rgw_parse_url_bucket(const string &bucket,
+extern void rgw_parse_url_bucket(const string& bucket,
+ const string& auth_tenant,
string &tenant_name, string &bucket_name);
/**
#define ERR_INVALID_SECRET_KEY 2034
#define ERR_INVALID_KEY_TYPE 2035
#define ERR_INVALID_CAP 2036
+#define ERR_INVALID_TENANT_NAME 2037
#define ERR_USER_SUSPENDED 2100
#define ERR_INTERNAL_ERROR 2200
#define ERR_NOT_IMPLEMENTED 2201
}
}
+struct req_init_state {
+ /* Keeps [[tenant]:]bucket until we parse the token. */
+ string url_bucket;
+ string src_bucket;
+};
+
/** Store all the state necessary to complete and respond to an HTTP request*/
struct req_state {
CephContext *cct;
uint32_t perm_mask;
utime_t header_time;
- /* Set once when req_state is initialized and not violated thereafter */
+ /* Set once when url_bucket is parsed and not violated thereafter. */
string bucket_tenant;
string bucket_name;
string trans_id;
req_info info;
+ req_init_state init_state;
req_state(CephContext *_cct, class RGWEnv *e);
~req_state();
{ ERR_INVALID_SECRET_KEY, 400, "InvalidSecretKey"},
{ ERR_INVALID_KEY_TYPE, 400, "InvalidKeyType"},
{ ERR_INVALID_CAP, 400, "InvalidCapability"},
+ { ERR_INVALID_TENANT_NAME, 400, "InvalidTenantName" },
{ ENOTEMPTY, 409, "BucketNotEmpty" },
{ ERR_PRECONDITION_FAILED, 412, "PreconditionFailed" },
{ ERANGE, 416, "InvalidRange" },
should_log = mgr->get_logging();
- req->log(s, "getting op");
+ req->log_format(s, "getting op %d", s->op);
op = handler->get_op(store);
if (!op) {
abort_early(s, NULL, -ERR_METHOD_NOT_ALLOWED);
goto done;
}
+ req->log(s, "normalizing buckets and tenants");
+ ret = handler->postauth_init();
+ if (ret < 0) {
+ dout(10) << "failed to run post-auth init" << dendl;
+ abort_early(s, op, ret);
+ goto done;
+ }
+
if (s->user.suspended) {
dout(10) << "user is suspended, uid=" << s->user.user_id << dendl;
abort_early(s, op, -ERR_USER_SUSPENDED);
virtual void put_op(RGWOp *op);
virtual int read_permissions(RGWOp *op) = 0;
virtual int authorize() = 0;
+ virtual int postauth_init() = 0;
};
#endif
int RGWHandler_ObjStore::validate_tenant_name(string const& t)
{
- struct tench {
- static bool is_good(char ch) {
- return isalnum(ch) || ch == '_';
- }
- };
- std::string::const_iterator it = std::find_if(t.begin(), t.end(), tench::is_good);
- return (it == t.end())? 0: -ERR_INVALID_BUCKET_NAME;
+ const char *p = t.c_str();
+ for (int i = 0; i < t.size(); i++) {
+ char ch = p[i];
+ if (!(isalnum(ch) || ch == '_'))
+ return -ERR_INVALID_TENANT_NAME;
+ }
+ return 0;
}
// This function enforces Amazon's spec for bucket names.
int read_permissions(RGWOp *op);
virtual int authorize() = 0;
+ // virtual int postauth_init(struct req_init_state *t) = 0;
};
class RGWHandler_ObjStore_SWIFT;
if (is_acl_op()) {
return new RGWPutACLs_ObjStore_S3;
}
- if (s->src_bucket_name.empty())
+ if (s->init_state.src_bucket.empty())
return new RGWPutObj_ObjStore_S3;
else
return new RGWCopyObj_ObjStore_S3;
* the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
* into req_status.bucket_name directly.
*/
- if (s->bucket_name.empty()) {
- rgw_parse_url_bucket(first, s->bucket_tenant, s->bucket_name);
- if (s->bucket_tenant.empty())
- s->bucket_tenant = s->user.user_id.tenant;
-
- ldout(s->cct, 20) << "s->user.user_id=" << s->user.user_id << " s->bucket_tenant=" << s->bucket_tenant << " s->bucket_name=" << s->bucket_name << dendl;
+ if (s->init_state.url_bucket.empty()) {
+ // Save bucket to tide us over until token is parsed.
+ s->init_state.url_bucket = first;
if (pos >= 0) {
string encoded_obj_str = req.substr(pos+1);
return 0;
}
+int RGWHandler_ObjStore_S3::postauth_init()
+{
+ struct req_init_state *t = &s->init_state;
+ bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
+
+ rgw_parse_url_bucket(t->url_bucket, s->user.user_id.tenant, s->bucket_tenant, s->bucket_name);
+
+ dout(10) << "s->object=" << (!s->object.empty() ? s->object : rgw_obj_key("<NULL>"))
+ << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
+
+ int ret;
+ ret = validate_tenant_name(s->bucket_tenant);
+ if (ret)
+ return ret;
+ ret = validate_bucket_name(s->bucket_name, relaxed_names);
+ if (ret)
+ return ret;
+ ret = validate_object_name(s->object.name);
+ if (ret)
+ return ret;
+
+ if (!t->src_bucket.empty()) {
+ rgw_parse_url_bucket(t->src_bucket, s->user.user_id.tenant, s->src_tenant_name, s->src_bucket_name);
+ ret = validate_tenant_name(s->src_tenant_name);
+ if (ret)
+ return ret;
+ ret = validate_bucket_name(s->src_bucket_name, relaxed_names);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
static bool looks_like_ip_address(const char *bucket)
{
int num_periods = 0;
int RGWHandler_ObjStore_S3::init(RGWRados *store, struct req_state *s, RGWClientIO *cio)
{
- dout(10) << "s->object=" << (!s->object.empty() ? s->object : rgw_obj_key("<NULL>"))
- << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
-
int ret;
- ret = validate_tenant_name(s->bucket_tenant);
- if (ret)
- return ret;
- bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
- ret = validate_bucket_name(s->bucket_name, relaxed_names);
- if (ret)
- return ret;
- ret = validate_object_name(s->object.name);
- if (ret)
- return ret;
+
+ s->dialect = "s3";
const char *cacl = s->info.env->get("HTTP_X_AMZ_ACL");
if (cacl)
const char *copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
if (copy_source) {
- string src_bucket_str;
- ret = RGWCopyObj::parse_copy_location(copy_source, src_bucket_str, s->src_object);
+ ret = RGWCopyObj::parse_copy_location(copy_source, s->init_state.src_bucket, s->src_object);
if (!ret) {
ldout(s->cct, 0) << "failed to parse copy location" << dendl;
return -EINVAL; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
}
- rgw_parse_url_bucket(src_bucket_str, s->src_tenant_name, s->src_bucket_name);
- if (s->src_tenant_name.empty())
- s->src_tenant_name = s->user.user_id.tenant;
}
- s->dialect = "s3";
-
return RGWHandler_ObjStore::init(store, s, cio);
}
}
s->perm_mask = RGW_PERM_FULL_CONTROL;
-
- if (s->bucket_tenant.empty()) {
- s->bucket_tenant = s->user.user_id.tenant;
- }
}
}
}
return -ERR_INVALID_ACCESS_KEY;
}
- if (s->bucket_tenant.empty()) {
- s->bucket_tenant = s->user.user_id.tenant;
- }
-
/* now verify signature */
string auth_hdr;
s->owner.set_id(s->user.user_id);
s->owner.set_name(s->user.display_name);
-
return 0;
}
if (ret < 0)
return NULL;
- if (s->bucket_name.empty())
+ if (s->init_state.url_bucket.empty())
return new RGWHandler_ObjStore_Service_S3;
if (s->object.empty())
virtual int validate_object_name(const string& bucket) { return 0; }
- virtual int init(RGWRados *store, struct req_state *state, RGWClientIO *cio);
+ virtual int init(RGWRados *store, struct req_state *s, RGWClientIO *cio);
virtual int authorize() {
return RGW_Auth_S3::authorize(store, s);
}
+ int postauth_init() { return 0; }
};
class RGWHandler_ObjStore_S3 : public RGWHandler_ObjStore {
int validate_bucket_name(const string& bucket, bool relaxed_names);
using RGWHandler_ObjStore::validate_bucket_name;
- virtual int init(RGWRados *store, struct req_state *state, RGWClientIO *cio);
+ virtual int init(RGWRados *store, struct req_state *s, RGWClientIO *cio);
virtual int authorize() {
return RGW_Auth_S3::authorize(store, s);
}
+ int postauth_init();
};
class RGWHandler_ObjStore_Service_S3 : public RGWHandler_ObjStore_S3 {
if_match = s->info.env->get("HTTP_COPY_IF_MATCH");
if_nomatch = s->info.env->get("HTTP_COPY_IF_NONE_MATCH");
- /* XXX why copy this? just use req_state in rgw_op.cc:verify_permission */
src_tenant_name = s->src_tenant_name;
src_bucket_name = s->src_bucket_name;
src_object = s->src_object;
if (is_acl_op()) {
return new RGWPutACLs_ObjStore_SWIFT;
}
- if (s->src_bucket_name.empty())
+ if (s->init_state.src_bucket.empty())
return new RGWPutObj_ObjStore_SWIFT;
else
return new RGWCopyObj_ObjStore_SWIFT;
return 0;
}
+int RGWHandler_ObjStore_SWIFT::postauth_init()
+{
+ struct req_init_state *t = &s->init_state;
+
+ /* XXX Stub this until Swift Auth sets account into URL. */
+ s->bucket_tenant = s->user.user_id.tenant;
+ s->bucket_name = t->url_bucket;
+
+ dout(10) << "s->object=" << (!s->object.empty() ? s->object : rgw_obj_key("<NULL>"))
+ << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
+
+ int ret;
+ ret = validate_tenant_name(s->bucket_tenant);
+ if (ret)
+ return ret;
+ ret = validate_bucket_name(s->bucket_name);
+ if (ret)
+ return ret;
+ ret = validate_object_name(s->object.name);
+ if (ret)
+ return ret;
+
+ if (!t->src_bucket.empty()) {
+ /*
+ * We don't allow cross-tenant copy at present. It requires account
+ * names in the URL for Swift.
+ */
+ s->src_tenant_name = s->user.user_id.tenant;
+ s->src_bucket_name = t->src_bucket;
+
+ ret = validate_bucket_name(s->src_bucket_name);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = validate_object_name(s->src_object.name);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
int RGWHandler_ObjStore_SWIFT::validate_bucket_name(const string& bucket)
{
int ret = RGWHandler_ObjStore::validate_bucket_name(bucket);
s->info.effective_uri = "/" + first;
- /* XXX Temporarily not parsing URL until Auth puts something in there. */
- s->bucket_tenant = s->user.user_id.tenant;
- s->bucket_name = first;
+ // Save bucket to tide us over until token is parsed.
+ s->init_state.url_bucket = first;
if (req.size()) {
s->object = rgw_obj_key(req, s->info.env->get("HTTP_X_OBJECT_VERSION_ID", "")); /* rgw swift extension */
int RGWHandler_ObjStore_SWIFT::init(RGWRados *store, struct req_state *s, RGWClientIO *cio)
{
- dout(10) << "s->object=" << (!s->object.empty() ? s->object : rgw_obj_key("<NULL>"))
- << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
+ struct req_init_state *t = &s->init_state;
- int ret;
- ret = validate_tenant_name(s->bucket_tenant);
- if (ret)
- return ret;
- ret = validate_bucket_name(s->bucket_name);
- if (ret)
- return ret;
- ret = validate_object_name(s->object.name);
- if (ret)
- return ret;
+ s->dialect = "swift";
const char *copy_source = s->info.env->get("HTTP_X_COPY_FROM");
if (copy_source) {
- bool result = RGWCopyObj::parse_copy_location(copy_source, s->src_bucket_name, s->src_object);
+ bool result = RGWCopyObj::parse_copy_location(copy_source, t->src_bucket, s->src_object);
if (!result)
return -ERR_BAD_URL;
- s->src_tenant_name = s->user.user_id.tenant;
}
- s->dialect = "swift";
-
if (s->op == OP_COPY) {
const char *req_dest = s->info.env->get("HTTP_DESTINATION");
if (!req_dest)
return -ERR_BAD_URL;
- string dest_tenant_name, dest_bucket_name;
+ string dest_bucket_name;
rgw_obj_key dest_obj_key;
bool result = RGWCopyObj::parse_copy_location(req_dest, dest_bucket_name, dest_obj_key);
if (!result)
return -ERR_BAD_URL;
- dest_tenant_name = s->user.user_id.tenant;
string dest_object = dest_obj_key.name;
- if (dest_bucket_name != s->bucket_name) {
- ret = validate_bucket_name(dest_bucket_name);
- if (ret < 0)
- return ret;
- }
-
- ret = validate_tenant_name(dest_tenant_name);
- if (ret < 0)
- return ret;
/* convert COPY operation into PUT */
- s->src_tenant_name = s->bucket_tenant;
- s->src_bucket_name = s->bucket_name;
+ t->src_bucket = t->url_bucket;
s->src_object = s->object;
- s->bucket_tenant = dest_tenant_name;
- s->bucket_name = dest_bucket_name;
+ t->url_bucket = dest_bucket_name;
s->object = rgw_obj_key(dest_object);
s->op = OP_PUT;
}
if (ret < 0)
return NULL;
- if (s->bucket_name.empty())
+ if (s->init_state.url_bucket.empty())
return new RGWHandler_ObjStore_Service_SWIFT;
+
if (s->object.empty())
return new RGWHandler_ObjStore_Bucket_SWIFT;
int validate_bucket_name(const string& bucket);
- int init(RGWRados *store, struct req_state *state, RGWClientIO *cio);
+ int init(RGWRados *store, struct req_state *s, RGWClientIO *cio);
int authorize();
+ int postauth_init();
RGWAccessControlPolicy *alloc_policy() { return NULL; /* return new RGWAccessControlPolicy_SWIFT; */ }
void free_policy(RGWAccessControlPolicy *policy) { delete policy; }
int init(RGWRados *store, struct req_state *state, RGWClientIO *cio);
int authorize();
+ int postauth_init() { return 0; }
int read_permissions(RGWOp *op) { return 0; }
virtual RGWAccessControlPolicy *alloc_policy() { return NULL; }