2019-11-17 11:57:02 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <Access/IAccessEntity.h>
|
2020-05-30 20:10:45 +00:00
|
|
|
#include <Access/RolesOrUsersSet.h>
|
2021-01-27 00:54:57 +00:00
|
|
|
#include <Core/Types.h>
|
2020-05-07 02:45:27 +00:00
|
|
|
#include <array>
|
2019-11-17 11:57:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
2020-05-07 02:45:27 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
}
|
|
|
|
|
2019-11-17 11:57:02 +00:00
|
|
|
|
|
|
|
/** Represents a row level security policy for a table.
|
|
|
|
*/
|
|
|
|
struct RowPolicy : public IAccessEntity
|
|
|
|
{
|
2020-05-02 22:30:28 +00:00
|
|
|
struct NameParts
|
2019-11-17 11:57:02 +00:00
|
|
|
{
|
2020-05-02 22:30:28 +00:00
|
|
|
String short_name;
|
2019-11-17 11:57:02 +00:00
|
|
|
String database;
|
|
|
|
String table_name;
|
2020-05-02 22:30:28 +00:00
|
|
|
|
2020-05-30 14:18:08 +00:00
|
|
|
bool empty() const { return short_name.empty(); }
|
2020-05-02 22:30:28 +00:00
|
|
|
String getName() const;
|
2020-05-30 14:18:08 +00:00
|
|
|
String toString() const { return getName(); }
|
2020-05-02 22:30:28 +00:00
|
|
|
auto toTuple() const { return std::tie(short_name, database, table_name); }
|
|
|
|
friend bool operator ==(const NameParts & left, const NameParts & right) { return left.toTuple() == right.toTuple(); }
|
|
|
|
friend bool operator !=(const NameParts & left, const NameParts & right) { return left.toTuple() != right.toTuple(); }
|
2019-11-17 11:57:02 +00:00
|
|
|
};
|
|
|
|
|
2020-05-02 22:30:28 +00:00
|
|
|
void setShortName(const String & short_name);
|
|
|
|
void setDatabase(const String & database);
|
|
|
|
void setTableName(const String & table_name);
|
|
|
|
void setNameParts(const String & short_name, const String & database, const String & table_name);
|
|
|
|
void setNameParts(const NameParts & name_parts);
|
|
|
|
|
|
|
|
const String & getDatabase() const { return name_parts.database; }
|
|
|
|
const String & getTableName() const { return name_parts.table_name; }
|
|
|
|
const String & getShortName() const { return name_parts.short_name; }
|
|
|
|
const NameParts & getNameParts() const { return name_parts; }
|
|
|
|
|
2019-11-17 11:57:02 +00:00
|
|
|
/// Filter is a SQL conditional expression used to figure out which rows should be visible
|
|
|
|
/// for user or available for modification. If the expression returns NULL or false for some rows
|
|
|
|
/// those rows are silently suppressed.
|
|
|
|
/// Check is a SQL condition expression used to check whether a row can be written into
|
|
|
|
/// the table. If the expression returns NULL or false an exception is thrown.
|
|
|
|
/// If a conditional expression here is empty it means no filtering is applied.
|
2020-03-07 17:37:38 +00:00
|
|
|
enum ConditionType
|
2019-11-17 11:57:02 +00:00
|
|
|
{
|
|
|
|
SELECT_FILTER,
|
2020-05-07 02:45:27 +00:00
|
|
|
|
|
|
|
#if 0 /// Row-level security for INSERT, UPDATE, DELETE is not implemented yet.
|
2019-11-17 11:57:02 +00:00
|
|
|
INSERT_CHECK,
|
|
|
|
UPDATE_FILTER,
|
|
|
|
UPDATE_CHECK,
|
|
|
|
DELETE_FILTER,
|
2020-05-07 02:45:27 +00:00
|
|
|
#endif
|
2020-05-02 16:05:01 +00:00
|
|
|
|
|
|
|
MAX_CONDITION_TYPE
|
2019-11-17 11:57:02 +00:00
|
|
|
};
|
|
|
|
|
2020-05-07 02:45:27 +00:00
|
|
|
struct ConditionTypeInfo
|
|
|
|
{
|
|
|
|
const char * const raw_name;
|
|
|
|
const String name; /// Lowercased with underscores, e.g. "select_filter".
|
|
|
|
const String command; /// Uppercased without last word, e.g. "SELECT".
|
|
|
|
const bool is_check; /// E.g. false for SELECT_FILTER.
|
|
|
|
static const ConditionTypeInfo & get(ConditionType type);
|
|
|
|
};
|
|
|
|
|
|
|
|
std::array<String, MAX_CONDITION_TYPE> conditions;
|
2019-11-17 11:57:02 +00:00
|
|
|
|
|
|
|
/// Sets that the policy is permissive.
|
|
|
|
/// A row is only accessible if at least one of the permissive policies passes,
|
|
|
|
/// in addition to all the restrictive policies.
|
|
|
|
void setPermissive(bool permissive_ = true) { setRestrictive(!permissive_); }
|
|
|
|
bool isPermissive() const { return !isRestrictive(); }
|
|
|
|
|
|
|
|
/// Sets that the policy is restrictive.
|
|
|
|
/// A row is only accessible if at least one of the permissive policies passes,
|
|
|
|
/// in addition to all the restrictive policies.
|
|
|
|
void setRestrictive(bool restrictive_ = true) { restrictive = restrictive_; }
|
|
|
|
bool isRestrictive() const { return restrictive; }
|
|
|
|
|
|
|
|
bool equal(const IAccessEntity & other) const override;
|
|
|
|
std::shared_ptr<IAccessEntity> clone() const override { return cloneImpl<RowPolicy>(); }
|
2020-05-03 03:12:03 +00:00
|
|
|
static constexpr const Type TYPE = Type::ROW_POLICY;
|
|
|
|
Type getType() const override { return TYPE; }
|
2019-11-17 11:57:02 +00:00
|
|
|
|
2020-02-10 02:26:56 +00:00
|
|
|
/// Which roles or users should use this row policy.
|
2020-05-30 20:10:45 +00:00
|
|
|
RolesOrUsersSet to_roles;
|
2019-11-17 11:57:02 +00:00
|
|
|
|
|
|
|
private:
|
2020-05-02 22:30:28 +00:00
|
|
|
void setName(const String & name_) override;
|
|
|
|
|
|
|
|
NameParts name_parts;
|
2019-11-17 11:57:02 +00:00
|
|
|
bool restrictive = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
using RowPolicyPtr = std::shared_ptr<const RowPolicy>;
|
2020-05-07 02:45:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
inline const RowPolicy::ConditionTypeInfo & RowPolicy::ConditionTypeInfo::get(ConditionType type_)
|
|
|
|
{
|
|
|
|
static constexpr auto make_info = [](const char * raw_name_)
|
|
|
|
{
|
|
|
|
String init_name = raw_name_;
|
|
|
|
boost::to_lower(init_name);
|
|
|
|
size_t underscore_pos = init_name.find('_');
|
|
|
|
String init_command = init_name.substr(0, underscore_pos);
|
|
|
|
boost::to_upper(init_command);
|
|
|
|
bool init_is_check = (std::string_view{init_name}.substr(underscore_pos + 1) == "check");
|
|
|
|
return ConditionTypeInfo{raw_name_, std::move(init_name), std::move(init_command), init_is_check};
|
|
|
|
};
|
|
|
|
|
|
|
|
switch (type_)
|
|
|
|
{
|
|
|
|
case SELECT_FILTER:
|
|
|
|
{
|
|
|
|
static const ConditionTypeInfo info = make_info("SELECT_FILTER");
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
#if 0 /// Row-level security for INSERT, UPDATE, DELETE is not implemented yet.
|
|
|
|
case INSERT_CHECK:
|
|
|
|
{
|
|
|
|
static const ConditionTypeInfo info = make_info("INSERT_CHECK");
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
case UPDATE_FILTER:
|
|
|
|
{
|
|
|
|
static const ConditionTypeInfo info = make_info("UPDATE_FILTER");
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
case UPDATE_CHECK:
|
|
|
|
{
|
|
|
|
static const ConditionTypeInfo info = make_info("UPDATE_CHECK");
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
case DELETE_FILTER:
|
|
|
|
{
|
|
|
|
static const ConditionTypeInfo info = make_info("DELETE_FILTER");
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case MAX_CONDITION_TYPE: break;
|
|
|
|
}
|
|
|
|
throw Exception("Unknown type: " + std::to_string(static_cast<size_t>(type_)), ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline String toString(RowPolicy::ConditionType type)
|
|
|
|
{
|
|
|
|
return RowPolicy::ConditionTypeInfo::get(type).raw_name;
|
|
|
|
}
|
|
|
|
|
2020-05-30 14:18:08 +00:00
|
|
|
|
|
|
|
inline String RowPolicy::NameParts::getName() const
|
|
|
|
{
|
|
|
|
String name;
|
|
|
|
name.reserve(database.length() + table_name.length() + short_name.length() + 6);
|
|
|
|
name += backQuoteIfNeed(short_name);
|
|
|
|
name += " ON ";
|
|
|
|
if (!database.empty())
|
|
|
|
{
|
|
|
|
name += backQuoteIfNeed(database);
|
|
|
|
name += '.';
|
|
|
|
}
|
|
|
|
name += backQuoteIfNeed(table_name);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2019-11-17 11:57:02 +00:00
|
|
|
}
|