/
pages.txt
378 lines (293 loc) · 11.1 KB
/
pages.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
Test browser pages
==================
Let's register a quite large amount of test pages:
>>> import Products.Five.browser.tests
>>> from Zope2.App import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('pages.zcml', package=Products.Five.browser.tests)
Let's add a test object that we view most of the pages off of:
>>> from Products.Five.tests.testing.simplecontent import manage_addSimpleContent
>>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
We also need to create a stub user account and login; otherwise we
wouldn't have all the rights to do traversal etc.:
>>> uf = self.folder.acl_users
>>> _ignored = uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> self.login('manager')
Now for some actual testing...
Simple pages
------------
A browser page that is a view class's attribute (method):
>>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
>>> view is not None
True
>>> from Products.Five.browser.tests.pages import SimpleView
>>> isinstance(view, SimpleView)
True
>>> view() == 'The eagle has landed'
True
A browser page that is a Page Template.
>>> view = self.folder.unrestrictedTraverse('testoid/owl.html')
>>> view() == '<p>2</p>'
True
A browser page that is a PageTemplate plus a view class:
>>> view = self.folder.unrestrictedTraverse('testoid/falcon.html')
>>> isinstance(view, SimpleView)
True
>>> view() == '<p>The falcon has taken flight</p>'
True
Test pages that have been registered through the cumulative
<browser:pages> directive:
>>> view = self.folder.unrestrictedTraverse('testoid/eagle-page.txt')
>>> isinstance(view, SimpleView)
True
>>> view() == 'The eagle has landed'
True
>>> view = self.folder.unrestrictedTraverse('testoid/mouse-page.txt')
>>> isinstance(view, SimpleView)
True
>>> view() == 'The mouse has been eaten by the eagle'
True
Zope 2 objects always need a docstring in order to be published. Five
adds a docstring automatically if a view method doesn't have it, but
it shouldn't modify existing ones:
>>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
>>> view.eagle.__doc__ == SimpleView.eagle.__doc__
True
Make sure new-style classes work fine as view classes:
>>> self.folder.unrestrictedTraverse('testoid/@@new_style_class')
<Products.Five.browser.metaconfigure.NewStyleClass ...>
At one point browser classes with no attribute and no template
values specified wasn't getting BrowserView mixed in. Lets make
sure it is now:
>>> self.folder.unrestrictedTraverse('testoid/@@new_style_class2')
<Products.Five.browser.metaconfigure.NewStyleClass ...>
Both browser:view and browser:page are ILocation providers, so make sure they
have a __name__ attribute with a str instance:
>>> page = self.folder.unrestrictedTraverse('testoid/eagle.txt')
>>> page.__name__
'eagle.txt'
>>> view = self.folder.unrestrictedTraverse('testoid/named_view')
>>> view.__name__
'named_view'
ZPT-based browser pages
-----------------------
Test access to ``context`` from ZPTs:
>>> view = self.folder.unrestrictedTraverse('testoid/flamingo.html')
>>> print(view())
<p>Hello world</p>
<p>Hello world</p>
Test macro access from ZPT pages:
>>> view = self.folder.unrestrictedTraverse('testoid/seagull.html')
>>> view() == ('<html><head><title>bird macro</title></head>'
... '<body>Color: gray</body></html>\n')
True
test_zpt_things:
>>> view = self.folder.unrestrictedTraverse('testoid/condor.html')
>>> print(view())
<p>Hello world</p>
<p>The eagle has landed</p>
<p>Hello world</p>
<p>Hello world</p>
Make sure that tal:repeat works in ZPT browser pages:
>>> view = self.folder.unrestrictedTraverse('testoid/ostrich.html')
>>> print(view())
<ul>
<li>Alpha</li>
<li>Beta</li>
<li>Gamma</li>
</ul>
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
</ul>
Test TALES traversal in ZPT pages:
>>> view = self.folder.unrestrictedTraverse('testoid/tales_traversal.html')
>>> print(view())
<p>testoid</p>
<p>test_folder_1_</p>
Make sure that global template variables in ZPT pages are correct:
>>> view = self.folder.unrestrictedTraverse('testoid/template_variables.html')
>>> print(view())
View is a view: True
Context is testoid: True
Context.__parent__ is test_folder_1_: True
Container is context: True
Here is context: True
Nothing is None: True
Default works: True
Root is the application: True
Template is a template: True
Traverse_subpath exists and is empty: True
Request is a request: True
User is manager: True
Options exist: True
Attrs exist: True
Repeat exists: True
Loop exists: True
Modules exists: True
Make sure that ZPT's aren't a security-less zone. Let's logout and
try to access some protected stuff. Let's not forgot to login again,
of course:
>>> from AccessControl import allow_module
>>> allow_module('smtpd')
>>> self.logout()
>>> view = self.folder.unrestrictedTraverse('testoid/security.html')
>>> print(view())
<div>NoneType</div>
<div>smtpd</div>
>>> self.login('manager')
Test pages registered through the <five:pagesFromDirectory /> directive:
>>> view = self.folder.unrestrictedTraverse('testoid/dirpage1')
>>> print(view())
<html>
<p>This is page 1</p>
</html>
>>> view = self.folder.unrestrictedTraverse('testoid/dirpage2')
>>> print(view())
<html>
<p>This is page 2</p>
</html>
Low-level security
------------------
This tests security on a low level (functional pages test has
high-level security tests). Let's manually look up a protected view:
>>> from zope.component import getMultiAdapter
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> view = getMultiAdapter((self.folder.testoid, request), name=u'eagle.txt')
With the current browserDefault implementation permissions are checked for the
object, and not for the attribute, but it is more robust to protect both:
>>> view.__ac_permissions__
(('View management screens', ('', 'eagle')),)
The view's __roles__ attribute can be evaluated correctly:
(We have to use aq_acquire here instead of a simple getattr. The
reason is that __roles__ actually is an object that expects being
called through the __of__ protocol upon which it renders the roles
tuple. aq_acquire will trigger this for us. This isn't a problem,
really, because AccessControl ends up using aq_acquire anyway, so it
Just Works.)
>>> from Acquisition import aq_acquire
>>> aq_acquire(view, '__roles__')
('Manager',)
>>> aq_acquire(view, 'eagle__roles__')
('Manager',)
Other attributes are private:
>>> from AccessControl import ACCESS_PRIVATE
>>> aq_acquire(view, 'mouse__roles__') is ACCESS_PRIVATE
True
In zope.browserpage this is just protected with the specified permission. Not
sure if this has to be private in Zope 2:
>>> aq_acquire(view, 'publishTraverse__roles__') is ACCESS_PRIVATE
True
Check to see if view's context properly acquires its true
parent
>>> from Acquisition import aq_parent, aq_base, aq_inner
>>> context = view.context
Check the wrapper type
>>> from Acquisition import ImplicitAcquisitionWrapper
>>> type(context) == ImplicitAcquisitionWrapper
True
The parent of the view is the view's context:
>>> view.__parent__ == view.context
True
>>> aq_parent(view) == view.context
True
The direct parent of the context is
>>> aq_inner(context).__parent__.__class__
<class 'OFS.Folder.Folder'>
>>> print(aq_inner(context).__parent__.getId())
test_folder_1_
C methods work the same
>>> aq_parent(aq_inner(context)).__class__
<class 'OFS.Folder.Folder'>
>>> print(aq_parent(aq_inner(context)).getId())
test_folder_1_
The same applies to a view registered with <browser:view /> instead of
<browser:page />
>>> request = TestRequest()
>>> view = getMultiAdapter((self.folder.testoid, request), name=u'permission_view')
>>> view.__ac_permissions__
(('View management screens', ('',)),)
>>> aq_acquire(view, '__roles__')
('Manager',)
>>> context = view.context
>>> from Acquisition import ImplicitAcquisitionWrapper
>>> type(context) == ImplicitAcquisitionWrapper
True
>>> view.__parent__ == view.context
True
>>> aq_parent(view) == view.context
True
>>> print(aq_inner(context).__parent__.getId())
test_folder_1_
>>> print(aq_parent(aq_inner(context)).getId())
test_folder_1_
Make sure that methods which are not included in the allowed interface or
attributes, but which already had security declarations from a base class,
don't get those declarations overridden to be private. (The roles for
restrictedTraverse should be None, indicating it is public.)
>>> view.restrictedTraverse__roles__
Other
-----
Make sure that browser pages can be overridden:
>>> zcml.load_string('''
... <includeOverrides
... package="Products.Five.browser.tests"
... file="overrides.zcml" />
... ''')
>>> view = self.folder.unrestrictedTraverse('testoid/overridden_view')
>>> view() == 'The mouse has been eaten by the eagle'
True
Test traversal to resources from within ZPT pages:
>>> zcml.load_config('resource.zcml', package=Products.Five.browser.tests)
>>> view = self.folder.unrestrictedTraverse('testoid/parakeet.html')
>>> print(view())
<html><body><img alt=""
src="http://nohost/test_folder_1_/testoid/++resource++pattern.png" /></body></html>
Security settings of the base class are combined with new settings based on the
view permission:
>>> from AccessControl import ACCESS_PUBLIC
>>> view = self.folder.unrestrictedTraverse('testoid/protected_class_page')
>>> view.__parent__ == self.folder.testoid
True
>>> view.__ac_permissions__
(('View', ('protected_method',)), ('View management screens', ('', '__call__')))
>>> aq_acquire(view, '__call____roles__')
('Manager',)
>>> aq_acquire(view, 'public_method__roles__') is ACCESS_PUBLIC
True
>>> aq_acquire(view, 'protected_method__roles__')
['Manager', 'test_role_1_', 'Manager', 'Anonymous']
>>> aq_acquire(view, 'private_method__roles__') is ACCESS_PRIVATE
True
>>> view = self.folder.unrestrictedTraverse('testoid/protected_template_class_page')
>>> view.__parent__ == self.folder.testoid
True
>>> view.__ac_permissions__
(('View', ('protected_method',)), ('View management screens', ('', '__call__')))
>>> aq_acquire(view, '__call____roles__')
('Manager',)
>>> aq_acquire(view, 'public_method__roles__') is ACCESS_PUBLIC
True
>>> aq_acquire(view, 'protected_method__roles__')
['Manager', 'test_role_1_', 'Manager', 'Anonymous']
>>> aq_acquire(view, 'private_method__roles__') is ACCESS_PRIVATE
True
>>> view = self.folder.unrestrictedTraverse('testoid/protected_class_view')
>>> view.__parent__ == self.folder.testoid
True
>>> view.__ac_permissions__
(('View', ('protected_method',)), ('View management screens', ('',)))
>>> getattr(view, '__call____roles__', False)
False
>>> aq_acquire(view, 'public_method__roles__') is ACCESS_PUBLIC
True
>>> aq_acquire(view, 'protected_method__roles__')
['Manager', 'test_role_1_', 'Manager', 'Anonymous']
>>> aq_acquire(view, 'private_method__roles__') is ACCESS_PRIVATE
True
Clean up
--------
>>> from zope.component.testing import tearDown
>>> tearDown()