1import unittest 2 3 4class TestWhoConfig(unittest.TestCase): 5 6 def _getTargetClass(self): 7 from repoze.who.config import WhoConfig 8 return WhoConfig 9 10 def _makeOne(self, here='/', *args, **kw): 11 return self._getTargetClass()(here, *args, **kw) 12 13 def _getDummyPluginClass(self, iface): 14 from zope.interface import classImplements 15 if not iface.implementedBy(DummyPlugin): 16 classImplements(DummyPlugin, iface) 17 return DummyPlugin 18 19 def test_defaults_before_parse(self): 20 config = self._makeOne() 21 self.assertEqual(config.request_classifier, None) 22 self.assertEqual(config.challenge_decider, None) 23 self.assertEqual(config.remote_user_key, 'REMOTE_USER') 24 self.assertEqual(len(config.plugins), 0) 25 self.assertEqual(len(config.identifiers), 0) 26 self.assertEqual(len(config.authenticators), 0) 27 self.assertEqual(len(config.challengers), 0) 28 self.assertEqual(len(config.mdproviders), 0) 29 30 def test_parse_empty_string(self): 31 config = self._makeOne() 32 config.parse('') 33 self.assertEqual(config.request_classifier, None) 34 self.assertEqual(config.challenge_decider, None) 35 self.assertEqual(config.remote_user_key, 'REMOTE_USER') 36 self.assertEqual(len(config.plugins), 0) 37 self.assertEqual(len(config.identifiers), 0) 38 self.assertEqual(len(config.authenticators), 0) 39 self.assertEqual(len(config.challengers), 0) 40 self.assertEqual(len(config.mdproviders), 0) 41 42 def test_parse_empty_file(self): 43 from repoze.who._compat import StringIO 44 config = self._makeOne() 45 config.parse(StringIO()) 46 self.assertEqual(config.request_classifier, None) 47 self.assertEqual(config.challenge_decider, None) 48 self.assertEqual(config.remote_user_key, 'REMOTE_USER') 49 self.assertEqual(len(config.plugins), 0) 50 self.assertEqual(len(config.identifiers), 0) 51 self.assertEqual(len(config.authenticators), 0) 52 self.assertEqual(len(config.challengers), 0) 53 self.assertEqual(len(config.mdproviders), 0) 54 55 def test_parse_plugins(self): 56 config = self._makeOne() 57 config.parse(PLUGINS_ONLY) 58 self.assertEqual(len(config.plugins), 2) 59 self.assertTrue(isinstance(config.plugins['foo'], 60 DummyPlugin)) 61 bar = config.plugins['bar'] 62 self.assertTrue(isinstance(bar, DummyPlugin)) 63 self.assertEqual(bar.credentials, 'qux') 64 65 def test_parse_general_empty(self): 66 config = self._makeOne() 67 config.parse('[general]') 68 self.assertEqual(config.request_classifier, None) 69 self.assertEqual(config.challenge_decider, None) 70 self.assertEqual(config.remote_user_key, 'REMOTE_USER') 71 self.assertEqual(len(config.plugins), 0) 72 73 def test_parse_general_only(self): 74 from repoze.who.interfaces import IRequestClassifier 75 from repoze.who.interfaces import IChallengeDecider 76 class IDummy(IRequestClassifier, IChallengeDecider): 77 pass 78 PLUGIN_CLASS = self._getDummyPluginClass(IDummy) 79 config = self._makeOne() 80 config.parse(GENERAL_ONLY) 81 self.assertTrue(isinstance(config.request_classifier, PLUGIN_CLASS)) 82 self.assertTrue(isinstance(config.challenge_decider, PLUGIN_CLASS)) 83 self.assertEqual(config.remote_user_key, 'ANOTHER_REMOTE_USER') 84 self.assertEqual(len(config.plugins), 0) 85 86 def test_parse_general_with_plugins(self): 87 from repoze.who.interfaces import IRequestClassifier 88 from repoze.who.interfaces import IChallengeDecider 89 class IDummy(IRequestClassifier, IChallengeDecider): 90 pass 91 PLUGIN_CLASS = self._getDummyPluginClass(IDummy) 92 config = self._makeOne() 93 config.parse(GENERAL_WITH_PLUGINS) 94 self.assertTrue(isinstance(config.request_classifier, PLUGIN_CLASS)) 95 self.assertTrue(isinstance(config.challenge_decider, PLUGIN_CLASS)) 96 97 def test_parse_identifiers_only(self): 98 from repoze.who.interfaces import IIdentifier 99 PLUGIN_CLASS = self._getDummyPluginClass(IIdentifier) 100 config = self._makeOne() 101 config.parse(IDENTIFIERS_ONLY) 102 identifiers = config.identifiers 103 self.assertEqual(len(identifiers), 2) 104 first, second = identifiers 105 self.assertEqual(first[0], 'repoze.who.tests.test_config:DummyPlugin') 106 self.assertTrue(isinstance(first[1], PLUGIN_CLASS)) 107 self.assertEqual(len(first[1].classifications), 1) 108 self.assertEqual(first[1].classifications[IIdentifier], 'klass1') 109 self.assertEqual(second[0], 'repoze.who.tests.test_config:DummyPlugin') 110 self.assertTrue(isinstance(second[1], PLUGIN_CLASS)) 111 112 def test_parse_identifiers_with_plugins(self): 113 from repoze.who.interfaces import IIdentifier 114 PLUGIN_CLASS = self._getDummyPluginClass(IIdentifier) 115 config = self._makeOne() 116 config.parse(IDENTIFIERS_WITH_PLUGINS) 117 identifiers = config.identifiers 118 self.assertEqual(len(identifiers), 2) 119 first, second = identifiers 120 self.assertEqual(first[0], 'foo') 121 self.assertTrue(isinstance(first[1], PLUGIN_CLASS)) 122 self.assertEqual(len(first[1].classifications), 1) 123 self.assertEqual(first[1].classifications[IIdentifier], 'klass1') 124 self.assertEqual(second[0], 'bar') 125 self.assertTrue(isinstance(second[1], PLUGIN_CLASS)) 126 127 def test_parse_authenticators_only(self): 128 from repoze.who.interfaces import IAuthenticator 129 PLUGIN_CLASS = self._getDummyPluginClass(IAuthenticator) 130 config = self._makeOne() 131 config.parse(AUTHENTICATORS_ONLY) 132 authenticators = config.authenticators 133 self.assertEqual(len(authenticators), 2) 134 first, second = authenticators 135 self.assertEqual(first[0], 'repoze.who.tests.test_config:DummyPlugin') 136 self.assertTrue(isinstance(first[1], PLUGIN_CLASS)) 137 self.assertEqual(len(first[1].classifications), 1) 138 self.assertEqual(first[1].classifications[IAuthenticator], 'klass1') 139 self.assertEqual(second[0], 'repoze.who.tests.test_config:DummyPlugin') 140 self.assertTrue(isinstance(second[1], PLUGIN_CLASS)) 141 142 def test_parse_authenticators_with_plugins(self): 143 from repoze.who.interfaces import IAuthenticator 144 PLUGIN_CLASS = self._getDummyPluginClass(IAuthenticator) 145 config = self._makeOne() 146 config.parse(AUTHENTICATORS_WITH_PLUGINS) 147 authenticators = config.authenticators 148 self.assertEqual(len(authenticators), 2) 149 first, second = authenticators 150 self.assertEqual(first[0], 'foo') 151 self.assertTrue(isinstance(first[1], PLUGIN_CLASS)) 152 self.assertEqual(len(first[1].classifications), 1) 153 self.assertEqual(first[1].classifications[IAuthenticator], 'klass1') 154 self.assertEqual(second[0], 'bar') 155 self.assertTrue(isinstance(second[1], PLUGIN_CLASS)) 156 157 def test_parse_challengers_only(self): 158 from repoze.who.interfaces import IChallenger 159 PLUGIN_CLASS = self._getDummyPluginClass(IChallenger) 160 config = self._makeOne() 161 config.parse(CHALLENGERS_ONLY) 162 challengers = config.challengers 163 self.assertEqual(len(challengers), 2) 164 first, second = challengers 165 self.assertEqual(first[0], 'repoze.who.tests.test_config:DummyPlugin') 166 self.assertTrue(isinstance(first[1], PLUGIN_CLASS)) 167 self.assertEqual(len(first[1].classifications), 1) 168 self.assertEqual(first[1].classifications[IChallenger], 'klass1') 169 self.assertEqual(second[0], 'repoze.who.tests.test_config:DummyPlugin') 170 self.assertTrue(isinstance(second[1], PLUGIN_CLASS)) 171 172 def test_parse_challengers_with_plugins(self): 173 from repoze.who.interfaces import IChallenger 174 PLUGIN_CLASS = self._getDummyPluginClass(IChallenger) 175 config = self._makeOne() 176 config.parse(CHALLENGERS_WITH_PLUGINS) 177 challengers = config.challengers 178 self.assertEqual(len(challengers), 2) 179 first, second = challengers 180 self.assertEqual(first[0], 'foo') 181 self.assertTrue(isinstance(first[1], PLUGIN_CLASS)) 182 self.assertEqual(len(first[1].classifications), 1) 183 self.assertEqual(first[1].classifications[IChallenger], 'klass1') 184 self.assertEqual(second[0], 'bar') 185 self.assertTrue(isinstance(second[1], PLUGIN_CLASS)) 186 187 def test_parse_mdproviders_only(self): 188 from repoze.who.interfaces import IMetadataProvider 189 PLUGIN_CLASS = self._getDummyPluginClass(IMetadataProvider) 190 config = self._makeOne() 191 config.parse(MDPROVIDERS_ONLY) 192 mdproviders = config.mdproviders 193 self.assertEqual(len(mdproviders), 2) 194 first, second = mdproviders 195 self.assertEqual(first[0], 'repoze.who.tests.test_config:DummyPlugin') 196 self.assertTrue(isinstance(first[1], PLUGIN_CLASS)) 197 self.assertEqual(len(first[1].classifications), 1) 198 self.assertEqual(first[1].classifications[IMetadataProvider], 'klass1') 199 self.assertEqual(second[0], 'repoze.who.tests.test_config:DummyPlugin') 200 self.assertTrue(isinstance(second[1], PLUGIN_CLASS)) 201 202 def test_parse_mdproviders_with_plugins(self): 203 from repoze.who.interfaces import IMetadataProvider 204 PLUGIN_CLASS = self._getDummyPluginClass(IMetadataProvider) 205 config = self._makeOne() 206 config.parse(MDPROVIDERS_WITH_PLUGINS) 207 mdproviders = config.mdproviders 208 self.assertEqual(len(mdproviders), 2) 209 first, second = mdproviders 210 self.assertEqual(first[0], 'foo') 211 self.assertTrue(isinstance(first[1], PLUGIN_CLASS)) 212 self.assertEqual(len(first[1].classifications), 1) 213 self.assertEqual(first[1].classifications[IMetadataProvider], 'klass1') 214 self.assertEqual(second[0], 'bar') 215 self.assertTrue(isinstance(second[1], PLUGIN_CLASS)) 216 217 def test_parse_make_plugin_names(self): 218 # see http://bugs.repoze.org/issue92 219 config = self._makeOne() 220 config.parse(MAKE_PLUGIN_ARG_NAMES) 221 self.assertEqual(len(config.plugins), 1) 222 foo = config.plugins['foo'] 223 self.assertTrue(isinstance(foo, DummyPlugin)) 224 self.assertEqual(foo.iface, 'iface') 225 self.assertEqual(foo.name, 'name') 226 self.assertEqual(foo.template, '%(template)s') 227 self.assertEqual(foo.template_with_eq, 228 'template_with_eq = %(template_with_eq)s') 229 230class DummyPlugin: 231 def __init__(self, **kw): 232 self.__dict__.update(kw) 233 234PLUGINS_ONLY = """\ 235[plugin:foo] 236use = repoze.who.tests.test_config:DummyPlugin 237 238[plugin:bar] 239use = repoze.who.tests.test_config:DummyPlugin 240credentials = qux 241""" 242 243GENERAL_ONLY = """\ 244[general] 245request_classifier = repoze.who.tests.test_config:DummyPlugin 246challenge_decider = repoze.who.tests.test_config:DummyPlugin 247remote_user_key = ANOTHER_REMOTE_USER 248""" 249 250GENERAL_WITH_PLUGINS = """\ 251[general] 252request_classifier = classifier 253challenge_decider = decider 254 255[plugin:classifier] 256use = repoze.who.tests.test_config:DummyPlugin 257 258[plugin:decider] 259use = repoze.who.tests.test_config:DummyPlugin 260""" 261 262IDENTIFIERS_ONLY = """\ 263[identifiers] 264plugins = 265 repoze.who.tests.test_config:DummyPlugin;klass1 266 repoze.who.tests.test_config:DummyPlugin 267""" 268 269IDENTIFIERS_WITH_PLUGINS = """\ 270[identifiers] 271plugins = 272 foo;klass1 273 bar 274 275[plugin:foo] 276use = repoze.who.tests.test_config:DummyPlugin 277 278[plugin:bar] 279use = repoze.who.tests.test_config:DummyPlugin 280""" 281 282AUTHENTICATORS_ONLY = """\ 283[authenticators] 284plugins = 285 repoze.who.tests.test_config:DummyPlugin;klass1 286 repoze.who.tests.test_config:DummyPlugin 287""" 288 289AUTHENTICATORS_WITH_PLUGINS = """\ 290[authenticators] 291plugins = 292 foo;klass1 293 bar 294 295[plugin:foo] 296use = repoze.who.tests.test_config:DummyPlugin 297 298[plugin:bar] 299use = repoze.who.tests.test_config:DummyPlugin 300""" 301 302CHALLENGERS_ONLY = """\ 303[challengers] 304plugins = 305 repoze.who.tests.test_config:DummyPlugin;klass1 306 repoze.who.tests.test_config:DummyPlugin 307""" 308 309CHALLENGERS_WITH_PLUGINS = """\ 310[challengers] 311plugins = 312 foo;klass1 313 bar 314 315[plugin:foo] 316use = repoze.who.tests.test_config:DummyPlugin 317 318[plugin:bar] 319use = repoze.who.tests.test_config:DummyPlugin 320""" 321 322MDPROVIDERS_ONLY = """\ 323[mdproviders] 324plugins = 325 repoze.who.tests.test_config:DummyPlugin;klass1 326 repoze.who.tests.test_config:DummyPlugin 327""" 328 329MDPROVIDERS_WITH_PLUGINS = """\ 330[mdproviders] 331plugins = 332 foo;klass1 333 bar 334 335[plugin:foo] 336use = repoze.who.tests.test_config:DummyPlugin 337 338[plugin:bar] 339use = repoze.who.tests.test_config:DummyPlugin 340""" 341 342MAKE_PLUGIN_ARG_NAMES = """\ 343[plugin:foo] 344use = repoze.who.tests.test_config:DummyPlugin 345name = name 346iface = iface 347template = %%(template)s 348template_with_eq = template_with_eq = %%(template_with_eq)s 349""" 350 351class TestConfigMiddleware(unittest.TestCase): 352 _tempdir = None 353 354 def setUp(self): 355 pass 356 357 def tearDown(self): 358 if self._tempdir is not None: 359 import shutil 360 shutil.rmtree(self._tempdir) 361 362 def _getFactory(self): 363 from repoze.who.config import make_middleware_with_config 364 return make_middleware_with_config 365 366 def _getTempfile(self, text): 367 import os 368 import tempfile 369 tempdir = self._tempdir = tempfile.mkdtemp() 370 path = os.path.join(tempdir, 'who.ini') 371 config = open(path, 'w') 372 config.write(text) 373 config.flush() 374 config.close() 375 return path 376 377 def test_sample_config(self): 378 import logging 379 app = DummyApp() 380 factory = self._getFactory() 381 path = self._getTempfile(SAMPLE_CONFIG) 382 global_conf = {'here': '/'} 383 middleware = factory(app, global_conf, config_file=path, 384 log_file='STDOUT', log_level='debug') 385 api_factory = middleware.api_factory 386 self.assertEqual(len(api_factory.identifiers), 2) 387 self.assertEqual(len(api_factory.authenticators), 1) 388 self.assertEqual(len(api_factory.challengers), 2) 389 self.assertEqual(len(api_factory.mdproviders), 0) 390 self.assertTrue(middleware.logger, middleware.logger) 391 self.assertEqual(middleware.logger.getEffectiveLevel(), logging.DEBUG) 392 393 def test_sample_config_no_log_level(self): 394 import logging 395 app = DummyApp() 396 factory = self._getFactory() 397 path = self._getTempfile(SAMPLE_CONFIG) 398 global_conf = {'here': '/'} 399 middleware = factory(app, global_conf, config_file=path, 400 log_file='STDOUT') 401 self.assertEqual(middleware.logger.getEffectiveLevel(), logging.INFO) 402 403 def test_sample_config_w_log_file(self): 404 import logging 405 import os 406 app = DummyApp() 407 factory = self._getFactory() 408 path = self._getTempfile(SAMPLE_CONFIG) 409 logfile = os.path.join(self._tempdir, 'who.log') 410 global_conf = {'here': '/'} 411 middleware = factory(app, global_conf, config_file=path, 412 log_file=logfile, log_level=logging.WARN) 413 self.assertEqual(middleware.logger.getEffectiveLevel(), logging.WARN) 414 handlers = middleware.logger.handlers 415 self.assertEqual(len(handlers), 1) 416 self.assertTrue(isinstance(handlers[0], logging.StreamHandler)) 417 self.assertEqual(handlers[0].stream.name, logfile) 418 logging.shutdown() 419 handlers[0].stream.close() 420 421 def test_sample_config_wo_log_file(self): 422 import logging 423 from repoze.who.config import NullHandler 424 app = DummyApp() 425 factory = self._getFactory() 426 path = self._getTempfile(SAMPLE_CONFIG) 427 global_conf = {'here': '/'} 428 middleware = factory(app, global_conf, config_file=path) 429 self.assertEqual(middleware.logger.getEffectiveLevel(), logging.INFO) 430 handlers = middleware.logger.handlers 431 self.assertEqual(len(handlers), 1) 432 self.assertTrue(isinstance(handlers[0], NullHandler)) 433 logging.shutdown() 434 435class NullHandlerTests(unittest.TestCase): 436 437 def _getTargetClass(self): 438 from repoze.who.config import NullHandler 439 return NullHandler 440 441 def _makeOne(self): 442 return self._getTargetClass()() 443 444 def test_inheritance(self): 445 import logging 446 handler = self._makeOne() 447 self.assertTrue(isinstance(handler, logging.Handler)) 448 449 def test_emit_doesnt_raise_NotImplementedError(self): 450 handler = self._makeOne() 451 handler.emit(object()) 452 453class Test_make_api_factory_with_config(unittest.TestCase): 454 _tempdir = None 455 456 def setUp(self): 457 pass 458 459 def tearDown(self): 460 if self._tempdir is not None: 461 import shutil 462 shutil.rmtree(self._tempdir) 463 464 def _getFactory(self): 465 from repoze.who.config import make_api_factory_with_config 466 return make_api_factory_with_config 467 468 def _getTempfile(self, text): 469 import os 470 import tempfile 471 tempdir = self._tempdir = tempfile.mkdtemp() 472 path = os.path.join(tempdir, 'who.ini') 473 config = open(path, 'w') 474 config.write(text) 475 config.flush() 476 config.close() 477 return path 478 479 def test_bad_config_filename(self): 480 import warnings 481 with warnings.catch_warnings(record=True) as warned: 482 factory = self._getFactory() 483 path = '/nonesuch/file/should/exist' 484 global_conf = {'here': '/'} 485 api_factory = factory(global_conf, config_file=path) 486 self.assertEqual(len(api_factory.identifiers), 0) 487 self.assertEqual(len(api_factory.authenticators), 0) 488 self.assertEqual(len(api_factory.challengers), 0) 489 self.assertEqual(len(api_factory.mdproviders), 0) 490 self.assertEqual(api_factory.remote_user_key, 'REMOTE_USER') 491 self.assertTrue(api_factory.logger is None) 492 self.assertTrue(warned) 493 494 def test_bad_config_content(self): 495 import warnings 496 with warnings.catch_warnings(record=True) as warned: 497 factory = self._getFactory() 498 path = self._getTempfile('this is not an INI file') 499 global_conf = {'here': '/'} 500 api_factory = factory(global_conf, config_file=path) 501 self.assertEqual(len(api_factory.identifiers), 0) 502 self.assertEqual(len(api_factory.authenticators), 0) 503 self.assertEqual(len(api_factory.challengers), 0) 504 self.assertEqual(len(api_factory.mdproviders), 0) 505 self.assertEqual(api_factory.remote_user_key, 'REMOTE_USER') 506 self.assertTrue(api_factory.logger is None) 507 self.assertTrue(warned) 508 509 def test_sample_config_no_logger(self): 510 factory = self._getFactory() 511 path = self._getTempfile(SAMPLE_CONFIG) 512 global_conf = {'here': '/'} 513 api_factory = factory(global_conf, config_file=path) 514 self.assertEqual(len(api_factory.identifiers), 2) 515 self.assertEqual(len(api_factory.authenticators), 1) 516 self.assertEqual(len(api_factory.challengers), 2) 517 self.assertEqual(len(api_factory.mdproviders), 0) 518 self.assertEqual(api_factory.remote_user_key, 'REMOTE_USER') 519 self.assertTrue(api_factory.logger is None) 520 521 def test_sample_config_w_remote_user_key(self): 522 factory = self._getFactory() 523 path = self._getTempfile(SAMPLE_CONFIG) 524 global_conf = {'here': '/'} 525 api_factory = factory(global_conf, config_file=path, 526 remote_user_key = 'X-OTHER-USER') 527 self.assertEqual(len(api_factory.identifiers), 2) 528 self.assertEqual(len(api_factory.authenticators), 1) 529 self.assertEqual(len(api_factory.challengers), 2) 530 self.assertEqual(len(api_factory.mdproviders), 0) 531 self.assertEqual(api_factory.remote_user_key, 'X-OTHER-USER') 532 533 def test_sample_config_w_logger(self): 534 factory = self._getFactory() 535 path = self._getTempfile(SAMPLE_CONFIG) 536 global_conf = {'here': '/'} 537 logger = object() 538 api_factory = factory(global_conf, config_file=path, logger=logger) 539 self.assertEqual(len(api_factory.identifiers), 2) 540 self.assertEqual(len(api_factory.authenticators), 1) 541 self.assertEqual(len(api_factory.challengers), 2) 542 self.assertEqual(len(api_factory.mdproviders), 0) 543 self.assertTrue(api_factory.logger is logger) 544 545SAMPLE_CONFIG = """\ 546[plugin:redirector] 547use = repoze.who.plugins.redirector:make_plugin 548login_url = /login.html 549 550[plugin:auth_tkt] 551use = repoze.who.plugins.auth_tkt:make_plugin 552secret = s33kr1t 553cookie_name = oatmeal 554secure = False 555include_ip = True 556 557[plugin:basicauth] 558use = repoze.who.plugins.basicauth:make_plugin 559realm = 'sample' 560 561[plugin:htpasswd] 562use = repoze.who.plugins.htpasswd:make_plugin 563filename = %(here)s/etc/passwd 564check_fn = repoze.who.plugins.htpasswd:crypt_check 565 566[general] 567request_classifier = repoze.who.classifiers:default_request_classifier 568challenge_decider = repoze.who.classifiers:default_challenge_decider 569 570[identifiers] 571plugins = 572 auth_tkt 573 basicauth 574 575[authenticators] 576plugins = htpasswd 577 578[challengers] 579plugins = 580 redirector;browser 581 basicauth 582 583[mdproviders] 584plugins = 585 586""" 587 588class DummyApp: 589 environ = None 590