Tres Seaver
2016-05-28 49809a763d96e8e48314c7cd6f6412a5cc6fb769
commit | author | age
419946 1 """ Configuration parser
TS 2 """
e8279c 3 import logging
419946 4 from pkg_resources import EntryPoint
e8279c 5 import sys
03fba8 6 import warnings
419946 7
a349a2 8 from repoze.who.api import APIFactory
515c69 9 from repoze.who.interfaces import IAuthenticator
419946 10 from repoze.who.interfaces import IChallengeDecider
515c69 11 from repoze.who.interfaces import IChallenger
TS 12 from repoze.who.interfaces import IIdentifier
13 from repoze.who.interfaces import IMetadataProvider
14 from repoze.who.interfaces import IPlugin
419946 15 from repoze.who.interfaces import IRequestClassifier
515c69 16 from repoze.who.middleware import PluggableAuthenticationMiddleware
adef05 17 from repoze.who._compat import StringIO
6866db 18 from repoze.who._compat import ConfigParser
adef05 19 from repoze.who._compat import ParsingError
419946 20
TS 21 def _resolve(name):
22     if name:
49809a 23         return EntryPoint.parse('x=%s' % name).resolve()
419946 24
TS 25 class WhoConfig:
2e3cf3 26     def __init__(self, here):
TS 27         self.here = here
419946 28         self.request_classifier = None
TS 29         self.challenge_decider = None
30         self.plugins = {}
31         self.identifiers = []
32         self.authenticators = []
33         self.challengers = []
34         self.mdproviders = []
b95a59 35         self.remote_user_key = 'REMOTE_USER'
419946 36
21a9c5 37     def _makePlugin(self, name, iface, options=None):
CM 38         if options is None:
39             options = {}
515c69 40         obj = _resolve(name)
TS 41         if not iface.providedBy(obj):
21a9c5 42             obj = obj(**options)
419946 43         return obj
TS 44
515c69 45     def _getPlugin(self, name, iface):
TS 46         obj = self.plugins.get(name)
47         if obj is None:
48             obj = self._makePlugin(name, iface)
49         return obj
50
51     def _parsePluginSequence(self, attr, proptext, iface):
419946 52         lines = proptext.split()
TS 53         for line in lines:
515c69 54
419946 55             if ';' in line:
TS 56                 plugin_name, classifier = line.split(';')
57             else:
58                 plugin_name = line
59                 classifier = None
515c69 60
TS 61             plugin = self._getPlugin(plugin_name, iface)
62
63             if classifier is not None:
64                 classifications = getattr(plugin, 'classifications', None)
65                 if classifications is None:
66                     classifications = plugin.classifications = {}
67                 classifications[iface] = classifier
68
69             attr.append((plugin_name, plugin))
419946 70
TS 71     def parse(self, text):
72         if getattr(text, 'readline', None) is None:
73             text = StringIO(text)
6866db 74         cp = ConfigParser(defaults={'here': self.here})
5bdad5 75         try:
TS 76             cp.read_file(text)
77         except AttributeError: #pragma NO COVER Python < 3.0
78             cp.readfp(text)
419946 79
TS 80         for s_id in [x for x in cp.sections() if x.startswith('plugin:')]:
81             plugin_id = s_id[len('plugin:'):]
82             options = dict(cp.items(s_id))
83             if 'use' in options:
515c69 84                 name = options.pop('use')
2e3cf3 85                 del options['here']
21a9c5 86                 obj = self._makePlugin(name, IPlugin, options)
419946 87                 self.plugins[plugin_id] = obj
TS 88
89         if 'general' in cp.sections():
90             general = dict(cp.items('general'))
91
92             rc = general.get('request_classifier')
515c69 93             if rc is not None:
TS 94                 rc = self._getPlugin(rc, IRequestClassifier)
95             self.request_classifier = rc
419946 96
TS 97             cd = general.get('challenge_decider')
515c69 98             if cd is not None:
TS 99                 cd = self._getPlugin(cd, IChallengeDecider)
100             self.challenge_decider = cd
b95a59 101
CM 102             ru = general.get('remote_user_key')
103             if ru is not None:
104                 self.remote_user_key = ru
419946 105
TS 106         if 'identifiers' in cp.sections():
107             identifiers = dict(cp.items('identifiers'))
108             self._parsePluginSequence(self.identifiers,
515c69 109                                       identifiers['plugins'],
TS 110                                       IIdentifier,
111                                      )
419946 112
TS 113         if 'authenticators' in cp.sections():
114             authenticators = dict(cp.items('authenticators'))
115             self._parsePluginSequence(self.authenticators,
515c69 116                                       authenticators['plugins'],
TS 117                                       IAuthenticator,
118                                      )
419946 119
TS 120         if 'challengers' in cp.sections():
121             challengers = dict(cp.items('challengers'))
122             self._parsePluginSequence(self.challengers,
515c69 123                                       challengers['plugins'],
TS 124                                       IChallenger,
125                                      )
419946 126
TS 127         if 'mdproviders' in cp.sections():
128             mdproviders = dict(cp.items('mdproviders'))
129             self._parsePluginSequence(self.mdproviders,
515c69 130                                       mdproviders['plugins'],
TS 131                                       IMetadataProvider,
132                                      )
133
134
d6b53f 135 class NullHandler(logging.Handler):
TS 136     def emit(self, record):
137         pass
138
139
e8279c 140 _LEVELS = {'debug': logging.DEBUG,
TS 141            'info': logging.INFO,
142            'warning': logging.WARNING,
143            'error': logging.ERROR,
144           }
145
a349a2 146 def make_api_factory_with_config(global_conf,
TS 147                                  config_file,
148                                  remote_user_key = 'REMOTE_USER',
149                                  logger=None,
150                                 ):
03fba8 151     identifiers = authenticators = challengers = mdproviders = ()
TS 152     request_classifier = None
153     challenge_decider = None
a349a2 154     parser = WhoConfig(global_conf['here'])
03fba8 155     try:
TS 156         opened = open(config_file)
157     except IOError:
158         warnings.warn('Non-existent who config file: %s' % config_file,
159                       stacklevel=2)
160     else:
161         try:
5bdad5 162             try:
TS 163                 parser.parse(opened)
164             except ParsingError:
165                 warnings.warn('Invalid who config file: %s' % config_file,
166                             stacklevel=2)
167             else:
168                 identifiers = parser.identifiers
169                 authenticators = parser.authenticators
170                 challengers = parser.challengers
171                 mdproviders = parser.mdproviders
172                 request_classifier = parser.request_classifier
173                 challenge_decider = parser.challenge_decider
174         finally:
175             opened.close()
03fba8 176
TS 177     return APIFactory(identifiers,
178                       authenticators,
179                       challengers,
180                       mdproviders,
181                       request_classifier,
182                       challenge_decider,
a349a2 183                       remote_user_key,
TS 184                       logger,
185                      )
186
e8279c 187 def make_middleware_with_config(app, global_conf, config_file,
TS 188                                 log_file=None, log_level=None):
2e3cf3 189     parser = WhoConfig(global_conf['here'])
5bdad5 190     with open(config_file) as f:
TS 191         parser.parse(f)
e8279c 192     log_stream = None
TS 193
e05ebf 194     if log_level is None:
TS 195         log_level = logging.INFO
196     elif not isinstance(log_level, int):
197         log_level = _LEVELS[log_level.lower()]
198
e8279c 199     if log_file is not None:
3617e0 200         if log_file.lower() == 'stdout':
e8279c 201             log_stream = sys.stdout
TS 202         else:
203             log_stream = open(log_file, 'wb')
d6b53f 204     else:
TS 205         log_stream = logging.getLogger('repoze.who')
206         log_stream.addHandler(NullHandler())
e05ebf 207         log_stream.setLevel(log_level or 0)
e8279c 208
515c69 209     return PluggableAuthenticationMiddleware(
TS 210                 app,
211                 parser.identifiers,
212                 parser.authenticators,
213                 parser.challengers,
214                 parser.mdproviders,
215                 parser.request_classifier,
216                 parser.challenge_decider,
e8279c 217                 log_stream,
3fee5b 218                 log_level,
b95a59 219                 parser.remote_user_key,
515c69 220            )