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 |
) |