mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-30 19:42:00 +00:00
Wildcard grants
This commit is contained in:
parent
905495e59b
commit
0fb239cd7f
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
14
tests/queries/0_stateless/03141_wildcard_grants.reference
Normal file
14
tests/queries/0_stateless/03141_wildcard_grants.reference
Normal file
@ -0,0 +1,14 @@
|
||||
OK
|
||||
0
|
||||
0
|
||||
0
|
||||
OK
|
||||
OK
|
||||
OK
|
||||
0
|
||||
0
|
||||
0
|
||||
OK
|
||||
OK
|
||||
0
|
||||
0
|
53
tests/queries/0_stateless/03141_wildcard_grants.sh
Normal file
53
tests/queries/0_stateless/03141_wildcard_grants.sh
Normal 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";
|
Loading…
Reference in New Issue
Block a user