Skip to content

Commit

Permalink
feat(Ranger): refactor the logic when ranger performs ACL (#1518)
Browse files Browse the repository at this point in the history
#1054

This patch fixes the judgment logic when ranger matches policies:

1. Traverse all resource policies
   i. If the current policy matches deny_condition
      a. does not match any deny_exclude, returns kDenied, and the traversal ends
      b. A deny_exclude is matched, return kPending, and continue to the next policy judgment
   ii. No policy is matched or the return value is kPending, enter 2
2. Traverse all resource policies again
   i. If the current policy matches allow_condition
      a. does not match any allow_exclude, returns kAllowed, and the traversal ends
      b. An allow_exclude is matched, return kPending, and continue to the next policy judgment
   ii. If the return value is kPending, it will return kDenied
3. dose not match any policy, return kDenied
  • Loading branch information
WHBANG authored Jun 15, 2023
1 parent 992cae4 commit 78136dd
Show file tree
Hide file tree
Showing 10 changed files with 832 additions and 158 deletions.
11 changes: 11 additions & 0 deletions src/runtime/ranger/access_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <cstdint>
#include <type_traits>

#include "utils/enum_helper.h"

namespace dsn {
namespace ranger {

Expand All @@ -35,6 +37,15 @@ enum class access_type : uint8_t
kMetadata = 1 << 5,
kControl = 1 << 6
};
ENUM_BEGIN(access_type, access_type::kInvalid)
ENUM_REG(access_type::kRead)
ENUM_REG(access_type::kWrite)
ENUM_REG(access_type::kCreate)
ENUM_REG(access_type::kDrop)
ENUM_REG(access_type::kList)
ENUM_REG(access_type::kMetadata)
ENUM_REG(access_type::kControl)
ENUM_END(access_type)

using act = std::underlying_type<access_type>::type;

Expand Down
248 changes: 222 additions & 26 deletions src/runtime/ranger/ranger_resource_policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "ranger_resource_policy.h"

#include "runtime/ranger/access_type.h"
#include "utils/fmt_logging.h"

namespace dsn {
namespace ranger {
Expand All @@ -27,45 +28,240 @@ bool policy_item::match(const access_type &ac_type, const std::string &user_name
return static_cast<bool>(access_types & ac_type) && users.count(user_name) != 0;
}

bool acl_policies::allowed(const access_type &ac_type, const std::string &user_name) const
template <>
policy_check_status
acl_policies::policies_check<policy_check_type::kAllow>(const access_type &ac_type,
const std::string &user_name) const
{
// 1. Check if it is not allowed.
for (const auto &deny_policy : deny_policies) {
// 1.1. In 'deny_policies'.
if (!deny_policy.match(ac_type, user_name)) {
return do_policies_check<policy_check_type::kAllow, policy_check_status::kAllowed>(ac_type,
user_name);
}

template <>
policy_check_status
acl_policies::policies_check<policy_check_type::kDeny>(const access_type &ac_type,
const std::string &user_name) const
{
return do_policies_check<policy_check_type::kDeny, policy_check_status::kDenied>(ac_type,
user_name);
}

template <>
policy_check_status
acl_policies::do_policies_check<policy_check_type::kAllow, policy_check_status::kAllowed>(
const access_type &ac_type, const std::string &user_name) const
{
for (const auto &policy : allow_policies) {
// 1. Doesn't match an allow_policies.
if (!policy.match(ac_type, user_name)) {
continue;
}
bool in_deny_policies_exclude = false;
for (const auto &deny_policy_exclude : deny_policies_exclude) {
if (deny_policy_exclude.match(ac_type, user_name)) {
in_deny_policies_exclude = true;
break;
// 2. Matches a policy.
for (const auto &exclude_policy : allow_policies_exclude) {
if (exclude_policy.match(ac_type, user_name)) {
// 2.1. Matches an allow_policies_exclude.
return policy_check_status::kPending;
}
}
// 1.2. Not in any 'deny_policies_exclude', it's not allowed.
if (!in_deny_policies_exclude) {
return false;
}
// 2.2. Doesn't match any allow_exclude_policies.
return policy_check_status::kAllowed;
}
// 3. Doesn't match any policy.
return policy_check_status::kNotMatched;
}

// 2. Check if it is allowed.
for (const auto &allow_policy : allow_policies) {
// 2.1. In 'allow_policies'.
if (!allow_policy.match(ac_type, user_name)) {
template <>
policy_check_status
acl_policies::do_policies_check<policy_check_type::kDeny, policy_check_status::kDenied>(
const access_type &ac_type, const std::string &user_name) const
{
for (const auto &policy : deny_policies) {
// 1. Doesn't match a deny_policies.
if (!policy.match(ac_type, user_name)) {
continue;
}
for (const auto &allow_policy_exclude : allow_policies_exclude) {
// 2.2. In some 'allow_policies_exclude', it's not allowed.
if (allow_policy_exclude.match(ac_type, user_name)) {
return false;
// 2. Matches a policy.
for (const auto &exclude_policy : deny_policies_exclude) {
if (exclude_policy.match(ac_type, user_name)) {
// 2.1. Matches a deny_policies_exclude.
return policy_check_status::kPending;
}
}
// 2.2. Doesn't match any deny_exclude_policies.
return policy_check_status::kDenied;
}
// 3. Doesn't match any policy.
return policy_check_status::kNotMatched;
}

access_control_result
check_ranger_resource_policy_allowed(const std::vector<ranger_resource_policy> &policies,
const access_type &ac_type,
const std::string &user_name,
const match_database_type &md_type,
const std::string &database_name,
const std::string &default_database_name)
{
// Check if it is denied by any policy in current resource.
auto check_res = do_check_ranger_resource_policy<policy_check_type::kDeny>(
policies, ac_type, user_name, md_type, database_name, default_database_name);
if (access_control_result::kDenied == check_res) {
return access_control_result::kDenied;
}
CHECK(access_control_result::kPending == check_res, "the access control result must kPending.");

// Check if it is allowed by any policy in current resource.
check_res = do_check_ranger_resource_policy<policy_check_type::kAllow>(
policies, ac_type, user_name, md_type, database_name, default_database_name);
if (access_control_result::kAllowed == check_res) {
return access_control_result::kAllowed;
}
CHECK(access_control_result::kPending == check_res, "the access control result must kPending.");

// The check that does not match any policy in current reosource returns false.
return access_control_result::kDenied;
}

template <>
access_control_result do_check_ranger_resource_policy<policy_check_type::kAllow>(
const std::vector<ranger_resource_policy> &policies,
const access_type &ac_type,
const std::string &user_name,
const match_database_type &md_type,
const std::string &database_name,
const std::string &default_database_name)
{
for (const auto &policy : policies) {
if (match_database_type::kNeed == md_type) {
// Lagacy table not match any database.
if (database_name.empty() && policy.database_names.count("*") == 0 &&
policy.database_names.count(default_database_name) == 0) {
continue;
}
// New table not match any database.
if (!database_name.empty() && policy.database_names.count("*") == 0 &&
policy.database_names.count(database_name) == 0) {
continue;
}
}
// 2.3. Not in any 'allow_policies_exclude', it's allowed.
return true;
auto check_status =
policy.policies.policies_check<policy_check_type::kAllow>(ac_type, user_name);
if (policy_check_status::kAllowed == check_status) {
return access_control_result::kAllowed;
}

// In a 'allow_policies' and in a 'allow_policies_exclude' or not match.
CHECK(policy_check_status::kPending == check_status ||
policy_check_status::kNotMatched == check_status,
"the policy check status must be kPending or kNotMatched");
}
return access_control_result::kPending;
}

// 3. Otherwise, it's not allowed.
return false;
template <>
access_control_result do_check_ranger_resource_policy<policy_check_type::kDeny>(
const std::vector<ranger_resource_policy> &policies,
const access_type &ac_type,
const std::string &user_name,
const match_database_type &md_type,
const std::string &database_name,
const std::string &default_database_name)
{
for (const auto &policy : policies) {
if (match_database_type::kNeed == md_type) {
// Lagacy table not match any database.
if (database_name.empty() && policy.database_names.count("*") == 0 &&
policy.database_names.count(default_database_name) == 0) {
continue;
}
// New table not match any database.
if (!database_name.empty() && policy.database_names.count("*") == 0 &&
policy.database_names.count(database_name) == 0) {
continue;
}
}
auto check_status =
policy.policies.policies_check<policy_check_type::kDeny>(ac_type, user_name);
if (policy_check_status::kDenied == check_status) {
return access_control_result::kDenied;
}

// In a 'deny_policies' and in a 'deny_policies_exclude' or not match.
CHECK(policy_check_status::kPending == check_status ||
policy_check_status::kNotMatched == check_status,
"the policy check status must be kPending or kNotMatched");
}
return access_control_result::kPending;
}

access_control_result check_ranger_database_table_policy_allowed(
const std::vector<matched_database_table_policy> &policies,
const access_type &ac_type,
const std::string &user_name)
{
// Check if it is denied by any DATABASE_TABLE policy.
auto check_res = do_check_ranger_database_table_policy<policy_check_type::kDeny>(
policies, ac_type, user_name);
if (access_control_result::kDenied == check_res) {
return access_control_result::kDenied;
}
CHECK(access_control_result::kPending == check_res, "the access control result must kPending.");

// Check if it is allowed by any DATABASE_TABLE policy.
check_res = do_check_ranger_database_table_policy<policy_check_type::kAllow>(
policies, ac_type, user_name);
if (access_control_result::kAllowed == check_res) {
return access_control_result::kAllowed;
}
CHECK(access_control_result::kPending == check_res, "the access control result must kPending.");

// The check that does not match any DATABASE_TABLE policy returns false.
return access_control_result::kDenied;
}

template <>
access_control_result do_check_ranger_database_table_policy<policy_check_type::kDeny>(
const std::vector<matched_database_table_policy> &policies,
const access_type &ac_type,
const std::string &user_name)
{
for (const auto &policy : policies) {
auto check_status =
policy.policies.policies_check<policy_check_type::kDeny>(ac_type, user_name);
// When policy_check_type is 'kDeny' and in a 'deny_policies' and not in any
// 'deny_policies_exclude'.
if (policy_check_status::kDenied == check_status) {
return access_control_result::kDenied;
}

// In a 'policies' and in a 'policies_exclude' or not match.
CHECK(policy_check_status::kPending == check_status ||
policy_check_status::kNotMatched == check_status,
"the policy check status must be kPending or kNotMatched");
}
return access_control_result::kPending;
}

template <>
access_control_result do_check_ranger_database_table_policy<policy_check_type::kAllow>(
const std::vector<matched_database_table_policy> &policies,
const access_type &ac_type,
const std::string &user_name)
{
for (const auto &policy : policies) {
auto check_status =
policy.policies.policies_check<policy_check_type::kAllow>(ac_type, user_name);
// When policy_check_type is 'kAllow' and in a 'allow_policies' and not in any
// 'allow_policies_exclude'.
if (policy_check_status::kAllowed == check_status) {
return access_control_result::kAllowed;
}
// In a 'policies' and in a 'policies_exclude' or not match.
CHECK(policy_check_status::kPending == check_status ||
policy_check_status::kNotMatched == check_status,
"the policy check status must be kPending or kNotMatched");
}
return access_control_result::kPending;
}

} // namespace ranger
Expand Down
Loading

0 comments on commit 78136dd

Please sign in to comment.