summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYehuda Sadeh <yehuda@inktank.com>2013-05-02 21:05:21 -0700
committerYehuda Sadeh <yehuda@inktank.com>2013-05-02 22:08:37 -0700
commitaab390529df56cabc75cc8fff2707b32c35aaaec (patch)
tree566c87c77e022fc657408faab598ab56c1b130c6
parentdebbc79e2756a48636508d43941976531df68f23 (diff)
downloadceph-wip-4716.tar.gz
rgw: user operation maskwip-4716
Fixes: #4716 add user operation mask for controlling user permissions. Also add admin controls for it. Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
-rw-r--r--src/rgw/rgw_admin.cc14
-rw-r--r--src/rgw/rgw_common.cc45
-rw-r--r--src/rgw/rgw_common.h19
-rw-r--r--src/rgw/rgw_json_enc.cc31
-rw-r--r--src/rgw/rgw_main.cc7
-rw-r--r--src/rgw/rgw_op.cc24
-rw-r--r--src/rgw/rgw_op.h29
-rw-r--r--src/rgw/rgw_user.cc6
-rw-r--r--src/rgw/rgw_user.h9
9 files changed, 166 insertions, 18 deletions
diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc
index 9804761e8ab..edbe887c208 100644
--- a/src/rgw/rgw_admin.cc
+++ b/src/rgw/rgw_admin.cc
@@ -485,6 +485,7 @@ int main(int argc, char **argv)
std::string infile;
RGWUserAdminOpState user_op;
RGWBucketAdminOpState bucket_op;
+ string op_mask_str;
std::string val;
std::ostringstream errs;
@@ -513,6 +514,8 @@ int main(int argc, char **argv)
pool_name = val;
} else if (ceph_argparse_witharg(args, i, &val, "-o", "--object", (char*)NULL)) {
object = val;
+ } else if (ceph_argparse_witharg(args, i, &val, "--op-mask", (char*)NULL)) {
+ op_mask_str = val;
} else if (ceph_argparse_witharg(args, i, &val, "--key-type", (char*)NULL)) {
key_type_str = val;
if (key_type_str.compare("swift") == 0) {
@@ -677,6 +680,17 @@ int main(int argc, char **argv)
if (set_perm)
user_op.set_perm(perm_mask);
+ if (!op_mask_str.empty()) {
+ uint32_t op_mask;
+ int ret = rgw_parse_op_type_list(op_mask_str, &op_mask);
+ if (ret < 0) {
+ cerr << "failed to parse op_mask: " << cpp_strerror(-ret) << std::endl;
+ return -ret;
+ }
+
+ user_op.set_op_mask(op_mask);
+ }
+
if (key_type != KEY_TYPE_UNDEFINED)
user_op.set_key_type(key_type);
diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc
index 48a377cd7dc..5a9bf3d2747 100644
--- a/src/rgw/rgw_common.cc
+++ b/src/rgw/rgw_common.cc
@@ -638,15 +638,13 @@ bool url_decode(string& src_str, string& dest_str)
return true;
}
-static struct {
+struct rgw_name_to_flag {
const char *type_name;
- uint32_t perm;
-} cap_names[] = { {"*", RGW_CAP_ALL},
- {"read", RGW_CAP_READ},
- {"write", RGW_CAP_WRITE},
- {NULL, 0} };
+ uint32_t flag;
+};
-int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
+static int parse_list_of_flags(struct rgw_name_to_flag *mapping,
+ const string& str, uint32_t *perm)
{
list<string> strs;
get_str_list(str, strs);
@@ -654,9 +652,9 @@ int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
uint32_t v = 0;
for (iter = strs.begin(); iter != strs.end(); ++iter) {
string& s = *iter;
- for (int i = 0; cap_names[i].type_name; i++) {
- if (s.compare(cap_names[i].type_name) == 0)
- v |= cap_names[i].perm;
+ for (int i = 0; mapping[i].type_name; i++) {
+ if (s.compare(mapping[i].type_name) == 0)
+ v |= mapping[i].flag;
}
}
@@ -664,6 +662,16 @@ int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
return 0;
}
+static struct rgw_name_to_flag cap_names[] = { {"*", RGW_CAP_ALL},
+ {"read", RGW_CAP_READ},
+ {"write", RGW_CAP_WRITE},
+ {NULL, 0} };
+
+int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
+{
+ return parse_list_of_flags(cap_names, str, perm);
+}
+
int RGWUserCaps::get_cap(const string& cap, string& type, uint32_t *pperm)
{
int pos = cap.find('=');
@@ -777,12 +785,12 @@ void RGWUserCaps::dump(Formatter *f, const char *name) const
uint32_t perm = iter->second;
string perm_str;
for (int i=0; cap_names[i].type_name; i++) {
- if ((perm & cap_names[i].perm) == cap_names[i].perm) {
+ if ((perm & cap_names[i].flag) == cap_names[i].flag) {
if (perm_str.size())
perm_str.append(", ");
perm_str.append(cap_names[i].type_name);
- perm &= ~cap_names[i].perm;
+ perm &= ~cap_names[i].flag;
}
}
if (perm_str.empty())
@@ -833,3 +841,16 @@ int RGWUserCaps::check_cap(const string& cap, uint32_t perm)
return 0;
}
+
+static struct rgw_name_to_flag op_type_mapping[] = { {"*", RGW_OP_TYPE_ALL},
+ {"read", RGW_OP_TYPE_READ},
+ {"write", RGW_OP_TYPE_WRITE},
+ {"delete", RGW_OP_TYPE_DELETE},
+ {NULL, 0} };
+
+
+int rgw_parse_op_type_list(const string& str, uint32_t *perm)
+{
+ return parse_list_of_flags(op_type_mapping, str, perm);
+}
+
diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h
index f4878f4b291..9b761810286 100644
--- a/src/rgw/rgw_common.h
+++ b/src/rgw/rgw_common.h
@@ -79,6 +79,12 @@ using ceph::crypto::MD5;
#define RGW_SUSPENDED_USER_AUID (uint64_t)-2
+#define RGW_OP_TYPE_READ 0x01
+#define RGW_OP_TYPE_WRITE 0x02
+#define RGW_OP_TYPE_DELETE 0x04
+
+#define RGW_OP_TYPE_ALL (RGW_OP_TYPE_READ | RGW_OP_TYPE_WRITE | RGW_OP_TYPE_DELETE)
+
#define RGW_DEFAULT_MAX_BUCKETS 1000
#define STATUS_CREATED 1900
@@ -381,12 +387,13 @@ struct RGWUserInfo
map<string, RGWSubUser> subusers;
__u8 suspended;
uint32_t max_buckets;
+ uint32_t op_mask;
RGWUserCaps caps;
- RGWUserInfo() : auid(0), suspended(0), max_buckets(RGW_DEFAULT_MAX_BUCKETS) {}
+ RGWUserInfo() : auid(0), suspended(0), max_buckets(RGW_DEFAULT_MAX_BUCKETS), op_mask(RGW_OP_TYPE_ALL) {}
void encode(bufferlist& bl) const {
- ENCODE_START(11, 9, bl);
+ ENCODE_START(12, 9, bl);
::encode(auid, bl);
string access_key;
string secret_key;
@@ -417,6 +424,7 @@ struct RGWUserInfo
::encode(swift_keys, bl);
::encode(max_buckets, bl);
::encode(caps, bl);
+ ::encode(op_mask, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& bl) {
@@ -462,6 +470,11 @@ struct RGWUserInfo
if (struct_v >= 11) {
::decode(caps, bl);
}
+ if (struct_v >= 12) {
+ ::decode(op_mask, bl);
+ } else {
+ op_mask = RGW_OP_TYPE_ALL;
+ }
DECODE_FINISH(bl);
}
void dump(Formatter *f) const;
@@ -1046,4 +1059,6 @@ extern void calc_hmac_sha1(const char *key, int key_len,
const char *msg, int msg_len, char *dest);
/* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */
+extern int rgw_parse_op_type_list(const string& str, uint32_t *perm);
+
#endif
diff --git a/src/rgw/rgw_json_enc.cc b/src/rgw/rgw_json_enc.cc
index 04dfb67c71d..e26299f24ad 100644
--- a/src/rgw/rgw_json_enc.cc
+++ b/src/rgw/rgw_json_enc.cc
@@ -235,7 +235,7 @@ static struct rgw_flags_desc rgw_perms[] = {
{ 0, NULL }
};
-static void perm_to_str(uint32_t mask, char *buf, int len)
+static void mask_to_str(rgw_flags_desc *mask_list, uint32_t mask, char *buf, int len)
{
const char *sep = "";
int pos = 0;
@@ -245,8 +245,8 @@ static void perm_to_str(uint32_t mask, char *buf, int len)
}
while (mask) {
uint32_t orig_mask = mask;
- for (int i = 0; rgw_perms[i].mask; i++) {
- struct rgw_flags_desc *desc = &rgw_perms[i];
+ for (int i = 0; mask_list[i].mask; i++) {
+ struct rgw_flags_desc *desc = &mask_list[i];
if ((mask & desc->mask) == desc->mask) {
pos += snprintf(buf + pos, len - pos, "%s%s", sep, desc->str);
if (pos == len)
@@ -262,6 +262,23 @@ static void perm_to_str(uint32_t mask, char *buf, int len)
}
}
+static void perm_to_str(uint32_t mask, char *buf, int len)
+{
+ return mask_to_str(rgw_perms, mask, buf, len);
+}
+
+static struct rgw_flags_desc op_type_flags[] = {
+ { RGW_OP_TYPE_READ, "read" },
+ { RGW_OP_TYPE_WRITE, "write" },
+ { RGW_OP_TYPE_DELETE, "delete" },
+ { 0, NULL }
+};
+
+static void op_type_to_str(uint32_t mask, char *buf, int len)
+{
+ return mask_to_str(op_type_flags, mask, buf, len);
+}
+
void RGWSubUser::dump(Formatter *f) const
{
encode_json("id", name, f);
@@ -340,6 +357,10 @@ void RGWUserInfo::dump(Formatter *f) const
encode_json_map("swift_keys", NULL, "key", NULL, user_info_dump_swift_key,(void *)this, swift_keys, f);
encode_json("caps", caps, f);
+
+ char buf[256];
+ op_type_to_str(op_mask, buf, sizeof(buf));
+ encode_json("op_mask", (const char *)buf, f);
}
@@ -380,6 +401,10 @@ void RGWUserInfo::decode_json(JSONObj *obj)
JSONDecoder::decode_json("subusers", subusers, decode_subusers, obj);
JSONDecoder::decode_json("caps", caps, obj);
+
+ string mask_str;
+ JSONDecoder::decode_json("op_mask", mask_str, obj);
+ rgw_parse_op_type_list(mask_str, &op_mask);
}
void rgw_bucket::dump(Formatter *f) const
diff --git a/src/rgw/rgw_main.cc b/src/rgw/rgw_main.cc
index c80037520b3..3d6bfb908f0 100644
--- a/src/rgw/rgw_main.cc
+++ b/src/rgw/rgw_main.cc
@@ -336,6 +336,13 @@ void RGWProcess::handle_request(RGWRequest *req)
req->log(s, "reading the cors attr");
handler->read_cors_config();
+ req->log(s, "verifying op mask");
+ ret = op->verify_op_mask();
+ if (ret < 0) {
+ abort_early(s, ret);
+ goto done;
+ }
+
req->log(s, "verifying op permissions");
ret = op->verify_permission();
if (ret < 0) {
diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc
index 0c157c561fb..acf31f51ecb 100644
--- a/src/rgw/rgw_op.cc
+++ b/src/rgw/rgw_op.cc
@@ -339,6 +339,20 @@ int RGWGetObj::verify_permission()
}
+int RGWOp::verify_op_mask()
+{
+ uint32_t required_mask = op_mask();
+
+ ldout(s->cct, 20) << "required_mask= " << required_mask << " user.op_mask=" << s->user.op_mask << dendl;
+
+ if ((s->user.op_mask & required_mask) != required_mask) {
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+
int RGWGetObj::read_user_manifest_part(rgw_bucket& bucket, RGWObjEnt& ent, RGWAccessControlPolicy *bucket_policy, off_t start_ofs, off_t end_ofs)
{
ldout(s->cct, 0) << "user manifest obj=" << ent.name << dendl;
@@ -1780,6 +1794,11 @@ int RGWGetACLs::verify_permission()
return 0;
}
+uint32_t RGWGetACLs::op_mask()
+{
+ return RGW_OP_TYPE_READ;
+}
+
void RGWGetACLs::execute()
{
stringstream ss;
@@ -1805,6 +1824,11 @@ int RGWPutACLs::verify_permission()
return 0;
}
+uint32_t RGWPutACLs::op_mask()
+{
+ return RGW_OP_TYPE_WRITE;
+}
+
void RGWPutACLs::execute()
{
bufferlist bl;
diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h
index b3e8c86e3f0..7fc9446c57c 100644
--- a/src/rgw/rgw_op.h
+++ b/src/rgw/rgw_op.h
@@ -29,7 +29,6 @@ class RGWHandler;
void rgw_get_request_metadata(struct req_state *s, map<string, bufferlist>& attrs);
int rgw_build_policies(RGWRados *store, struct req_state *s, bool only_bucket, bool prefetch_data);
-
/**
* Provide the base class for all ops.
*/
@@ -50,10 +49,13 @@ public:
virtual int verify_params() { return 0; }
virtual bool prefetch_data() { return false; }
virtual int verify_permission() = 0;
+ virtual int verify_op_mask();
virtual void execute() = 0;
virtual void send_response() {}
virtual void complete() { send_response(); }
virtual const char *name() = 0;
+
+ virtual uint32_t op_mask() { return 0; }
};
class RGWGetObj : public RGWOp {
@@ -119,6 +121,7 @@ public:
virtual int send_response_data(bufferlist& bl, off_t ofs, off_t len) = 0;
virtual const char *name() { return "get_obj"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
#define RGW_LIST_BUCKETS_LIMIT_MAX 10000
@@ -148,6 +151,7 @@ public:
virtual bool should_get_stats() { return false; }
virtual const char *name() { return "list_buckets"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
class RGWStatAccount : public RGWOp {
@@ -172,6 +176,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "stat_account"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
class RGWListBucket : public RGWOp {
@@ -203,6 +208,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "list_bucket"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
class RGWGetBucketLogging : public RGWOp {
@@ -213,6 +219,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "get_bucket_logging"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
class RGWStatBucket : public RGWOp {
@@ -229,6 +236,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "stat_bucket"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
class RGWCreateBucket : public RGWOp {
@@ -248,6 +256,7 @@ public:
virtual int get_params() { return 0; }
virtual void send_response() = 0;
virtual const char *name() { return "create_bucket"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWDeleteBucket : public RGWOp {
@@ -262,6 +271,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "delete_bucket"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; }
};
class RGWPutObjProcessor
@@ -330,6 +340,7 @@ public:
virtual int get_data(bufferlist& bl) = 0;
virtual void send_response() = 0;
virtual const char *name() { return "put_obj"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWPostObj : public RGWOp {
@@ -371,6 +382,7 @@ public:
virtual int get_data(bufferlist& bl) = 0;
virtual void send_response() = 0;
virtual const char *name() { return "post_obj"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWPutMetadata : public RGWOp {
@@ -398,6 +410,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "put_obj_metadata"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWDeleteObj : public RGWOp {
@@ -412,6 +425,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "delete_obj"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; }
};
class RGWCopyObj : public RGWOp {
@@ -473,6 +487,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "copy_obj"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWGetACLs : public RGWOp {
@@ -488,6 +503,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "get_acls"; }
+ virtual uint32_t op_mask();
};
class RGWPutACLs : public RGWOp {
@@ -513,6 +529,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "put_acls"; }
+ virtual uint32_t op_mask();
};
class RGWGetCORS : public RGWOp {
@@ -528,6 +545,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "get_cors"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
class RGWPutCORS : public RGWOp {
@@ -552,6 +570,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "put_cors"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWDeleteCORS : public RGWOp {
@@ -566,6 +585,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "delete_cors"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWOptionsCORS : public RGWOp {
@@ -585,6 +605,7 @@ public:
void get_response_params(string& allowed_hdrs, string& exp_hdrs, unsigned *max_age);
virtual void send_response() = 0;
virtual const char *name() { return "options_cors"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
class RGWInitMultipart : public RGWOp {
@@ -608,6 +629,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "init_multipart"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWCompleteMultipart : public RGWOp {
@@ -636,6 +658,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "complete_multipart"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; }
};
class RGWAbortMultipart : public RGWOp {
@@ -650,6 +673,7 @@ public:
virtual void send_response() = 0;
virtual const char *name() { return "abort_multipart"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; }
};
class RGWListMultipart : public RGWOp {
@@ -678,6 +702,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "list_multipart"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
#define MP_META_SUFFIX ".meta"
@@ -786,6 +811,7 @@ public:
virtual int get_params() = 0;
virtual void send_response() = 0;
virtual const char *name() { return "list_bucket_multiparts"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; }
};
class RGWDeleteMultiObj : public RGWOp {
@@ -818,6 +844,7 @@ public:
virtual void send_partial_response(pair<string,int>& result) = 0;
virtual void end_response() = 0;
virtual const char *name() { return "multi_object_delete"; }
+ virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; }
};
diff --git a/src/rgw/rgw_user.cc b/src/rgw/rgw_user.cc
index 9a75ba69b9e..8a636d1cae3 100644
--- a/src/rgw/rgw_user.cc
+++ b/src/rgw/rgw_user.cc
@@ -1639,6 +1639,9 @@ int RGWUser::execute_add(RGWUserAdminOpState& op_state, std::string *err_msg)
user_info.max_buckets = op_state.get_max_buckets();
user_info.suspended = op_state.get_suspension_status();
+ if (op_state.op_mask_specified)
+ user_info.op_mask = op_state.get_op_mask();
+
// update the request
op_state.set_user_info(user_info);
op_state.set_populated();
@@ -1835,6 +1838,9 @@ int RGWUser::execute_modify(RGWUserAdminOpState& op_state, std::string *err_msg)
if (op_state.max_buckets_specified)
user_info.max_buckets = max_buckets;
+ if (op_state.op_mask_specified)
+ user_info.op_mask = op_state.get_op_mask();
+
if (op_state.has_suspension_op()) {
__u8 suspended = op_state.get_suspension_status();
user_info.suspended = suspended;
diff --git a/src/rgw/rgw_user.h b/src/rgw/rgw_user.h
index 3b277000b57..804f67c4565 100644
--- a/src/rgw/rgw_user.h
+++ b/src/rgw/rgw_user.h
@@ -139,6 +139,7 @@ struct RGWUserAdminOpState {
uint32_t max_buckets;
__u8 suspended;
std::string caps;
+ uint32_t op_mask;
// subuser attributes
std::string subuser;
@@ -167,6 +168,7 @@ struct RGWUserAdminOpState {
bool user_email_specified;
bool max_buckets_specified;
bool perm_specified;
+ bool op_mask_specified;
bool caps_specified;
bool suspension_op;
bool key_op;
@@ -243,6 +245,10 @@ struct RGWUserAdminOpState {
perm_mask = perm;
perm_specified = true;
}
+ void set_op_mask(uint32_t mask) {
+ op_mask = mask;
+ op_mask_specified = true;
+ }
void set_key_type(int32_t type) {
key_type = type;
type_specified = true;
@@ -294,6 +300,7 @@ struct RGWUserAdminOpState {
bool has_caps_op() { return caps_specified; };
bool has_suspension_op() { return suspension_op; };
bool has_subuser_perm() { return perm_specified; };
+ bool has_op_mask() { return op_mask_specified; };
bool will_gen_access() { return gen_access; };
bool will_gen_secret() { return gen_secret; };
bool will_gen_subuser() { return gen_subuser; };
@@ -313,6 +320,7 @@ struct RGWUserAdminOpState {
int32_t get_key_type() {return key_type; };
uint32_t get_subuser_perm() { return perm_mask; };
uint32_t get_max_buckets() { return max_buckets; };
+ uint32_t get_op_mask() { return op_mask; };
std::string get_user_id() { return user_id; };
std::string get_subuser() { return subuser; };
@@ -389,6 +397,7 @@ struct RGWUserAdminOpState {
user_email_specified = false;
max_buckets_specified = false;
perm_specified = false;
+ op_mask_specified = false;
suspension_op = false;
key_op = false;
populated = false;