1#include <Access/UsersConfigAccessStorage.h>
2#include <Access/Quota.h>
3#include <Access/RowPolicy.h>
4#include <Common/StringUtils/StringUtils.h>
5#include <Common/quoteString.h>
6#include <Poco/Util/AbstractConfiguration.h>
7#include <Poco/MD5Engine.h>
8#include <cstring>
9
10
11namespace DB
12{
13namespace
14{
15 char getTypeChar(std::type_index type)
16 {
17 if (type == typeid(Quota))
18 return 'Q';
19 if (type == typeid(RowPolicy))
20 return 'P';
21 return 0;
22 }
23
24
25 UUID generateID(std::type_index type, const String & name)
26 {
27 Poco::MD5Engine md5;
28 md5.update(name);
29 char type_storage_chars[] = " USRSXML";
30 type_storage_chars[0] = getTypeChar(type);
31 md5.update(type_storage_chars, strlen(type_storage_chars));
32 UUID result;
33 memcpy(&result, md5.digest().data(), md5.digestLength());
34 return result;
35 }
36
37
38 UUID generateID(const IAccessEntity & entity) { return generateID(entity.getType(), entity.getFullName()); }
39
40 QuotaPtr parseQuota(const Poco::Util::AbstractConfiguration & config, const String & quota_name, const Strings & user_names)
41 {
42 auto quota = std::make_shared<Quota>();
43 quota->setName(quota_name);
44
45 using KeyType = Quota::KeyType;
46 String quota_config = "quotas." + quota_name;
47 if (config.has(quota_config + ".keyed_by_ip"))
48 quota->key_type = KeyType::IP_ADDRESS;
49 else if (config.has(quota_config + ".keyed"))
50 quota->key_type = KeyType::CLIENT_KEY_OR_USER_NAME;
51 else
52 quota->key_type = KeyType::USER_NAME;
53
54 Poco::Util::AbstractConfiguration::Keys interval_keys;
55 config.keys(quota_config, interval_keys);
56
57 for (const String & interval_key : interval_keys)
58 {
59 if (!startsWith(interval_key, "interval"))
60 continue;
61
62 String interval_config = quota_config + "." + interval_key;
63 std::chrono::seconds duration{config.getInt(interval_config + ".duration", 0)};
64 if (duration.count() <= 0) /// Skip quotas with non-positive duration.
65 continue;
66
67 quota->all_limits.emplace_back();
68 auto & limits = quota->all_limits.back();
69 limits.duration = duration;
70 limits.randomize_interval = config.getBool(interval_config + ".randomize", false);
71
72 using ResourceType = Quota::ResourceType;
73 limits.max[ResourceType::QUERIES] = config.getUInt64(interval_config + ".queries", Quota::UNLIMITED);
74 limits.max[ResourceType::ERRORS] = config.getUInt64(interval_config + ".errors", Quota::UNLIMITED);
75 limits.max[ResourceType::RESULT_ROWS] = config.getUInt64(interval_config + ".result_rows", Quota::UNLIMITED);
76 limits.max[ResourceType::RESULT_BYTES] = config.getUInt64(interval_config + ".result_bytes", Quota::UNLIMITED);
77 limits.max[ResourceType::READ_ROWS] = config.getUInt64(interval_config + ".read_rows", Quota::UNLIMITED);
78 limits.max[ResourceType::READ_BYTES] = config.getUInt64(interval_config + ".read_bytes", Quota::UNLIMITED);
79 limits.max[ResourceType::EXECUTION_TIME] = Quota::secondsToExecutionTime(config.getUInt64(interval_config + ".execution_time", Quota::UNLIMITED));
80 }
81
82 quota->roles = user_names;
83
84 return quota;
85 }
86
87
88 std::vector<AccessEntityPtr> parseQuotas(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
89 {
90 Poco::Util::AbstractConfiguration::Keys user_names;
91 config.keys("users", user_names);
92 std::unordered_map<String, Strings> quota_to_user_names;
93 for (const auto & user_name : user_names)
94 {
95 if (config.has("users." + user_name + ".quota"))
96 quota_to_user_names[config.getString("users." + user_name + ".quota")].push_back(user_name);
97 }
98
99 Poco::Util::AbstractConfiguration::Keys quota_names;
100 config.keys("quotas", quota_names);
101 std::vector<AccessEntityPtr> quotas;
102 quotas.reserve(quota_names.size());
103 for (const auto & quota_name : quota_names)
104 {
105 try
106 {
107 auto it = quota_to_user_names.find(quota_name);
108 const Strings quota_users = (it != quota_to_user_names.end()) ? std::move(it->second) : Strings{};
109 quotas.push_back(parseQuota(config, quota_name, quota_users));
110 }
111 catch (...)
112 {
113 tryLogCurrentException(log, "Could not parse quota " + backQuote(quota_name));
114 }
115 }
116 return quotas;
117 }
118
119
120 std::vector<AccessEntityPtr> parseRowPolicies(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
121 {
122 std::vector<AccessEntityPtr> policies;
123 Poco::Util::AbstractConfiguration::Keys user_names;
124 config.keys("users", user_names);
125
126 for (const String & user_name : user_names)
127 {
128 const String databases_config = "users." + user_name + ".databases";
129 if (config.has(databases_config))
130 {
131 Poco::Util::AbstractConfiguration::Keys databases;
132 config.keys(databases_config, databases);
133
134 /// Read tables within databases
135 for (const String & database : databases)
136 {
137 const String database_config = databases_config + "." + database;
138 Poco::Util::AbstractConfiguration::Keys table_names;
139 config.keys(database_config, table_names);
140
141 /// Read table properties
142 for (const String & table_name : table_names)
143 {
144 const auto filter_config = database_config + "." + table_name + ".filter";
145 if (config.has(filter_config))
146 {
147 try
148 {
149 auto policy = std::make_shared<RowPolicy>();
150 policy->setFullName(database, table_name, user_name);
151 policy->conditions[RowPolicy::SELECT_FILTER] = config.getString(filter_config);
152 policy->roles.push_back(user_name);
153 policies.push_back(policy);
154 }
155 catch (...)
156 {
157 tryLogCurrentException(
158 log,
159 "Could not parse row policy " + backQuote(user_name) + " on table " + backQuoteIfNeed(database) + "."
160 + backQuoteIfNeed(table_name));
161 }
162 }
163 }
164 }
165 }
166 }
167 return policies;
168 }
169}
170
171
172UsersConfigAccessStorage::UsersConfigAccessStorage() : IAccessStorage("users.xml")
173{
174}
175
176
177UsersConfigAccessStorage::~UsersConfigAccessStorage() {}
178
179
180void UsersConfigAccessStorage::loadFromConfig(const Poco::Util::AbstractConfiguration & config)
181{
182 std::vector<std::pair<UUID, AccessEntityPtr>> all_entities;
183 for (const auto & entity : parseQuotas(config, getLogger()))
184 all_entities.emplace_back(generateID(*entity), entity);
185 for (const auto & entity : parseRowPolicies(config, getLogger()))
186 all_entities.emplace_back(generateID(*entity), entity);
187 memory_storage.setAll(all_entities);
188}
189
190
191std::optional<UUID> UsersConfigAccessStorage::findImpl(std::type_index type, const String & name) const
192{
193 return memory_storage.find(type, name);
194}
195
196
197std::vector<UUID> UsersConfigAccessStorage::findAllImpl(std::type_index type) const
198{
199 return memory_storage.findAll(type);
200}
201
202
203bool UsersConfigAccessStorage::existsImpl(const UUID & id) const
204{
205 return memory_storage.exists(id);
206}
207
208
209AccessEntityPtr UsersConfigAccessStorage::readImpl(const UUID & id) const
210{
211 return memory_storage.read(id);
212}
213
214
215String UsersConfigAccessStorage::readNameImpl(const UUID & id) const
216{
217 return memory_storage.readName(id);
218}
219
220
221UUID UsersConfigAccessStorage::insertImpl(const AccessEntityPtr & entity, bool)
222{
223 throwReadonlyCannotInsert(entity->getType(), entity->getFullName());
224}
225
226
227void UsersConfigAccessStorage::removeImpl(const UUID & id)
228{
229 auto entity = read(id);
230 throwReadonlyCannotRemove(entity->getType(), entity->getFullName());
231}
232
233
234void UsersConfigAccessStorage::updateImpl(const UUID & id, const UpdateFunc &)
235{
236 auto entity = read(id);
237 throwReadonlyCannotUpdate(entity->getType(), entity->getFullName());
238}
239
240
241IAccessStorage::SubscriptionPtr UsersConfigAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const
242{
243 return memory_storage.subscribeForChanges(id, handler);
244}
245
246
247IAccessStorage::SubscriptionPtr UsersConfigAccessStorage::subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const
248{
249 return memory_storage.subscribeForChanges(type, handler);
250}
251
252
253bool UsersConfigAccessStorage::hasSubscriptionImpl(const UUID & id) const
254{
255 return memory_storage.hasSubscription(id);
256}
257
258
259bool UsersConfigAccessStorage::hasSubscriptionImpl(std::type_index type) const
260{
261 return memory_storage.hasSubscription(type);
262}
263}
264