1#include <Common/Config/ConfigProcessor.h>
2#include <Interpreters/UsersManager.h>
3#include <filesystem>
4#include <vector>
5#include <string>
6#include <tuple>
7#include <iostream>
8#include <fstream>
9#include <sstream>
10#include <stdexcept>
11#include <cstdlib>
12#include <port/unistd.h>
13
14namespace
15{
16
17namespace fs = std::filesystem;
18
19struct TestEntry
20{
21 std::string user_name;
22 std::string database_name;
23 bool is_allowed;
24};
25
26using TestEntries = std::vector<TestEntry>;
27
28struct TestDescriptor
29{
30 const char * config_content;
31 TestEntries entries;
32};
33
34using TestSet = std::vector<TestDescriptor>;
35
36/// Tests description.
37
38TestSet test_set =
39{
40 {
41 "<?xml version=\"1.0\"?><yandex>"
42 " <profiles><default></default></profiles>"
43 " <users>"
44 " <default>"
45 " <password></password><profile>default</profile><quota>default</quota>"
46 " <allow_databases>"
47 " <database>default</database>"
48 " <database>test</database>"
49 " </allow_databases>"
50 " </default>"
51 " <web>"
52 " <password></password><profile>default</profile><quota>default</quota>"
53 " </web>"
54 " </users>"
55 " <quotas><default></default></quotas>"
56 "</yandex>",
57
58 {
59 { "default", "default", true },
60 { "default", "test", true },
61 { "default", "stats", false },
62 { "web", "default", true },
63 { "web", "test", true },
64 { "web", "stats", true },
65 { "analytics", "default", false },
66 { "analytics", "test", false },
67 { "analytics", "stats", false }
68 }
69 },
70
71 {
72 "<?xml version=\"1.0\"?><yandex>"
73 " <profiles><default></default></profiles>"
74 " <users>"
75 " <default>"
76 " <password></password><profile>default</profile><quota>default</quota>"
77 " <allow_databases>"
78 " <database>default</database>"
79 " </allow_databases>"
80 " </default>"
81 " <web>"
82 " <password></password><profile>default</profile><quota>default</quota>"
83 " </web>"
84 " </users>"
85 " <quotas><default></default></quotas>"
86 "</yandex>",
87
88 {
89 { "default", "default", true },
90 { "default", "test", false },
91 { "default", "stats", false },
92 { "web", "default", true },
93 { "web", "test", true },
94 { "web", "stats", true },
95 { "analytics", "default", false },
96 { "analytics", "test", false },
97 { "analytics", "stats", false }
98 }
99 },
100
101 {
102 "<?xml version=\"1.0\"?><yandex>"
103 " <profiles><default></default></profiles>"
104 " <users>"
105 " <default>"
106 " <password></password><profile>default</profile><quota>default</quota>"
107 " <allow_databases>"
108 " </allow_databases>"
109 " </default>"
110 " <web>"
111 " <password></password><profile>default</profile><quota>default</quota>"
112 " </web>"
113 " </users>"
114 " <quotas><default></default></quotas>"
115 "</yandex>",
116
117 {
118 { "default", "default", true },
119 { "default", "test", true },
120 { "default", "stats", true },
121 { "web", "default", true },
122 { "web", "test", true },
123 { "web", "stats", true },
124 { "analytics", "default", false },
125 { "analytics", "test", false },
126 { "analytics", "stats", false }
127 }
128 },
129
130 {
131 "<?xml version=\"1.0\"?><yandex>"
132 " <profiles><default></default></profiles>"
133 " <users>"
134 " <default>"
135 " <password></password><profile>default</profile><quota>default</quota>"
136 " <allow_databases>"
137 " <database>default</database>"
138 " </allow_databases>"
139 " </default>"
140 " <web>"
141 " <password></password><profile>default</profile><quota>default</quota>"
142 " <allow_databases>"
143 " <database>test</database>"
144 " </allow_databases>"
145 " </web>"
146 " </users>"
147 " <quotas><default></default></quotas>"
148 "</yandex>",
149
150 {
151 { "default", "default", true },
152 { "default", "test", false },
153 { "default", "stats", false },
154 { "web", "default", false },
155 { "web", "test", true },
156 { "web", "stats", false },
157 { "analytics", "default", false },
158 { "analytics", "test", false },
159 { "analytics", "stats", false }
160 }
161 }
162};
163
164std::string createTmpPath(const std::string & filename)
165{
166 char pattern[] = "/tmp/fileXXXXXX";
167 char * dir = mkdtemp(pattern);
168 if (dir == nullptr)
169 throw std::runtime_error("Could not create directory");
170
171 return std::string(dir) + "/" + filename;
172}
173
174void createFile(const std::string & filename, const char * data)
175{
176 std::ofstream ofs(filename.c_str());
177 if (!ofs.is_open())
178 throw std::runtime_error("Could not open file " + filename);
179 ofs << data;
180}
181
182void runOneTest(const TestDescriptor & test_descriptor)
183{
184 const auto path_name = createTmpPath("users.xml");
185 createFile(path_name, test_descriptor.config_content);
186
187 DB::ConfigurationPtr config;
188
189 try
190 {
191 config = DB::ConfigProcessor(path_name).loadConfig().configuration;
192 }
193 catch (const Poco::Exception & ex)
194 {
195 std::ostringstream os;
196 os << "Error: " << ex.what() << ": " << ex.displayText();
197 throw std::runtime_error(os.str());
198 }
199
200 DB::UsersManager users_manager;
201
202 try
203 {
204 users_manager.loadFromConfig(*config);
205 }
206 catch (const Poco::Exception & ex)
207 {
208 std::ostringstream os;
209 os << "Error: " << ex.what() << ": " << ex.displayText();
210 throw std::runtime_error(os.str());
211 }
212
213 for (const auto & entry : test_descriptor.entries)
214 {
215 bool res;
216
217 try
218 {
219 res = users_manager.hasAccessToDatabase(entry.user_name, entry.database_name);
220 }
221 catch (const Poco::Exception &)
222 {
223 res = false;
224 }
225
226 if (res != entry.is_allowed)
227 {
228 auto to_string = [](bool access){ return (access ? "'granted'" : "'denied'"); };
229 std::ostringstream os;
230 os << "(user=" << entry.user_name << ", database=" << entry.database_name << "): ";
231 os << "Expected " << to_string(entry.is_allowed) << " but got " << to_string(res);
232 throw std::runtime_error(os.str());
233 }
234 }
235
236 fs::remove_all(fs::path(path_name).parent_path().string());
237}
238
239auto runTestSet()
240{
241 size_t test_num = 1;
242 size_t failure_count = 0;
243
244 for (const auto & test_descriptor : test_set)
245 {
246 try
247 {
248 runOneTest(test_descriptor);
249 std::cout << "Test " << test_num << " passed\n";
250 }
251 catch (const std::runtime_error & ex)
252 {
253 std::cerr << "Test " << test_num << " failed with reason: " << ex.what() << "\n";
254 ++failure_count;
255 }
256 catch (...)
257 {
258 std::cerr << "Test " << test_num << " failed with unknown reason\n";
259 ++failure_count;
260 }
261
262 ++test_num;
263 }
264
265 return std::make_tuple(test_set.size(), failure_count);
266}
267
268}
269
270int main()
271{
272 size_t test_count;
273 size_t failure_count;
274
275 std::tie(test_count, failure_count) = runTestSet();
276
277 std::cout << (test_count - failure_count) << " test(s) passed out of " << test_count << "\n";
278
279 return (failure_count == 0) ? 0 : EXIT_FAILURE;
280}
281