Wildcard grants

This commit is contained in:
pufit 2024-06-14 19:11:24 -04:00
parent 905495e59b
commit 0fb239cd7f
17 changed files with 1118 additions and 498 deletions

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@
#include <base/types.h>
#include <Access/Common/AccessRightsElement.h>
#include <IO/WriteBuffer.h>
#include <functional>
#include <memory>
#include <vector>
@ -47,6 +49,13 @@ public:
void grant(const AccessRightsElement & element);
void grant(const AccessRightsElements & elements);
void grantWildcard(const AccessFlags & flags);
void grantWildcard(const AccessFlags & flags, std::string_view database);
void grantWildcard(const AccessFlags & flags, std::string_view database, std::string_view table);
void grantWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, std::string_view column);
void grantWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, const std::vector<std::string_view> & columns);
void grantWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, const Strings & columns);
void grantWithGrantOption(const AccessFlags & flags);
void grantWithGrantOption(const AccessFlags & flags, std::string_view database);
void grantWithGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table);
@ -56,6 +65,13 @@ public:
void grantWithGrantOption(const AccessRightsElement & element);
void grantWithGrantOption(const AccessRightsElements & elements);
void grantWildcardWithGrantOption(const AccessFlags & flags);
void grantWildcardWithGrantOption(const AccessFlags & flags, std::string_view database);
void grantWildcardWithGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table);
void grantWildcardWithGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table, std::string_view column);
void grantWildcardWithGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table, const std::vector<std::string_view> & columns);
void grantWildcardWithGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table, const Strings & columns);
/// Revokes a specified access granted earlier on a specified database/table/column.
/// For example, revoke(AccessType::ALL) revokes all grants at all, just like clear();
void revoke(const AccessFlags & flags);
@ -67,6 +83,13 @@ public:
void revoke(const AccessRightsElement & element);
void revoke(const AccessRightsElements & elements);
void revokeWildcard(const AccessFlags & flags);
void revokeWildcard(const AccessFlags & flags, std::string_view database);
void revokeWildcard(const AccessFlags & flags, std::string_view database, std::string_view table);
void revokeWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, std::string_view column);
void revokeWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, const std::vector<std::string_view> & columns);
void revokeWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, const Strings & columns);
void revokeGrantOption(const AccessFlags & flags);
void revokeGrantOption(const AccessFlags & flags, std::string_view database);
void revokeGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table);
@ -76,6 +99,13 @@ public:
void revokeGrantOption(const AccessRightsElement & element);
void revokeGrantOption(const AccessRightsElements & elements);
void revokeWildcardGrantOption(const AccessFlags & flags);
void revokeWildcardGrantOption(const AccessFlags & flags, std::string_view database);
void revokeWildcardGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table);
void revokeWildcardGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table, std::string_view column);
void revokeWildcardGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table, const std::vector<std::string_view> & columns);
void revokeWildcardGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table, const Strings & columns);
/// Whether a specified access granted.
bool isGranted(const AccessFlags & flags) const;
bool isGranted(const AccessFlags & flags, std::string_view database) const;
@ -86,6 +116,13 @@ public:
bool isGranted(const AccessRightsElement & element) const;
bool isGranted(const AccessRightsElements & elements) const;
bool isGrantedWildcard(const AccessFlags & flags) const;
bool isGrantedWildcard(const AccessFlags & flags, std::string_view database) const;
bool isGrantedWildcard(const AccessFlags & flags, std::string_view database, std::string_view table) const;
bool isGrantedWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, std::string_view column) const;
bool isGrantedWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, const std::vector<std::string_view> & columns) const;
bool isGrantedWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, const Strings & columns) const;
bool hasGrantOption(const AccessFlags & flags) const;
bool hasGrantOption(const AccessFlags & flags, std::string_view database) const;
bool hasGrantOption(const AccessFlags & flags, std::string_view database, std::string_view table) const;
@ -95,6 +132,13 @@ public:
bool hasGrantOption(const AccessRightsElement & element) const;
bool hasGrantOption(const AccessRightsElements & elements) const;
bool hasGrantOptionWildcard(const AccessFlags & flags) const;
bool hasGrantOptionWildcard(const AccessFlags & flags, std::string_view database) const;
bool hasGrantOptionWildcard(const AccessFlags & flags, std::string_view database, std::string_view table) const;
bool hasGrantOptionWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, std::string_view column) const;
bool hasGrantOptionWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, const std::vector<std::string_view> & columns) const;
bool hasGrantOptionWildcard(const AccessFlags & flags, std::string_view database, std::string_view table, const Strings & columns) const;
/// Checks if a given `access_rights` is a subset for the current access rights.
bool contains(const AccessRights & access_rights) const;
bool containsWithGrantOption(const AccessRights & access_rights) const;
@ -111,9 +155,7 @@ public:
const AccessFlags & flags,
const AccessFlags & min_flags_with_children,
const AccessFlags & max_flags_with_children,
std::string_view database,
std::string_view table,
std::string_view column,
const size_t level,
bool grant_option)>;
void modifyFlags(const ModifyFlagsFunction & function);
@ -123,32 +165,36 @@ public:
/// Makes full access rights (GRANT ALL ON *.* WITH GRANT OPTION).
static AccessRights getFullAccess();
/// Methods for tests
void dumpTree(WriteBuffer & buffer) const;
std::vector<String> dumpNodes() const;
private:
template <bool with_grant_option, typename... Args>
template <bool with_grant_option, bool wildcard = false, typename... Args>
void grantImpl(const AccessFlags & flags, const Args &... args);
template <bool with_grant_option>
template <bool with_grant_option, bool wildcard = false>
void grantImpl(const AccessRightsElement & element);
template <bool with_grant_option>
void grantImpl(const AccessRightsElements & elements);
template <bool with_grant_option>
template <bool with_grant_option, bool wildcard = false>
void grantImplHelper(const AccessRightsElement & element);
template <bool grant_option, typename... Args>
template <bool grant_option, bool wildcard = false, typename... Args>
void revokeImpl(const AccessFlags & flags, const Args &... args);
template <bool grant_option>
template <bool grant_option, bool wildcard = false>
void revokeImpl(const AccessRightsElement & element);
template <bool grant_option>
void revokeImpl(const AccessRightsElements & elements);
template <bool grant_option>
template <bool grant_option, bool wildcard = false>
void revokeImplHelper(const AccessRightsElement & element);
template <bool grant_option, typename... Args>
template <bool grant_option, bool wildcard = false, typename... Args>
bool isGrantedImpl(const AccessFlags & flags, const Args &... args) const;
template <bool grant_option>
@ -163,8 +209,6 @@ private:
template <bool grant_option>
bool isGrantedImplHelper(const AccessRightsElement & element) const;
void logTree() const;
struct Node;
std::unique_ptr<Node> root;
std::unique_ptr<Node> root_with_grant_option;

View File

@ -1,5 +1,9 @@
#include <Access/Common/AccessRightsElement.h>
#include <Common/quoteString.h>
#include <IO/Operators.h>
#include <IO/WriteBufferFromString.h>
#include <Parsers/IAST.h>
#include <boost/range/algorithm_ext/erase.hpp>
@ -7,48 +11,6 @@ namespace DB
{
namespace
{
void formatColumnNames(const Strings & columns, String & result)
{
result += "(";
bool need_comma = false;
for (const auto & column : columns)
{
if (need_comma)
result += ", ";
need_comma = true;
result += backQuoteIfNeed(column);
}
result += ")";
}
void formatONClause(const AccessRightsElement & element, String & result)
{
result += "ON ";
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
result += "*";
else
result += backQuoteIfNeed(element.parameter);
}
else if (element.any_database)
{
result += "*.*";
}
else
{
if (!element.database.empty())
{
result += backQuoteIfNeed(element.database);
result += ".";
}
if (element.any_table)
result += "*";
else
result += backQuoteIfNeed(element.table);
}
}
void formatOptions(bool grant_option, bool is_partial_revoke, String & result)
{
if (is_partial_revoke)
@ -67,20 +29,16 @@ namespace
}
}
void formatAccessFlagsWithColumns(const AccessFlags & access_flags, const Strings & columns, bool any_column, String & result)
void formatAccessFlagsWithColumns(const AccessRightsElement & element, String & result)
{
String columns_as_str;
if (!any_column)
if (!element.anyColumn())
{
if (columns.empty())
{
result += "USAGE";
return;
}
formatColumnNames(columns, columns_as_str);
WriteBufferFromString buffer(columns_as_str);
element.formatColumnNames(buffer);
}
auto keywords = access_flags.toKeywords();
auto keywords = element.access_flags.toKeywords();
if (keywords.empty())
{
result += "USAGE";
@ -101,9 +59,14 @@ namespace
String toStringImpl(const AccessRightsElement & element, bool with_options)
{
String result;
formatAccessFlagsWithColumns(element.access_flags, element.columns, element.any_column, result);
formatAccessFlagsWithColumns(element, result);
result += " ";
formatONClause(element, result);
{
WriteBufferFromString buffer(result);
element.formatONClause(buffer);
}
if (with_options)
formatOptions(element.grant_option, element.is_partial_revoke, result);
return result;
@ -123,7 +86,7 @@ namespace
if (!part.empty())
part += ", ";
formatAccessFlagsWithColumns(element.access_flags, element.columns, element.any_column, part);
formatAccessFlagsWithColumns(element, part);
bool next_element_uses_same_table_and_options = false;
if (i != elements.size() - 1)
@ -138,7 +101,13 @@ namespace
if (!next_element_uses_same_table_and_options)
{
part += " ";
formatONClause(element, part);
String on_clause;
{
WriteBufferFromString buffer(on_clause);
element.formatONClause(buffer);
}
part.append(std::move(on_clause));
if (with_options)
formatOptions(element.grant_option, element.is_partial_revoke, part);
if (result.empty())
@ -153,14 +122,66 @@ namespace
}
}
void AccessRightsElement::formatColumnNames(WriteBuffer & buffer) const
{
buffer << "(";
bool need_comma = false;
for (const auto & column : columns)
{
if (std::exchange(need_comma, true))
buffer << ", ";
buffer << backQuoteIfNeed(column);
if (wildcard)
buffer << "*";
}
buffer << ")";
}
void AccessRightsElement::formatONClause(WriteBuffer & buffer, bool hilite) const
{
buffer << (hilite ? IAST::hilite_keyword : "") << "ON " << (hilite ? IAST::hilite_none : "");
if (isGlobalWithParameter())
{
if (anyParameter())
buffer << "*";
else
{
buffer << backQuoteIfNeed(parameter);
if (wildcard)
buffer << "*";
}
}
else if (anyDatabase())
buffer << "*.*";
else if (!table.empty())
{
if (!database.empty())
buffer << backQuoteIfNeed(database) << ".";
buffer << backQuoteIfNeed(table);
if (columns.empty() && wildcard)
buffer << "*";
}
else
{
buffer << backQuoteIfNeed(database);
if (wildcard)
buffer << "*";
buffer << ".*";
}
}
AccessRightsElement::AccessRightsElement(AccessFlags access_flags_, std::string_view database_)
: access_flags(access_flags_), database(database_), parameter(database_), any_database(false), any_parameter(false)
: access_flags(access_flags_), database(database_), parameter(database_)
{
}
AccessRightsElement::AccessRightsElement(AccessFlags access_flags_, std::string_view database_, std::string_view table_)
: access_flags(access_flags_), database(database_), table(table_), any_database(false), any_table(false)
: access_flags(access_flags_), database(database_), table(table_)
{
}
@ -170,10 +191,6 @@ AccessRightsElement::AccessRightsElement(
, database(database_)
, table(table_)
, columns({String{column_}})
, any_database(false)
, any_table(false)
, any_column(false)
, any_parameter(false)
{
}
@ -182,7 +199,7 @@ AccessRightsElement::AccessRightsElement(
std::string_view database_,
std::string_view table_,
const std::vector<std::string_view> & columns_)
: access_flags(access_flags_), database(database_), table(table_), any_database(false), any_table(false), any_column(false)
: access_flags(access_flags_), database(database_), table(table_)
{
columns.resize(columns_.size());
for (size_t i = 0; i != columns_.size(); ++i)
@ -195,22 +212,18 @@ AccessRightsElement::AccessRightsElement(
, database(database_)
, table(table_)
, columns(columns_)
, any_database(false)
, any_table(false)
, any_column(false)
, any_parameter(false)
{
}
void AccessRightsElement::eraseNonGrantable()
{
if (isGlobalWithParameter() && !any_parameter)
if (isGlobalWithParameter() && !anyParameter())
access_flags &= AccessFlags::allFlagsGrantableOnGlobalWithParameterLevel();
else if (!any_column)
else if (!anyColumn())
access_flags &= AccessFlags::allFlagsGrantableOnColumnLevel();
else if (!any_table)
else if (!anyTable())
access_flags &= AccessFlags::allFlagsGrantableOnTableLevel();
else if (!any_database)
else if (!anyDatabase())
access_flags &= AccessFlags::allFlagsGrantableOnDatabaseLevel();
else
access_flags &= AccessFlags::allFlagsGrantableOnGlobalLevel();

View File

@ -1,6 +1,7 @@
#pragma once
#include <Access/Common/AccessFlags.h>
#include <IO/WriteBuffer.h>
#include <tuple>
@ -17,10 +18,7 @@ struct AccessRightsElement
Strings columns;
String parameter;
bool any_database = true;
bool any_table = true;
bool any_column = true;
bool any_parameter = false;
bool wildcard = false;
bool grant_option = false;
bool is_partial_revoke = false;
@ -47,28 +45,32 @@ struct AccessRightsElement
AccessRightsElement(
AccessFlags access_flags_, std::string_view database_, std::string_view table_, const Strings & columns_);
bool empty() const { return !access_flags || (!any_column && columns.empty()); }
bool empty() const { return !access_flags || (!anyColumn() && columns.empty()); }
auto toTuple() const { return std::tie(access_flags, any_database, database, any_table, table, any_column, columns, any_parameter, parameter, grant_option, is_partial_revoke); }
bool anyDatabase() const { return database.empty() && table.empty(); }
bool anyTable() const { return table.empty(); }
bool anyColumn() const { return columns.empty(); }
bool anyParameter() const { return parameter.empty(); }
auto toTuple() const { return std::tie(access_flags, database, table, columns, parameter, wildcard, grant_option, is_partial_revoke); }
friend bool operator==(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() == right.toTuple(); }
friend bool operator!=(const AccessRightsElement & left, const AccessRightsElement & right) { return !(left == right); }
bool sameDatabaseAndTableAndParameter(const AccessRightsElement & other) const
{
return sameDatabaseAndTable(other) && sameParameter(other);
return sameDatabaseAndTable(other) && sameParameter(other) && (wildcard == other.wildcard);
}
bool sameParameter(const AccessRightsElement & other) const
{
return (parameter == other.parameter) && (any_parameter == other.any_parameter)
return (parameter == other.parameter)
&& (access_flags.getParameterType() == other.access_flags.getParameterType())
&& (isGlobalWithParameter() == other.isGlobalWithParameter());
}
bool sameDatabaseAndTable(const AccessRightsElement & other) const
{
return (database == other.database) && (any_database == other.any_database)
&& (table == other.table) && (any_table == other.any_table);
return (database == other.database) && (table == other.table);
}
bool sameOptions(const AccessRightsElement & other) const
@ -79,7 +81,7 @@ struct AccessRightsElement
/// Resets flags which cannot be granted.
void eraseNonGrantable();
bool isEmptyDatabase() const { return !any_database && database.empty(); }
bool isEmptyDatabase() const { return database.empty() and !table.empty(); }
/// If the database is empty, replaces it with `current_database`. Otherwise does nothing.
void replaceEmptyDatabase(const String & current_database);
@ -89,6 +91,9 @@ struct AccessRightsElement
/// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table".
String toString() const;
String toStringWithoutOptions() const;
void formatColumnNames(WriteBuffer & buffer) const;
void formatONClause(WriteBuffer & buffer, bool hilite = false) const;
};

View File

@ -52,12 +52,9 @@ namespace
auto modifier = [&](const AccessFlags & flags,
const AccessFlags & min_flags_with_children,
const AccessFlags & max_flags_with_children,
std::string_view database,
std::string_view table,
std::string_view column,
const size_t level,
bool /* grant_option */) -> AccessFlags
{
size_t level = !database.empty() + !table.empty() + !column.empty();
AccessFlags res = flags;
/// CREATE_TABLE => CREATE_VIEW, DROP_TABLE => DROP_VIEW, ALTER_TABLE => ALTER_VIEW
@ -559,7 +556,7 @@ std::shared_ptr<const AccessRights> ContextAccess::getAccessRightsWithImplicit()
}
template <bool throw_if_denied, bool grant_option, typename... Args>
template <bool throw_if_denied, bool grant_option, bool wildcard, typename... Args>
bool ContextAccess::checkAccessImplHelper(AccessFlags flags, const Args &... args) const
{
if (user_was_dropped)
@ -607,10 +604,20 @@ bool ContextAccess::checkAccessImplHelper(AccessFlags flags, const Args &... arg
auto acs = getAccessRightsWithImplicit();
bool granted;
if constexpr (grant_option)
granted = acs->hasGrantOption(flags, args...);
if constexpr (wildcard)
{
if constexpr (grant_option)
granted = acs->hasGrantOptionWildcard(flags, args...);
else
granted = acs->isGrantedWildcard(flags, args...);
}
else
granted = acs->isGranted(flags, args...);
{
if constexpr (grant_option)
granted = acs->hasGrantOption(flags, args...);
else
granted = acs->isGranted(flags, args...);
}
if (!granted)
{
@ -685,52 +692,52 @@ bool ContextAccess::checkAccessImplHelper(AccessFlags flags, const Args &... arg
return access_granted();
}
template <bool throw_if_denied, bool grant_option>
template <bool throw_if_denied, bool grant_option, bool wildcard>
bool ContextAccess::checkAccessImpl(const AccessFlags & flags) const
{
return checkAccessImplHelper<throw_if_denied, grant_option>(flags);
return checkAccessImplHelper<throw_if_denied, grant_option, wildcard>(flags);
}
template <bool throw_if_denied, bool grant_option, typename... Args>
template <bool throw_if_denied, bool grant_option, bool wildcard, typename... Args>
bool ContextAccess::checkAccessImpl(const AccessFlags & flags, std::string_view database, const Args &... args) const
{
return checkAccessImplHelper<throw_if_denied, grant_option>(flags, database.empty() ? params.current_database : database, args...);
return checkAccessImplHelper<throw_if_denied, grant_option, wildcard>(flags, database.empty() ? params.current_database : database, args...);
}
template <bool throw_if_denied, bool grant_option>
template <bool throw_if_denied, bool grant_option, bool wildcard>
bool ContextAccess::checkAccessImplHelper(const AccessRightsElement & element) const
{
assert(!element.grant_option || grant_option);
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags);
if (element.anyParameter())
return checkAccessImpl<throw_if_denied, grant_option, wildcard>(element.access_flags);
else
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.parameter);
return checkAccessImpl<throw_if_denied, grant_option, wildcard>(element.access_flags, element.parameter);
}
else if (element.any_database)
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags);
else if (element.any_table)
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database);
else if (element.any_column)
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database, element.table);
else if (element.anyDatabase())
return checkAccessImpl<throw_if_denied, grant_option, wildcard>(element.access_flags);
else if (element.anyTable())
return checkAccessImpl<throw_if_denied, grant_option, wildcard>(element.access_flags, element.database);
else if (element.anyColumn())
return checkAccessImpl<throw_if_denied, grant_option, wildcard>(element.access_flags, element.database, element.table);
else
return checkAccessImpl<throw_if_denied, grant_option>(element.access_flags, element.database, element.table, element.columns);
return checkAccessImpl<throw_if_denied, grant_option, wildcard>(element.access_flags, element.database, element.table, element.columns);
}
template <bool throw_if_denied, bool grant_option>
template <bool throw_if_denied, bool grant_option, bool wildcard>
bool ContextAccess::checkAccessImpl(const AccessRightsElement & element) const
{
if constexpr (grant_option)
{
return checkAccessImplHelper<throw_if_denied, true>(element);
return checkAccessImplHelper<throw_if_denied, true, wildcard>(element);
}
else
{
if (element.grant_option)
return checkAccessImplHelper<throw_if_denied, true>(element);
return checkAccessImplHelper<throw_if_denied, true, wildcard>(element);
else
return checkAccessImplHelper<throw_if_denied, false>(element);
return checkAccessImplHelper<throw_if_denied, false, wildcard>(element);
}
}
@ -738,8 +745,18 @@ template <bool throw_if_denied, bool grant_option>
bool ContextAccess::checkAccessImpl(const AccessRightsElements & elements) const
{
for (const auto & element : elements)
if (!checkAccessImpl<throw_if_denied, grant_option>(element))
return false;
{
if (element.wildcard)
{
if (!checkAccessImpl<throw_if_denied, grant_option, true>(element))
return false;
}
else
{
if (!checkAccessImpl<throw_if_denied, grant_option>(element))
return false;
}
}
return true;
}

View File

@ -141,22 +141,22 @@ private:
void setRolesInfo(const std::shared_ptr<const EnabledRolesInfo> & roles_info_) const TSA_REQUIRES(mutex);
void calculateAccessRights() const TSA_REQUIRES(mutex);
template <bool throw_if_denied, bool grant_option>
template <bool throw_if_denied, bool grant_option, bool wildcard = false>
bool checkAccessImpl(const AccessFlags & flags) const;
template <bool throw_if_denied, bool grant_option, typename... Args>
template <bool throw_if_denied, bool grant_option, bool wildcard = false, typename... Args>
bool checkAccessImpl(const AccessFlags & flags, std::string_view database, const Args &... args) const;
template <bool throw_if_denied, bool grant_option>
template <bool throw_if_denied, bool grant_option, bool wildcard = false>
bool checkAccessImpl(const AccessRightsElement & element) const;
template <bool throw_if_denied, bool grant_option>
bool checkAccessImpl(const AccessRightsElements & elements) const;
template <bool throw_if_denied, bool grant_option, typename... Args>
template <bool throw_if_denied, bool grant_option, bool wildcard = false, typename... Args>
bool checkAccessImplHelper(AccessFlags flags, const Args &... args) const;
template <bool throw_if_denied, bool grant_option>
template <bool throw_if_denied, bool grant_option, bool wildcard = false>
bool checkAccessImplHelper(const AccessRightsElement & element) const;
template <bool throw_if_denied>

View File

@ -1,8 +1,198 @@
#include <gtest/gtest.h>
#include <Access/AccessRights.h>
#include <Access/AccessRights.cpp>
#include <IO/WriteBufferFromString.h>
#include <list>
using namespace DB;
TEST(AccessRights, Radix)
{
AccessRights root;
root.grant(AccessType::SELECT, "team");
root.grant(AccessType::SELECT, "toast");
root.grant(AccessType::SELECT, "toaster");
root.grant(AccessType::INSERT, "toaster", "bread");
root.grant(AccessType::ALTER_ADD_COLUMN, "toaster", "bread", "jam");
root.grantWildcard(AccessType::CREATE_TABLE, "t");
WriteBufferFromOwnString out;
root.dumpTree(out);
ASSERT_EQ(out.str(),
"Tree(): level=GLOBAL_LEVEL, name=NULL, flags=USAGE, min_flags=USAGE, max_flags=SELECT, INSERT, ALTER ADD COLUMN, CREATE TABLE, wildcard_grant=false, num_children=1\n"
"Tree(): - level=DATABASE_LEVEL, name=t, flags=CREATE TABLE, min_flags=CREATE TABLE, max_flags=SELECT, INSERT, ALTER ADD COLUMN, CREATE TABLE, wildcard_grant=true, num_children=2\n"
"Tree(): -- level=DATABASE_LEVEL, name=eam, flags=CREATE TABLE, min_flags=CREATE TABLE, max_flags=SELECT, CREATE TABLE, wildcard_grant=false, num_children=1\n"
"Tree(): --- level=DATABASE_LEVEL, name=NULL, flags=SELECT, CREATE TABLE, min_flags=SELECT, CREATE TABLE, max_flags=SELECT, CREATE TABLE, wildcard_grant=false, num_children=0\n"
"Tree(): -- level=DATABASE_LEVEL, name=oast, flags=CREATE TABLE, min_flags=CREATE TABLE, max_flags=SELECT, INSERT, ALTER ADD COLUMN, CREATE TABLE, wildcard_grant=false, num_children=2\n"
"Tree(): --- level=DATABASE_LEVEL, name=NULL, flags=SELECT, CREATE TABLE, min_flags=SELECT, CREATE TABLE, max_flags=SELECT, CREATE TABLE, wildcard_grant=false, num_children=0\n"
"Tree(): --- level=DATABASE_LEVEL, name=er, flags=CREATE TABLE, min_flags=CREATE TABLE, max_flags=SELECT, INSERT, ALTER ADD COLUMN, CREATE TABLE, wildcard_grant=false, num_children=1\n"
"Tree(): ---- level=DATABASE_LEVEL, name=NULL, flags=SELECT, CREATE TABLE, min_flags=SELECT, CREATE TABLE, max_flags=SELECT, INSERT, ALTER ADD COLUMN, CREATE TABLE, wildcard_grant=false, num_children=1\n"
"Tree(): ----- level=TABLE_LEVEL, name=bread, flags=SELECT, CREATE TABLE, min_flags=SELECT, CREATE TABLE, max_flags=SELECT, INSERT, ALTER ADD COLUMN, CREATE TABLE, wildcard_grant=false, num_children=1\n"
"Tree(): ------ level=TABLE_LEVEL, name=NULL, flags=SELECT, INSERT, CREATE TABLE, min_flags=SELECT, INSERT, CREATE TABLE, max_flags=SELECT, INSERT, ALTER ADD COLUMN, CREATE TABLE, wildcard_grant=false, num_children=1\n"
"Tree(): ------- level=COLUMN_LEVEL, name=jam, flags=SELECT, INSERT, min_flags=SELECT, INSERT, max_flags=SELECT, INSERT, ALTER ADD COLUMN, wildcard_grant=false, num_children=1\n"
"Tree(): -------- level=COLUMN_LEVEL, name=NULL, flags=SELECT, INSERT, ALTER ADD COLUMN, min_flags=SELECT, INSERT, ALTER ADD COLUMN, max_flags=SELECT, INSERT, ALTER ADD COLUMN, wildcard_grant=false, num_children=0\n");
}
TEST(AccessRights, GrantWildcard)
{
AccessRights root;
root.grant(AccessType::SELECT, "team");
root.grant(AccessType::SELECT, "toast");
root.grant(AccessType::SELECT, "toaster");
root.grant(AccessType::INSERT, "toaster", "bread");
root.grant(AccessType::ALTER_ADD_COLUMN, "toaster", "bread", "jam");
root.grantWildcard(AccessType::CREATE_TABLE, "t");
ASSERT_EQ(root.isGranted(AccessType::CREATE_TABLE, "tick"), true);
ASSERT_EQ(root.isGranted(AccessType::CREATE_TABLE, "team"), true);
ASSERT_EQ(root.isGranted(AccessType::CREATE_TABLE, "to"), true);
ASSERT_EQ(root.isGranted(AccessType::SELECT, "t"), false);
ASSERT_EQ(root.isGranted(AccessType::SELECT, "to"), false);
ASSERT_EQ(root.isGranted(AccessType::SELECT, "team"), true);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "toaster"), false);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "toaster", "bread"), true);
ASSERT_EQ(root.isGranted(AccessType::ALTER_ADD_COLUMN, "toaster", "bread", "jam"), true);
ASSERT_EQ(root.toString(), "GRANT CREATE TABLE ON t*.*, GRANT SELECT ON team.*, GRANT SELECT ON toast.*, GRANT SELECT ON toaster.*, GRANT INSERT, ALTER ADD COLUMN(jam) ON toaster.bread");
root.revokeWildcard(AccessType::CREATE_TABLE, "t");
ASSERT_EQ(root.toString(), "GRANT SELECT ON team.*, GRANT SELECT ON toast.*, GRANT SELECT ON toaster.*, GRANT INSERT, ALTER ADD COLUMN(jam) ON toaster.bread");
root.revokeWildcard(AccessType::SELECT, "t");
ASSERT_EQ(root.toString(), "GRANT INSERT, ALTER ADD COLUMN(jam) ON toaster.bread");
root.revokeWildcard(AccessType::ALL, "t");
ASSERT_EQ(root.toString(), "GRANT USAGE ON *.*");
root.grant(AccessType::SELECT);
root.revokeWildcard(AccessType::SELECT, "test");
root.grant(AccessType::SELECT, "tester", "foo");
ASSERT_EQ(root.toString(), "GRANT SELECT ON *.*, REVOKE SELECT ON test*.*, GRANT SELECT ON tester.foo");
root.grant(AccessType::SELECT);
ASSERT_EQ(root.toString(), "GRANT SELECT ON *.*");
root = {};
root.grant(AccessType::SELECT, "test");
root.grantWildcard(AccessType::CREATE_TABLE, "test");
ASSERT_EQ(root.toString(), "GRANT CREATE TABLE ON test*.*, GRANT SELECT ON test.*");
root = {};
root.grant(AccessType::SELECT, "test");
root.grantWildcard(AccessType::SELECT, "test");
ASSERT_EQ(root.toString(), "GRANT SELECT ON test*.*");
root = {};
root.grantWildcard(AccessType::SELECT, "default", "test");
root.grantWildcard(AccessType::SELECT, "default", "t");
ASSERT_EQ(root.toString(), "GRANT SELECT ON default.t*");
root = {};
root.grant(AccessType::SELECT, "default", "t");
root.grantWildcard(AccessType::INSERT, "default", "t");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "default", "t"), true);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "default", "t"), true);
ASSERT_EQ(root.toString(), "GRANT SELECT ON default.t, GRANT INSERT ON default.t*");
root.revoke(AccessType::INSERT, "default", "t");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "default", "t"), true);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "default", "t"), false);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "default", "test"), true);
ASSERT_EQ(root.toString(), "GRANT SELECT ON default.t, GRANT INSERT ON default.t*, REVOKE INSERT ON default.t");
root = {};
root.grant(AccessType::SELECT, "default", "t");
root.revokeWildcard(AccessType::SELECT, "default", "t", "col");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "default", "t"), false);
ASSERT_EQ(root.isGrantedWildcard(AccessType::SELECT, "default", "t"), false);
ASSERT_EQ(root.isGranted(AccessType::SELECT, "default", "t", "col1"), false);
ASSERT_EQ(root.isGranted(AccessType::SELECT, "default", "t", "test"), true);
ASSERT_EQ(root.toString(), "GRANT SELECT ON default.t, REVOKE SELECT(col*) ON default.t");
root = {};
root.grantWildcard(AccessType::ALTER_UPDATE, "prod");
root.grant(AccessType::ALTER_UPDATE, "prod", "users");
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "prod"), true);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "prod", "users"), true);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "prod", "orders"), true);
ASSERT_EQ(root.isGranted(AccessType::SELECT, "prod"), false);
root.revoke(AccessType::ALTER_UPDATE, "prod", "users");
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "prod", "users"), false);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "prod", "orders"), true);
root.grantWildcard(AccessType::ALTER_DELETE, "prod");
root.grant(AccessType::ALTER_DELETE, "prod", "archive");
ASSERT_EQ(root.isGranted(AccessType::ALTER_DELETE, "prod", "archive"), true);
ASSERT_EQ(root.isGranted(AccessType::ALTER_DELETE, "prod", "current"), true);
root.revokeWildcard(AccessType::ALTER_DELETE, "prod");
ASSERT_EQ(root.isGranted(AccessType::ALTER_DELETE, "prod", "archive"), false);
ASSERT_EQ(root.isGranted(AccessType::ALTER_DELETE, "prod", "current"), false);
ASSERT_EQ(root.toString(), "GRANT ALTER UPDATE ON prod*.*, REVOKE ALTER UPDATE ON prod.users");
root.grantWildcard(AccessType::SELECT, "test");
root.grantWildcard(AccessType::INSERT, "test");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "test"), true);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "test"), true);
ASSERT_EQ(root.isGranted(AccessType::SELECT, "testdata"), true);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "testdata"), true);
root.revokeWildcard(AccessType::SELECT, "test");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "test"), false);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "test"), true);
ASSERT_EQ(root.isGranted(AccessType::SELECT, "testdata"), false);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "testdata"), true);
root = {};
root.grantWildcard(AccessType::ALTER_UPDATE, "");
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, ""), true);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "anything"), true);
root.revokeWildcard(AccessType::ALTER_UPDATE, "");
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, ""), false);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "anything"), false);
root.grantWildcard(AccessType::CREATE_VIEW, "proj");
ASSERT_EQ(root.isGranted(AccessType::CREATE_VIEW, "project"), true);
ASSERT_EQ(root.isGranted(AccessType::CREATE_VIEW, "pro"), false);
ASSERT_EQ(root.isGranted(AccessType::CREATE_VIEW, "projX"), true);
root.revokeWildcard(AccessType::CREATE_VIEW, "proj");
ASSERT_EQ(root.isGranted(AccessType::CREATE_VIEW, "project"), false);
ASSERT_EQ(root.isGranted(AccessType::CREATE_VIEW, "projX"), false);
root = {};
root.grantWildcard(AccessType::SELECT, "db");
root.grantWildcard(AccessType::INSERT, "db");
root.grantWildcard(AccessType::ALTER_UPDATE, "db");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "db"), true);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "db"), true);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "db"), true);
root.revokeWildcard(AccessType::SELECT, "db");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "db"), false);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "db"), true);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "db"), true);
root.revokeWildcard(AccessType::INSERT, "db");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "db"), false);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "db"), false);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "db"), true);
root.revokeWildcard(AccessType::ALTER_UPDATE, "db");
ASSERT_EQ(root.isGranted(AccessType::SELECT, "db"), false);
ASSERT_EQ(root.isGranted(AccessType::INSERT, "db"), false);
ASSERT_EQ(root.isGranted(AccessType::ALTER_UPDATE, "db"), false);
}
TEST(AccessRights, Union)
{
@ -56,6 +246,28 @@ TEST(AccessRights, Union)
"SYSTEM RESTORE REPLICA, SYSTEM WAIT LOADING PARTS, SYSTEM SYNC DATABASE REPLICA, SYSTEM FLUSH DISTRIBUTED, "
"SYSTEM UNLOAD PRIMARY KEY, dictGet ON db1.*, GRANT TABLE ENGINE ON db1, "
"GRANT SET DEFINER ON db1, GRANT NAMED COLLECTION ADMIN ON db1");
lhs = {};
rhs = {};
lhs.grant(AccessType::SELECT, "db1", "tb1", Strings{"col1", "col2", "test"});
rhs.grant(AccessType::SELECT, "db1", "tb1");
lhs.makeUnion(rhs);
ASSERT_EQ(lhs.toString(), "GRANT SELECT ON db1.tb1");
lhs = {};
rhs = {};
lhs.grant(AccessType::SELECT, "db1", "tb1", Strings{"col1", "col2", "test"});
rhs.grantWildcardWithGrantOption(AccessType::SELECT, "db1", "tb1");
rhs.revokeWildcardGrantOption(AccessType::SELECT, "db1", "tb1", "col");
lhs.makeUnion(rhs);
ASSERT_EQ(lhs.toString(), "GRANT SELECT ON db1.tb1*, GRANT SELECT ON db1.tb1* WITH GRANT OPTION, REVOKE GRANT OPTION SELECT(col*) ON db1.tb1");
lhs = {};
rhs = {};
lhs.grant(AccessType::SELECT, "db1", "tb1", Strings{"col1", "col2", "test"});
rhs.grantWildcardWithGrantOption(AccessType::SELECT, "db1", "tb1", "col");
lhs.makeUnion(rhs);
ASSERT_EQ(lhs.toString(), "GRANT SELECT(col*) ON db1.tb1 WITH GRANT OPTION, GRANT SELECT(test) ON db1.tb1");
}
@ -101,4 +313,53 @@ TEST(AccessRights, Intersection)
rhs.grant(AccessType::ALL, "db1");
lhs.makeIntersection(rhs);
ASSERT_EQ(lhs.toString(), "GRANT INSERT ON db1.*");
lhs = {};
rhs = {};
lhs.grant(AccessType::SELECT, "db1", "tb1", Strings{"col1", "col2", "test"});
rhs.grant(AccessType::SELECT, "db1", "tb1");
lhs.makeIntersection(rhs);
ASSERT_EQ(lhs.toString(), "GRANT SELECT(col1, col2, test) ON db1.tb1");
lhs = {};
rhs = {};
lhs.grant(AccessType::SELECT, "db1", "tb1", Strings{"col1", "col2", "test"});
rhs.grantWildcardWithGrantOption(AccessType::SELECT, "db1", "tb1");
rhs.revokeWildcardGrantOption(AccessType::SELECT, "db1", "tb1", "col");
lhs.makeIntersection(rhs);
ASSERT_EQ(lhs.toString(), "GRANT SELECT(col1, col2, test) ON db1.tb1");
lhs = {};
rhs = {};
lhs.grant(AccessType::SELECT, "db1", "tb1", Strings{"col1", "col2", "test"});
rhs.grantWildcard(AccessType::SELECT, "db1", "tb1", "col");
lhs.makeIntersection(rhs);
ASSERT_EQ(lhs.toString(), "GRANT SELECT(col1, col2) ON db1.tb1");
lhs = {};
rhs = {};
lhs.grant(AccessType::SELECT, "db1", "tb1", Strings{"col1", "col2", "test"});
lhs.grantWithGrantOption(AccessType::SELECT, "db1", "tb1", "col1");
rhs.grantWildcard(AccessType::SELECT, "db1", "tb1", "col");
rhs.grantWithGrantOption(AccessType::SELECT, "db1", "tb1", "col1");
lhs.makeIntersection(rhs);
ASSERT_EQ(lhs.toString(), "GRANT SELECT(col1) ON db1.tb1 WITH GRANT OPTION, GRANT SELECT(col2) ON db1.tb1");
}
TEST(AccessRights, Iterator)
{
AccessRights root;
root.grant(AccessType::SELECT, "team");
root.grant(AccessType::SELECT, "toast");
root.grant(AccessType::SELECT, "toaster");
root.grant(AccessType::INSERT, "toaster", "bread");
root.grant(AccessType::ALTER_ADD_COLUMN, "toaster", "bread", "jam");
root.grantWildcard(AccessType::CREATE_TABLE, "t");
auto res = root.dumpNodes();
ASSERT_EQ(res.size(), 4);
ASSERT_EQ(res[0], "t");
ASSERT_EQ(res[1], "team");
ASSERT_EQ(res[2], "toast");
ASSERT_EQ(res[3], "toaster");
}

View File

@ -13,46 +13,6 @@ namespace ErrorCodes
namespace
{
void formatColumnNames(const Strings & columns, const IAST::FormatSettings & settings)
{
settings.ostr << "(";
bool need_comma = false;
for (const auto & column : columns)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << backQuoteIfNeed(column);
}
settings.ostr << ")";
}
void formatONClause(const AccessRightsElement & element, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "ON " << (settings.hilite ? IAST::hilite_none : "");
if (element.isGlobalWithParameter())
{
if (element.any_parameter)
settings.ostr << "*";
else
settings.ostr << backQuoteIfNeed(element.parameter);
}
else if (element.any_database)
{
settings.ostr << "*.*";
}
else
{
if (!element.database.empty())
settings.ostr << backQuoteIfNeed(element.database) << ".";
if (element.any_table)
settings.ostr << "*";
else
settings.ostr << backQuoteIfNeed(element.table);
}
}
void formatElementsWithoutOptions(const AccessRightsElements & elements, const IAST::FormatSettings & settings)
{
bool no_output = true;
@ -60,7 +20,7 @@ namespace
{
const auto & element = elements[i];
auto keywords = element.access_flags.toKeywords();
if (keywords.empty() || (!element.any_column && element.columns.empty()))
if (keywords.empty() || (!element.anyColumn() && element.columns.empty()))
continue;
for (const auto & keyword : keywords)
@ -69,8 +29,8 @@ namespace
settings.ostr << ", ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << keyword << (settings.hilite ? IAST::hilite_none : "");
if (!element.any_column)
formatColumnNames(element.columns, settings);
if (!element.anyColumn())
element.formatColumnNames(settings.ostr);
}
bool next_element_on_same_db_and_table = false;
@ -86,7 +46,7 @@ namespace
if (!next_element_on_same_db_and_table)
{
settings.ostr << " ";
formatONClause(element, settings);
element.formatONClause(settings.ostr, settings.hilite);
}
}
@ -112,7 +72,7 @@ namespace
if (!next_element_on_same_db_and_table)
{
settings.ostr << " ";
formatONClause(element, settings);
element.formatONClause(settings.ostr, settings.hilite);
}
}
}

View File

@ -123,7 +123,6 @@ namespace
return false;
String database_name, table_name, parameter;
bool any_database = false, any_table = false, any_parameter = false;
size_t is_global_with_parameter = 0;
for (const auto & elem : access_and_columns)
@ -135,40 +134,34 @@ namespace
if (!ParserKeyword{Keyword::ON}.ignore(pos, expected))
return false;
bool wildcard = false;
if (is_global_with_parameter && is_global_with_parameter == access_and_columns.size())
{
ASTPtr parameter_ast;
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
if (!ParserToken{TokenType::Asterisk}.ignore(pos, expected))
{
any_parameter = true;
if (ParserIdentifier{}.parse(pos, parameter_ast, expected))
parameter = getIdentifierName(parameter_ast);
else
return false;
}
else if (ParserIdentifier{}.parse(pos, parameter_ast, expected))
{
any_parameter = false;
parameter = getIdentifierName(parameter_ast);
}
else
return false;
any_database = any_table = true;
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
wildcard = true;
}
else if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, database_name, any_database, table_name, any_table))
{
else if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, database_name, table_name, wildcard))
return false;
}
for (auto & [access_flags, columns] : access_and_columns)
{
AccessRightsElement element;
element.access_flags = access_flags;
element.any_column = columns.empty();
element.columns = std::move(columns);
element.any_database = any_database;
element.database = database_name;
element.any_table = any_table;
element.any_parameter = any_parameter;
element.table = table_name;
element.parameter = parameter;
element.wildcard = wildcard;
res_elements.emplace_back(std::move(element));
}
@ -202,15 +195,13 @@ namespace
String database_name;
String table_name;
bool any_database = false;
bool any_table = false;
if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, database_name, any_database, table_name, any_table))
bool wildcard = false;
if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, database_name, table_name, wildcard))
return false;
default_element.any_database = any_database;
default_element.database = database_name;
default_element.any_table = any_table;
default_element.table = table_name;
default_element.wildcard = wildcard;
elements.push_back(std::move(default_element));
}
@ -228,13 +219,13 @@ namespace
if (!element.empty())
return false;
if (!element.any_column)
if (!element.anyColumn())
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the column level", old_flags.toString());
else if (!element.any_table)
else if (!element.anyTable())
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the table level", old_flags.toString());
else if (!element.any_database)
else if (!element.anyDatabase())
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the database level", old_flags.toString());
else if (!element.any_parameter)
else if (!element.anyParameter())
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the global with parameter level", old_flags.toString());
else
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted", old_flags.toString());

View File

@ -26,15 +26,13 @@ namespace
return IParserBase::wrapParseImpl(pos, [&]
{
String res_database, res_table_name;
bool is_any_database = false;
bool is_any_table = false;
if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, res_database, is_any_database, res_table_name, is_any_table)
|| is_any_database)
bool wildcard = false;
if (!parseDatabaseAndTableNameOrAsterisks(pos, expected, res_database, res_table_name, wildcard) || res_database.empty())
{
return false;
}
else if (is_any_table)
else if (res_table_name.empty())
{
res_table_name = RowPolicyName::ANY_TABLE_MARK;
}

View File

@ -25,12 +25,12 @@ namespace
return false;
}
bool parseOnDBAndTableName(IParserBase::Pos & pos, Expected & expected, String & database, bool & any_database, String & table, bool & any_table)
bool parseOnDBAndTableName(IParserBase::Pos & pos, Expected & expected, String & database, String & table, bool & wildcard)
{
return IParserBase::wrapParseImpl(pos, [&]
{
return ParserKeyword{Keyword::ON}.ignore(pos, expected)
&& parseDatabaseAndTableNameOrAsterisks(pos, expected, database, any_database, table, any_table);
&& parseDatabaseAndTableNameOrAsterisks(pos, expected, database, table, wildcard);
});
}
}
@ -74,10 +74,10 @@ bool ParserShowAccessEntitiesQuery::parseImpl(Pos & pos, ASTPtr & node, Expected
if (type == AccessEntityType::ROW_POLICY)
{
String database, table_name;
bool any_database, any_table;
if (parseOnDBAndTableName(pos, expected, database, any_database, table_name, any_table))
bool wildcard = false;
if (parseOnDBAndTableName(pos, expected, database, table_name, wildcard))
{
if (any_database)
if (database.empty())
all = true;
else
database_and_table_name.emplace(database, table_name);

View File

@ -49,12 +49,12 @@ namespace
return false;
}
bool parseOnDBAndTableName(IParserBase::Pos & pos, Expected & expected, String & database, bool & any_database, String & table, bool & any_table)
bool parseOnDBAndTableName(IParserBase::Pos & pos, Expected & expected, String & database, String & table, bool & wildcard)
{
return IParserBase::wrapParseImpl(pos, [&]
{
return ParserKeyword{Keyword::ON}.ignore(pos, expected)
&& parseDatabaseAndTableNameOrAsterisks(pos, expected, database, any_database, table, any_table);
&& parseDatabaseAndTableNameOrAsterisks(pos, expected, database, table, wildcard);
});
}
}
@ -108,12 +108,12 @@ bool ParserShowCreateAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expe
{
ASTPtr ast;
String database, table_name;
bool any_database, any_table;
bool wildcard = false;
if (ParserRowPolicyNames{}.parse(pos, ast, expected))
row_policy_names = typeid_cast<std::shared_ptr<ASTRowPolicyNames>>(ast);
else if (parseOnDBAndTableName(pos, expected, database, any_database, table_name, any_table))
else if (parseOnDBAndTableName(pos, expected, database, table_name, wildcard))
{
if (any_database)
if (database.empty())
all = true;
else
database_and_table_name.emplace(database, table_name);

View File

@ -67,7 +67,7 @@ bool parseDatabaseAsAST(IParser::Pos & pos, Expected & expected, ASTPtr & databa
}
bool parseDatabaseAndTableNameOrAsterisks(IParser::Pos & pos, Expected & expected, String & database, bool & any_database, String & table, bool & any_table)
bool parseDatabaseAndTableNameOrAsterisks(IParser::Pos & pos, Expected & expected, String & database, String & table, bool & wildcard)
{
return IParserBase::wrapParseImpl(pos, [&]
{
@ -78,18 +78,14 @@ bool parseDatabaseAndTableNameOrAsterisks(IParser::Pos & pos, Expected & expecte
&& ParserToken{TokenType::Asterisk}.ignore(pos, expected))
{
/// *.*
any_database = true;
database.clear();
any_table = true;
table.clear();
return true;
}
/// *
pos = pos_before_dot;
any_database = false;
database.clear();
any_table = true;
table.clear();
return true;
}
@ -106,29 +102,30 @@ bool parseDatabaseAndTableNameOrAsterisks(IParser::Pos & pos, Expected & expecte
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
{
/// db.*
any_database = false;
database = std::move(first_identifier);
any_table = true;
table.clear();
return true;
}
else if (identifier_parser.parse(pos, ast, expected))
{
/// db.table
any_database = false;
database = std::move(first_identifier);
any_table = false;
table = getIdentifierName(ast);
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
wildcard = true;
return true;
}
}
/// table
pos = pos_before_dot;
any_database = false;
database.clear();
any_table = false;
table = std::move(first_identifier);
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
wildcard = true;
return true;
}

View File

@ -10,7 +10,7 @@ bool parseDatabaseAndTableName(IParser::Pos & pos, Expected & expected, String &
bool parseDatabaseAndTableAsAST(IParser::Pos & pos, Expected & expected, ASTPtr & database, ASTPtr & table);
/// Parses [db.]name or [db.]* or [*.]*
bool parseDatabaseAndTableNameOrAsterisks(IParser::Pos & pos, Expected & expected, String & database, bool & any_database, String & table, bool & any_table);
bool parseDatabaseAndTableNameOrAsterisks(IParser::Pos & pos, Expected & expected, String & database, String & table, bool & wildcard);
bool parseDatabase(IParser::Pos & pos, Expected & expected, String & database_str);

View File

@ -135,13 +135,13 @@ void StorageSystemGrants::fillData(MutableColumns & res_columns, ContextPtr cont
for (const auto & element : elements)
{
auto access_types = element.access_flags.toAccessTypes();
if (access_types.empty() || (!element.any_column && element.columns.empty()))
if (access_types.empty() || (!element.anyColumn() && element.columns.empty()))
continue;
const auto * database = element.any_database ? nullptr : &element.database;
const auto * table = element.any_table ? nullptr : &element.table;
const auto * database = element.anyDatabase() ? nullptr : &element.database;
const auto * table = element.anyTable() ? nullptr : &element.table;
if (element.any_column)
if (element.anyColumn())
{
for (const auto & access_type : access_types)
add_row(grantee_name, grantee_type, access_type, database, table, nullptr, element.is_partial_revoke, element.grant_option);

View File

@ -0,0 +1,14 @@
OK
0
0
0
OK
OK
OK
0
0
0
OK
OK
0
0

View File

@ -0,0 +1,53 @@
#!/usr/bin/env bash
# Tags: no-replicated-database
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CURDIR"/../shell_config.sh
user1="user03141_1_${CLICKHOUSE_DATABASE}_$RANDOM"
user2="user03141_2_${CLICKHOUSE_DATABASE}_$RANDOM"
user3="user03141_3_${CLICKHOUSE_DATABASE}_$RANDOM"
db=${CLICKHOUSE_DATABASE}
${CLICKHOUSE_CLIENT} --multiquery <<EOF
CREATE TABLE $db.test (s String) ENGINE = MergeTree ORDER BY s;
CREATE TABLE $db.test_table (s String) ENGINE = MergeTree ORDER BY s;
CREATE TABLE $db.test_table_prefix1 (s String) ENGINE = MergeTree ORDER BY s;
CREATE TABLE $db.test_table_prefix2 (s String) ENGINE = MergeTree ORDER BY s;
CREATE TABLE $db.test_table_prefix3 (s String) ENGINE = MergeTree ORDER BY s;
CREATE TABLE $db.test_table_another_prefix (s String) ENGINE = MergeTree ORDER BY s;
DROP USER IF EXISTS $user1, $user2, $user3;
CREATE USER $user1, $user2, $user3;
GRANT SELECT ON $db.* TO $user1;
EOF
${CLICKHOUSE_CLIENT} --multiquery <<EOF
GRANT SELECT ON $db.test_table* TO $user1;
GRANT SELECT ON $db.test_table_prefix* TO $user2;
GRANT SELECT ON $db.test_table* TO $user3;
REVOKE SELECT ON $db.test_table_prefix* FROM $user3;
EOF
(( $(${CLICKHOUSE_CLIENT} --user $user1 --query "SELECT * FROM $db.test" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
${CLICKHOUSE_CLIENT} --user $user1 --query "SELECT count() FROM $db.test_table"
${CLICKHOUSE_CLIENT} --user $user1 --query "SELECT count() FROM $db.test_table_prefix1"
${CLICKHOUSE_CLIENT} --user $user1 --query "SELECT count() FROM $db.test_table_another_prefix"
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_table" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_table_another_prefix" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_table_prefix1"
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_table_prefix2"
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_table_prefix3"
(( $(${CLICKHOUSE_CLIENT} --user $user3 --query "SELECT * FROM $db.test" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
(( $(${CLICKHOUSE_CLIENT} --user $user3 --query "SELECT * FROM $db.test_table_prefix1" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
${CLICKHOUSE_CLIENT} --user $user3 --query "SELECT count() FROM $db.test_table"
${CLICKHOUSE_CLIENT} --user $user3 --query "SELECT count() FROM $db.test_table_another_prefix"
${CLICKHOUSE_CLIENT} --query "DROP USER IF EXISTS $user1, $user2, $user3";