Michael Merickel
2018-10-15 2b024920847481592b1a13d4006d2a9fa8881d72
commit | author | age
464973 1 import unittest
fdd1f8 2 from zope.interface import implementer
464973 3
CD 4 from pyramid import testing
488fdd 5 from pyramid.exceptions import ConfigurationError
fdd1f8 6 from pyramid.interfaces import (
MM 7     IResponse,
8     IRequest,
9     )
464973 10
CD 11 class TestDeriveView(unittest.TestCase):
12
13     def setUp(self):
14         self.config = testing.setUp()
313c25 15         self.config.set_default_csrf_options(require_csrf=False)
464973 16
CD 17     def tearDown(self):
18         self.config = None
174bb5 19         testing.tearDown()
464973 20
CD 21     def _makeRequest(self):
22         request = DummyRequest()
23         request.registry = self.config.registry
24         return request
25
26     def _registerLogger(self):
27         from pyramid.interfaces import IDebugLogger
28         logger = DummyLogger()
29         self.config.registry.registerUtility(logger, IDebugLogger)
30         return logger
31
32     def _registerSecurityPolicy(self, permissive):
33         from pyramid.interfaces import IAuthenticationPolicy
34         from pyramid.interfaces import IAuthorizationPolicy
35         policy = DummySecurityPolicy(permissive)
36         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
37         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
38
39     def test_function_returns_non_adaptable(self):
40         def view(request):
41             return None
42         result = self.config.derive_view(view)
43         self.assertFalse(result is view)
44         try:
45             result(None, None)
46         except ValueError as e:
47             self.assertEqual(
48                 e.args[0],
49                 'Could not convert return value of the view callable function '
ecc9d8 50                 'pyramid.tests.test_viewderivers.view into a response '
464973 51                 'object. The value returned was None. You may have forgotten '
CD 52                 'to return a value from the view callable.'
53                 )
54         else: # pragma: no cover
55             raise AssertionError
56
57     def test_function_returns_non_adaptable_dict(self):
58         def view(request):
59             return {'a':1}
60         result = self.config.derive_view(view)
61         self.assertFalse(result is view)
62         try:
63             result(None, None)
64         except ValueError as e:
65             self.assertEqual(
66                 e.args[0],
67                 "Could not convert return value of the view callable function "
ecc9d8 68                 "pyramid.tests.test_viewderivers.view into a response "
464973 69                 "object. The value returned was {'a': 1}. You may have "
CD 70                 "forgotten to define a renderer in the view configuration."
71                 )
72         else: # pragma: no cover
73             raise AssertionError
74
75     def test_instance_returns_non_adaptable(self):
76         class AView(object):
77             def __call__(self, request):
78                 return None
79         view = AView()
80         result = self.config.derive_view(view)
81         self.assertFalse(result is view)
82         try:
83             result(None, None)
84         except ValueError as e:
85             msg = e.args[0]
86             self.assertTrue(msg.startswith(
87                 'Could not convert return value of the view callable object '
ecc9d8 88                 '<pyramid.tests.test_viewderivers.'))
464973 89             self.assertTrue(msg.endswith(
CD 90                 '> into a response object. The value returned was None. You '
91                 'may have forgotten to return a value from the view callable.'))
92         else: # pragma: no cover
93             raise AssertionError
94
95     def test_function_returns_true_Response_no_renderer(self):
96         from pyramid.response import Response
97         r = Response('Hello')
98         def view(request):
99             return r
100         result = self.config.derive_view(view)
101         self.assertFalse(result is view)
102         response = result(None, None)
103         self.assertEqual(response, r)
104
105     def test_function_returns_true_Response_with_renderer(self):
106         from pyramid.response import Response
107         r = Response('Hello')
108         def view(request):
109             return r
110         renderer = object()
111         result = self.config.derive_view(view)
112         self.assertFalse(result is view)
113         response = result(None, None)
114         self.assertEqual(response, r)
115
116     def test_requestonly_default_method_returns_non_adaptable(self):
117         request = DummyRequest()
118         class AView(object):
119             def __init__(self, request):
120                 pass
121             def __call__(self):
122                 return None
123         result = self.config.derive_view(AView)
124         self.assertFalse(result is AView)
125         try:
126             result(None, request)
127         except ValueError as e:
128             self.assertEqual(
129                 e.args[0],
130                 'Could not convert return value of the view callable '
131                 'method __call__ of '
ecc9d8 132                 'class pyramid.tests.test_viewderivers.AView into a '
464973 133                 'response object. The value returned was None. You may have '
CD 134                 'forgotten to return a value from the view callable.'
135                 )
136         else: # pragma: no cover
137             raise AssertionError
138
139     def test_requestonly_nondefault_method_returns_non_adaptable(self):
140         request = DummyRequest()
141         class AView(object):
142             def __init__(self, request):
143                 pass
144             def theviewmethod(self):
145                 return None
96c87b 146         result = self.config.derive_view(AView, attr='theviewmethod')
464973 147         self.assertFalse(result is AView)
CD 148         try:
149             result(None, request)
150         except ValueError as e:
151             self.assertEqual(
152                 e.args[0],
153                 'Could not convert return value of the view callable '
154                 'method theviewmethod of '
ecc9d8 155                 'class pyramid.tests.test_viewderivers.AView into a '
464973 156                 'response object. The value returned was None. You may have '
CD 157                 'forgotten to return a value from the view callable.'
158                 )
159         else: # pragma: no cover
160             raise AssertionError
161
162     def test_requestonly_function(self):
163         response = DummyResponse()
164         def view(request):
165             return response
166         result = self.config.derive_view(view)
167         self.assertFalse(result is view)
168         self.assertEqual(result(None, None), response)
169
170     def test_requestonly_function_with_renderer(self):
171         response = DummyResponse()
172         class moo(object):
173             def render_view(inself, req, resp, view_inst, ctx):
174                 self.assertEqual(req, request)
175                 self.assertEqual(resp, 'OK')
176                 self.assertEqual(view_inst, view)
177                 self.assertEqual(ctx, context)
178                 return response
179             def clone(self):
180                 return self
181         def view(request):
182             return 'OK'
96c87b 183         result = self.config.derive_view(view, renderer=moo())
464973 184         self.assertFalse(result.__wraps__ is view)
CD 185         request = self._makeRequest()
186         context = testing.DummyResource()
187         self.assertEqual(result(context, request), response)
188
189     def test_requestonly_function_with_renderer_request_override(self):
190         def moo(info):
191             def inner(value, system):
192                 self.assertEqual(value, 'OK')
193                 self.assertEqual(system['request'], request)
194                 self.assertEqual(system['context'], context)
195                 return b'moo'
196             return inner
197         def view(request):
198             return 'OK'
199         self.config.add_renderer('moo', moo)
200         result = self.config.derive_view(view, renderer='string')
201         self.assertFalse(result is view)
202         request = self._makeRequest()
203         request.override_renderer = 'moo'
204         context = testing.DummyResource()
205         self.assertEqual(result(context, request).body, b'moo')
206
207     def test_requestonly_function_with_renderer_request_has_view(self):
208         response = DummyResponse()
209         class moo(object):
210             def render_view(inself, req, resp, view_inst, ctx):
211                 self.assertEqual(req, request)
212                 self.assertEqual(resp, 'OK')
213                 self.assertEqual(view_inst, 'view')
214                 self.assertEqual(ctx, context)
215                 return response
216             def clone(self):
217                 return self
218         def view(request):
219             return 'OK'
220         result = self.config.derive_view(view, renderer=moo())
221         self.assertFalse(result.__wraps__ is view)
222         request = self._makeRequest()
223         request.__view__ = 'view'
224         context = testing.DummyResource()
225         r = result(context, request)
226         self.assertEqual(r, response)
227         self.assertFalse(hasattr(request, '__view__'))
228
229     def test_class_without_attr(self):
230         response = DummyResponse()
231         class View(object):
232             def __init__(self, request):
233                 pass
234             def __call__(self):
235                 return response
236         result = self.config.derive_view(View)
237         request = self._makeRequest()
238         self.assertEqual(result(None, request), response)
239         self.assertEqual(request.__view__.__class__, View)
240
241     def test_class_with_attr(self):
242         response = DummyResponse()
243         class View(object):
244             def __init__(self, request):
245                 pass
246             def another(self):
247                 return response
248         result = self.config.derive_view(View, attr='another')
249         request = self._makeRequest()
250         self.assertEqual(result(None, request), response)
251         self.assertEqual(request.__view__.__class__, View)
252
253     def test_as_function_context_and_request(self):
254         def view(context, request):
255             return 'OK'
256         result = self.config.derive_view(view)
257         self.assertTrue(result.__wraps__ is view)
258         self.assertFalse(hasattr(result, '__call_permissive__'))
259         self.assertEqual(view(None, None), 'OK')
260
261     def test_as_function_requestonly(self):
262         response = DummyResponse()
263         def view(request):
264             return response
265         result = self.config.derive_view(view)
266         self.assertFalse(result is view)
267         self.assertEqual(view.__module__, result.__module__)
268         self.assertEqual(view.__doc__, result.__doc__)
269         self.assertEqual(view.__name__, result.__name__)
270         self.assertFalse(hasattr(result, '__call_permissive__'))
271         self.assertEqual(result(None, None), response)
272
273     def test_as_newstyle_class_context_and_request(self):
274         response = DummyResponse()
275         class view(object):
276             def __init__(self, context, request):
277                 pass
278             def __call__(self):
279                 return response
280         result = self.config.derive_view(view)
281         self.assertFalse(result is view)
282         self.assertEqual(view.__module__, result.__module__)
283         self.assertEqual(view.__doc__, result.__doc__)
284         self.assertEqual(view.__name__, result.__name__)
285         self.assertFalse(hasattr(result, '__call_permissive__'))
286         request = self._makeRequest()
287         self.assertEqual(result(None, request), response)
288         self.assertEqual(request.__view__.__class__, view)
289
290     def test_as_newstyle_class_requestonly(self):
291         response = DummyResponse()
292         class view(object):
293             def __init__(self, context, request):
294                 pass
295             def __call__(self):
296                 return response
297         result = self.config.derive_view(view)
298         self.assertFalse(result is view)
299         self.assertEqual(view.__module__, result.__module__)
300         self.assertEqual(view.__doc__, result.__doc__)
301         self.assertEqual(view.__name__, result.__name__)
302         self.assertFalse(hasattr(result, '__call_permissive__'))
303         request = self._makeRequest()
304         self.assertEqual(result(None, request), response)
305         self.assertEqual(request.__view__.__class__, view)
306
307     def test_as_oldstyle_class_context_and_request(self):
308         response = DummyResponse()
309         class view:
310             def __init__(self, context, request):
311                 pass
312             def __call__(self):
313                 return response
314         result = self.config.derive_view(view)
315         self.assertFalse(result is view)
316         self.assertEqual(view.__module__, result.__module__)
317         self.assertEqual(view.__doc__, result.__doc__)
318         self.assertEqual(view.__name__, result.__name__)
319         self.assertFalse(hasattr(result, '__call_permissive__'))
320         request = self._makeRequest()
321         self.assertEqual(result(None, request), response)
322         self.assertEqual(request.__view__.__class__, view)
323
324     def test_as_oldstyle_class_requestonly(self):
325         response = DummyResponse()
326         class view:
327             def __init__(self, context, request):
328                 pass
329             def __call__(self):
330                 return response
331         result = self.config.derive_view(view)
332         self.assertFalse(result is view)
333         self.assertEqual(view.__module__, result.__module__)
334         self.assertEqual(view.__doc__, result.__doc__)
335         self.assertEqual(view.__name__, result.__name__)
336         self.assertFalse(hasattr(result, '__call_permissive__'))
337         request = self._makeRequest()
338         self.assertEqual(result(None, request), response)
339         self.assertEqual(request.__view__.__class__, view)
340
341     def test_as_instance_context_and_request(self):
342         response = DummyResponse()
343         class View:
344             def __call__(self, context, request):
345                 return response
346         view = View()
347         result = self.config.derive_view(view)
348         self.assertTrue(result.__wraps__ is view)
349         self.assertFalse(hasattr(result, '__call_permissive__'))
350         self.assertEqual(result(None, None), response)
351
352     def test_as_instance_requestonly(self):
353         response = DummyResponse()
354         class View:
355             def __call__(self, request):
356                 return response
357         view = View()
358         result = self.config.derive_view(view)
359         self.assertFalse(result is view)
360         self.assertEqual(view.__module__, result.__module__)
361         self.assertEqual(view.__doc__, result.__doc__)
ecc9d8 362         self.assertTrue('test_viewderivers' in result.__name__)
464973 363         self.assertFalse(hasattr(result, '__call_permissive__'))
CD 364         self.assertEqual(result(None, None), response)
365
366     def test_with_debug_authorization_no_authpol(self):
367         response = DummyResponse()
368         view = lambda *arg: response
369         self.config.registry.settings = dict(
370             debug_authorization=True, reload_templates=True)
371         logger = self._registerLogger()
372         result = self.config._derive_view(view, permission='view')
373         self.assertEqual(view.__module__, result.__module__)
374         self.assertEqual(view.__doc__, result.__doc__)
375         self.assertEqual(view.__name__, result.__name__)
376         self.assertFalse(hasattr(result, '__call_permissive__'))
377         request = self._makeRequest()
378         request.view_name = 'view_name'
379         request.url = 'url'
380         self.assertEqual(result(None, request), response)
381         self.assertEqual(len(logger.messages), 1)
382         self.assertEqual(logger.messages[0],
383                          "debug_authorization of url url (view name "
384                          "'view_name' against context None): Allowed "
385                          "(no authorization policy in use)")
386
387     def test_with_debug_authorization_authn_policy_no_authz_policy(self):
388         response = DummyResponse()
389         view = lambda *arg: response
390         self.config.registry.settings = dict(debug_authorization=True)
391         from pyramid.interfaces import IAuthenticationPolicy
392         policy = DummySecurityPolicy(False)
393         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
394         logger = self._registerLogger()
395         result = self.config._derive_view(view, permission='view')
396         self.assertEqual(view.__module__, result.__module__)
397         self.assertEqual(view.__doc__, result.__doc__)
398         self.assertEqual(view.__name__, result.__name__)
399         self.assertFalse(hasattr(result, '__call_permissive__'))
400         request = self._makeRequest()
401         request.view_name = 'view_name'
402         request.url = 'url'
403         self.assertEqual(result(None, request), response)
404         self.assertEqual(len(logger.messages), 1)
405         self.assertEqual(logger.messages[0],
406                          "debug_authorization of url url (view name "
407                          "'view_name' against context None): Allowed "
408                          "(no authorization policy in use)")
409
410     def test_with_debug_authorization_authz_policy_no_authn_policy(self):
411         response = DummyResponse()
412         view = lambda *arg: response
413         self.config.registry.settings = dict(debug_authorization=True)
414         from pyramid.interfaces import IAuthorizationPolicy
415         policy = DummySecurityPolicy(False)
416         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
417         logger = self._registerLogger()
418         result = self.config._derive_view(view, permission='view')
419         self.assertEqual(view.__module__, result.__module__)
420         self.assertEqual(view.__doc__, result.__doc__)
421         self.assertEqual(view.__name__, result.__name__)
422         self.assertFalse(hasattr(result, '__call_permissive__'))
423         request = self._makeRequest()
424         request.view_name = 'view_name'
425         request.url = 'url'
426         self.assertEqual(result(None, request), response)
427         self.assertEqual(len(logger.messages), 1)
428         self.assertEqual(logger.messages[0],
429                          "debug_authorization of url url (view name "
430                          "'view_name' against context None): Allowed "
431                          "(no authorization policy in use)")
432
433     def test_with_debug_authorization_no_permission(self):
434         response = DummyResponse()
435         view = lambda *arg: response
436         self.config.registry.settings = dict(
437             debug_authorization=True, reload_templates=True)
438         self._registerSecurityPolicy(True)
439         logger = self._registerLogger()
440         result = self.config._derive_view(view)
441         self.assertEqual(view.__module__, result.__module__)
442         self.assertEqual(view.__doc__, result.__doc__)
443         self.assertEqual(view.__name__, result.__name__)
444         self.assertFalse(hasattr(result, '__call_permissive__'))
445         request = self._makeRequest()
446         request.view_name = 'view_name'
447         request.url = 'url'
448         self.assertEqual(result(None, request), response)
449         self.assertEqual(len(logger.messages), 1)
450         self.assertEqual(logger.messages[0],
451                          "debug_authorization of url url (view name "
452                          "'view_name' against context None): Allowed ("
453                          "no permission registered)")
454
455     def test_debug_auth_permission_authpol_permitted(self):
456         response = DummyResponse()
457         view = lambda *arg: response
458         self.config.registry.settings = dict(
459             debug_authorization=True, reload_templates=True)
460         logger = self._registerLogger()
461         self._registerSecurityPolicy(True)
462         result = self.config._derive_view(view, permission='view')
463         self.assertEqual(view.__module__, result.__module__)
464         self.assertEqual(view.__doc__, result.__doc__)
465         self.assertEqual(view.__name__, result.__name__)
466         self.assertEqual(result.__call_permissive__.__wraps__, view)
467         request = self._makeRequest()
468         request.view_name = 'view_name'
469         request.url = 'url'
470         self.assertEqual(result(None, request), response)
471         self.assertEqual(len(logger.messages), 1)
472         self.assertEqual(logger.messages[0],
473                          "debug_authorization of url url (view name "
474                          "'view_name' against context None): True")
475
476     def test_debug_auth_permission_authpol_permitted_no_request(self):
477         response = DummyResponse()
478         view = lambda *arg: response
479         self.config.registry.settings = dict(
480             debug_authorization=True, reload_templates=True)
481         logger = self._registerLogger()
482         self._registerSecurityPolicy(True)
483         result = self.config._derive_view(view, permission='view')
484         self.assertEqual(view.__module__, result.__module__)
485         self.assertEqual(view.__doc__, result.__doc__)
486         self.assertEqual(view.__name__, result.__name__)
487         self.assertEqual(result.__call_permissive__.__wraps__, view)
488         self.assertEqual(result(None, None), response)
489         self.assertEqual(len(logger.messages), 1)
490         self.assertEqual(logger.messages[0],
491                          "debug_authorization of url None (view name "
492                          "None against context None): True")
493
494     def test_debug_auth_permission_authpol_denied(self):
495         from pyramid.httpexceptions import HTTPForbidden
496         response = DummyResponse()
497         view = lambda *arg: response
498         self.config.registry.settings = dict(
499             debug_authorization=True, reload_templates=True)
500         logger = self._registerLogger()
501         self._registerSecurityPolicy(False)
502         result = self.config._derive_view(view, permission='view')
503         self.assertEqual(view.__module__, result.__module__)
504         self.assertEqual(view.__doc__, result.__doc__)
505         self.assertEqual(view.__name__, result.__name__)
506         self.assertEqual(result.__call_permissive__.__wraps__, view)
507         request = self._makeRequest()
508         request.view_name = 'view_name'
509         request.url = 'url'
510         self.assertRaises(HTTPForbidden, result, None, request)
511         self.assertEqual(len(logger.messages), 1)
512         self.assertEqual(logger.messages[0],
513                          "debug_authorization of url url (view name "
514                          "'view_name' against context None): False")
515
516     def test_debug_auth_permission_authpol_denied2(self):
517         view = lambda *arg: 'OK'
518         self.config.registry.settings = dict(
519             debug_authorization=True, reload_templates=True)
520         self._registerLogger()
521         self._registerSecurityPolicy(False)
522         result = self.config._derive_view(view, permission='view')
523         self.assertEqual(view.__module__, result.__module__)
524         self.assertEqual(view.__doc__, result.__doc__)
525         self.assertEqual(view.__name__, result.__name__)
526         request = self._makeRequest()
527         request.view_name = 'view_name'
528         request.url = 'url'
529         permitted = result.__permitted__(None, None)
530         self.assertEqual(permitted, False)
531
532     def test_debug_auth_permission_authpol_overridden(self):
533         from pyramid.security import NO_PERMISSION_REQUIRED
534         response = DummyResponse()
535         view = lambda *arg: response
536         self.config.registry.settings = dict(
537             debug_authorization=True, reload_templates=True)
538         logger = self._registerLogger()
539         self._registerSecurityPolicy(False)
540         result = self.config._derive_view(view, permission=NO_PERMISSION_REQUIRED)
541         self.assertEqual(view.__module__, result.__module__)
542         self.assertEqual(view.__doc__, result.__doc__)
543         self.assertEqual(view.__name__, result.__name__)
544         self.assertFalse(hasattr(result, '__call_permissive__'))
545         request = self._makeRequest()
546         request.view_name = 'view_name'
547         request.url = 'url'
548         self.assertEqual(result(None, request), response)
549         self.assertEqual(len(logger.messages), 1)
550         self.assertEqual(logger.messages[0],
551                          "debug_authorization of url url (view name "
552                          "'view_name' against context None): "
553                          "Allowed (NO_PERMISSION_REQUIRED)")
554
e8c66a 555     def test_debug_auth_permission_authpol_permitted_excview(self):
MM 556         response = DummyResponse()
557         view = lambda *arg: response
558         self.config.registry.settings = dict(
559             debug_authorization=True, reload_templates=True)
560         logger = self._registerLogger()
561         self._registerSecurityPolicy(True)
562         result = self.config._derive_view(
563             view, context=Exception, permission='view')
564         self.assertEqual(view.__module__, result.__module__)
565         self.assertEqual(view.__doc__, result.__doc__)
566         self.assertEqual(view.__name__, result.__name__)
567         self.assertEqual(result.__call_permissive__.__wraps__, view)
568         request = self._makeRequest()
569         request.view_name = 'view_name'
570         request.url = 'url'
571         self.assertEqual(result(Exception(), request), response)
572         self.assertEqual(len(logger.messages), 1)
573         self.assertEqual(logger.messages[0],
574                          "debug_authorization of url url (view name "
575                          "'view_name' against context Exception()): True")
576
464973 577     def test_secured_view_authn_policy_no_authz_policy(self):
CD 578         response = DummyResponse()
579         view = lambda *arg: response
580         self.config.registry.settings = {}
581         from pyramid.interfaces import IAuthenticationPolicy
582         policy = DummySecurityPolicy(False)
583         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
584         result = self.config._derive_view(view, permission='view')
585         self.assertEqual(view.__module__, result.__module__)
586         self.assertEqual(view.__doc__, result.__doc__)
587         self.assertEqual(view.__name__, result.__name__)
588         self.assertFalse(hasattr(result, '__call_permissive__'))
589         request = self._makeRequest()
590         request.view_name = 'view_name'
591         request.url = 'url'
592         self.assertEqual(result(None, request), response)
593
594     def test_secured_view_authz_policy_no_authn_policy(self):
595         response = DummyResponse()
596         view = lambda *arg: response
597         self.config.registry.settings = {}
598         from pyramid.interfaces import IAuthorizationPolicy
599         policy = DummySecurityPolicy(False)
600         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
601         result = self.config._derive_view(view, permission='view')
602         self.assertEqual(view.__module__, result.__module__)
603         self.assertEqual(view.__doc__, result.__doc__)
604         self.assertEqual(view.__name__, result.__name__)
605         self.assertFalse(hasattr(result, '__call_permissive__'))
606         request = self._makeRequest()
607         request.view_name = 'view_name'
608         request.url = 'url'
609         self.assertEqual(result(None, request), response)
610
611     def test_secured_view_raises_forbidden_no_name(self):
612         from pyramid.interfaces import IAuthenticationPolicy
613         from pyramid.interfaces import IAuthorizationPolicy
614         from pyramid.httpexceptions import HTTPForbidden
615         response = DummyResponse()
616         view = lambda *arg: response
617         self.config.registry.settings = {}
618         policy = DummySecurityPolicy(False)
619         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
620         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
621         result = self.config._derive_view(view, permission='view')
622         request = self._makeRequest()
623         request.view_name = 'view_name'
624         request.url = 'url'
625         try:
626             result(None, request)
627         except HTTPForbidden as e:
628             self.assertEqual(e.message,
629                              'Unauthorized: <lambda> failed permission check')
630         else: # pragma: no cover
631             raise AssertionError
632
633     def test_secured_view_raises_forbidden_with_name(self):
634         from pyramid.interfaces import IAuthenticationPolicy
635         from pyramid.interfaces import IAuthorizationPolicy
636         from pyramid.httpexceptions import HTTPForbidden
637         def myview(request): pass
638         self.config.registry.settings = {}
639         policy = DummySecurityPolicy(False)
640         self.config.registry.registerUtility(policy, IAuthenticationPolicy)
641         self.config.registry.registerUtility(policy, IAuthorizationPolicy)
642         result = self.config._derive_view(myview, permission='view')
643         request = self._makeRequest()
644         request.view_name = 'view_name'
645         request.url = 'url'
646         try:
647             result(None, request)
648         except HTTPForbidden as e:
649             self.assertEqual(e.message,
650                              'Unauthorized: myview failed permission check')
651         else: # pragma: no cover
652             raise AssertionError
653
2160ce 654     def test_secured_view_skipped_by_default_on_exception_view(self):
MM 655         from pyramid.request import Request
656         from pyramid.security import NO_PERMISSION_REQUIRED
657         def view(request):
658             raise ValueError
659         def excview(request):
660             return 'hello'
661         self._registerSecurityPolicy(False)
662         self.config.add_settings({'debug_authorization': True})
663         self.config.set_default_permission('view')
664         self.config.add_view(view, name='foo', permission=NO_PERMISSION_REQUIRED)
665         self.config.add_view(excview, context=ValueError, renderer='string')
666         app = self.config.make_wsgi_app()
667         request = Request.blank('/foo', base_url='http://example.com')
668         request.method = 'POST'
669         response = request.get_response(app)
670         self.assertTrue(b'hello' in response.body)
671
672     def test_secured_view_failed_on_explicit_exception_view(self):
673         from pyramid.httpexceptions import HTTPForbidden
674         from pyramid.request import Request
675         from pyramid.security import NO_PERMISSION_REQUIRED
676         def view(request):
677             raise ValueError
678         def excview(request): pass
679         self._registerSecurityPolicy(False)
680         self.config.add_view(view, name='foo', permission=NO_PERMISSION_REQUIRED)
681         self.config.add_view(excview, context=ValueError, renderer='string',
682                              permission='view')
683         app = self.config.make_wsgi_app()
684         request = Request.blank('/foo', base_url='http://example.com')
685         request.method = 'POST'
686         try:
687             request.get_response(app)
688         except HTTPForbidden:
689             pass
690         else: # pragma: no cover
691             raise AssertionError
692
693     def test_secured_view_passed_on_explicit_exception_view(self):
694         from pyramid.request import Request
695         from pyramid.security import NO_PERMISSION_REQUIRED
696         def view(request):
697             raise ValueError
698         def excview(request):
699             return 'hello'
700         self._registerSecurityPolicy(True)
701         self.config.add_view(view, name='foo', permission=NO_PERMISSION_REQUIRED)
702         self.config.add_view(excview, context=ValueError, renderer='string',
703                              permission='view')
704         app = self.config.make_wsgi_app()
705         request = Request.blank('/foo', base_url='http://example.com')
706         request.method = 'POST'
707         request.headers['X-CSRF-Token'] = 'foo'
708         response = request.get_response(app)
709         self.assertTrue(b'hello' in response.body)
710
464973 711     def test_predicate_mismatch_view_has_no_name(self):
CD 712         from pyramid.exceptions import PredicateMismatch
713         response = DummyResponse()
714         view = lambda *arg: response
715         def predicate1(context, request):
716             return False
717         predicate1.text = lambda *arg: 'text'
718         result = self.config._derive_view(view, predicates=[predicate1])
719         request = self._makeRequest()
720         request.method = 'POST'
721         try:
722             result(None, None)
723         except PredicateMismatch as e:
724             self.assertEqual(e.detail,
725                              'predicate mismatch for view <lambda> (text)')
726         else: # pragma: no cover
727             raise AssertionError
728
729     def test_predicate_mismatch_view_has_name(self):
730         from pyramid.exceptions import PredicateMismatch
731         def myview(request): pass
732         def predicate1(context, request):
733             return False
734         predicate1.text = lambda *arg: 'text'
735         result = self.config._derive_view(myview, predicates=[predicate1])
736         request = self._makeRequest()
737         request.method = 'POST'
738         try:
739             result(None, None)
740         except PredicateMismatch as e:
741             self.assertEqual(e.detail,
742                              'predicate mismatch for view myview (text)')
743         else: # pragma: no cover
744             raise AssertionError
745
746     def test_predicate_mismatch_exception_has_text_in_detail(self):
747         from pyramid.exceptions import PredicateMismatch
748         def myview(request): pass
749         def predicate1(context, request):
750             return True
751         predicate1.text = lambda *arg: 'pred1'
752         def predicate2(context, request):
753             return False
754         predicate2.text = lambda *arg: 'pred2'
755         result = self.config._derive_view(myview, 
756             predicates=[predicate1, predicate2])
757         request = self._makeRequest()
758         request.method = 'POST'
759         try:
760             result(None, None)
761         except PredicateMismatch as e:
762             self.assertEqual(e.detail,
763                              'predicate mismatch for view myview (pred2)')
764         else: # pragma: no cover
765             raise AssertionError
766
767     def test_with_predicates_all(self):
768         response = DummyResponse()
769         view = lambda *arg: response
770         predicates = []
771         def predicate1(context, request):
772             predicates.append(True)
773             return True
774         def predicate2(context, request):
775             predicates.append(True)
776             return True
777         result = self.config._derive_view(view, 
778             predicates=[predicate1, predicate2])
779         request = self._makeRequest()
780         request.method = 'POST'
781         next = result(None, None)
782         self.assertEqual(next, response)
783         self.assertEqual(predicates, [True, True])
784
785     def test_with_predicates_checker(self):
786         view = lambda *arg: 'OK'
787         predicates = []
788         def predicate1(context, request):
789             predicates.append(True)
790             return True
791         def predicate2(context, request):
792             predicates.append(True)
793             return True
794         result = self.config._derive_view(view, 
795             predicates=[predicate1, predicate2])
796         request = self._makeRequest()
797         request.method = 'POST'
798         next = result.__predicated__(None, None)
799         self.assertEqual(next, True)
800         self.assertEqual(predicates, [True, True])
801
802     def test_with_predicates_notall(self):
803         from pyramid.httpexceptions import HTTPNotFound
804         view = lambda *arg: 'OK'
805         predicates = []
806         def predicate1(context, request):
807             predicates.append(True)
808             return True
809         predicate1.text = lambda *arg: 'text'
810         def predicate2(context, request):
811             predicates.append(True)
812             return False
813         predicate2.text = lambda *arg: 'text'
814         result = self.config._derive_view(view, 
815             predicates=[predicate1, predicate2])
816         request = self._makeRequest()
817         request.method = 'POST'
818         self.assertRaises(HTTPNotFound, result, None, None)
819         self.assertEqual(predicates, [True, True])
820
821     def test_with_wrapper_viewname(self):
822         from pyramid.response import Response
823         from pyramid.interfaces import IView
824         from pyramid.interfaces import IViewClassifier
825         inner_response = Response('OK')
826         def inner_view(context, request):
827             return inner_response
828         def outer_view(context, request):
829             self.assertEqual(request.wrapped_response, inner_response)
830             self.assertEqual(request.wrapped_body, inner_response.body)
831             self.assertEqual(request.wrapped_view.__original_view__,
832                              inner_view)
833             return Response(b'outer ' + request.wrapped_body)
834         self.config.registry.registerAdapter(
835             outer_view, (IViewClassifier, None, None), IView, 'owrap')
836         result = self.config._derive_view(inner_view, viewname='inner',
837                                 wrapper_viewname='owrap')
838         self.assertFalse(result is inner_view)
839         self.assertEqual(inner_view.__module__, result.__module__)
840         self.assertEqual(inner_view.__doc__, result.__doc__)
841         request = self._makeRequest()
842         response = result(None, request)
843         self.assertEqual(response.body, b'outer OK')
844
845     def test_with_wrapper_viewname_notfound(self):
846         from pyramid.response import Response
847         inner_response = Response('OK')
848         def inner_view(context, request):
849             return inner_response
36d622 850         wrapped = self.config._derive_view(inner_view, viewname='inner',
464973 851                                 wrapper_viewname='owrap')
CD 852         request = self._makeRequest()
853         self.assertRaises(ValueError, wrapped, None, request)
854
855     def test_as_newstyle_class_context_and_request_attr_and_renderer(self):
856         response = DummyResponse()
857         class renderer(object):
858             def render_view(inself, req, resp, view_inst, ctx):
859                 self.assertEqual(req, request)
860                 self.assertEqual(resp, {'a':'1'})
861                 self.assertEqual(view_inst.__class__, View)
862                 self.assertEqual(ctx, context)
863                 return response
864             def clone(self):
865                 return self
866         class View(object):
867             def __init__(self, context, request):
868                 pass
869             def index(self):
870                 return {'a':'1'}
871         result = self.config._derive_view(View, 
872             renderer=renderer(), attr='index')
873         self.assertFalse(result is View)
874         self.assertEqual(result.__module__, View.__module__)
875         self.assertEqual(result.__doc__, View.__doc__)
876         self.assertEqual(result.__name__, View.__name__)
877         request = self._makeRequest()
878         context = testing.DummyResource()
879         self.assertEqual(result(context, request), response)
880
881     def test_as_newstyle_class_requestonly_attr_and_renderer(self):
882         response = DummyResponse()
883         class renderer(object):
884             def render_view(inself, req, resp, view_inst, ctx):
885                 self.assertEqual(req, request)
886                 self.assertEqual(resp, {'a':'1'})
887                 self.assertEqual(view_inst.__class__, View)
888                 self.assertEqual(ctx, context)
889                 return response
890             def clone(self):
891                 return self
892         class View(object):
893             def __init__(self, request):
894                 pass
895             def index(self):
896                 return {'a':'1'}
897         result = self.config.derive_view(View, 
898             renderer=renderer(), attr='index')
899         self.assertFalse(result is View)
900         self.assertEqual(result.__module__, View.__module__)
901         self.assertEqual(result.__doc__, View.__doc__)
902         self.assertEqual(result.__name__, View.__name__)
903         request = self._makeRequest()
904         context = testing.DummyResource()
905         self.assertEqual(result(context, request), response)
906
907     def test_as_oldstyle_cls_context_request_attr_and_renderer(self):
908         response = DummyResponse()
909         class renderer(object):
910             def render_view(inself, req, resp, view_inst, ctx):
911                 self.assertEqual(req, request)
912                 self.assertEqual(resp, {'a':'1'})
913                 self.assertEqual(view_inst.__class__, View)
914                 self.assertEqual(ctx, context)
915                 return response
916             def clone(self):
917                 return self
918         class View:
919             def __init__(self, context, request):
920                 pass
921             def index(self):
922                 return {'a':'1'}
488fdd 923         result = self.config.derive_view(View, 
464973 924             renderer=renderer(), attr='index')
CD 925         self.assertFalse(result is View)
926         self.assertEqual(result.__module__, View.__module__)
927         self.assertEqual(result.__doc__, View.__doc__)
928         self.assertEqual(result.__name__, View.__name__)
929         request = self._makeRequest()
930         context = testing.DummyResource()
931         self.assertEqual(result(context, request), response)
932
933     def test_as_oldstyle_cls_requestonly_attr_and_renderer(self):
934         response = DummyResponse()
935         class renderer(object):
936             def render_view(inself, req, resp, view_inst, ctx):
937                 self.assertEqual(req, request)
938                 self.assertEqual(resp, {'a':'1'})
939                 self.assertEqual(view_inst.__class__, View)
940                 self.assertEqual(ctx, context)
941                 return response
942             def clone(self):
943                 return self
944         class View:
945             def __init__(self, request):
946                 pass
947             def index(self):
948                 return {'a':'1'}
488fdd 949         result = self.config.derive_view(View, 
464973 950             renderer=renderer(), attr='index')
CD 951         self.assertFalse(result is View)
952         self.assertEqual(result.__module__, View.__module__)
953         self.assertEqual(result.__doc__, View.__doc__)
954         self.assertEqual(result.__name__, View.__name__)
955         request = self._makeRequest()
956         context = testing.DummyResource()
957         self.assertEqual(result(context, request), response)
958
959     def test_as_instance_context_and_request_attr_and_renderer(self):
960         response = DummyResponse()
961         class renderer(object):
962             def render_view(inself, req, resp, view_inst, ctx):
963                 self.assertEqual(req, request)
964                 self.assertEqual(resp, {'a':'1'})
965                 self.assertEqual(view_inst, view)
966                 self.assertEqual(ctx, context)
967                 return response
968             def clone(self):
969                 return self
970         class View:
971             def index(self, context, request):
972                 return {'a':'1'}
973         view = View()
974         result = self.config.derive_view(view, 
975             renderer=renderer(), attr='index')
976         self.assertFalse(result is view)
977         self.assertEqual(result.__module__, view.__module__)
978         self.assertEqual(result.__doc__, view.__doc__)
979         request = self._makeRequest()
980         context = testing.DummyResource()
981         self.assertEqual(result(context, request), response)
982
983     def test_as_instance_requestonly_attr_and_renderer(self):
984         response = DummyResponse()
985         class renderer(object):
986             def render_view(inself, req, resp, view_inst, ctx):
987                 self.assertEqual(req, request)
988                 self.assertEqual(resp, {'a':'1'})
989                 self.assertEqual(view_inst, view)
990                 self.assertEqual(ctx, context)
991                 return response
992             def clone(self):
993                 return self
994         class View:
995             def index(self, request):
996                 return {'a':'1'}
997         view = View()
998         result = self.config.derive_view(view, 
999             renderer=renderer(), attr='index')
1000         self.assertFalse(result is view)
1001         self.assertEqual(result.__module__, view.__module__)
1002         self.assertEqual(result.__doc__, view.__doc__)
1003         request = self._makeRequest()
1004         context = testing.DummyResource()
1005         self.assertEqual(result(context, request), response)
1006
1007     def test_with_view_mapper_config_specified(self):
1008         response = DummyResponse()
1009         class mapper(object):
1010             def __init__(self, **kw):
1011                 self.kw = kw
1012             def __call__(self, view):
1013                 def wrapped(context, request):
1014                     return response
1015                 return wrapped
1016         def view(context, request): return 'NOTOK'
1017         result = self.config._derive_view(view, mapper=mapper)
1018         self.assertFalse(result.__wraps__ is view)
1019         self.assertEqual(result(None, None), response)
1020
1021     def test_with_view_mapper_view_specified(self):
1022         from pyramid.response import Response
1023         response = Response()
1024         def mapper(**kw):
1025             def inner(view):
1026                 def superinner(context, request):
1027                     self.assertEqual(request, None)
1028                     return response
1029                 return superinner
1030             return inner
1031         def view(context, request): return 'NOTOK'
1032         view.__view_mapper__ = mapper
1033         result = self.config.derive_view(view)
1034         self.assertFalse(result.__wraps__ is view)
1035         self.assertEqual(result(None, None), response)
1036
1037     def test_with_view_mapper_default_mapper_specified(self):
1038         from pyramid.response import Response
1039         response = Response()
1040         def mapper(**kw):
1041             def inner(view):
1042                 def superinner(context, request):
1043                     self.assertEqual(request, None)
1044                     return  response
1045                 return superinner
1046             return inner
1047         self.config.set_view_mapper(mapper)
1048         def view(context, request): return 'NOTOK'
1049         result = self.config.derive_view(view)
1050         self.assertFalse(result.__wraps__ is view)
1051         self.assertEqual(result(None, None), response)
1052
1053     def test_attr_wrapped_view_branching_default_phash(self):
1054         from pyramid.config.util import DEFAULT_PHASH
1055         def view(context, request): pass
611aa3 1056         result = self.config._derive_view(view, phash=DEFAULT_PHASH)
464973 1057         self.assertEqual(result.__wraps__, view)
CD 1058
1059     def test_attr_wrapped_view_branching_nondefault_phash(self):
1060         def view(context, request): pass
611aa3 1061         result = self.config._derive_view(view, phash='nondefault')
464973 1062         self.assertNotEqual(result, view)
CD 1063
1064     def test_http_cached_view_integer(self):
1065         import datetime
1066         from pyramid.response import Response
1067         response = Response('OK')
1068         def inner_view(context, request):
1069             return response
611aa3 1070         result = self.config._derive_view(inner_view, http_cache=3600)
464973 1071         self.assertFalse(result is inner_view)
CD 1072         self.assertEqual(inner_view.__module__, result.__module__)
1073         self.assertEqual(inner_view.__doc__, result.__doc__)
1074         request = self._makeRequest()
1075         when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
1076         result = result(None, request)
1077         self.assertEqual(result, response)
1078         headers = dict(result.headerlist)
1079         expires = parse_httpdate(headers['Expires'])
1080         assert_similar_datetime(expires, when)
1081         self.assertEqual(headers['Cache-Control'], 'max-age=3600')
1082
1083     def test_http_cached_view_timedelta(self):
1084         import datetime
1085         from pyramid.response import Response
1086         response = Response('OK')
1087         def inner_view(context, request):
1088             return response
611aa3 1089         result = self.config._derive_view(inner_view, 
464973 1090             http_cache=datetime.timedelta(hours=1))
CD 1091         self.assertFalse(result is inner_view)
1092         self.assertEqual(inner_view.__module__, result.__module__)
1093         self.assertEqual(inner_view.__doc__, result.__doc__)
1094         request = self._makeRequest()
1095         when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
1096         result = result(None, request)
1097         self.assertEqual(result, response)
1098         headers = dict(result.headerlist)
1099         expires = parse_httpdate(headers['Expires'])
1100         assert_similar_datetime(expires, when)
1101         self.assertEqual(headers['Cache-Control'], 'max-age=3600')
1102
1103     def test_http_cached_view_tuple(self):
1104         import datetime
1105         from pyramid.response import Response
1106         response = Response('OK')
1107         def inner_view(context, request):
1108             return response
611aa3 1109         result = self.config._derive_view(inner_view,
464973 1110             http_cache=(3600, {'public':True}))
CD 1111         self.assertFalse(result is inner_view)
1112         self.assertEqual(inner_view.__module__, result.__module__)
1113         self.assertEqual(inner_view.__doc__, result.__doc__)
1114         request = self._makeRequest()
1115         when = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
1116         result = result(None, request)
1117         self.assertEqual(result, response)
1118         headers = dict(result.headerlist)
1119         expires = parse_httpdate(headers['Expires'])
1120         assert_similar_datetime(expires, when)
1121         self.assertEqual(headers['Cache-Control'], 'max-age=3600, public')
1122
1123     def test_http_cached_view_tuple_seconds_None(self):
1124         from pyramid.response import Response
1125         response = Response('OK')
1126         def inner_view(context, request):
1127             return response
1128         result = self.config._derive_view(inner_view,
1129             http_cache=(None, {'public':True}))
1130         self.assertFalse(result is inner_view)
1131         self.assertEqual(inner_view.__module__, result.__module__)
1132         self.assertEqual(inner_view.__doc__, result.__doc__)
1133         request = self._makeRequest()
1134         result = result(None, request)
1135         self.assertEqual(result, response)
1136         headers = dict(result.headerlist)
1137         self.assertFalse('Expires' in headers)
1138         self.assertEqual(headers['Cache-Control'], 'public')
1139
1140     def test_http_cached_view_prevent_auto_set(self):
1141         from pyramid.response import Response
1142         response = Response()
1143         response.cache_control.prevent_auto = True
1144         def inner_view(context, request):
1145             return response
1146         result = self.config._derive_view(inner_view, http_cache=3600)
1147         request = self._makeRequest()
1148         result = result(None, request)
1149         self.assertEqual(result, response) # doesn't blow up
1150         headers = dict(result.headerlist)
1151         self.assertFalse('Expires' in headers)
1152         self.assertFalse('Cache-Control' in headers)
1153
1154     def test_http_cached_prevent_http_cache_in_settings(self):
1155         self.config.registry.settings['prevent_http_cache'] = True
1156         from pyramid.response import Response
1157         response = Response()
1158         def inner_view(context, request):
1159             return response
1160         result = self.config._derive_view(inner_view, http_cache=3600)
1161         request = self._makeRequest()
1162         result = result(None, request)
1163         self.assertEqual(result, response)
1164         headers = dict(result.headerlist)
1165         self.assertFalse('Expires' in headers)
1166         self.assertFalse('Cache-Control' in headers)
1167
1168     def test_http_cached_view_bad_tuple(self):
1169         def view(request): pass
96c87b 1170         self.assertRaises(ConfigurationError, self.config._derive_view, 
464973 1171             view, http_cache=(None,))
CD 1172
de3d0c 1173     def test_csrf_view_ignores_GET(self):
MM 1174         response = DummyResponse()
1175         def inner_view(request):
1176             return response
1177         request = self._makeRequest()
1178         request.method = 'GET'
1179         view = self.config._derive_view(inner_view, require_csrf=True)
1180         result = view(None, request)
1181         self.assertTrue(result is response)
6b35eb 1182
de3d0c 1183     def test_csrf_view_fails_with_bad_POST_header(self):
MM 1184         from pyramid.exceptions import BadCSRFToken
1185         def inner_view(request): pass
1186         request = self._makeRequest()
1187         request.scheme = "http"
1188         request.method = 'POST'
1189         request.session = DummySession({'csrf_token': 'foo'})
1190         request.headers = {'X-CSRF-Token': 'bar'}
1191         view = self.config._derive_view(inner_view, require_csrf=True)
1192         self.assertRaises(BadCSRFToken, lambda: view(None, request))
6b35eb 1193
de3d0c 1194     def test_csrf_view_passes_with_good_POST_header(self):
9e9fa9 1195         response = DummyResponse()
MM 1196         def inner_view(request):
1197             return response
1198         request = self._makeRequest()
65dee6 1199         request.scheme = "http"
6b35eb 1200         request.method = 'POST'
9e9fa9 1201         request.session = DummySession({'csrf_token': 'foo'})
MM 1202         request.headers = {'X-CSRF-Token': 'foo'}
1203         view = self.config._derive_view(inner_view, require_csrf=True)
1204         result = view(None, request)
1205         self.assertTrue(result is response)
1206
de3d0c 1207     def test_csrf_view_fails_with_bad_POST_token(self):
MM 1208         from pyramid.exceptions import BadCSRFToken
1209         def inner_view(request): pass
1210         request = self._makeRequest()
1211         request.scheme = "http"
1212         request.method = 'POST'
1213         request.session = DummySession({'csrf_token': 'foo'})
1214         request.POST = {'csrf_token': 'bar'}
1215         view = self.config._derive_view(inner_view, require_csrf=True)
1216         self.assertRaises(BadCSRFToken, lambda: view(None, request))
1217
1218     def test_csrf_view_passes_with_good_POST_token(self):
9e9fa9 1219         response = DummyResponse()
MM 1220         def inner_view(request):
1221             return response
1222         request = self._makeRequest()
65dee6 1223         request.scheme = "http"
DS 1224         request.method = 'POST'
1225         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1226         request.POST = {'csrf_token': 'foo'}
MM 1227         view = self.config._derive_view(inner_view, require_csrf=True)
65dee6 1228         result = view(None, request)
DS 1229         self.assertTrue(result is response)
1230
1231     def test_csrf_view_https_domain(self):
1232         response = DummyResponse()
1233         def inner_view(request):
1234             return response
1235         request = self._makeRequest()
1236         request.scheme = "https"
1237         request.domain = "example.com"
884043 1238         request.host_port = "443"
65dee6 1239         request.referrer = "https://example.com/login/"
6b35eb 1240         request.method = 'POST'
9e9fa9 1241         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1242         request.POST = {'csrf_token': 'foo'}
6b35eb 1243         view = self.config._derive_view(inner_view, require_csrf=True)
MM 1244         result = view(None, request)
1245         self.assertTrue(result is response)
1246
21d5be 1247     def test_csrf_view_fails_on_bad_PUT_header(self):
DS 1248         from pyramid.exceptions import BadCSRFToken
1249         def inner_view(request): pass
1250         request = self._makeRequest()
65dee6 1251         request.scheme = "http"
21d5be 1252         request.method = 'PUT'
DS 1253         request.session = DummySession({'csrf_token': 'foo'})
1254         request.headers = {'X-CSRF-Token': 'bar'}
de3d0c 1255         view = self.config._derive_view(inner_view, require_csrf=True)
21d5be 1256         self.assertRaises(BadCSRFToken, lambda: view(None, request))
DS 1257
65dee6 1258     def test_csrf_view_fails_on_bad_referrer(self):
DS 1259         from pyramid.exceptions import BadCSRFOrigin
1260         def inner_view(request): pass
1261         request = self._makeRequest()
1262         request.method = "POST"
1263         request.scheme = "https"
884043 1264         request.host_port = "443"
65dee6 1265         request.domain = "example.com"
DS 1266         request.referrer = "https://not-example.com/evil/"
1267         request.registry.settings = {}
de3d0c 1268         view = self.config._derive_view(inner_view, require_csrf=True)
65dee6 1269         self.assertRaises(BadCSRFOrigin, lambda: view(None, request))
DS 1270
1271     def test_csrf_view_fails_on_bad_origin(self):
1272         from pyramid.exceptions import BadCSRFOrigin
1273         def inner_view(request): pass
1274         request = self._makeRequest()
1275         request.method = "POST"
1276         request.scheme = "https"
884043 1277         request.host_port = "443"
65dee6 1278         request.domain = "example.com"
DS 1279         request.headers = {"Origin": "https://not-example.com/evil/"}
1280         request.registry.settings = {}
de3d0c 1281         view = self.config._derive_view(inner_view, require_csrf=True)
65dee6 1282         self.assertRaises(BadCSRFOrigin, lambda: view(None, request))
DS 1283
de3d0c 1284     def test_csrf_view_enabled_by_default(self):
MM 1285         from pyramid.exceptions import BadCSRFToken
1286         def inner_view(request): pass
6b35eb 1287         request = self._makeRequest()
65dee6 1288         request.scheme = "http"
6b35eb 1289         request.method = 'POST'
MM 1290         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1291         self.config.set_default_csrf_options(require_csrf=True)
6b35eb 1292         view = self.config._derive_view(inner_view)
de3d0c 1293         self.assertRaises(BadCSRFToken, lambda: view(None, request))
6b35eb 1294
17fa5e 1295     def test_csrf_view_enabled_via_callback(self):
MM 1296         def callback(request):
1297             return True
1298         from pyramid.exceptions import BadCSRFToken
1299         def inner_view(request): pass
1300         request = self._makeRequest()
1301         request.scheme = "http"
1302         request.method = 'POST'
1303         request.session = DummySession({'csrf_token': 'foo'})
1304         self.config.set_default_csrf_options(require_csrf=True, callback=callback)
1305         view = self.config._derive_view(inner_view)
1306         self.assertRaises(BadCSRFToken, lambda: view(None, request))
1307
1308     def test_csrf_view_disabled_via_callback(self):
1309         def callback(request):
1310             return False
1311         response = DummyResponse()
1312         def inner_view(request):
1313             return response
1314         request = self._makeRequest()
1315         request.scheme = "http"
1316         request.method = 'POST'
1317         request.session = DummySession({'csrf_token': 'foo'})
1318         self.config.set_default_csrf_options(require_csrf=True, callback=callback)
1319         view = self.config._derive_view(inner_view)
1320         result = view(None, request)
1321         self.assertTrue(result is response)
1322
de3d0c 1323     def test_csrf_view_uses_custom_csrf_token(self):
6b35eb 1324         response = DummyResponse()
MM 1325         def inner_view(request):
1326             return response
1327         request = self._makeRequest()
65dee6 1328         request.scheme = "http"
6b35eb 1329         request.method = 'POST'
MM 1330         request.session = DummySession({'csrf_token': 'foo'})
f12005 1331         request.POST = {'DUMMY': 'foo'}
de3d0c 1332         self.config.set_default_csrf_options(require_csrf=True, token='DUMMY')
6b35eb 1333         view = self.config._derive_view(inner_view)
MM 1334         result = view(None, request)
1335         self.assertTrue(result is response)
1336
de3d0c 1337     def test_csrf_view_uses_custom_csrf_header(self):
6b35eb 1338         response = DummyResponse()
MM 1339         def inner_view(request):
1340             return response
1341         request = self._makeRequest()
de3d0c 1342         request.scheme = "http"
6b35eb 1343         request.method = 'POST'
MM 1344         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1345         request.headers = {'DUMMY': 'foo'}
MM 1346         self.config.set_default_csrf_options(require_csrf=True, header='DUMMY')
1347         view = self.config._derive_view(inner_view)
1348         result = view(None, request)
1349         self.assertTrue(result is response)
1350
1351     def test_csrf_view_uses_custom_methods(self):
1352         response = DummyResponse()
1353         def inner_view(request):
1354             return response
1355         request = self._makeRequest()
1356         request.scheme = "http"
1357         request.method = 'PUT'
1358         request.session = DummySession({'csrf_token': 'foo'})
1359         self.config.set_default_csrf_options(
1360             require_csrf=True, safe_methods=['PUT'])
6b35eb 1361         view = self.config._derive_view(inner_view)
MM 1362         result = view(None, request)
1363         self.assertTrue(result is response)
1364
1365     def test_csrf_view_uses_view_option_override(self):
1366         response = DummyResponse()
1367         def inner_view(request):
1368             return response
1369         request = self._makeRequest()
65dee6 1370         request.scheme = "http"
6b35eb 1371         request.method = 'POST'
MM 1372         request.session = DummySession({'csrf_token': 'foo'})
de3d0c 1373         request.POST = {'csrf_token': 'bar'}
MM 1374         self.config.set_default_csrf_options(require_csrf=True)
1375         view = self.config._derive_view(inner_view, require_csrf=False)
9e9fa9 1376         result = view(None, request)
MM 1377         self.assertTrue(result is response)
1378
6f524a 1379     def test_csrf_view_skipped_by_default_on_exception_view(self):
MM 1380         from pyramid.request import Request
1381         def view(request):
1382             raise ValueError
1383         def excview(request):
1384             return 'hello'
de3d0c 1385         self.config.set_default_csrf_options(require_csrf=True)
6f524a 1386         self.config.set_session_factory(
MM 1387             lambda request: DummySession({'csrf_token': 'foo'}))
1388         self.config.add_view(view, name='foo', require_csrf=False)
1389         self.config.add_view(excview, context=ValueError, renderer='string')
1390         app = self.config.make_wsgi_app()
1391         request = Request.blank('/foo', base_url='http://example.com')
1392         request.method = 'POST'
1393         response = request.get_response(app)
1394         self.assertTrue(b'hello' in response.body)
1395
1396     def test_csrf_view_failed_on_explicit_exception_view(self):
1397         from pyramid.exceptions import BadCSRFToken
1398         from pyramid.request import Request
1399         def view(request):
1400             raise ValueError
1401         def excview(request): pass
de3d0c 1402         self.config.set_default_csrf_options(require_csrf=True)
6f524a 1403         self.config.set_session_factory(
MM 1404             lambda request: DummySession({'csrf_token': 'foo'}))
1405         self.config.add_view(view, name='foo', require_csrf=False)
1406         self.config.add_view(excview, context=ValueError, renderer='string',
1407                              require_csrf=True)
1408         app = self.config.make_wsgi_app()
1409         request = Request.blank('/foo', base_url='http://example.com')
1410         request.method = 'POST'
1411         try:
1412             request.get_response(app)
1413         except BadCSRFToken:
1414             pass
1415         else: # pragma: no cover
1416             raise AssertionError
1417
1418     def test_csrf_view_passed_on_explicit_exception_view(self):
1419         from pyramid.request import Request
1420         def view(request):
1421             raise ValueError
1422         def excview(request):
1423             return 'hello'
de3d0c 1424         self.config.set_default_csrf_options(require_csrf=True)
6f524a 1425         self.config.set_session_factory(
MM 1426             lambda request: DummySession({'csrf_token': 'foo'}))
1427         self.config.add_view(view, name='foo', require_csrf=False)
1428         self.config.add_view(excview, context=ValueError, renderer='string',
1429                              require_csrf=True)
1430         app = self.config.make_wsgi_app()
1431         request = Request.blank('/foo', base_url='http://example.com')
1432         request.method = 'POST'
1433         request.headers['X-CSRF-Token'] = 'foo'
1434         response = request.get_response(app)
1435         self.assertTrue(b'hello' in response.body)
1436
07d4a4 1437
d11119 1438 class TestDerivationOrder(unittest.TestCase):
BJR 1439     def setUp(self):
1440         self.config = testing.setUp()
1441
1442     def tearDown(self):
1443         self.config = None
1444         testing.tearDown()
1445
1446     def test_right_order_user_sorted(self):
1447         from pyramid.interfaces import IViewDerivers
1448
cbf686 1449         self.config.add_view_deriver(None, 'deriv1')
a3db3c 1450         self.config.add_view_deriver(None, 'deriv2', 'decorated_view', 'deriv1')
MM 1451         self.config.add_view_deriver(None, 'deriv3', 'deriv2', 'deriv1')
d11119 1452
46fd86 1453         derivers = self.config.registry.getUtility(IViewDerivers)
d11119 1454         derivers_sorted = derivers.sorted()
BJR 1455         dlist = [d for (d, _) in derivers_sorted]
1456         self.assertEqual([
cf9dcb 1457             'secured_view',
6b35eb 1458             'csrf_view',
cf9dcb 1459             'owrapped_view',
MM 1460             'http_cached_view',
1461             'decorated_view',
d11119 1462             'deriv2',
BJR 1463             'deriv3',
1464             'deriv1',
cf9dcb 1465             'rendered_view',
a3db3c 1466             'mapped_view',
d11119 1467             ], dlist)
BJR 1468
1469     def test_right_order_implicit(self):
1470         from pyramid.interfaces import IViewDerivers
1471
cbf686 1472         self.config.add_view_deriver(None, 'deriv1')
MM 1473         self.config.add_view_deriver(None, 'deriv2')
1474         self.config.add_view_deriver(None, 'deriv3')
d11119 1475
46fd86 1476         derivers = self.config.registry.getUtility(IViewDerivers)
d11119 1477         derivers_sorted = derivers.sorted()
BJR 1478         dlist = [d for (d, _) in derivers_sorted]
1479         self.assertEqual([
cf9dcb 1480             'secured_view',
6b35eb 1481             'csrf_view',
cf9dcb 1482             'owrapped_view',
MM 1483             'http_cached_view',
1484             'decorated_view',
1485             'deriv3',
1486             'deriv2',
1487             'deriv1',
1488             'rendered_view',
a3db3c 1489             'mapped_view',
d11119 1490             ], dlist)
BJR 1491
cf9dcb 1492     def test_right_order_under_rendered_view(self):
d11119 1493         from pyramid.interfaces import IViewDerivers
BJR 1494
a3db3c 1495         self.config.add_view_deriver(None, 'deriv1', 'rendered_view', 'mapped_view')
d11119 1496
46fd86 1497         derivers = self.config.registry.getUtility(IViewDerivers)
d11119 1498         derivers_sorted = derivers.sorted()
BJR 1499         dlist = [d for (d, _) in derivers_sorted]
cf9dcb 1500         self.assertEqual([
MM 1501             'secured_view',
6b35eb 1502             'csrf_view',
cf9dcb 1503             'owrapped_view',
MM 1504             'http_cached_view',
1505             'decorated_view',
1506             'rendered_view',
1507             'deriv1',
a3db3c 1508             'mapped_view',
d11119 1509             ], dlist)
BJR 1510
1511
cf9dcb 1512     def test_right_order_under_rendered_view_others(self):
d11119 1513         from pyramid.interfaces import IViewDerivers
BJR 1514
a3db3c 1515         self.config.add_view_deriver(None, 'deriv1', 'rendered_view', 'mapped_view')
cbf686 1516         self.config.add_view_deriver(None, 'deriv2')
MM 1517         self.config.add_view_deriver(None, 'deriv3')
d11119 1518
46fd86 1519         derivers = self.config.registry.getUtility(IViewDerivers)
d11119 1520         derivers_sorted = derivers.sorted()
BJR 1521         dlist = [d for (d, _) in derivers_sorted]
cf9dcb 1522         self.assertEqual([
MM 1523             'secured_view',
6b35eb 1524             'csrf_view',
cf9dcb 1525             'owrapped_view',
MM 1526             'http_cached_view',
1527             'decorated_view',
1528             'deriv3',
1529             'deriv2',
1530             'rendered_view',
1531             'deriv1',
a3db3c 1532             'mapped_view',
d11119 1533             ], dlist)
BJR 1534
1535
ceb1f2 1536 class TestAddDeriver(unittest.TestCase):
07d4a4 1537
CD 1538     def setUp(self):
1539         self.config = testing.setUp()
1540
1541     def tearDown(self):
1542         self.config = None
174bb5 1543         testing.tearDown()
07d4a4 1544
ceb1f2 1545     def test_add_single_deriver(self):
07d4a4 1546         response = DummyResponse()
CD 1547         response.deriv = False
1548         view = lambda *arg: response
1549
007600 1550         def deriv(view, info):
07d4a4 1551             self.assertFalse(response.deriv)
CD 1552             response.deriv = True
1553             return view
1554
1555         result = self.config._derive_view(view)
1556         self.assertFalse(response.deriv)
cbf686 1557         self.config.add_view_deriver(deriv, 'test_deriv')
07d4a4 1558
CD 1559         result = self.config._derive_view(view)
1560         self.assertTrue(response.deriv)
1561
ceb1f2 1562     def test_override_deriver(self):
07d4a4 1563         flags = {}
CD 1564
1565         class AView:
1566             def __init__(self):
1567                 self.response = DummyResponse()
1568
a3db3c 1569         def deriv1(view, info):
07d4a4 1570             flags['deriv1'] = True
CD 1571             return view
1572
a3db3c 1573         def deriv2(view, info):
07d4a4 1574             flags['deriv2'] = True
CD 1575             return view
1576
1577         view1 = AView()
cbf686 1578         self.config.add_view_deriver(deriv1, 'test_deriv')
07d4a4 1579         result = self.config._derive_view(view1)
CD 1580         self.assertTrue(flags.get('deriv1'))
1581         self.assertFalse(flags.get('deriv2'))
1582
1583         flags.clear()
1584         view2 = AView()
cbf686 1585         self.config.add_view_deriver(deriv2, 'test_deriv')
07d4a4 1586         result = self.config._derive_view(view2)
CD 1587         self.assertFalse(flags.get('deriv1'))
1588         self.assertTrue(flags.get('deriv2'))
1589
c231d8 1590     def test_override_mapped_view(self):
MM 1591         from pyramid.viewderivers import VIEW
1592         response = DummyResponse()
1593         view = lambda *arg: response
1594         flags = {}
1595
1596         def deriv1(view, info):
1597             flags['deriv1'] = True
1598             return view
1599
1600         result = self.config._derive_view(view)
1601         self.assertFalse(flags.get('deriv1'))
1602
1603         flags.clear()
1604         self.config.add_view_deriver(
1605             deriv1, name='mapped_view', under='rendered_view', over=VIEW)
1606         result = self.config._derive_view(view)
1607         self.assertTrue(flags.get('deriv1'))
1608
ceb1f2 1609     def test_add_multi_derivers_ordered(self):
a3db3c 1610         from pyramid.viewderivers import INGRESS
07d4a4 1611         response = DummyResponse()
CD 1612         view = lambda *arg: response
1613         response.deriv = []
1614
a3db3c 1615         def deriv1(view, info):
07d4a4 1616             response.deriv.append('deriv1')
CD 1617             return view
1618
a3db3c 1619         def deriv2(view, info):
07d4a4 1620             response.deriv.append('deriv2')
CD 1621             return view
1622
a3db3c 1623         def deriv3(view, info):
07d4a4 1624             response.deriv.append('deriv3')
CD 1625             return view
1626
cbf686 1627         self.config.add_view_deriver(deriv1, 'deriv1')
a3db3c 1628         self.config.add_view_deriver(deriv2, 'deriv2', INGRESS, 'deriv1')
MM 1629         self.config.add_view_deriver(deriv3, 'deriv3', 'deriv2', 'deriv1')
07d4a4 1630         result = self.config._derive_view(view)
a3db3c 1631         self.assertEqual(response.deriv, ['deriv1', 'deriv3', 'deriv2'])
MM 1632
1633     def test_add_deriver_without_name(self):
1634         from pyramid.interfaces import IViewDerivers
1635         def deriv1(view, info): pass
1636         self.config.add_view_deriver(deriv1)
1637         derivers = self.config.registry.getUtility(IViewDerivers)
1638         self.assertTrue('deriv1' in derivers.names)
1639
1640     def test_add_deriver_reserves_ingress(self):
1641         from pyramid.exceptions import ConfigurationError
1642         from pyramid.viewderivers import INGRESS
1643         def deriv1(view, info): pass
1644         self.assertRaises(
1645             ConfigurationError, self.config.add_view_deriver, deriv1, INGRESS)
1646
1647     def test_add_deriver_enforces_ingress_is_first(self):
1648         from pyramid.exceptions import ConfigurationError
1649         from pyramid.viewderivers import INGRESS
1650         def deriv1(view, info): pass
1651         try:
c231d8 1652             self.config.add_view_deriver(deriv1, over=INGRESS)
a3db3c 1653         except ConfigurationError as ex:
c231d8 1654             self.assertTrue('cannot be over INGRESS' in ex.args[0])
MM 1655         else: # pragma: no cover
1656             raise AssertionError
1657
1658     def test_add_deriver_enforces_view_is_last(self):
1659         from pyramid.exceptions import ConfigurationError
1660         from pyramid.viewderivers import VIEW
1661         def deriv1(view, info): pass
1662         try:
1663             self.config.add_view_deriver(deriv1, under=VIEW)
1664         except ConfigurationError as ex:
1665             self.assertTrue('cannot be under VIEW' in ex.args[0])
a3db3c 1666         else: # pragma: no cover
MM 1667             raise AssertionError
1668
1669     def test_add_deriver_enforces_mapped_view_is_last(self):
1670         from pyramid.exceptions import ConfigurationError
1671         def deriv1(view, info): pass
1672         try:
c231d8 1673             self.config.add_view_deriver(deriv1, 'deriv1', under='mapped_view')
a3db3c 1674         except ConfigurationError as ex:
c231d8 1675             self.assertTrue('cannot be under "mapped_view"' in ex.args[0])
a3db3c 1676         else: # pragma: no cover
MM 1677             raise AssertionError
cbf686 1678
d11119 1679
ceb1f2 1680 class TestDeriverIntegration(unittest.TestCase):
174bb5 1681     def setUp(self):
BJR 1682         self.config = testing.setUp()
1683
1684     def tearDown(self):
1685         self.config = None
1686         testing.tearDown()
1687
1688     def _getViewCallable(self, config, ctx_iface=None, request_iface=None,
1689                          name=''):
1690         from zope.interface import Interface
1691         from pyramid.interfaces import IRequest
1692         from pyramid.interfaces import IView
1693         from pyramid.interfaces import IViewClassifier
1694         classifier = IViewClassifier
1695         if ctx_iface is None:
1696             ctx_iface = Interface
1697         if request_iface is None:
1698             request_iface = IRequest
1699         return config.registry.adapters.lookup(
1700             (classifier, request_iface, ctx_iface), IView, name=name,
1701             default=None)
1702
1703     def _makeRequest(self, config):
1704         request = DummyRequest()
1705         request.registry = config.registry
1706         return request
1707
1708     def test_view_options(self):
1709         response = DummyResponse()
1710         view = lambda *arg: response
1711         response.deriv = []
1712
007600 1713         def deriv1(view, info):
MM 1714             response.deriv.append(info.options['deriv1'])
174bb5 1715             return view
e4b931 1716         deriv1.options = ('deriv1',)
174bb5 1717
007600 1718         def deriv2(view, info):
MM 1719             response.deriv.append(info.options['deriv2'])
174bb5 1720             return view
e4b931 1721         deriv2.options = ('deriv2',)
174bb5 1722
cbf686 1723         self.config.add_view_deriver(deriv1, 'deriv1')
MM 1724         self.config.add_view_deriver(deriv2, 'deriv2')
174bb5 1725         self.config.add_view(view, deriv1='test1', deriv2='test2')
BJR 1726
1727         wrapper = self._getViewCallable(self.config)
1728         request = self._makeRequest(self.config)
1729         request.method = 'GET'
1730         self.assertEqual(wrapper(None, request), response)
1731         self.assertEqual(['test1', 'test2'], response.deriv)
6b089d 1732
e4b931 1733     def test_unexpected_view_options(self):
MM 1734         from pyramid.exceptions import ConfigurationError
1735         def deriv1(view, info): pass
cbf686 1736         self.config.add_view_deriver(deriv1, 'deriv1')
e4b931 1737         self.assertRaises(
MM 1738             ConfigurationError,
1739             lambda: self.config.add_view(lambda r: {}, deriv1='test1'))
1740
464973 1741 @implementer(IResponse)
CD 1742 class DummyResponse(object):
1743     content_type = None
1744     default_content_type = None
1745     body = None
1746
1747 class DummyRequest:
1748     subpath = ()
1749     matchdict = None
1750     request_iface  = IRequest
1751
1752     def __init__(self, environ=None):
1753         if environ is None:
1754             environ = {}
1755         self.environ = environ
1756         self.params = {}
de3d0c 1757         self.POST = {}
464973 1758         self.cookies = {}
9e9fa9 1759         self.headers = {}
464973 1760         self.response = DummyResponse()
CD 1761
1762 class DummyLogger:
1763     def __init__(self):
1764         self.messages = []
1765     def info(self, msg):
1766         self.messages.append(msg)
1767     warn = info
1768     debug = info
1769
1770 class DummySecurityPolicy:
1771     def __init__(self, permitted=True):
1772         self.permitted = permitted
1773
1774     def effective_principals(self, request):
1775         return []
1776
1777     def permits(self, context, principals, permission):
1778         return self.permitted
1779
9e9fa9 1780 class DummySession(dict):
MM 1781     def get_csrf_token(self):
1782         return self['csrf_token']
1783
611aa3 1784 def parse_httpdate(s):
CD 1785     import datetime
1786     # cannot use %Z, must use literal GMT; Jython honors timezone
1787     # but CPython does not
1788     return datetime.datetime.strptime(s, "%a, %d %b %Y %H:%M:%S GMT")
1789
1790 def assert_similar_datetime(one, two):
1791     for attr in ('year', 'month', 'day', 'hour', 'minute'):
1792         one_attr = getattr(one, attr)
1793         two_attr = getattr(two, attr)
1794         if not one_attr == two_attr: # pragma: no cover
1795             raise AssertionError('%r != %r in %s' % (one_attr, two_attr, attr))