diff --git a/src/Access/Common/RowPolicyDefs.cpp b/src/Access/Common/RowPolicyDefs.cpp index 953709d95190..e1722501eaa9 100644 --- a/src/Access/Common/RowPolicyDefs.cpp +++ b/src/Access/Common/RowPolicyDefs.cpp @@ -78,4 +78,35 @@ const RowPolicyFilterTypeInfo & RowPolicyFilterTypeInfo::get(RowPolicyFilterType throw Exception("Unknown type: " + std::to_string(static_cast(type_)), ErrorCodes::LOGICAL_ERROR); } +String toString(RowPolicyKind type) +{ + return RowPolicyKindInfo::get(type).raw_name; +} + +const RowPolicyKindInfo & RowPolicyKindInfo::get(RowPolicyKind kind_) +{ + static constexpr auto make_info = [](const char * raw_name_) + { + String init_name = raw_name_; + boost::to_lower(init_name); + return RowPolicyKindInfo{raw_name_, std::move(init_name)}; + }; + + switch (kind_) + { + case RowPolicyKind::PERMISSIVE: + { + static const auto info = make_info("PERMISSIVE"); + return info; + } + case RowPolicyKind::RESTRICTIVE: + { + static const auto info = make_info("RESTRICTIVE"); + return info; + } + case RowPolicyKind::MAX: break; + } + throw Exception("Unknown kind: " + std::to_string(static_cast(kind_)), ErrorCodes::LOGICAL_ERROR); +} + } diff --git a/src/Access/Common/RowPolicyDefs.h b/src/Access/Common/RowPolicyDefs.h index 792884c56df9..3fa684bcc367 100644 --- a/src/Access/Common/RowPolicyDefs.h +++ b/src/Access/Common/RowPolicyDefs.h @@ -54,4 +54,25 @@ struct RowPolicyFilterTypeInfo static const RowPolicyFilterTypeInfo & get(RowPolicyFilterType type); }; + +/// Kinds of row policies. It affects how row policies are applied. +/// A row is only accessible if at least one of the permissive policies passes, +/// in addition to all the restrictive policies. +enum class RowPolicyKind +{ + PERMISSIVE, + RESTRICTIVE, + + MAX, +}; + +String toString(RowPolicyKind kind); + +struct RowPolicyKindInfo +{ + const char * const raw_name; + const String name; /// Lowercased with underscores, e.g. "permissive". + static const RowPolicyKindInfo & get(RowPolicyKind kind); +}; + } diff --git a/src/Access/RolesOrUsersSet.cpp b/src/Access/RolesOrUsersSet.cpp index 2c302fde229c..d811a857a2d7 100644 --- a/src/Access/RolesOrUsersSet.cpp +++ b/src/Access/RolesOrUsersSet.cpp @@ -281,6 +281,46 @@ std::vector RolesOrUsersSet::getMatchingIDs(const AccessControl & access_c } +bool RolesOrUsersSet::contains(const RolesOrUsersSet & other) const +{ + if (all && other.all) + { + for (const auto & id : except_ids) + { + if (!other.except_ids.contains(id)) + return false; + } + return true; + } + else if (all /* && !other.all */) + { + for (const auto & id : other.ids) + { + if (other.except_ids.contains(id)) + continue; + if (except_ids.contains(id)) + return false; + } + return true; + } + else if (other.all /* && !all */) + { + return false; + } + else /* !all && !other.all */ + { + for (const auto & id : other.ids) + { + if (other.except_ids.contains(id)) + continue; + if (!ids.contains(id) || except_ids.contains(id)) + return false; + } + return true; + } +} + + bool operator ==(const RolesOrUsersSet & lhs, const RolesOrUsersSet & rhs) { return (lhs.all == rhs.all) && (lhs.ids == rhs.ids) && (lhs.except_ids == rhs.except_ids); diff --git a/src/Access/RolesOrUsersSet.h b/src/Access/RolesOrUsersSet.h index 1d5842e31a6a..bdf355041f20 100644 --- a/src/Access/RolesOrUsersSet.h +++ b/src/Access/RolesOrUsersSet.h @@ -59,6 +59,9 @@ struct RolesOrUsersSet /// Returns a list of matching users and roles. std::vector getMatchingIDs(const AccessControl & access_control) const; + /// Returns true if this set contains each element of another set. + bool contains(const RolesOrUsersSet & other) const; + friend bool operator ==(const RolesOrUsersSet & lhs, const RolesOrUsersSet & rhs); friend bool operator !=(const RolesOrUsersSet & lhs, const RolesOrUsersSet & rhs) { return !(lhs == rhs); } diff --git a/src/Access/RowPolicy.cpp b/src/Access/RowPolicy.cpp index c09675e0e349..5c436b8943e2 100644 --- a/src/Access/RowPolicy.cpp +++ b/src/Access/RowPolicy.cpp @@ -53,9 +53,18 @@ bool RowPolicy::equal(const IAccessEntity & other) const { if (!IAccessEntity::equal(other)) return false; + const auto & other_policy = typeid_cast(other); - return (full_name == other_policy.full_name) && boost::range::equal(filters, other_policy.filters) - && restrictive == other_policy.restrictive && (to_roles == other_policy.to_roles); + if ((full_name != other_policy.full_name) || !boost::range::equal(filters, other_policy.filters) + || (kind != other_policy.kind) || (to_set != other_policy.to_set)) + return false; + + if (kind == RowPolicyKind::PERMISSIVE) + { + if (of_set != other_policy.of_set) + return false; + } + return true; } } diff --git a/src/Access/RowPolicy.h b/src/Access/RowPolicy.h index 9c143aff7253..1db1d1b88ba3 100644 --- a/src/Access/RowPolicy.h +++ b/src/Access/RowPolicy.h @@ -29,31 +29,36 @@ struct RowPolicy : public IAccessEntity /// for user or available for modification. std::array(RowPolicyFilterType::MAX)> filters; - /// Sets that the policy is permissive. - /// A row is only accessible if at least one of the permissive policies passes, - /// in addition to all the restrictive policies. - void setPermissive(bool permissive_ = true) { setRestrictive(!permissive_); } - bool isPermissive() const { return !isRestrictive(); } - - /// Sets that the policy is restrictive. - /// A row is only accessible if at least one of the permissive policies passes, - /// in addition to all the restrictive policies. - void setRestrictive(bool restrictive_ = true) { restrictive = restrictive_; } - bool isRestrictive() const { return restrictive; } + /// Sets the kind of the policy, it affects how row policies are applied. + void setKind(RowPolicyKind kind_) { kind = kind_; } + RowPolicyKind getKind() const { return kind; } bool equal(const IAccessEntity & other) const override; std::shared_ptr clone() const override { return cloneImpl(); } static constexpr const auto TYPE = AccessEntityType::ROW_POLICY; AccessEntityType getType() const override { return TYPE; } - /// Which roles or users should use this row policy. - RolesOrUsersSet to_roles; + /// Users and roles written in the TO clause. + /// For each user in this set this row policy is applied, + /// and for each role in this set this row policy is applied for any user using that role. + RolesOrUsersSet to_set; + + /// Users and roles written in the OF clause (used for permissive row policies only). + /// Contains a list of users this row policy affects. + /// There can be one of the three cases: + /// 1) If some user is in `to_set` set he will see filtered rows; + /// 2) If some user is not in `to_set` set but he's in `of_set` set + /// he won't see any rows unless other permissive row policies allow him + /// to see something; + /// 3) If some user is not in `to_set` set and not in `of_set` set + /// this row policy won't affect this user at all, but other row policies can. + RolesOrUsersSet of_set; private: void setName(const String &) override; RowPolicyName full_name; - bool restrictive = false; + RowPolicyKind kind = RowPolicyKind::PERMISSIVE; }; using RowPolicyPtr = std::shared_ptr; diff --git a/src/Access/RowPolicyCache.cpp b/src/Access/RowPolicyCache.cpp index 55bec4271580..e03578ecc555 100644 --- a/src/Access/RowPolicyCache.cpp +++ b/src/Access/RowPolicyCache.cpp @@ -20,21 +20,34 @@ namespace class FiltersMixer { public: - void add(const ASTPtr & filter, bool is_restrictive) + void add(const ASTPtr & filter, RowPolicyKind kind) { - if (is_restrictive) - restrictions.push_back(filter); + if (kind == RowPolicyKind::PERMISSIVE) + { + setPermissiveFiltersExist(); + permissive_filters.push_back(filter); + } else - permissions.push_back(filter); + { + restrictive_filters.push_back(filter); + } + } + + void setPermissiveFiltersExist() + { + permissive_filters_exist = true; } ASTPtr getResult() && { - /// Process permissive filters. - restrictions.push_back(makeASTForLogicalOr(std::move(permissions))); + if (permissive_filters_exist) + { + /// Process permissive filters. + restrictive_filters.push_back(makeASTForLogicalOr(std::move(permissive_filters))); + } /// Process restrictive filters. - auto result = makeASTForLogicalAnd(std::move(restrictions)); + auto result = makeASTForLogicalAnd(std::move(restrictive_filters)); bool value; if (tryGetLiteralBool(result.get(), value) && value) @@ -44,8 +57,9 @@ namespace } private: - ASTs permissions; - ASTs restrictions; + ASTs permissive_filters; + bool permissive_filters_exist = false; + ASTs restrictive_filters; }; } @@ -53,7 +67,8 @@ namespace void RowPolicyCache::PolicyInfo::setPolicy(const RowPolicyPtr & policy_) { policy = policy_; - roles = &policy->to_roles; + to_set = &policy->to_set; + of_set = &policy->of_set; database_and_table_name = std::make_shared>(policy->getDatabase(), policy->getTableName()); for (auto filter_type : collections::range(0, RowPolicyFilterType::MAX)) @@ -211,7 +226,8 @@ void RowPolicyCache::mixFiltersFor(EnabledRowPolicies & enabled) for (const auto & [policy_id, info] : all_policies) { const auto & policy = *info.policy; - bool match = info.roles->match(enabled.params.user_id, enabled.params.enabled_roles); + bool matches = info.to_set->match(enabled.params.user_id, enabled.params.enabled_roles); + bool affects = !matches && (policy.getKind() == RowPolicyKind::PERMISSIVE) && info.of_set->match(enabled.params.user_id, enabled.params.enabled_roles); MixedFiltersKey key; key.database = info.database_and_table_name->first; key.table_name = info.database_and_table_name->second; @@ -223,8 +239,10 @@ void RowPolicyCache::mixFiltersFor(EnabledRowPolicies & enabled) key.filter_type = filter_type; auto & mixer = mixers[key]; mixer.database_and_table_name = info.database_and_table_name; - if (match) - mixer.mixer.add(info.parsed_filters[filter_type_i], policy.isRestrictive()); + if (matches) + mixer.mixer.add(info.parsed_filters[filter_type_i], policy.getKind()); + else if (affects) + mixer.mixer.setPermissiveFiltersExist(); } } } diff --git a/src/Access/RowPolicyCache.h b/src/Access/RowPolicyCache.h index dc416fe59f05..d880c64f1d8d 100644 --- a/src/Access/RowPolicyCache.h +++ b/src/Access/RowPolicyCache.h @@ -30,7 +30,8 @@ class RowPolicyCache void setPolicy(const RowPolicyPtr & policy_); RowPolicyPtr policy; - const RolesOrUsersSet * roles = nullptr; + const RolesOrUsersSet * to_set = nullptr; + const RolesOrUsersSet * of_set = nullptr; std::shared_ptr> database_and_table_name; ASTPtr parsed_filters[static_cast(RowPolicyFilterType::MAX)]; }; diff --git a/src/Access/UsersConfigAccessStorage.cpp b/src/Access/UsersConfigAccessStorage.cpp index 5bd2da97445f..94e587860c0d 100644 --- a/src/Access/UsersConfigAccessStorage.cpp +++ b/src/Access/UsersConfigAccessStorage.cpp @@ -371,7 +371,8 @@ namespace auto policy = std::make_shared(); policy->setFullName(user_name, database, table_name); policy->filters[static_cast(RowPolicyFilterType::SELECT_FILTER)] = filter; - policy->to_roles.add(generateID(AccessEntityType::USER, user_name)); + policy->to_set.add(generateID(AccessEntityType::USER, user_name)); + policy->of_set.all = true; policies.push_back(policy); } } diff --git a/src/Interpreters/Access/InterpreterCreateRowPolicyQuery.cpp b/src/Interpreters/Access/InterpreterCreateRowPolicyQuery.cpp index 72b4b149bd79..ce174f33f593 100644 --- a/src/Interpreters/Access/InterpreterCreateRowPolicyQuery.cpp +++ b/src/Interpreters/Access/InterpreterCreateRowPolicyQuery.cpp @@ -20,7 +20,8 @@ namespace RowPolicy & policy, const ASTCreateRowPolicyQuery & query, const RowPolicyName & override_name, - const std::optional & override_to_roles) + const std::optional & override_to_set, + const std::optional & override_of_set) { if (!override_name.empty()) policy.setFullName(override_name); @@ -29,16 +30,33 @@ namespace else if (query.names->full_names.size() == 1) policy.setFullName(query.names->full_names.front()); - if (query.is_restrictive) - policy.setRestrictive(*query.is_restrictive); + auto old_kind = policy.getKind(); + if (query.kind) + policy.setKind(*query.kind); + bool kind_changed = (policy.getKind() != old_kind) || !query.alter; for (const auto & [filter_type, filter] : query.filters) policy.filters[static_cast(filter_type)] = filter ? serializeAST(*filter) : String{}; - if (override_to_roles) - policy.to_roles = *override_to_roles; - else if (query.roles) - policy.to_roles = *query.roles; + if (override_to_set) + policy.to_set = *override_to_set; + else if (query.to_set) + policy.to_set = *query.to_set; + + if (override_of_set) + policy.of_set = *override_of_set; + else if (query.of_set) + policy.of_set = *query.of_set; + else if ((policy.getKind() == RowPolicyKind::PERMISSIVE) && (override_to_set || query.to_set || kind_changed)) + policy.of_set = RolesOrUsersSet::AllTag{}; /// By default permissive row policies are OF ALL. + else if (policy.getKind() == RowPolicyKind::RESTRICTIVE) + policy.of_set.clear(); + + if ((policy.getKind() == RowPolicyKind::PERMISSIVE) && !policy.of_set.contains(policy.to_set)) + throw Exception("Users and roles in the TO clause must be a subset of ones in the OF clause", ErrorCodes::BAD_ARGUMENTS); + + if ((policy.getKind() == RowPolicyKind::RESTRICTIVE) && !policy.of_set.empty()) + throw Exception("OF clause can only be used with permissive row policies", ErrorCodes::BAD_ARGUMENTS); } } @@ -60,16 +78,18 @@ BlockIO InterpreterCreateRowPolicyQuery::execute() query.replaceEmptyDatabase(getContext()->getCurrentDatabase()); - std::optional roles_from_query; - if (query.roles) - roles_from_query = RolesOrUsersSet{*query.roles, access_control, getContext()->getUserID()}; + std::optional to_set_from_query, of_set_from_query; + if (query.to_set) + to_set_from_query = RolesOrUsersSet{*query.to_set, access_control, getContext()->getUserID()}; + if (query.of_set) + of_set_from_query = RolesOrUsersSet{*query.of_set, access_control, getContext()->getUserID()}; if (query.alter) { auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr { auto updated_policy = typeid_cast>(entity->clone()); - updateRowPolicyFromQueryImpl(*updated_policy, query, {}, roles_from_query); + updateRowPolicyFromQueryImpl(*updated_policy, query, {}, to_set_from_query, of_set_from_query); return updated_policy; }; Strings names = query.names->toStrings(); @@ -87,7 +107,7 @@ BlockIO InterpreterCreateRowPolicyQuery::execute() for (const auto & full_name : query.names->full_names) { auto new_policy = std::make_shared(); - updateRowPolicyFromQueryImpl(*new_policy, query, full_name, roles_from_query); + updateRowPolicyFromQueryImpl(*new_policy, query, full_name, to_set_from_query, of_set_from_query); new_policies.emplace_back(std::move(new_policy)); } @@ -105,7 +125,7 @@ BlockIO InterpreterCreateRowPolicyQuery::execute() void InterpreterCreateRowPolicyQuery::updateRowPolicyFromQuery(RowPolicy & policy, const ASTCreateRowPolicyQuery & query) { - updateRowPolicyFromQueryImpl(policy, query, {}, {}); + updateRowPolicyFromQueryImpl(policy, query, {}, {}, {}); } diff --git a/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp b/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp index 27345218e078..dafe9a585c05 100644 --- a/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp +++ b/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp @@ -189,9 +189,7 @@ namespace query->names = std::make_shared(); query->names->full_names.emplace_back(policy.getFullName()); query->attach = attach_mode; - - if (policy.isRestrictive()) - query->is_restrictive = policy.isRestrictive(); + query->kind = policy.getKind(); for (auto type : collections::range(RowPolicyFilterType::MAX)) { @@ -204,12 +202,20 @@ namespace } } - if (!policy.to_roles.empty()) + if (!policy.to_set.empty()) + { + if (attach_mode) + query->to_set = policy.to_set.toAST(); + else + query->to_set = policy.to_set.toASTWithNames(*access_control); + } + + if (!policy.of_set.empty()) { if (attach_mode) - query->roles = policy.to_roles.toAST(); + query->of_set = policy.of_set.toAST(); else - query->roles = policy.to_roles.toASTWithNames(*access_control); + query->of_set = policy.of_set.toASTWithNames(*access_control); } return query; diff --git a/src/Parsers/Access/ASTCreateRowPolicyQuery.cpp b/src/Parsers/Access/ASTCreateRowPolicyQuery.cpp index d968fdd32501..fa339e40880c 100644 --- a/src/Parsers/Access/ASTCreateRowPolicyQuery.cpp +++ b/src/Parsers/Access/ASTCreateRowPolicyQuery.cpp @@ -20,10 +20,10 @@ namespace } - void formatAsRestrictiveOrPermissive(bool is_restrictive, const IAST::FormatSettings & settings) + void formatAsKind(RowPolicyKind kind, const IAST::FormatSettings & settings) { settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " AS " << (settings.hilite ? IAST::hilite_none : "") - << (is_restrictive ? "restrictive" : "permissive"); + << RowPolicyKindInfo::get(kind).name; } @@ -108,10 +108,23 @@ namespace } - void formatToRoles(const ASTRolesOrUsersSet & roles, const IAST::FormatSettings & settings) + void formatToSet(const ASTRolesOrUsersSet & to_set, const IAST::FormatSettings & settings) { settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " TO " << (settings.hilite ? IAST::hilite_none : ""); - roles.format(settings); + to_set.format(settings); + } + + void formatOfSet(const ASTRolesOrUsersSet & of_set, bool same_as_to_set, const IAST::FormatSettings & settings) + { + if (same_as_to_set) + { + settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " ONLY" << (settings.hilite ? IAST::hilite_none : ""); + } + else + { + settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " OF " << (settings.hilite ? IAST::hilite_none : ""); + of_set.format(settings); + } } } @@ -156,20 +169,27 @@ void ASTCreateRowPolicyQuery::formatImpl(const FormatSettings & settings, Format if (!new_short_name.empty()) formatRenameTo(new_short_name, settings); - if (is_restrictive) - formatAsRestrictiveOrPermissive(*is_restrictive, settings); + if (kind) + formatAsKind(*kind, settings); formatForClauses(filters, alter, settings); - if (roles && (!roles->empty() || alter)) - formatToRoles(*roles, settings); + if (to_set || of_set) + { + auto to_set_not_null = to_set ? to_set : std::make_shared(); + formatToSet(*to_set_not_null, settings); + if (of_set) + formatOfSet(*of_set, of_set->equals(*to_set_not_null), settings); + } } void ASTCreateRowPolicyQuery::replaceCurrentUserTag(const String & current_user_name) const { - if (roles) - roles->replaceCurrentUserTag(current_user_name); + if (to_set) + to_set->replaceCurrentUserTag(current_user_name); + if (of_set) + of_set->replaceCurrentUserTag(current_user_name); } void ASTCreateRowPolicyQuery::replaceEmptyDatabase(const String & current_database) const diff --git a/src/Parsers/Access/ASTCreateRowPolicyQuery.h b/src/Parsers/Access/ASTCreateRowPolicyQuery.h index dc698c25c6d1..2c6e595dc61c 100644 --- a/src/Parsers/Access/ASTCreateRowPolicyQuery.h +++ b/src/Parsers/Access/ASTCreateRowPolicyQuery.h @@ -17,6 +17,7 @@ class ASTRolesOrUsersSet; * [USING condition] * [WITH CHECK condition] [,...] * [TO {role [,...] | ALL | ALL EXCEPT role [,...]}] + * [OF {role [,...] | ALL | ALL EXCEPT role [,...]}] * * ALTER [ROW] POLICY [IF EXISTS] name ON [database.]table * [RENAME TO new_name] @@ -25,6 +26,7 @@ class ASTRolesOrUsersSet; * [USING {condition | NONE}] * [WITH CHECK {condition | NONE}] [,...] * [TO {role [,...] | ALL | ALL EXCEPT role [,...]}] + * [OF {role [,...] | ALL | ALL EXCEPT role [,...]}] */ class ASTCreateRowPolicyQuery : public IAST, public ASTQueryWithOnCluster { @@ -39,10 +41,11 @@ class ASTCreateRowPolicyQuery : public IAST, public ASTQueryWithOnCluster std::shared_ptr names; String new_short_name; - std::optional is_restrictive; + std::optional kind; std::vector> filters; /// `nullptr` means set to NONE. - std::shared_ptr roles; + std::shared_ptr to_set; + std::shared_ptr of_set; String getID(char) const override; ASTPtr clone() const override; diff --git a/src/Parsers/Access/ASTRolesOrUsersSet.cpp b/src/Parsers/Access/ASTRolesOrUsersSet.cpp index dc7626b90d62..db904f459a7f 100644 --- a/src/Parsers/Access/ASTRolesOrUsersSet.cpp +++ b/src/Parsers/Access/ASTRolesOrUsersSet.cpp @@ -77,6 +77,16 @@ void ASTRolesOrUsersSet::formatImpl(const FormatSettings & settings, FormatState } +bool ASTRolesOrUsersSet::equals(const ASTRolesOrUsersSet & other) const +{ + if (all != other.all) + return false; + if (all) + return except_names == other.except_names; + return (names == other.names) && (except_names == other.except_names); +} + + void ASTRolesOrUsersSet::replaceCurrentUserTag(const String & current_user_name) { if (current_user) diff --git a/src/Parsers/Access/ASTRolesOrUsersSet.h b/src/Parsers/Access/ASTRolesOrUsersSet.h index 15d42ee39a0f..5c6a9941878a 100644 --- a/src/Parsers/Access/ASTRolesOrUsersSet.h +++ b/src/Parsers/Access/ASTRolesOrUsersSet.h @@ -26,6 +26,9 @@ class ASTRolesOrUsersSet : public IAST bool use_keyword_any = false; /// whether the keyword ANY should be used instead of the keyword ALL bool empty() const { return names.empty() && !current_user && !all; } + bool isAll() const { return all && except_names.empty() && !except_current_user; } + bool equals(const ASTRolesOrUsersSet & other) const; + void replaceCurrentUserTag(const String & current_user_name); String getID(char) const override { return "RolesOrUsersSet"; } diff --git a/src/Parsers/Access/ParserCreateRowPolicyQuery.cpp b/src/Parsers/Access/ParserCreateRowPolicyQuery.cpp index 731564a14c7e..a2917238fbe9 100644 --- a/src/Parsers/Access/ParserCreateRowPolicyQuery.cpp +++ b/src/Parsers/Access/ParserCreateRowPolicyQuery.cpp @@ -29,24 +29,24 @@ namespace }); } - bool parseAsRestrictiveOrPermissive(IParserBase::Pos & pos, Expected & expected, bool & is_restrictive) + bool parseAsKind(IParserBase::Pos & pos, Expected & expected, RowPolicyKind & kind) { return IParserBase::wrapParseImpl(pos, [&] { if (!ParserKeyword{"AS"}.ignore(pos, expected)) return false; - if (ParserKeyword{"RESTRICTIVE"}.ignore(pos, expected)) + for (auto current_kind : collections::range(RowPolicyKind::MAX)) { - is_restrictive = true; - return true; + const std::string_view & kind_name = RowPolicyKindInfo::get(current_kind).name; + if (ParserKeyword{kind_name.data()}.ignore(pos, expected)) + { + kind = current_kind; + return true; + } } - if (!ParserKeyword{"PERMISSIVE"}.ignore(pos, expected)) - return false; - - is_restrictive = false; - return true; + return false; }); } @@ -173,24 +173,48 @@ namespace return true; } - bool parseToRoles(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::shared_ptr & roles) + bool parseRolesOrUsersSet( + IParserBase::Pos & pos, Expected & expected, const char * keyword, bool id_mode, std::shared_ptr & set) { return IParserBase::wrapParseImpl(pos, [&] { ASTPtr ast; - if (!ParserKeyword{"TO"}.ignore(pos, expected)) + if (!ParserKeyword{keyword}.ignore(pos, expected)) return false; - ParserRolesOrUsersSet roles_p; - roles_p.allowAll().allowRoles().allowUsers().allowCurrentUser().useIDMode(id_mode); - if (!roles_p.parse(pos, ast, expected)) + ParserRolesOrUsersSet set_p; + set_p.allowAll().allowRoles().allowUsers().allowCurrentUser().useIDMode(id_mode); + if (!set_p.parse(pos, ast, expected)) return false; - roles = std::static_pointer_cast(ast); + set = std::static_pointer_cast(ast); return true; }); } + bool parseToSet(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::shared_ptr & to_set) + { + return parseRolesOrUsersSet(pos, expected, "TO", id_mode, to_set); + } + + bool parseOfSet(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::shared_ptr & of_set, bool & same_as_to_set) + { + if (parseRolesOrUsersSet(pos, expected, "OF", id_mode, of_set)) + { + same_as_to_set = false; + return true; + } + if (ParserKeyword{"ONLY"}.ignore(pos, expected)) + { + of_set = nullptr; + same_as_to_set = true; + return true; + } + of_set = nullptr; + same_as_to_set = false; + return false; + } + bool parseOnCluster(IParserBase::Pos & pos, Expected & expected, String & cluster) { return IParserBase::wrapParseImpl(pos, [&] @@ -243,7 +267,7 @@ bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & String cluster = std::exchange(names->cluster, ""); String new_short_name; - std::optional is_restrictive; + std::optional kind; std::vector> filters; while (true) @@ -251,12 +275,12 @@ bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & if (alter && (names->full_names.size() == 1) && new_short_name.empty() && parseRenameTo(pos, expected, new_short_name)) continue; - if (!is_restrictive) + if (!kind) { - bool new_is_restrictive; - if (parseAsRestrictiveOrPermissive(pos, expected, new_is_restrictive)) + RowPolicyKind new_kind; + if (parseAsKind(pos, expected, new_kind)) { - is_restrictive = new_is_restrictive; + kind = new_kind; continue; } } @@ -274,12 +298,31 @@ bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & break; } - std::shared_ptr roles; - parseToRoles(pos, expected, attach_mode, roles); + std::shared_ptr to_set, of_set; + if (parseToSet(pos, expected, attach_mode, to_set)) + { + bool same_as_to_set = false; + if (parseOfSet(pos, expected, attach_mode, of_set, same_as_to_set)) + { + if (same_as_to_set) + of_set = typeid_cast>(to_set->clone()); + } + } if (cluster.empty()) parseOnCluster(pos, expected, cluster); + /// By default row policies are permissive. + if (!kind && !alter) + kind = RowPolicyKind::PERMISSIVE; + + /// By default permissive row policies are OF ALL. + if (!of_set && to_set && !to_set->isAll() && (kind == RowPolicyKind::PERMISSIVE)) + { + of_set = std::make_shared(); + of_set->all = true; + } + auto query = std::make_shared(); node = query; @@ -291,9 +334,10 @@ bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & query->cluster = std::move(cluster); query->names = std::move(names); query->new_short_name = std::move(new_short_name); - query->is_restrictive = is_restrictive; + query->kind = kind; query->filters = std::move(filters); - query->roles = std::move(roles); + query->to_set = std::move(to_set); + query->of_set = std::move(of_set); return true; } diff --git a/src/Parsers/Access/ParserCreateRowPolicyQuery.h b/src/Parsers/Access/ParserCreateRowPolicyQuery.h index f05dca8179c2..c66d540a1510 100644 --- a/src/Parsers/Access/ParserCreateRowPolicyQuery.h +++ b/src/Parsers/Access/ParserCreateRowPolicyQuery.h @@ -12,6 +12,7 @@ namespace DB * [USING condition] * [WITH CHECK condition] [,...] * [TO {role [,...] | ALL | ALL EXCEPT role [,...]}] + * [OF {role [,...] | ALL | ALL EXCEPT role [,...]}] * * ALTER [ROW] POLICY [IF EXISTS] name ON [database.]table * [RENAME TO new_name] @@ -20,6 +21,7 @@ namespace DB * [USING {condition | NONE}] * [WITH CHECK {condition | NONE}] [,...] * [TO {role [,...] | ALL | ALL EXCEPT role [,...]}] + * [OF {role [,...] | ALL | ALL EXCEPT role [,...]}] */ class ParserCreateRowPolicyQuery : public IParserBase { diff --git a/src/Storages/System/StorageSystemRowPolicies.cpp b/src/Storages/System/StorageSystemRowPolicies.cpp index 455d715d5da9..43cb84f1d124 100644 --- a/src/Storages/System/StorageSystemRowPolicies.cpp +++ b/src/Storages/System/StorageSystemRowPolicies.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,26 @@ namespace DB { + +namespace +{ + const std::vector> & getRowPolicyKindEnumValues() + { + static const std::vector> values = [] + { + std::vector> res; + for (auto kind : collections::range(RowPolicyKind::MAX)) + { + const std::string_view & kind_name = RowPolicyKindInfo::get(kind).name; + res.emplace_back(kind_name, static_cast(kind)); + } + return res; + }(); + return values; + } +} + + NamesAndTypesList StorageSystemRowPolicies::getNamesAndTypes() { NamesAndTypesList names_and_types{ @@ -37,7 +58,7 @@ NamesAndTypesList StorageSystemRowPolicies::getNamesAndTypes() } NamesAndTypesList extra_names_and_types{ - {"is_restrictive", std::make_shared()}, + {"kind", std::make_shared(getRowPolicyKindEnumValues())}, {"apply_to_all", std::make_shared()}, {"apply_to_list", std::make_shared(std::make_shared())}, {"apply_to_except", std::make_shared(std::make_shared())} @@ -71,7 +92,7 @@ void StorageSystemRowPolicies::fillData(MutableColumns & res_columns, ContextPtr column_filter_null_map[filter_type_i] = &assert_cast(*res_columns[column_index++]).getNullMapData(); } - auto & column_is_restrictive = assert_cast(*res_columns[column_index++]).getData(); + auto & column_kind = assert_cast(*res_columns[column_index++]).getData(); auto & column_apply_to_all = assert_cast(*res_columns[column_index++]).getData(); auto & column_apply_to_list = assert_cast(assert_cast(*res_columns[column_index]).getData()); auto & column_apply_to_list_offsets = assert_cast(*res_columns[column_index++]).getOffsets(); @@ -83,7 +104,7 @@ void StorageSystemRowPolicies::fillData(MutableColumns & res_columns, ContextPtr const UUID & id, const String & storage_name, const std::array(RowPolicyFilterType::MAX)> & filters, - bool is_restrictive, + RowPolicyKind kind, const RolesOrUsersSet & apply_to) { column_name.insertData(name.data(), name.length()); @@ -109,7 +130,7 @@ void StorageSystemRowPolicies::fillData(MutableColumns & res_columns, ContextPtr } } - column_is_restrictive.push_back(is_restrictive); + column_kind.push_back(static_cast(kind)); auto apply_to_ast = apply_to.toASTWithNames(access_control); column_apply_to_all.push_back(apply_to_ast->all); @@ -132,7 +153,7 @@ void StorageSystemRowPolicies::fillData(MutableColumns & res_columns, ContextPtr if (!storage) continue; - add_row(policy->getName(), policy->getFullName(), id, storage->getStorageName(), policy->filters, policy->isRestrictive(), policy->to_roles); + add_row(policy->getName(), policy->getFullName(), id, storage->getStorageName(), policy->filters, policy->getKind(), policy->to_set); } } } diff --git a/tests/integration/test_row_policy/test.py b/tests/integration/test_row_policy/test.py index 0a7f6958b4a1..47c6f2efa32a 100644 --- a/tests/integration/test_row_policy/test.py +++ b/tests/integration/test_row_policy/test.py @@ -256,21 +256,21 @@ def test_reload_users_xml_by_timer(): def test_introspection(): policies = [ ["another ON mydb.filtered_table1", "another", "mydb", "filtered_table1", - "6068883a-0e9d-f802-7e22-0144f8e66d3c", "users.xml", "1", 0, 0, "['another']", "[]"], + "6068883a-0e9d-f802-7e22-0144f8e66d3c", "users.xml", "1", "permissive", 0, "['another']", "[]"], ["another ON mydb.filtered_table2", "another", "mydb", "filtered_table2", - "c019e957-c60b-d54e-cc52-7c90dac5fb01", "users.xml", "1", 0, 0, "['another']", "[]"], + "c019e957-c60b-d54e-cc52-7c90dac5fb01", "users.xml", "1", "permissive", 0, "['another']", "[]"], ["another ON mydb.filtered_table3", "another", "mydb", "filtered_table3", - "4cb080d0-44e8-dbef-6026-346655143628", "users.xml", "1", 0, 0, "['another']", "[]"], + "4cb080d0-44e8-dbef-6026-346655143628", "users.xml", "1", "permissive", 0, "['another']", "[]"], ["another ON mydb.local", "another", "mydb", "local", "5b23c389-7e18-06bf-a6bc-dd1afbbc0a97", "users.xml", - "a = 1", 0, 0, "['another']", "[]"], + "a = 1", "permissive", 0, "['another']", "[]"], ["default ON mydb.filtered_table1", "default", "mydb", "filtered_table1", - "9e8a8f62-4965-2b5e-8599-57c7b99b3549", "users.xml", "a = 1", 0, 0, "['default']", "[]"], + "9e8a8f62-4965-2b5e-8599-57c7b99b3549", "users.xml", "a = 1", "permissive", 0, "['default']", "[]"], ["default ON mydb.filtered_table2", "default", "mydb", "filtered_table2", - "cffae79d-b9bf-a2ef-b798-019c18470b25", "users.xml", "a + b < 1 or c - d > 5", 0, 0, "['default']", "[]"], + "cffae79d-b9bf-a2ef-b798-019c18470b25", "users.xml", "a + b < 1 or c - d > 5", "permissive", 0, "['default']", "[]"], ["default ON mydb.filtered_table3", "default", "mydb", "filtered_table3", - "12fc5cef-e3da-3940-ec79-d8be3911f42b", "users.xml", "c = 1", 0, 0, "['default']", "[]"], + "12fc5cef-e3da-3940-ec79-d8be3911f42b", "users.xml", "c = 1", "permissive", 0, "['default']", "[]"], ["default ON mydb.local", "default", "mydb", "local", "cdacaeb5-1d97-f99d-2bb0-4574f290629c", "users.xml", "1", - 0, 0, "['default']", "[]"] + "permissive", 0, "['default']", "[]"] ] assert node.query("SELECT * from system.row_policies ORDER BY short_name, database, table") == TSV(policies) @@ -292,49 +292,49 @@ def test_dcl_introspection(): "default ON mydb.local"]) assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE ROW POLICY default ON mydb.filtered_table1 FOR SELECT USING a = 1 TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE ROW POLICY default ON mydb.filtered_table1 AS permissive FOR SELECT USING a = 1 TO default\n" assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE ROW POLICY default ON mydb.filtered_table2 FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE ROW POLICY default ON mydb.filtered_table2 AS permissive FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default\n" assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE ROW POLICY default ON mydb.filtered_table3 FOR SELECT USING c = 1 TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE ROW POLICY default ON mydb.filtered_table3 AS permissive FOR SELECT USING c = 1 TO default\n" assert node.query( - "SHOW CREATE POLICY default ON mydb.local") == "CREATE ROW POLICY default ON mydb.local FOR SELECT USING 1 TO default\n" + "SHOW CREATE POLICY default ON mydb.local") == "CREATE ROW POLICY default ON mydb.local AS permissive FOR SELECT USING 1 TO default\n" assert node.query("SHOW CREATE POLICY default") == TSV( - ["CREATE ROW POLICY default ON mydb.filtered_table1 FOR SELECT USING a = 1 TO default", - "CREATE ROW POLICY default ON mydb.filtered_table2 FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default", - "CREATE ROW POLICY default ON mydb.filtered_table3 FOR SELECT USING c = 1 TO default", - "CREATE ROW POLICY default ON mydb.local FOR SELECT USING 1 TO default"]) + ["CREATE ROW POLICY default ON mydb.filtered_table1 AS permissive FOR SELECT USING a = 1 TO default", + "CREATE ROW POLICY default ON mydb.filtered_table2 AS permissive FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default", + "CREATE ROW POLICY default ON mydb.filtered_table3 AS permissive FOR SELECT USING c = 1 TO default", + "CREATE ROW POLICY default ON mydb.local AS permissive FOR SELECT USING 1 TO default"]) assert node.query("SHOW CREATE POLICIES ON mydb.filtered_table1") == TSV( - ["CREATE ROW POLICY another ON mydb.filtered_table1 FOR SELECT USING 1 TO another", - "CREATE ROW POLICY default ON mydb.filtered_table1 FOR SELECT USING a = 1 TO default"]) + ["CREATE ROW POLICY another ON mydb.filtered_table1 AS permissive FOR SELECT USING 1 TO another", + "CREATE ROW POLICY default ON mydb.filtered_table1 AS permissive FOR SELECT USING a = 1 TO default"]) assert node.query("SHOW CREATE POLICIES ON mydb.*") == TSV( - ["CREATE ROW POLICY another ON mydb.filtered_table1 FOR SELECT USING 1 TO another", - "CREATE ROW POLICY another ON mydb.filtered_table2 FOR SELECT USING 1 TO another", - "CREATE ROW POLICY another ON mydb.filtered_table3 FOR SELECT USING 1 TO another", - "CREATE ROW POLICY another ON mydb.local FOR SELECT USING a = 1 TO another", - "CREATE ROW POLICY default ON mydb.filtered_table1 FOR SELECT USING a = 1 TO default", - "CREATE ROW POLICY default ON mydb.filtered_table2 FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default", - "CREATE ROW POLICY default ON mydb.filtered_table3 FOR SELECT USING c = 1 TO default", - "CREATE ROW POLICY default ON mydb.local FOR SELECT USING 1 TO default"]) + ["CREATE ROW POLICY another ON mydb.filtered_table1 AS permissive FOR SELECT USING 1 TO another", + "CREATE ROW POLICY another ON mydb.filtered_table2 AS permissive FOR SELECT USING 1 TO another", + "CREATE ROW POLICY another ON mydb.filtered_table3 AS permissive FOR SELECT USING 1 TO another", + "CREATE ROW POLICY another ON mydb.local AS permissive FOR SELECT USING a = 1 TO another", + "CREATE ROW POLICY default ON mydb.filtered_table1 AS permissive FOR SELECT USING a = 1 TO default", + "CREATE ROW POLICY default ON mydb.filtered_table2 AS permissive FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default", + "CREATE ROW POLICY default ON mydb.filtered_table3 AS permissive FOR SELECT USING c = 1 TO default", + "CREATE ROW POLICY default ON mydb.local AS permissive FOR SELECT USING 1 TO default"]) assert node.query("SHOW CREATE POLICIES") == TSV( - ["CREATE ROW POLICY another ON mydb.filtered_table1 FOR SELECT USING 1 TO another", - "CREATE ROW POLICY another ON mydb.filtered_table2 FOR SELECT USING 1 TO another", - "CREATE ROW POLICY another ON mydb.filtered_table3 FOR SELECT USING 1 TO another", - "CREATE ROW POLICY another ON mydb.local FOR SELECT USING a = 1 TO another", - "CREATE ROW POLICY default ON mydb.filtered_table1 FOR SELECT USING a = 1 TO default", - "CREATE ROW POLICY default ON mydb.filtered_table2 FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default", - "CREATE ROW POLICY default ON mydb.filtered_table3 FOR SELECT USING c = 1 TO default", - "CREATE ROW POLICY default ON mydb.local FOR SELECT USING 1 TO default"]) - - expected_access = "CREATE ROW POLICY another ON mydb.filtered_table1 FOR SELECT USING 1 TO another\n" \ - "CREATE ROW POLICY another ON mydb.filtered_table2 FOR SELECT USING 1 TO another\n" \ - "CREATE ROW POLICY another ON mydb.filtered_table3 FOR SELECT USING 1 TO another\n" \ - "CREATE ROW POLICY another ON mydb.local FOR SELECT USING a = 1 TO another\n" \ - "CREATE ROW POLICY default ON mydb.filtered_table1 FOR SELECT USING a = 1 TO default\n" \ - "CREATE ROW POLICY default ON mydb.filtered_table2 FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default\n" \ - "CREATE ROW POLICY default ON mydb.filtered_table3 FOR SELECT USING c = 1 TO default\n" \ - "CREATE ROW POLICY default ON mydb.local FOR SELECT USING 1 TO default\n" + ["CREATE ROW POLICY another ON mydb.filtered_table1 AS permissive FOR SELECT USING 1 TO another", + "CREATE ROW POLICY another ON mydb.filtered_table2 AS permissive FOR SELECT USING 1 TO another", + "CREATE ROW POLICY another ON mydb.filtered_table3 AS permissive FOR SELECT USING 1 TO another", + "CREATE ROW POLICY another ON mydb.local AS permissive FOR SELECT USING a = 1 TO another", + "CREATE ROW POLICY default ON mydb.filtered_table1 AS permissive FOR SELECT USING a = 1 TO default", + "CREATE ROW POLICY default ON mydb.filtered_table2 AS permissive FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default", + "CREATE ROW POLICY default ON mydb.filtered_table3 AS permissive FOR SELECT USING c = 1 TO default", + "CREATE ROW POLICY default ON mydb.local AS permissive FOR SELECT USING 1 TO default"]) + + expected_access = "CREATE ROW POLICY another ON mydb.filtered_table1 AS permissive FOR SELECT USING 1 TO another\n" \ + "CREATE ROW POLICY another ON mydb.filtered_table2 AS permissive FOR SELECT USING 1 TO another\n" \ + "CREATE ROW POLICY another ON mydb.filtered_table3 AS permissive FOR SELECT USING 1 TO another\n" \ + "CREATE ROW POLICY another ON mydb.local AS permissive FOR SELECT USING a = 1 TO another\n" \ + "CREATE ROW POLICY default ON mydb.filtered_table1 AS permissive FOR SELECT USING a = 1 TO default\n" \ + "CREATE ROW POLICY default ON mydb.filtered_table2 AS permissive FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default\n" \ + "CREATE ROW POLICY default ON mydb.filtered_table3 AS permissive FOR SELECT USING c = 1 TO default\n" \ + "CREATE ROW POLICY default ON mydb.local AS permissive FOR SELECT USING 1 TO default\n" assert expected_access in node.query("SHOW ACCESS") copy_policy_xml('all_rows.xml') @@ -342,22 +342,22 @@ def test_dcl_introspection(): ["another ON mydb.filtered_table1", "another ON mydb.filtered_table2", "another ON mydb.filtered_table3", "default ON mydb.filtered_table1", "default ON mydb.filtered_table2", "default ON mydb.filtered_table3"]) assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE ROW POLICY default ON mydb.filtered_table1 FOR SELECT USING 1 TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE ROW POLICY default ON mydb.filtered_table1 AS permissive FOR SELECT USING 1 TO default\n" assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE ROW POLICY default ON mydb.filtered_table2 FOR SELECT USING 1 TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE ROW POLICY default ON mydb.filtered_table2 AS permissive FOR SELECT USING 1 TO default\n" assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE ROW POLICY default ON mydb.filtered_table3 FOR SELECT USING 1 TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE ROW POLICY default ON mydb.filtered_table3 AS permissive FOR SELECT USING 1 TO default\n" copy_policy_xml('no_rows.xml') assert node.query("SHOW POLICIES") == TSV( ["another ON mydb.filtered_table1", "another ON mydb.filtered_table2", "another ON mydb.filtered_table3", "default ON mydb.filtered_table1", "default ON mydb.filtered_table2", "default ON mydb.filtered_table3"]) assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE ROW POLICY default ON mydb.filtered_table1 FOR SELECT USING NULL TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE ROW POLICY default ON mydb.filtered_table1 AS permissive FOR SELECT USING NULL TO default\n" assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE ROW POLICY default ON mydb.filtered_table2 FOR SELECT USING NULL TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE ROW POLICY default ON mydb.filtered_table2 AS permissive FOR SELECT USING NULL TO default\n" assert node.query( - "SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE ROW POLICY default ON mydb.filtered_table3 FOR SELECT USING NULL TO default\n" + "SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE ROW POLICY default ON mydb.filtered_table3 AS permissive FOR SELECT USING NULL TO default\n" copy_policy_xml('no_filters.xml') assert node.query("SHOW POLICIES") == "" @@ -382,7 +382,7 @@ def test_dcl_management(): assert node.query("SELECT * FROM mydb.filtered_table1") == TSV([[1, 0]]) assert node.query("SHOW POLICIES ON mydb.filtered_table1") == "pB\n" assert node.query( - "SHOW CREATE POLICY pB ON mydb.filtered_table1") == "CREATE ROW POLICY pB ON mydb.filtered_table1 FOR SELECT USING a > b TO default\n" + "SHOW CREATE POLICY pB ON mydb.filtered_table1") == "CREATE ROW POLICY pB ON mydb.filtered_table1 AS permissive FOR SELECT USING a > b TO default\n" node.query("DROP POLICY pB ON mydb.filtered_table1") assert node.query("SELECT * FROM mydb.filtered_table1") == TSV([[0, 0], [0, 1], [1, 0], [1, 1]]) @@ -435,6 +435,23 @@ def test_grant_create_row_policy(): node.query("DROP USER X") +def test_some_users_without_policies(): + copy_policy_xml('no_filters.xml') + assert node.query("SHOW POLICIES") == "" + node.query("CREATE USER X, Y") + node.query("GRANT SELECT ON mydb.filtered_table1 TO X, Y") + + node.query("CREATE POLICY pA ON mydb.filtered_table1 AS permissive FOR SELECT USING a (d + 5) TO default", - "CREATE ROW POLICY default ON mydb.filtered_table3 FOR SELECT USING c = 0 TO default", - "CREATE ROW POLICY default ON mydb.table FOR SELECT USING a = 0 TO default"]) + ["CREATE ROW POLICY default ON mydb.`.filtered_table4` AS permissive FOR SELECT USING c = 2 TO default", + "CREATE ROW POLICY default ON mydb.filtered_table2 AS permissive FOR SELECT USING c > (d + 5) TO default", + "CREATE ROW POLICY default ON mydb.filtered_table3 AS permissive FOR SELECT USING c = 0 TO default", + "CREATE ROW POLICY default ON mydb.table AS permissive FOR SELECT USING a = 0 TO default"]) def test_miscellaneous_engines(): diff --git a/tests/queries/0_stateless/02131_multiple_row_policies_on_same_column.reference b/tests/queries/0_stateless/02131_multiple_row_policies_on_same_column.reference new file mode 100644 index 000000000000..b76028d50775 --- /dev/null +++ b/tests/queries/0_stateless/02131_multiple_row_policies_on_same_column.reference @@ -0,0 +1,33 @@ +None +1 +2 +3 +4 +R1: x == 1 +1 +R1, R2: (x == 1) OR (x == 2) +1 +2 +R1, R2, R3: (x == 1) OR (x == 2) OR (x == 3) +1 +2 +3 +R1, R2, R3, R4: ((x == 1) OR (x == 2) OR (x == 3)) AND (x <= 2) +1 +2 +R1, R2, R3, R4, R5: ((x == 1) OR (x == 2) OR (x == 3)) AND (x <= 2) AND (x >= 2) +2 +R2, R3, R4, R5: ((x == 2) OR (x == 3)) AND (x <= 2) AND (x >= 2) +2 +R3, R4, R5: (x == 3) AND (x <= 2) AND (x >= 2) +R4, R5: (x <= 2) AND (x >= 2) +2 +R5: (x >= 2) +2 +3 +4 +None +1 +2 +3 +4 diff --git a/tests/queries/0_stateless/02131_multiple_row_policies_on_same_column.sql b/tests/queries/0_stateless/02131_multiple_row_policies_on_same_column.sql new file mode 100644 index 000000000000..69f11b5d13ab --- /dev/null +++ b/tests/queries/0_stateless/02131_multiple_row_policies_on_same_column.sql @@ -0,0 +1,55 @@ +DROP TABLE IF EXISTS 02131_multiple_row_policies_on_same_column; +CREATE TABLE 02131_multiple_row_policies_on_same_column (x UInt8) ENGINE = MergeTree ORDER BY x; +INSERT INTO 02131_multiple_row_policies_on_same_column VALUES (1), (2), (3), (4); + + +DROP ROW POLICY IF EXISTS 02131_filter_1 ON 02131_multiple_row_policies_on_same_column; +DROP ROW POLICY IF EXISTS 02131_filter_2 ON 02131_multiple_row_policies_on_same_column; +DROP ROW POLICY IF EXISTS 02131_filter_3 ON 02131_multiple_row_policies_on_same_column; +DROP ROW POLICY IF EXISTS 02131_filter_4 ON 02131_multiple_row_policies_on_same_column; +DROP ROW POLICY IF EXISTS 02131_filter_5 ON 02131_multiple_row_policies_on_same_column; + +SELECT 'None'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +CREATE ROW POLICY 02131_filter_1 ON 02131_multiple_row_policies_on_same_column USING x=1 AS permissive TO ALL; +SELECT 'R1: x == 1'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +CREATE ROW POLICY 02131_filter_2 ON 02131_multiple_row_policies_on_same_column USING x=2 AS permissive TO ALL; +SELECT 'R1, R2: (x == 1) OR (x == 2)'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +CREATE ROW POLICY 02131_filter_3 ON 02131_multiple_row_policies_on_same_column USING x=3 AS permissive TO ALL; +SELECT 'R1, R2, R3: (x == 1) OR (x == 2) OR (x == 3)'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +CREATE ROW POLICY 02131_filter_4 ON 02131_multiple_row_policies_on_same_column USING x<=2 AS restrictive TO ALL; +SELECT 'R1, R2, R3, R4: ((x == 1) OR (x == 2) OR (x == 3)) AND (x <= 2)'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +CREATE ROW POLICY 02131_filter_5 ON 02131_multiple_row_policies_on_same_column USING x>=2 AS restrictive TO ALL; +SELECT 'R1, R2, R3, R4, R5: ((x == 1) OR (x == 2) OR (x == 3)) AND (x <= 2) AND (x >= 2)'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +DROP ROW POLICY 02131_filter_1 ON 02131_multiple_row_policies_on_same_column; +SELECT 'R2, R3, R4, R5: ((x == 2) OR (x == 3)) AND (x <= 2) AND (x >= 2)'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +DROP ROW POLICY 02131_filter_2 ON 02131_multiple_row_policies_on_same_column; +SELECT 'R3, R4, R5: (x == 3) AND (x <= 2) AND (x >= 2)'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +DROP ROW POLICY 02131_filter_3 ON 02131_multiple_row_policies_on_same_column; +SELECT 'R4, R5: (x <= 2) AND (x >= 2)'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +DROP ROW POLICY 02131_filter_4 ON 02131_multiple_row_policies_on_same_column; +SELECT 'R5: (x >= 2)'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +DROP ROW POLICY 02131_filter_5 ON 02131_multiple_row_policies_on_same_column; +SELECT 'None'; +SELECT * FROM 02131_multiple_row_policies_on_same_column; + +DROP TABLE 02131_multiple_row_policies_on_same_column; diff --git a/tests/queries/0_stateless/02131_multiply_row_policies_on_same_column.reference b/tests/queries/0_stateless/02131_multiply_row_policies_on_same_column.reference deleted file mode 100644 index 3f71510f3a5a..000000000000 --- a/tests/queries/0_stateless/02131_multiply_row_policies_on_same_column.reference +++ /dev/null @@ -1,8 +0,0 @@ -4 -1 -2 -3 -3 -3 -3 -4 diff --git a/tests/queries/0_stateless/02131_multiply_row_policies_on_same_column.sql b/tests/queries/0_stateless/02131_multiply_row_policies_on_same_column.sql deleted file mode 100644 index 75f7f737e858..000000000000 --- a/tests/queries/0_stateless/02131_multiply_row_policies_on_same_column.sql +++ /dev/null @@ -1,30 +0,0 @@ -DROP TABLE IF EXISTS 02131_multiply_row_policies_on_same_column; -CREATE TABLE 02131_multiply_row_policies_on_same_column (x UInt8) ENGINE = MergeTree ORDER BY x; -INSERT INTO 02131_multiply_row_policies_on_same_column VALUES (1), (2), (3), (4); - - -DROP ROW POLICY IF EXISTS 02131_filter_1 ON 02131_multiply_row_policies_on_same_column; -DROP ROW POLICY IF EXISTS 02131_filter_2 ON 02131_multiply_row_policies_on_same_column; -DROP ROW POLICY IF EXISTS 02131_filter_3 ON 02131_multiply_row_policies_on_same_column; -SELECT count() FROM 02131_multiply_row_policies_on_same_column; - - -CREATE ROW POLICY 02131_filter_1 ON 02131_multiply_row_policies_on_same_column USING x=1 TO ALL; -SELECT count() FROM 02131_multiply_row_policies_on_same_column; -CREATE ROW POLICY 02131_filter_2 ON 02131_multiply_row_policies_on_same_column USING x=2 TO ALL; -SELECT count() FROM 02131_multiply_row_policies_on_same_column; -CREATE ROW POLICY 02131_filter_3 ON 02131_multiply_row_policies_on_same_column USING x=3 TO ALL; -SELECT count() FROM 02131_multiply_row_policies_on_same_column; -SELECT count() FROM 02131_multiply_row_policies_on_same_column; - - -CREATE ROW POLICY 02131_filter_4 ON 02131_multiply_row_policies_on_same_column USING x<4 AS RESTRICTIVE TO ALL; -SELECT count() FROM 02131_multiply_row_policies_on_same_column; -SELECT count() FROM 02131_multiply_row_policies_on_same_column; - -DROP ROW POLICY 02131_filter_1 ON 02131_multiply_row_policies_on_same_column; -DROP ROW POLICY 02131_filter_2 ON 02131_multiply_row_policies_on_same_column; -DROP ROW POLICY 02131_filter_3 ON 02131_multiply_row_policies_on_same_column; -DROP ROW POLICY 02131_filter_4 ON 02131_multiply_row_policies_on_same_column; -SELECT count() FROM 02131_multiply_row_policies_on_same_column; -DROP TABLE 02131_multiply_row_policies_on_same_column;