-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathallnotes.txt
More file actions
287 lines (214 loc) · 12 KB
/
Copy pathallnotes.txt
File metadata and controls
287 lines (214 loc) · 12 KB
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
app1
====
No security; users can both show and delete any blog entry.
Features:
[1] Show and delete views stubbed out for purposes of brevity.
Noteworthy:
- When you visit /blog/id it will invoke the "show" view.
- When you visit /blog/id/delete it will invoke the "delete" view.
app2
====
Basic security; only authenticated users can delete blog entries. all others
can view blog entries.
New features:
[4] Wiring up login and logout views into config.
[2] Login view to service authorization checks. Just a stub, a real app
would require password checking.
[3] Logout view to forget login credentials.
[1] Imperative authorization code to check whether a logged in user can delete
Noteworthy:
- No frameworky bits, it's all your code.
- Imperative code to check whether a logged in user can delete must
be repeated everywhere to be useful.
- Married to cookie-based authentication throughout codebase (everywhere:
[1], [2], and [3]). A change to the authentication mechanism implies
visiting each place the imperative security checking is done.
- Authentication is "who you are". Authorization is "what you can do". In
this application, authentication and authorization are intertwined.
- While authorization typically depends on authentication, authentication is
almost always logically independent of authorization. Our application does
not take this into consideration, however. It has no abstractions that
would allow them to be changed independently.
- Curiosity: httpexceptions can either be raised or returned. Typically you
raise if you want the work done in the current transaction to be rolled
back. We raise HTTPForbidden above, but return HTTPFound, for this
notional reason.
app3
====
Same thing as app1.py. Basic security; only authenticated users can delete
blog entries and all others can view blog entries.
New features:
[3] We wire the authentication and authorization policies into config via
the Configurator.
[1] A user-defined Pyramid authentication policy. It implements the notional
Pyramid authentication policy API (IAuthenticationPolicy).
[2] A user-defined Pyramid authorization policy. It implements the notional
Pyramid authorization policy API (IAuthorizationPolicy).
[4] A "permission" argument to the ``view_config`` of blogentry_delete
replaces imperative authorization code that used to live in method body.
[5] We use ``pyramid.security.remember`` instead of setting a cookie by
hand in the login view.
[6] We use ``pyramid.security.forget`` instead of removing a cookie by hand
in the logout view.
Noteworthy:
- The new authentication policy [1] uses the same cookie-based authentication
scheme that we used in app2.py. We've just moved the code. In the login
view [5], we set the cookie via ``remember``, which calls our
authentication policy's ``remember`` method. Our ``logout`` method in
[6] causes our authentication policy's ``forget`` method to be called, which
expires the cookie.
- The ``permission`` attached to our ``delete`` method [4] will be the
``permission`` value passed to the ``permits`` method of our authorization
policy [2] before Pyramid attempts to execute the ``blogentry_delete``
view. . ``permission='delete'`` means "allow execution of this view if
the user who causes its invocation is permitted to delete"; it's the
authorization policy's job to answer this question.
- Our new authorization policy [2] performs very basic security checking
using the principals output by our authentication policy [1]. The return
value of ``effective_principals`` of our authentication policy [1] is the
``principals`` value passed to the ``permits`` method of our authorization
policy [2].
- Our new authorization policy [2] ``permits`` method checks if the principal
named ``Authenticated`` is in the ``principals`` passed and allows
the action if the permission under consideration is ``delete``.
- Authentication policy completely divorced from application code. Changing
to, e.g. session-based authentication or Basic HTTP Authentication would
require changing the authentication_policy argument in [3] to use a
different implementation, but app code would be otherwise unchanged.
- Pyramid is now made responsible for allowing or permitting the execution
of the ``delete`` view [4] instead of us. Authorization is normalized via
the ``permission`` attached to the view via the ``view_config`` decorator.
- If our authorization policy's ``permits`` method returns False, Pyramid
returns a 403 Forbidden error by default (configurable via a "forbidden
view").
- Definitely more frameworky. You couldn't successfully hand this code off
to someone who was unwilling to learn about Pyramid security. Trade-offs
are rampant when you outsource behavior to framework code.
Extra:
- ``permission`` is not a "view predicate". Two view configurations with
different permission arguments but otherwise the same will conflict.
- Show PYRAMID_DEBUG_AUTHORIZATION
app4
====
Exactly the same as app3.py except we ditch our authorization policy for
one provided by Pyramid.
New features:
[1] We use AuthTktAuthenticationPolicy instead of our homebrewed
DumbAuthenticationPolicy.
Noteworthy:
- AuthTktAuthenticationPolicy used in [1] is offered by Pyramid. It
implements the authentication policy API just like our homebrewed one
did, except it uses an Apache ``mod_auth_tkt`` "authentication ticket"
stored in a cookie. Nominally it's more secure as a result, but the real
win lies more in needing to maintain less security code. It's typically a
good idea to use a built-in authentication policy unless you need the
control.
- Pyramid has several built in authentication policies:
AuthTktAuthenticationPolicy, SessionAuthenticationPolicy,
BasicAuthenticationPolicy, RemoteUserAuthenticationPolicy.
- If your clients don't authenticate in a single way, you can combine
existing authentication policies using the ``pyramid_multiauth`` package.
For example, web service clients need basic authentication but
other clients need cookie-based auth.
- We continue to use our DumbAuthorizationPolicy unchanged.
Authentication and authorization policies are independent. If written
correctly, an authentication policy can be swapped for another
implementation as necessary without needing to change the application's
authorization policy. Likewise, an authorization policy can be
swapped for another without needing to change the authentication policy.
app5
====
Exactly the same as app2.py, app3.py, and app4.py. Basic security; only
authenticated users can delete blog entries. all others can view blog
entries.
New features:
[1] We use Pyramid's ACLAuthorizationPolicy instead of our homebrewed
DumbAuthorizationPolicy. We now pass a "root factory" into our
configurator. Our ACLAuthorizationPolicy will use instances
of objects produced by this root factory to determine whether or
not someone is permitted to execute a view.
[2] We supply a "RootFactory" class. Instances of this class possess an
ACL (as ``__acl__``). ACL stands for "Access Control List".
Noteworthy:
- This step is typically where people's brains start to steam. It's
because we've added yet another layer of abstraction by using the
ACLAuthorizationPolicy. This abstraction requires us to understand this
"root factory" thing and what the policy does with the "root" it produces.
- The root object produced by the root factory is a "resource". You can
think of a resource as a security context. It's called "root" because it's
presumed that there will be some number of resources arranged in a tree
with the object produced by the root factory at the root of the resource
tree. In the above application the root object has no children, so it's
the *only* resource that the system ever sees when the application is run.
More complex authorization requirements can make use of children.
- Every authorization policy is passed a *context* argument to its
``permits`` method. This argument will be a resource. Our
DumbAuthorizationPolicy ignored this argument. The ACLAuthorizationPolicy
does not; it uses the values in the ``__acl__`` attribute of the resource
its passed to determine whether the user possesses the permission relative
to this view execution.
- An ACL is an access control list. It is a sequence of ACEs (access control
entries). ``(Allow, Authenticated, 'edit')`` means Allow people who
possess the Authenticated principal (you can think of it as a group) to
edit. Only users who are explicitly allowed a permission in an ACL (by
mention of their userid or any group they're in such as "Authenticated")
will be permitted to execute a view protected by that permission.
- The benefit of all this indirection: we no longer own any imperative code
which does authorization or authentication. Our application uses entirely
declarative stuff to protect view execution. We only add ACLs and
permissions, and we have no conditions in our code. Declarative code
requires less testing, and you can typically be surer of the result as long
as you understand the abstraction.
app6
====
Not the same anymore. Only 'fred' can delete blog entries.
New features:
[1] We changed our ACL to allow only the principal id 'fred' to delete
blog entries. No longer an any authenticated principal delete
blog entries.
Noteworthy:
- "Principal" means "userid" or "groupid". It's just a string, typically
(although it can be any basic Python type).
app7
====
New outcome: Fred can delete all blog entries. Additionally, any
authenticated user can delete blog entry named '1'.
New features:
[1] A Resource class. It defines a __getitem__ that allows Pyramid to ask it
for a child. Instances of Resource also get a __parent__ attribute which
point at the parent of a resource (another Resource). Resource instances
may also have an ``__acl__``.
[2] We use a function named root_factory to return the root object, which is
a Resource instance. Before we return the root object, we give it a
single child named '1'.
[3] We've added a ``traverse`` argument to the ``add_route`` call for the
``blogentry_delete`` route. This argument composes the security traversal
path for this view. It uses the same pattern language as the route
pattern, so if the URL is ``/blog/1/delete``, the traversal path will
be ``/1``.
Noteworthy:
- We did not change our view code at all. The changes we made were made to
the root factory and to the route associated ``blogentry_delete``.
- Since we now have formed a security tree by returning an object that has
children from the root factory, and since the ``blogentry_delete`` route
now traverses the tree when it is matched, we can now protect individual
URLs differently from each other.
- In the above application, ``/blog/1/delete`` can be executed by an
authenticated user, but ``/blog/2/delete`` cannot. This is because the
effective ACL will allow the Authenticated user to delete as the result of
traversing to ``/1`` because there is such a node in the tree, but there
isn't any node in the security tree for ``/2`` so the root ACL is the only
ACL consulted. This is often referred to as "object level security", and
is often a requirement for CMS-like systems.
- The ACLAuthorizationPolicy *inherits* ACLs from further down the tree
unless an explicit Deny is encountered. This means that, since the root
resource has an __acl__ that says "Allow fred to delete", fred will have
the delete permission "everywhere" in the tree. Unless fred (or another
principal possesed by the requesting user) is explicitly denied in an ACL
somewhere.
- FYI, circref formed but not cleaned up when Resource instances are created
(a resource points to its parent using ``__parent__`` and the parent points
at its children via the ``children`` dictionary).
- Note that in a real application, the resource tree would typically be
persisted somewhere (maybe in a database, or in a pickle), and not
recomposed on every request.