]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw: User Policy evaluation for bucket/object/user permission.
authorPritha Srivastava <prsrivas@redhat.com>
Fri, 2 Mar 2018 05:28:31 +0000 (10:58 +0530)
committerPritha Srivastava <prsrivas@redhat.com>
Fri, 29 Jun 2018 10:07:49 +0000 (15:37 +0530)
Signed-off-by: Pritha Srivastava <prsrivas@redhat.com>
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_iam_policy.cc
src/rgw/rgw_iam_policy.h
src/rgw/rgw_op.cc

index ad409d392c0438d520ac102d1d9620175f2cfb35..e0c22bc3488ec2e8fb0e0261ee853f4e4604b741 100644 (file)
@@ -1031,7 +1031,53 @@ string RGWHTTPArgs::sys_get(const string& name, bool * const exists) const
   return e ? iter->second : string();
 }
 
+namespace {
+Effect eval_or_pass(const boost::optional<Policy>& policy,
+                   const rgw::IAM::Environment& env,
+                   boost::optional<const rgw::auth::Identity&> id,
+                   const uint64_t op,
+                   const ARN& arn) {
+  if (!policy)
+    return Effect::Pass;
+  else
+    return policy->eval(env, id, op, arn);
+}
+
+}
+
+Effect eval_user_policies(const vector<Policy>& user_policies,
+                          const rgw::IAM::Environment& env,
+                          boost::optional<const rgw::auth::Identity&> id,
+                          const uint64_t op,
+                          const ARN& arn) {
+  auto usr_policy_res = Effect::Pass, prev_res = Effect::Pass;
+  for (auto& user_policy : user_policies) {
+    if (usr_policy_res = eval_or_pass(user_policy, env, id, op, arn); usr_policy_res == Effect::Deny)
+      return usr_policy_res;
+    else if (usr_policy_res == Effect::Allow)
+      prev_res = Effect::Allow;
+    else if (usr_policy_res == Effect::Pass && prev_res == Effect::Allow)
+      usr_policy_res = Effect::Allow;
+  }
+  return usr_policy_res;
+}
+
 bool verify_user_permission(struct req_state * const s,
+                            RGWAccessControlPolicy * const user_acl,
+                            const vector<rgw::IAM::Policy>& user_policies,
+                            const rgw::IAM::ARN& res,
+                            const uint64_t op)
+{
+  auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, res);
+  if (usr_policy_res == Effect::Deny)
+    return false;
+
+  auto perm = op_to_perm(op);
+
+  return verify_user_permission_no_policy(s, user_acl, perm);
+}
+
+bool verify_user_permission_no_policy(struct req_state * const s,
                             RGWAccessControlPolicy * const user_acl,
                             const int perm)
 {
@@ -1046,9 +1092,16 @@ bool verify_user_permission(struct req_state * const s,
 }
 
 bool verify_user_permission(struct req_state * const s,
-                            const int perm)
+                            const rgw::IAM::ARN& res,
+                            const uint64_t op)
+{
+  return verify_user_permission(s, s->user_acl.get(), s->iam_user_policies, res, op);
+}
+
+bool verify_user_permission_no_policy(struct req_state * const s,
+                                      const int perm)
 {
-  return verify_user_permission(s, s->user_acl.get(), perm);
+  return verify_user_permission_no_policy(s, s->user_acl.get(), perm);
 }
 
 bool verify_requester_payer_permission(struct req_state *s)
@@ -1079,29 +1132,21 @@ bool verify_requester_payer_permission(struct req_state *s)
   return false;
 }
 
-namespace {
-Effect eval_or_pass(const boost::optional<Policy>& policy,
-                   const rgw::IAM::Environment& env,
-                   const rgw::auth::Identity& id,
-                   const uint64_t op,
-                   const ARN& arn) {
-  if (!policy)
-    return Effect::Pass;
-  else
-    return policy->eval(env, id, op, arn);
-}
-}
-
 bool verify_bucket_permission(struct req_state * const s,
                              const rgw_bucket& bucket,
                               RGWAccessControlPolicy * const user_acl,
                               RGWAccessControlPolicy * const bucket_acl,
                              const boost::optional<Policy>& bucket_policy,
+                              const vector<Policy>& user_policies,
                               const uint64_t op)
 {
   if (!verify_requester_payer_permission(s))
     return false;
 
+  auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, ARN(bucket));
+  if (usr_policy_res == Effect::Deny)
+    return false;
+
   auto r = eval_or_pass(bucket_policy, s->env, *s->auth.identity,
                        op, ARN(bucket));
   if (r == Effect::Allow)
@@ -1110,6 +1155,8 @@ bool verify_bucket_permission(struct req_state * const s,
     return true;
   else if (r == Effect::Deny)
     return false;
+  else if (usr_policy_res == Effect::Allow) // r is Effect::Pass at this point
+    return true;
 
   const auto perm = op_to_perm(op);
 
@@ -1155,6 +1202,7 @@ bool verify_bucket_permission(struct req_state * const s, const uint64_t op)
                                   s->user_acl.get(),
                                   s->bucket_acl.get(),
                                   s->iam_policy,
+                                  s->iam_user_policies,
                                   op);
 }
 
@@ -1182,11 +1230,12 @@ static inline bool check_deferred_bucket_perms(struct req_state * const s,
                                               RGWAccessControlPolicy * const user_acl,
                                               RGWAccessControlPolicy * const bucket_acl,
                                               const boost::optional<Policy>& bucket_policy,
+                 const vector<Policy>& user_policies,
                                               const uint8_t deferred_check,
                                               const uint64_t op)
 {
   return (s->defer_to_bucket_acls == deferred_check \
-         && verify_bucket_permission(s, bucket, user_acl, bucket_acl, bucket_policy, op));
+         && verify_bucket_permission(s, bucket, user_acl, bucket_acl, bucket_policy, user_policies,op));
 }
 
 static inline bool check_deferred_bucket_only_acl(struct req_state * const s,
@@ -1205,11 +1254,15 @@ bool verify_object_permission(struct req_state * const s,
                               RGWAccessControlPolicy * const bucket_acl,
                               RGWAccessControlPolicy * const object_acl,
                               const boost::optional<Policy>& bucket_policy,
+                              const vector<Policy>& user_policies,
                               const uint64_t op)
 {
   if (!verify_requester_payer_permission(s))
     return false;
 
+  auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, ARN(obj));
+  if (usr_policy_res == Effect::Deny)
+    return false;
 
   auto r = eval_or_pass(bucket_policy, s->env, *s->auth.identity, op, ARN(obj));
   if (r == Effect::Allow)
@@ -1218,13 +1271,15 @@ bool verify_object_permission(struct req_state * const s,
     return true;
   else if (r == Effect::Deny)
     return false;
+  else if (usr_policy_res == Effect::Allow)
+    return true;
 
   const auto perm = op_to_perm(op);
 
   if (check_deferred_bucket_perms(s, obj.bucket, user_acl, bucket_acl, bucket_policy,
-                                 RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) ||
+                                 user_policies, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) ||
       check_deferred_bucket_perms(s, obj.bucket, user_acl, bucket_acl, bucket_policy,
-                                 RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) {
+                                 user_policies, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) {
     return true;
   }
 
@@ -1331,6 +1386,7 @@ bool verify_object_permission(struct req_state *s, uint64_t op)
                                   s->bucket_acl.get(),
                                   s->object_acl.get(),
                                   s->iam_policy,
+                                  s->iam_user_policies,
                                   op);
 }
 
index 969106020438b5750dda994f5d486f88d198dbec..82fd597409d5cef8b146caee0cc2349a9d810e26 100644 (file)
@@ -1864,6 +1864,7 @@ struct req_state : DoutPrefixProvider {
 
   rgw::IAM::Environment env;
   boost::optional<rgw::IAM::Policy> iam_policy;
+  vector<rgw::IAM::Policy> iam_user_policies;
 
   /* Is the request made by an user marked as a system one?
    * Being system user means we also have the admin status. */
@@ -2271,17 +2272,31 @@ extern std::string rgw_to_asctime(const utime_t& t);
 
 /** Check if the req_state's user has the necessary permissions
  * to do the requested action */
+rgw::IAM::Effect eval_user_policies(const vector<rgw::IAM::Policy>& user_policies,
+                          const rgw::IAM::Environment& env,
+                          boost::optional<const rgw::auth::Identity&> id,
+                          const uint64_t op,
+                          const rgw::IAM::ARN& arn);
 bool verify_user_permission(struct req_state * const s,
                             RGWAccessControlPolicy * const user_acl,
-                            const int perm);
+                            const vector<rgw::IAM::Policy>& user_policies,
+                            const rgw::IAM::ARN& res,
+                            const uint64_t op);
+bool verify_user_permission_no_policy(struct req_state * const s,
+                                      RGWAccessControlPolicy * const user_acl,
+                                      const int perm);
 bool verify_user_permission(struct req_state * const s,
-                            const int perm);
+                            const rgw::IAM::ARN& res,
+                            const uint64_t op);
+bool verify_user_permission_no_policy(struct req_state * const s,
+                                      int perm);
 bool verify_bucket_permission(
   struct req_state * const s,
   const rgw_bucket& bucket,
   RGWAccessControlPolicy * const user_acl,
   RGWAccessControlPolicy * const bucket_acl,
   const boost::optional<rgw::IAM::Policy>& bucket_policy,
+  const vector<rgw::IAM::Policy>& user_policies,
   const uint64_t op);
 bool verify_bucket_permission(struct req_state * const s, const uint64_t op);
 bool verify_bucket_permission_no_policy(
@@ -2300,6 +2315,7 @@ extern bool verify_object_permission(
   RGWAccessControlPolicy * const bucket_acl,
   RGWAccessControlPolicy * const object_acl,
   const boost::optional<rgw::IAM::Policy>& bucket_policy,
+  const vector<rgw::IAM::Policy>& user_policies,
   const uint64_t op);
 extern bool verify_object_permission(struct req_state *s, uint64_t op);
 extern bool verify_object_permission_no_policy(
index 2eb60879bf56c97c57f63554449f873b5311bc1f..1735773bfc65599330b040e19c341762474291b7 100644 (file)
@@ -208,6 +208,16 @@ ARN::ARN(const rgw_bucket& b, const string& o)
   resource.append(o);
 }
 
+ARN::ARN(const string& resource_name, const string& type, const string& tenant)
+  : partition(Partition::aws),
+    service(Service::iam),
+    region(),
+    account(tenant),
+    resource(type) {
+  resource.push_back('/');
+  resource.append(resource_name);
+}
+
 boost::optional<ARN> ARN::parse(const string& s, bool wildcards) {
   static const regex rx_wild("arn:([^:]*):([^:]*):([^:]*):([^:]*):([^:]*)",
                             std::regex_constants::ECMAScript |
index a923b837b3fae6e005f54a3b21985ffeb4eaaf59..8c2edebef36f26d745f8874e4c9ba774c39c7e82 100644 (file)
@@ -220,6 +220,7 @@ struct ARN {
   ARN(const rgw_obj& o);
   ARN(const rgw_bucket& b);
   ARN(const rgw_bucket& b, const std::string& o);
+  ARN(const string& resource_name, const string& type, const string& tenant);
 
   static boost::optional<ARN> parse(const std::string& s,
                                    bool wildcard = false);
index d539af4a4b96ca621c08dd1c01c475a43926fa6f..cae79ee057a081458b69cc39db6f557614177421 100644 (file)
@@ -278,6 +278,24 @@ static boost::optional<Policy> get_iam_policy_from_attr(CephContext* cct,
   }
 }
 
+static vector<Policy> get_iam_user_policy_from_attr(CephContext* cct,
+                        RGWRados* store,
+                        map<string, bufferlist>& attrs,
+                        const string& tenant) {
+  vector<Policy> policies;
+  if (auto it = attrs.find(RGW_ATTR_USER_POLICY); it != attrs.end()) {
+   bufferlist out_bl = attrs[RGW_ATTR_USER_POLICY];
+   map<string, string> policy_map;
+   decode(policy_map, out_bl);
+   for (auto& it : policy_map) {
+     bufferlist bl = bufferlist::static_from_string(it.second);
+     Policy p(cct, tenant, bl);
+     policies.push_back(std::move(p));
+   }
+  }
+  return policies;
+}
+
 static int get_obj_attrs(RGWRados *store, struct req_state *s, rgw_obj& obj, map<string, bufferlist>& attrs)
 {
   RGWRados::Object op_target(store, s->bucket_info, *static_cast<RGWObjectCtx *>(s->obj_ctx), obj);
@@ -518,10 +536,10 @@ int rgw_build_bucket_policies(RGWRados* store, struct req_state* s)
     }
   }
 
+  
   /* handle user ACL only for those APIs which support it */
   if (s->user_acl) {
     map<string, bufferlist> uattrs;
-
     ret = rgw_get_user_attrs_by_uid(store, acct_acl_user.uid, uattrs);
     if (!ret) {
       ret = get_user_policy_from_attr(s->cct, store, uattrs, *s->user_acl);
@@ -544,6 +562,16 @@ int rgw_build_bucket_policies(RGWRados* store, struct req_state* s)
     }
   }
 
+  try {
+    map<string, bufferlist> uattrs;
+    if (ret = rgw_get_user_attrs_by_uid(store, s->user->user_id, uattrs); ! ret) {
+      s->iam_user_policies = get_iam_user_policy_from_attr(s->cct, store, uattrs, s->user->user_id.tenant);
+    }
+  } catch (const std::exception& e) {
+    lderr(s->cct) << "Error reading IAM User Policy: " << e.what() << dendl;
+    ret = -EACCES;
+  }
+
   try {
     s->iam_policy = get_iam_policy_from_attr(s->cct, store, s->bucket_attrs,
                                             s->bucket_tenant);
@@ -768,6 +796,12 @@ int RGWGetObj::verify_permission()
     }
     if (s->iam_policy && s->iam_policy->has_partial_conditional(S3_EXISTING_OBJTAG))
       rgw_iam_add_existing_objtags(store, s, obj, action);
+    if (! s->iam_user_policies.empty()) {
+      for (auto& user_policy : s->iam_user_policies) {
+        if (user_policy.has_partial_conditional(S3_EXISTING_OBJTAG))
+          rgw_iam_add_existing_objtags(store, s, obj, action);
+      }
+    }
   }
 
   if (!verify_object_permission(s, action)) {
@@ -809,7 +843,14 @@ int RGWGetObjTags::verify_permission()
     rgw_obj obj = rgw_obj(s->bucket, s->object);
     rgw_iam_add_existing_objtags(store, s, obj, iam_action);
   }
-
+  if (! s->iam_user_policies.empty()) {
+    for (auto& user_policy : s->iam_user_policies) {
+      if (user_policy.has_partial_conditional(S3_EXISTING_OBJTAG)) {
+        rgw_obj obj = rgw_obj(s->bucket, s->object);
+        rgw_iam_add_existing_objtags(store, s, obj, iam_action);
+      }
+    }
+  }
   if (!verify_object_permission(s,iam_action))
     return -EACCES;
 
@@ -898,7 +939,14 @@ int RGWDeleteObjTags::verify_permission()
       auto obj = rgw_obj(s->bucket, s->object);
       rgw_iam_add_existing_objtags(store, s, obj, iam_action);
     }
-
+    if (! s->iam_user_policies.empty()) {
+    for (auto& user_policy : s->iam_user_policies) {
+      if (user_policy.has_partial_conditional(S3_EXISTING_OBJTAG)) {
+        auto obj = rgw_obj(s->bucket, s->object);
+        rgw_iam_add_existing_objtags(store, s, obj, iam_action);
+      }
+    }
+  }
     if (!verify_object_permission(s, iam_action))
       return -EACCES;
   }
@@ -1222,10 +1270,9 @@ int RGWGetObj::read_user_manifest_part(rgw_bucket& bucket,
   } else if (s->auth.identity->is_admin_of(s->user->user_id)) {
     ldpp_dout(this, 2) << "overriding permissions due to admin operation" << dendl;
   } else if (!verify_object_permission(s, part, s->user_acl.get(), bucket_acl,
-                                      &obj_policy, bucket_policy, action)) {
+                                      &obj_policy, bucket_policy, s->iam_user_policies, action)) {
     return -EPERM;
   }
-
   if (ent.meta.size == 0) {
     return 0;
   }
@@ -1922,7 +1969,7 @@ int RGWGetObj::init_common()
 
 int RGWListBuckets::verify_permission()
 {
-  if (!verify_user_permission(s, RGW_PERM_READ)) {
+  if (!verify_user_permission(s, ARN(), rgw::IAM::s3ListAllMyBuckets)) {
     return -EACCES;
   }
 
@@ -2097,7 +2144,7 @@ void RGWGetUsage::execute()
 
 int RGWStatAccount::verify_permission()
 {
-  if (!verify_user_permission(s, RGW_PERM_READ)) {
+  if (!verify_user_permission_no_policy(s, RGW_PERM_READ)) {
     return -EACCES;
   }
 
@@ -2473,7 +2520,7 @@ int RGWCreateBucket::verify_permission()
     return -EACCES;
   }
 
-  if (!verify_user_permission(s, RGW_PERM_WRITE)) {
+  if (!verify_user_permission(s, ARN(s->bucket), rgw::IAM::s3CreateBucket)) {
     return -EACCES;
   }
 
@@ -3099,7 +3146,18 @@ int RGWPutObj::verify_permission()
 
     /* admin request overrides permission checks */
     if (! s->auth.identity->is_admin_of(cs_acl.get_owner().get_id())) {
-      if (policy) {
+      if (policy || ! s->iam_user_policies.empty()) {
+        auto usr_policy_res = Effect::Pass;
+        for (auto& user_policy : s->iam_user_policies) {
+          if (usr_policy_res = user_policy.eval(s->env, *s->auth.identity,
+                             cs_object.instance.empty() ?
+                             rgw::IAM::s3GetObject :
+                             rgw::IAM::s3GetObjectVersion,
+                             rgw::IAM::ARN(obj)); usr_policy_res == Effect::Deny)
+            return -EACCES;
+          else if (usr_policy_res == Effect::Allow)
+            break;
+        }
        auto e = policy->eval(s->env, *s->auth.identity,
                              cs_object.instance.empty() ?
                              rgw::IAM::s3GetObject :
@@ -3107,7 +3165,7 @@ int RGWPutObj::verify_permission()
                              rgw::IAM::ARN(obj));
        if (e == Effect::Deny) {
          return -EACCES; 
-       } else if (e == Effect::Pass &&
+       } else if (usr_policy_res == Effect::Pass && e == Effect::Pass &&
                   !cs_acl.verify_permission(*s->auth.identity, s->perm_mask,
                                                RGW_PERM_READ)) {
          return -EACCES;
@@ -3125,7 +3183,7 @@ int RGWPutObj::verify_permission()
     return op_ret;
   }
 
-  if (s->iam_policy) {
+  if (s->iam_policy || ! s->iam_user_policies.empty()) {
     rgw_add_grant_to_iam_environment(s->env, s);
 
     rgw_add_to_iam_environment(s->env, "s3:x-amz-acl", s->canned_acl);
@@ -3151,6 +3209,13 @@ int RGWPutObj::verify_permission()
       rgw_add_to_iam_environment(s->env, s3_kms_attr, kms_header->second);
     }
 
+    auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+                                            *s->auth.identity,
+                                            rgw::IAM::s3PutObject,
+                                            rgw_obj(s->bucket, s->object));
+    if (usr_policy_res == Effect::Deny)
+      return -EACCES;
+
     auto e = s->iam_policy->eval(s->env, *s->auth.identity,
                                 rgw::IAM::s3PutObject,
                                 rgw_obj(s->bucket, s->object));
@@ -3158,6 +3223,8 @@ int RGWPutObj::verify_permission()
       return 0;
     } else if (e == Effect::Deny) {
       return -EACCES;
+    } else if (usr_policy_res == Effect::Allow) {
+      return 0;
     }
   }
 
@@ -3848,14 +3915,22 @@ void RGWPostObj::execute()
     return;
   }
 
-  if (s->iam_policy) {
+  if (s->iam_policy || ! s->iam_user_policies.empty()) {
+    auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+                                            *s->auth.identity,
+                                            rgw::IAM::s3PutObject,
+                                            rgw_obj(s->bucket, s->object));
+    if (usr_policy_res == Effect::Deny) {
+      op_ret = -EACCES;
+      return;
+    }
     auto e = s->iam_policy->eval(s->env, *s->auth.identity,
                                 rgw::IAM::s3PutObject,
                                 rgw_obj(s->bucket, s->object));
     if (e == Effect::Deny) {
       op_ret = -EACCES;
       return;
-    } else if (e == Effect::Pass && !verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) {
+    } else if (usr_policy_res == Effect::Pass && e == Effect::Pass && !verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) {
       op_ret = -EACCES;
       return;
     }
@@ -4111,7 +4186,7 @@ int RGWPutMetadataAccount::verify_permission()
     return -EACCES;
   }
 
-  if (!verify_user_permission(s, RGW_PERM_WRITE)) {
+  if (!verify_user_permission_no_policy(s, RGW_PERM_WRITE)) {
     return -EACCES;
   }
 
@@ -4360,7 +4435,16 @@ int RGWDeleteObj::handle_slo_manifest(bufferlist& bl)
 
 int RGWDeleteObj::verify_permission()
 {
-  if (s->iam_policy) {
+  if (s->iam_policy || ! s->iam_user_policies.empty()) {
+    auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+                                              *s->auth.identity,
+                                              s->object.instance.empty() ?
+                                              rgw::IAM::s3DeleteObject :
+                                              rgw::IAM::s3DeleteObjectVersion,
+                                              ARN(s->bucket, s->object.name));
+    if (usr_policy_res == Effect::Deny) {
+      return false;
+    }
     auto r = s->iam_policy->eval(s->env, *s->auth.identity,
                                 s->object.instance.empty() ?
                                 rgw::IAM::s3DeleteObject :
@@ -4370,6 +4454,8 @@ int RGWDeleteObj::verify_permission()
       return true;
     else if (r == Effect::Deny)
       return false;
+    else if (usr_policy_res == Effect::Allow)
+      return true;
   }
 
   if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) {
@@ -4780,7 +4866,14 @@ int RGWGetACLs::verify_permission()
       rgw_obj obj = rgw_obj(s->bucket, s->object);
       rgw_iam_add_existing_objtags(store, s, obj, iam_action);
     }
-
+    if (! s->iam_user_policies.empty()) {
+      for (auto& user_policy : s->iam_user_policies) {
+        if (user_policy.has_partial_conditional(S3_EXISTING_OBJTAG)) {
+          rgw_obj obj = rgw_obj(s->bucket, s->object);
+          rgw_iam_add_existing_objtags(store, s, obj, iam_action);
+        }
+      }
+    }
     perm = verify_object_permission(s, iam_action);
   } else {
     perm = verify_bucket_permission(s, rgw::IAM::s3GetBucketAcl);
@@ -5357,7 +5450,14 @@ void RGWSetRequestPayment::execute()
 
 int RGWInitMultipart::verify_permission()
 {
-  if (s->iam_policy) {
+  if (s->iam_policy || ! s->iam_user_policies.empty()) {
+    auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+                                              *s->auth.identity,
+                                              rgw::IAM::s3PutObject,
+                                              rgw_obj(s->bucket, s->object));
+    if (usr_policy_res == Effect::Deny) {
+      return -EACCES;
+    }
     auto e = s->iam_policy->eval(s->env, *s->auth.identity,
                                 rgw::IAM::s3PutObject,
                                 rgw_obj(s->bucket, s->object));
@@ -5365,6 +5465,8 @@ int RGWInitMultipart::verify_permission()
       return 0;
     } else if (e == Effect::Deny) {
       return -EACCES;
+    } else if (usr_policy_res == Effect::Allow) {
+      return 0;
     }
   }
 
@@ -5477,7 +5579,14 @@ static int get_multipart_info(RGWRados *store, struct req_state *s,
 
 int RGWCompleteMultipart::verify_permission()
 {
-  if (s->iam_policy) {
+  if (s->iam_policy || ! s->iam_user_policies.empty()) {
+    auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+                                              *s->auth.identity,
+                                              rgw::IAM::s3PutObject,
+                                              rgw_obj(s->bucket, s->object));
+    if (usr_policy_res == Effect::Deny) {
+      return -EACCES;
+    }
     auto e = s->iam_policy->eval(s->env, *s->auth.identity,
                                 rgw::IAM::s3PutObject,
                                 rgw_obj(s->bucket, s->object));
@@ -5485,6 +5594,8 @@ int RGWCompleteMultipart::verify_permission()
       return 0;
     } else if (e == Effect::Deny) {
       return -EACCES;
+    } else if (usr_policy_res == Effect::Allow) {
+      return 0;
     }
   }
 
@@ -5796,7 +5907,15 @@ void RGWCompleteMultipart::complete()
 
 int RGWAbortMultipart::verify_permission()
 {
-  if (s->iam_policy) {
+  if (s->iam_policy || ! s->iam_user_policies.empty()) {
+    auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+                                              *s->auth.identity,
+                                              rgw::IAM::s3AbortMultipartUpload,
+                                              rgw_obj(s->bucket, s->object));
+    if (usr_policy_res == Effect::Deny) {
+      return -EACCES;
+    }
+
     auto e = s->iam_policy->eval(s->env, *s->auth.identity,
                                 rgw::IAM::s3AbortMultipartUpload,
                                 rgw_obj(s->bucket, s->object));
@@ -5804,7 +5923,8 @@ int RGWAbortMultipart::verify_permission()
       return 0;
     } else if (e == Effect::Deny) {
       return -EACCES;
-    }
+    } else if (usr_policy_res == Effect::Allow)
+      return 0;
   }
 
   if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) {
@@ -5948,7 +6068,7 @@ void RGWGetHealthCheck::execute()
 int RGWDeleteMultiObj::verify_permission()
 {
   acl_allowed = verify_bucket_permission_no_policy(s, RGW_PERM_WRITE);
-  if (!acl_allowed && !s->iam_policy)
+  if (!acl_allowed && !s->iam_policy && s->iam_user_policies.empty())
     return -EACCES;
 
   return 0;
@@ -6020,7 +6140,17 @@ void RGWDeleteMultiObj::execute()
         iter != multi_delete->objects.end() && num_processed < max_to_delete;
         ++iter, num_processed++) {
     rgw_obj obj(bucket, *iter);
-    if (s->iam_policy) {
+    if (s->iam_policy || ! s->iam_user_policies.empty()) {
+      auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+                                              *s->auth.identity,
+                                              iter->instance.empty() ?
+                                              rgw::IAM::s3DeleteObject :
+                                              rgw::IAM::s3DeleteObjectVersion,
+                                              obj);
+      if (usr_policy_res == Effect::Deny) {
+        send_partial_response(*iter, false, "", -EACCES);
+        continue;
+      }
       auto e = s->iam_policy->eval(s->env,
                                   *s->auth.identity,
                                   iter->instance.empty() ?
@@ -6028,7 +6158,7 @@ void RGWDeleteMultiObj::execute()
                                   rgw::IAM::s3DeleteObjectVersion,
                                   obj);
       if ((e == Effect::Deny) ||
-         (e == Effect::Pass && !acl_allowed)) {
+         (usr_policy_res == Effect::Pass && e == Effect::Pass && !acl_allowed)) {
        send_partial_response(*iter, false, "", -EACCES);
        continue;
       }
@@ -6086,7 +6216,7 @@ bool RGWBulkDelete::Deleter::verify_permission(RGWBucketInfo& binfo,
   /* We can use global user_acl because each BulkDelete request is allowed
    * to work on entities from a single account only. */
   return verify_bucket_permission(s, binfo.bucket, s->user_acl.get(),
-                                 &bacl, policy, rgw::IAM::s3DeleteBucket);
+                                 &bacl, policy, s->iam_user_policies, rgw::IAM::s3DeleteBucket);
 }
 
 bool RGWBulkDelete::Deleter::delete_single(const acct_path_t& path)
@@ -6248,7 +6378,7 @@ int RGWBulkUploadOp::verify_permission()
     return -EACCES;
   }
 
-  if (! verify_user_permission(s, RGW_PERM_WRITE)) {
+  if (! verify_user_permission_no_policy(s, RGW_PERM_WRITE)) {
     return -EACCES;
   }
 
@@ -6527,13 +6657,21 @@ bool RGWBulkUploadOp::handle_file_verify_permission(RGWBucketInfo& binfo,
   auto policy = get_iam_policy_from_attr(s->cct, store, battrs, binfo.bucket.tenant);
 
   bucket_owner = bacl.get_owner();
-  if (policy) {
+  if (policy || ! s->iam_user_policies.empty()) {
+    auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+                                              *s->auth.identity,
+                                              rgw::IAM::s3PutObject, obj);
+    if (usr_policy_res == Effect::Deny) {
+      return false;
+    }
     auto e = policy->eval(s->env, *s->auth.identity,
                          rgw::IAM::s3PutObject, obj);
     if (e == Effect::Allow) {
       return true;
     } else if (e == Effect::Deny) {
       return false;
+    } else if (usr_policy_res == Effect::Allow) {
+      return true;
     }
   }