blob: 7a6d39d1202b3d8ed2e95af86f0ef051262f943d [file] [log] [blame]
Steve DiBenedetto2c2b8892014-02-27 11:46:48 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (C) 2014 Named Data Networking Project
4 * See COPYING for copyright and distribution information.
5 */
6
7#include "mgmt/command-validator.hpp"
8#include "mgmt/config-file.hpp"
9
10#include "tests/test-common.hpp"
11
12#include <boost/test/unit_test.hpp>
13#include <ndn-cpp-dev/util/command-interest-generator.hpp>
14#include <ndn-cpp-dev/util/io.hpp>
15#include <boost/filesystem.hpp>
16
17namespace nfd {
18
19namespace tests {
20
21NFD_LOG_INIT("CommandValidatorTest");
22
23BOOST_FIXTURE_TEST_SUITE(MgmtCommandValidator, BaseFixture)
24
25// authorizations
26// {
27// ; an authorize section grants privileges to a key
28// authorize
29// {
30// keyfile "tests/mgmt/key1.pub" ; public key file
31// privileges ; set of privileges granted to this public key
32// {
33// fib
34// stats
35// }
36// }
37
38// authorize
39// {
40// keyfile "tests/mgmt/key2.pub" ; public key file
41// privileges ; set of privileges granted to this public key
42// {
43// faces
44// }
45// }
46// }
47
48const std::string CONFIG =
49"authorizations\n"
50"{\n"
51" authorize\n"
52" {\n"
53" keyfile \"tests/mgmt/key1.pub\"\n"
54" privileges\n"
55" {\n"
56" fib\n"
57" stats\n"
58" }\n"
59" }\n"
60" authorize\n"
61" {\n"
62" keyfile \"tests/mgmt/key2.pub\"\n"
63" privileges\n"
64" {\n"
65" faces\n"
66" }\n"
67" }\n"
68 "}\n";
69
70class CommandValidatorTester
71{
72public:
73
74 CommandValidatorTester()
75 : m_validated(false),
76 m_validationFailed(false)
77 {
78
79 }
80
81 void
82 generateIdentity(const Name& prefix)
83 {
84 m_identityName = prefix;
85 m_identityName.append(boost::lexical_cast<std::string>(ndn::time::now()));
86
87 const Name certName = m_keys.createIdentity(m_identityName);
88
89 m_certificate = m_keys.getCertificate(certName);
90 }
91
92 void
93 saveIdentityToFile(const char* filename)
94 {
95 std::ofstream out;
96 out.open(filename);
97
98 BOOST_REQUIRE(out.is_open());
99 BOOST_REQUIRE(static_cast<bool>(m_certificate));
100
101 ndn::io::save<ndn::IdentityCertificate>(*m_certificate, out);
102
103 out.close();
104 }
105
106 const Name&
107 getIdentityName() const
108 {
109 BOOST_REQUIRE_NE(m_identityName, Name());
110 return m_identityName;
111 }
112
113 const Name&
114 getPublicKeyName() const
115 {
116 BOOST_REQUIRE(static_cast<bool>(m_certificate));
117 return m_certificate->getPublicKeyName();
118 }
119
120 void
121 onValidated(const shared_ptr<const Interest>& interest)
122 {
123 // NFD_LOG_DEBUG("validated command");
124 m_validated = true;
125 }
126
127 void
128 onValidationFailed(const shared_ptr<const Interest>& interest, const std::string& info)
129 {
130 NFD_LOG_DEBUG("validation failed: " << info);
131 m_validationFailed = true;
132 }
133
134 bool
135 commandValidated() const
136 {
137 return m_validated;
138 }
139
140 bool
141 commandValidationFailed() const
142 {
143 return m_validationFailed;
144 }
145
146 void
147 resetValidation()
148 {
149 m_validated = false;
150 m_validationFailed = false;
151 }
152
153 ~CommandValidatorTester()
154 {
155 m_keys.deleteIdentity(m_identityName);
156 }
157
158private:
159 bool m_validated;
160 bool m_validationFailed;
161
162 ndn::KeyChain m_keys;
163 Name m_identityName;
164 shared_ptr<ndn::IdentityCertificate> m_certificate;
165};
166
167class TwoValidatorFixture : public BaseFixture
168{
169public:
170 TwoValidatorFixture()
171 {
172 m_tester1.generateIdentity("/test/CommandValidator/TwoKeys/id1");
173 m_tester1.saveIdentityToFile("tests/mgmt/key1.pub");
174
175 m_tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
176 m_tester2.saveIdentityToFile("tests/mgmt/key2.pub");
177 }
178
179 ~TwoValidatorFixture()
180 {
181 boost::system::error_code error;
182 boost::filesystem::remove("tests/mgmt/key1.pub", error);
183 boost::filesystem::remove("tests/mgmt/key2.pub", error);
184 }
185
186protected:
187 CommandValidatorTester m_tester1;
188 CommandValidatorTester m_tester2;
189};
190
191BOOST_FIXTURE_TEST_CASE(TwoKeys, TwoValidatorFixture)
192{
193 shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
194 shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
195 shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
196
197 ndn::CommandInterestGenerator generator;
198 generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
199 generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
200 generator.generateWithIdentity(*facesCommand, m_tester2.getIdentityName());
201
202 ConfigFile config;
203 CommandValidator validator;
204 validator.addSupportedPrivilege("faces");
205 validator.addSupportedPrivilege("fib");
206 validator.addSupportedPrivilege("stats");
207
208 config.addSectionHandler("authorizations",
209 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
210 config.parse(CONFIG, false);
211
212 validator.validate(*fibCommand,
213 bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
214 bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
215
216 BOOST_REQUIRE(m_tester1.commandValidated());
217 m_tester1.resetValidation();
218
219 validator.validate(*statsCommand,
220 bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
221 bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
222
223 BOOST_REQUIRE(m_tester1.commandValidated());
224
225 validator.validate(*facesCommand,
226 bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
227 bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
228
229 BOOST_REQUIRE(m_tester2.commandValidated());
230 m_tester2.resetValidation();
231
232 // use key2 for fib command (authorized for key1 only)
233 shared_ptr<Interest> unauthorizedFibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
234 generator.generateWithIdentity(*unauthorizedFibCommand, m_tester2.getIdentityName());
235
236 validator.validate(*unauthorizedFibCommand,
237 bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
238 bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
239
240 BOOST_REQUIRE(m_tester2.commandValidationFailed());
241}
242
243BOOST_FIXTURE_TEST_CASE(TwoKeysDryRun, TwoValidatorFixture)
244{
245 CommandValidatorTester tester1;
246 tester1.generateIdentity("/test/CommandValidator/TwoKeys/id1");
247 tester1.saveIdentityToFile("tests/mgmt/key1.pub");
248
249 CommandValidatorTester tester2;
250 tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
251 tester2.saveIdentityToFile("tests/mgmt/key2.pub");
252
253 shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
254 shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
255 shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
256
257 ndn::CommandInterestGenerator generator;
258 generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
259 generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
260 generator.generateWithIdentity(*facesCommand, m_tester2.getIdentityName());
261
262 ConfigFile config;
263 CommandValidator validator;
264 validator.addSupportedPrivilege("faces");
265 validator.addSupportedPrivilege("fib");
266 validator.addSupportedPrivilege("stats");
267
268 config.addSectionHandler("authorizations",
269 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
270 config.parse(CONFIG, true);
271
272 validator.validate(*fibCommand,
273 bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
274 bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
275
276 BOOST_REQUIRE(m_tester1.commandValidationFailed());
277 m_tester1.resetValidation();
278
279 validator.validate(*statsCommand,
280 bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
281 bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
282
283 BOOST_REQUIRE(m_tester1.commandValidationFailed());
284
285 validator.validate(*facesCommand,
286 bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
287 bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
288
289 BOOST_REQUIRE(m_tester2.commandValidationFailed());
290 m_tester2.resetValidation();
291
292 // use key2 for fib command (authorized for key1 only)
293 shared_ptr<Interest> unauthorizedFibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
294 generator.generateWithIdentity(*unauthorizedFibCommand, m_tester2.getIdentityName());
295
296 validator.validate(*unauthorizedFibCommand,
297 bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
298 bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
299
300 BOOST_REQUIRE(m_tester2.commandValidationFailed());
301}
302
303BOOST_AUTO_TEST_CASE(NoAuthorizeSections)
304{
305 const std::string NO_AUTHORIZE_CONFIG =
306 "authorizations\n"
307 "{\n"
308 "}\n";
309
310 ConfigFile config;
311 CommandValidator validator;
312
313 config.addSectionHandler("authorizations",
314 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
315 BOOST_CHECK_THROW(config.parse(NO_AUTHORIZE_CONFIG, false), ConfigFile::Error);
316}
317
318BOOST_AUTO_TEST_CASE(NoPrivilegesSections)
319{
320 const std::string NO_PRIVILEGES_CONFIG =
321 "authorizations\n"
322 "{\n"
323 " authorize\n"
324 " {\n"
325 " keyfile \"tests/mgmt/key1.pub\"\n"
326 " }\n"
327 "}\n";
328
329 ConfigFile config;
330 CommandValidator validator;
331
332 config.addSectionHandler("authorizations",
333 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
334 BOOST_CHECK_THROW(config.parse(NO_PRIVILEGES_CONFIG, false), ConfigFile::Error);
335}
336
337BOOST_AUTO_TEST_CASE(InvalidKeyFile)
338{
339 const std::string INVALID_KEY_CONFIG =
340 "authorizations\n"
341 "{\n"
342 " authorize\n"
343 " {\n"
344 " keyfile \"tests/mgmt/notakeyfile.pub\"\n"
345 " privileges\n"
346 " {\n"
347 " fib\n"
348 " stats\n"
349 " }\n"
350 " }\n"
351 "}\n";
352
353 ConfigFile config;
354 CommandValidator validator;
355
356 config.addSectionHandler("authorizations",
357 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
358 BOOST_CHECK_THROW(config.parse(INVALID_KEY_CONFIG, false), ConfigFile::Error);
359}
360
361BOOST_AUTO_TEST_CASE(NoKeyFile)
362{
363 const std::string NO_KEY_CONFIG =
364 "authorizations\n"
365 "{\n"
366 " authorize\n"
367 " {\n"
368 " privileges\n"
369 " {\n"
370 " fib\n"
371 " stats\n"
372 " }\n"
373 " }\n"
374 "}\n";
375
376
377 ConfigFile config;
378 CommandValidator validator;
379
380 config.addSectionHandler("authorizations",
381 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
382 BOOST_CHECK_THROW(config.parse(NO_KEY_CONFIG, false), ConfigFile::Error);
383}
384
385BOOST_AUTO_TEST_CASE(MalformedKey)
386{
387 const std::string MALFORMED_KEY_CONFIG =
388 "authorizations\n"
389 "{\n"
390 " authorize\n"
391 " {\n"
392 " keyfile \"tests/mgmt/malformedkey.pub\"\n"
393 " privileges\n"
394 " {\n"
395 " fib\n"
396 " stats\n"
397 " }\n"
398 " }\n"
399 "}\n";
400
401
402 ConfigFile config;
403 CommandValidator validator;
404
405 config.addSectionHandler("authorizations",
406 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
407 BOOST_CHECK_THROW(config.parse(MALFORMED_KEY_CONFIG, false), ConfigFile::Error);
408}
409
410bool
411validateErrorMessage(const std::string& expectedMessage, const ConfigFile::Error& error)
412{
413 bool gotExpected = error.what() == expectedMessage;
414 if (!gotExpected)
415 {
416 NFD_LOG_WARN("\ncaught exception: " << error.what()
417 << "\n\nexpected exception: " << expectedMessage);
418 }
419 return gotExpected;
420}
421
422BOOST_AUTO_TEST_CASE(NoAuthorizeSectionsDryRun)
423{
424 const std::string NO_AUTHORIZE_CONFIG =
425 "authorizations\n"
426 "{\n"
427 "}\n";
428
429 ConfigFile config;
430 CommandValidator validator;
431
432 config.addSectionHandler("authorizations",
433 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
434 BOOST_CHECK_EXCEPTION(config.parse(NO_AUTHORIZE_CONFIG, true),
435 ConfigFile::Error,
436 bind(&validateErrorMessage,
437 "No authorize sections found", _1));
438}
439
440BOOST_FIXTURE_TEST_CASE(NoPrivilegesSectionsDryRun, TwoValidatorFixture)
441{
442 const std::string NO_PRIVILEGES_CONFIG =
443 "authorizations\n"
444 "{\n"
445 " authorize\n"
446 " {\n"
447 " keyfile \"tests/mgmt/key1.pub\"\n"
448 " }\n"
449 " authorize\n"
450 " {\n"
451 " keyfile \"tests/mgmt/key2.pub\"\n"
452 " }\n"
453 "}\n";
454
455 // CommandValidatorTester tester1;
456 // tester1.generateIdentity("/tests/CommandValidator/TwoKeys/id1");
457 // tester1.saveIdentityToFile("tests/mgmt/key1.pub");
458
459 // CommandValidatorTester tester2;
460 // tester2.generateIdentity("/tests/CommandValidator/TwoKeys/id2");
461 // tester2.saveIdentityToFile("tests/mgmt/key2.pub");
462
463 ConfigFile config;
464 CommandValidator validator;
465
466 config.addSectionHandler("authorizations",
467 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
468
469 std::stringstream expectedError;
470 expectedError << "No privileges section found for key file tests/mgmt/key1.pub "
471 << "(" << m_tester1.getPublicKeyName().toUri() << ")\n"
472 << "No privileges section found for key file tests/mgmt/key2.pub "
473 << "(" << m_tester2.getPublicKeyName().toUri() << ")";
474
475 BOOST_CHECK_EXCEPTION(config.parse(NO_PRIVILEGES_CONFIG, true),
476 ConfigFile::Error,
477 bind(&validateErrorMessage, expectedError.str(), _1));
478}
479
480BOOST_AUTO_TEST_CASE(InvalidKeyFileDryRun)
481{
482 const std::string INVALID_KEY_CONFIG =
483 "authorizations\n"
484 "{\n"
485 " authorize\n"
486 " {\n"
487 " keyfile \"tests/mgmt/notakeyfile.pub\"\n"
488 " privileges\n"
489 " {\n"
490 " fib\n"
491 " stats\n"
492 " }\n"
493 " }\n"
494 " authorize\n"
495 " {\n"
496 " keyfile \"tests/mgmt/stillnotakeyfile.pub\"\n"
497 " privileges\n"
498 " {\n"
499 " }\n"
500 " }\n"
501 "}\n";
502
503 ConfigFile config;
504 CommandValidator validator;
505
506 config.addSectionHandler("authorizations",
507 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
508
509 BOOST_CHECK_EXCEPTION(config.parse(INVALID_KEY_CONFIG, true),
510 ConfigFile::Error,
511 bind(&validateErrorMessage,
512 "Unable to open key file tests/mgmt/notakeyfile.pub\n"
513 "Unable to open key file tests/mgmt/stillnotakeyfile.pub", _1));
514}
515
516BOOST_AUTO_TEST_CASE(NoKeyFileDryRun)
517{
518 const std::string NO_KEY_CONFIG =
519 "authorizations\n"
520 "{\n"
521 " authorize\n"
522 " {\n"
523 " privileges\n"
524 " {\n"
525 " fib\n"
526 " stats\n"
527 " }\n"
528 " }\n"
529 " authorize\n"
530 " {\n"
531 " }\n"
532 "}\n";
533
534
535 ConfigFile config;
536 CommandValidator validator;
537
538 config.addSectionHandler("authorizations",
539 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
540 BOOST_CHECK_EXCEPTION(config.parse(NO_KEY_CONFIG, true),
541 ConfigFile::Error,
542 bind(&validateErrorMessage,
543 "No keyfile specified\n"
544 "No keyfile specified", _1));
545}
546
547BOOST_AUTO_TEST_CASE(MalformedKeyDryRun)
548{
549 const std::string MALFORMED_KEY_CONFIG =
550 "authorizations\n"
551 "{\n"
552 " authorize\n"
553 " {\n"
554 " keyfile \"tests/mgmt/malformedkey.pub\"\n"
555 " privileges\n"
556 " {\n"
557 " fib\n"
558 " stats\n"
559 " }\n"
560 " }\n"
561 " authorize\n"
562 " {\n"
563 " keyfile \"tests/mgmt/malformedkey.pub\"\n"
564 " }\n"
565 "}\n";
566
567
568 ConfigFile config;
569 CommandValidator validator;
570
571 config.addSectionHandler("authorizations",
572 bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
573 BOOST_CHECK_EXCEPTION(config.parse(MALFORMED_KEY_CONFIG, true),
574 ConfigFile::Error,
575 bind(&validateErrorMessage,
576 "Malformed key file tests/mgmt/malformedkey.pub\n"
577 "Malformed key file tests/mgmt/malformedkey.pub", _1));
578}
579
580BOOST_AUTO_TEST_SUITE_END()
581
582} // namespace tests
583
584} // namespace nfd
585