strelka 0.0.1.pre148 → 0.0.1.pre177
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +1294 -0
- data/IDEAS.rdoc +6 -0
- data/Manifest.txt +20 -0
- data/README.rdoc +8 -2
- data/Rakefile +9 -7
- data/examples/auth-demo.rb +32 -0
- data/examples/auth-demo2.rb +37 -0
- data/examples/auth-form.tmpl +10 -0
- data/examples/auth-success.tmpl +3 -0
- data/examples/config.yml +12 -0
- data/examples/examples.css +4 -0
- data/examples/examples.html +31 -0
- data/examples/gen-config.rb +5 -2
- data/examples/layout.tmpl +31 -0
- data/lib/strelka/app/auth.rb +480 -0
- data/lib/strelka/app/sessions.rb +8 -6
- data/lib/strelka/app/templating.rb +78 -17
- data/lib/strelka/app.rb +13 -5
- data/lib/strelka/authprovider/basic.rb +134 -0
- data/lib/strelka/authprovider/hostaccess.rb +91 -0
- data/lib/strelka/authprovider.rb +122 -0
- data/lib/strelka/cookie.rb +1 -1
- data/lib/strelka/cookieset.rb +1 -1
- data/lib/strelka/httprequest/auth.rb +31 -0
- data/lib/strelka/logging.rb +69 -14
- data/lib/strelka/mixins.rb +35 -65
- data/lib/strelka/session/db.rb +115 -0
- data/lib/strelka/session/default.rb +38 -49
- data/lib/strelka/session.rb +1 -1
- data/lib/strelka.rb +4 -1
- data/spec/lib/helpers.rb +8 -3
- data/spec/strelka/app/auth_spec.rb +367 -0
- data/spec/strelka/authprovider/basic_spec.rb +192 -0
- data/spec/strelka/authprovider/hostaccess_spec.rb +70 -0
- data/spec/strelka/authprovider_spec.rb +99 -0
- data/spec/strelka/cookie_spec.rb +1 -1
- data/spec/strelka/httprequest/auth_spec.rb +55 -0
- data/spec/strelka/httprequest/session_spec.rb +63 -3
- data/spec/strelka/session/db_spec.rb +85 -0
- data/spec/strelka/session/default_spec.rb +5 -51
- data.tar.gz.sig +0 -0
- metadata +88 -57
- metadata.gz.sig +0 -0
@@ -0,0 +1,480 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set nosta noet ts=4 sw=4:
|
3
|
+
#encoding: utf-8
|
4
|
+
|
5
|
+
require 'strelka' unless defined?( Strelka )
|
6
|
+
require 'strelka/app' unless defined?( Strelka::App )
|
7
|
+
|
8
|
+
require 'strelka/httprequest/auth'
|
9
|
+
require 'strelka/authprovider'
|
10
|
+
|
11
|
+
|
12
|
+
# Pluggable authentication and authorization for Strelka applications.
|
13
|
+
#
|
14
|
+
# Enabling the +:auth+ plugin by default causes all requests to your
|
15
|
+
# handler to go through an authentication and authorization provider
|
16
|
+
# first. This provider checks the request for the necessary credentials,
|
17
|
+
# then either forwards it on if sufficient conditions are met, or
|
18
|
+
# responds with the appropriate 4xx status response.
|
19
|
+
#
|
20
|
+
# The conditions are broken down into two stages:
|
21
|
+
#
|
22
|
+
# * Authentication -- the client is who they say they are
|
23
|
+
# * Authorization -- the client is allowed to access the resources in question
|
24
|
+
#
|
25
|
+
# Auth providers are plugins that are named <tt>Strelka::AuthProvider::<MyType></tt>, and
|
26
|
+
# inherit from Strelka::AuthProvider. In order for them to be discoverable, each one
|
27
|
+
# should be in a file named <tt>lib/strelka/authprovider/<mytype>.rb</tt>. They
|
28
|
+
# can implement one or both of the stages; see the API docs for Strelka::AuthProvider
|
29
|
+
# for details on how to write your own plugin.
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# == Applying Authentication
|
33
|
+
#
|
34
|
+
# The default authentication policy is to require authentification from every
|
35
|
+
# request, but sometimes you may wish to narrow the restrictions a bit.
|
36
|
+
#
|
37
|
+
# === Relaxing \Auth for A Few Methods
|
38
|
+
#
|
39
|
+
# Sometimes you want to expose just one or two resources to the world, say in the
|
40
|
+
# case of a REST API that includes the authentication endpoint. Obviously, clients
|
41
|
+
# can't be authenticated until after they send their request that authenticates
|
42
|
+
# them, so you can expose just the +/login+ URI by using the 'no_auth_for' directive:
|
43
|
+
#
|
44
|
+
# class MyService < Strelka::App
|
45
|
+
# plugins :auth
|
46
|
+
# no_auth_for '/login'
|
47
|
+
#
|
48
|
+
# # ...
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# A String or a Regexp argument will be used to match against the request's
|
52
|
+
# {#app_path}[rdoc-ref:Strelka::HTTPRequest#app_path] (the path of the request
|
53
|
+
# URI with the Mongrel2 route omitted), and any requests which match are sent
|
54
|
+
# along as-is.
|
55
|
+
#
|
56
|
+
# If you require some more-complex criteria for determining if the request should
|
57
|
+
# skip the auth plugin, you can provide a block to +no_auth_for+ instead.
|
58
|
+
#
|
59
|
+
# # Allow requests from 'localhost' without auth, but require it from
|
60
|
+
# # everywhere else
|
61
|
+
# no_auth_for do |request|
|
62
|
+
# return 'internal-user' if request.header.x_forwarded_for == '127.0.0.1'
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# If the block returns a true-ish value, it will be used in the place of the
|
66
|
+
# authenticated username and the request will be handed to your app.
|
67
|
+
#
|
68
|
+
# Returning a false-ish value will go ahead with the rest of the auth
|
69
|
+
# processing.
|
70
|
+
#
|
71
|
+
# You can also combine String and Regexp arguments with a block to further refine
|
72
|
+
# the conditions:
|
73
|
+
#
|
74
|
+
# # Allow people to visit the seminar registration view without an account
|
75
|
+
# # if there are still slots open
|
76
|
+
# no_auth_for( '/register' ) do |request|
|
77
|
+
# if Seminars.any? {|seminar| !seminar.full? }
|
78
|
+
# 'register'
|
79
|
+
# else
|
80
|
+
#
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
#
|
85
|
+
# === Relaxing \Auth for All But a Few Methods
|
86
|
+
#
|
87
|
+
# Sometimes, though, you want just the opposite -- a few methods are available
|
88
|
+
# only to a select few, but the majority are unrestricted.
|
89
|
+
#
|
90
|
+
# To do this, use the 'require_auth_for' directive:
|
91
|
+
#
|
92
|
+
# class MyBlog < Strelka::App
|
93
|
+
# plugins :auth
|
94
|
+
# require_auth_for '/admin'
|
95
|
+
#
|
96
|
+
# # ...
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# Note that this inverts the usual behavior of the +:auth+ plugin: resources
|
100
|
+
# will, by default, be unguarded, so be sure you keep this in mind when
|
101
|
+
# using +require_auth_for+.
|
102
|
+
#
|
103
|
+
# Like +no_auth_for+, +require_auth_for+ can also take a block, and
|
104
|
+
# a true-ish return value will cause the request to pass through the
|
105
|
+
# AuthProvider.
|
106
|
+
#
|
107
|
+
# You can't use +no_auth_for+ and +require_auth_for+ in the same application; doing
|
108
|
+
# so will result in a ScriptError being raised when the application is loaded.
|
109
|
+
#
|
110
|
+
#
|
111
|
+
# == Adding Authorization
|
112
|
+
#
|
113
|
+
# Sometimes simple authentication isn't sufficient for accessing some
|
114
|
+
# resources, especially if you have some kind of permissions system that
|
115
|
+
# dictates who can see/use what. That's where the second stage of the
|
116
|
+
# authentification process comes into play: Authorization.
|
117
|
+
#
|
118
|
+
# The AuthProvider you're using may provide some form of general authorization
|
119
|
+
# itself (especially a custom one), but typically authorization is particular to an application and
|
120
|
+
# even particular actions within the application.
|
121
|
+
#
|
122
|
+
# To provide the particulars for your app's authorization, you can declare
|
123
|
+
# a block with the +authz_callback+ directive.
|
124
|
+
#
|
125
|
+
# authz_callback do |request, user, *other_auth_info|
|
126
|
+
# user.can?( request.app_path )
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# The block will be called once authentication has succeeded, and any general authorization has been
|
130
|
+
# checked. It will be called with at least the credentials object returned from the authentication
|
131
|
+
# stage and the request object. Some AuthProviders may opt to return authentication credentials
|
132
|
+
# as a User object of some kind (e.g., a database row, LDAP entry, model object, etc.), but the
|
133
|
+
# simpler ones just return the login of the authenticated +user+. The AuthProvider may also
|
134
|
+
# furnish additional useful arguments such as a database handle, permission objects, etc. to your
|
135
|
+
# authorization block. See the documentation for your chosen AuthProvider for details.
|
136
|
+
#
|
137
|
+
# == Customizing Failure
|
138
|
+
#
|
139
|
+
# As mentioned before, an authentication or authorization failure results in a
|
140
|
+
# 4xx status response. By default Strelka will present this back to the
|
141
|
+
# browser as a simple error response, but oftentimes you will want to customize
|
142
|
+
# it to look a little nicer, or to behave in a more-intuitive way.
|
143
|
+
# The easiest way to do this is to use the {:errors}[rdoc-ref:Strelka::App::Errors]
|
144
|
+
# plugin.
|
145
|
+
#
|
146
|
+
# === Redirecting to a Form
|
147
|
+
#
|
148
|
+
# If you're using form-based session authentication (as opposed to basic
|
149
|
+
# auth, which has its own UI), you can rewrite the response to instruct
|
150
|
+
# the browser to go to a static HTML form instead:
|
151
|
+
#
|
152
|
+
# class FormAuthApp < Strelka::App
|
153
|
+
# plugins :errors, :auth, :sessions
|
154
|
+
# auth_provider :session
|
155
|
+
#
|
156
|
+
# on_status HTTP::AUTH_REQUIRED do |res, status|
|
157
|
+
# formuri = res.request.uri
|
158
|
+
# formuri.path = '/loginform.html'
|
159
|
+
#
|
160
|
+
# res.reset
|
161
|
+
# res.status = HTTP::SEE_OTHER
|
162
|
+
# res.content_type = 'text/plain'
|
163
|
+
# res.puts "This resource requires authentication."
|
164
|
+
# res.header.location = formuri
|
165
|
+
#
|
166
|
+
# return res
|
167
|
+
# end
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# === Responding With a Form
|
171
|
+
#
|
172
|
+
# With the addition of the {:templating}[rdoc-ref:Strelka::App::Templating] plugin,
|
173
|
+
# you can respond with the form directly instead:
|
174
|
+
#
|
175
|
+
# class TemplateFormAuthApp < Strelka::App
|
176
|
+
# plugins :auth, :errors, :templating
|
177
|
+
# auth_provider :session
|
178
|
+
#
|
179
|
+
# layout 'examples/layout.tmpl'
|
180
|
+
# templates \
|
181
|
+
# form: 'examples/auth-form.tmpl',
|
182
|
+
# success: 'examples/auth-success.tmpl'
|
183
|
+
#
|
184
|
+
# on_status HTTP::AUTH_REQUIRED, :form
|
185
|
+
#
|
186
|
+
# ### Handle any (authenticated) HTTP request
|
187
|
+
# def handle_request( req )
|
188
|
+
# return :success
|
189
|
+
# end
|
190
|
+
#
|
191
|
+
# end
|
192
|
+
#
|
193
|
+
# == Examples
|
194
|
+
#
|
195
|
+
# Here are a few more examples using a few different AuthProviders.
|
196
|
+
#
|
197
|
+
# # Guard every request the app does behind a simple passphrase
|
198
|
+
# class MyGuardedApp < Strelka::App
|
199
|
+
# plugins :auth
|
200
|
+
#
|
201
|
+
# auth_provider :passphrase
|
202
|
+
# end
|
203
|
+
#
|
204
|
+
# # Require LDAP authentication for one route
|
205
|
+
# class MyGuardedApp < Strelka::App
|
206
|
+
# plugins :auth, :routing
|
207
|
+
#
|
208
|
+
# auth_provider :ldap
|
209
|
+
# authz_callback do |user, request, directory|
|
210
|
+
# authgroup = directory.ou( :appperms ).cn( :guarded_app )
|
211
|
+
# authgroup.members.include?( user.dn )
|
212
|
+
# end
|
213
|
+
#
|
214
|
+
# authenticated %r{^/admin}
|
215
|
+
# end
|
216
|
+
#
|
217
|
+
# # Use a user table in a PostgreSQL database for authentication for
|
218
|
+
# # all routes except one
|
219
|
+
# class MyGuardedApp < Strelka::App
|
220
|
+
# plugins :auth
|
221
|
+
#
|
222
|
+
# auth_provider :sequel
|
223
|
+
# authz_callback do |user, request, db|
|
224
|
+
# db[:permissions].filter( :user_id => user[:id] ).
|
225
|
+
# filter( :permname => 'guardedapp' )
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# unauthenticated %r{^/auth}
|
229
|
+
#
|
230
|
+
# # Only authenticated users can use this
|
231
|
+
# post '/servers' do
|
232
|
+
# # ...
|
233
|
+
# end
|
234
|
+
# end
|
235
|
+
#
|
236
|
+
module Strelka::App::Auth
|
237
|
+
extend Strelka::App::Plugin,
|
238
|
+
Strelka::MethodUtilities,
|
239
|
+
Configurability
|
240
|
+
include Strelka::Loggable,
|
241
|
+
Strelka::Constants
|
242
|
+
|
243
|
+
run_before :routing, :restresources
|
244
|
+
run_after :templating, :errors, :sessions
|
245
|
+
|
246
|
+
|
247
|
+
# The name of the default plugin to use for authentication
|
248
|
+
DEFAULT_AUTH_PROVIDER = :hostaccess
|
249
|
+
|
250
|
+
|
251
|
+
# Class methods to add to app classes that enable Auth
|
252
|
+
module ClassMethods
|
253
|
+
|
254
|
+
@auth_provider = nil
|
255
|
+
@authz_callback = nil
|
256
|
+
@positive_auth_criteria = {}
|
257
|
+
@negative_auth_criteria = {}
|
258
|
+
|
259
|
+
##
|
260
|
+
# Arrays of criteria for applying and skipping auth for a request.
|
261
|
+
attr_reader :positive_auth_criteria, :negative_auth_criteria
|
262
|
+
|
263
|
+
|
264
|
+
### Get/set the authentication type.
|
265
|
+
def auth_provider( type=nil )
|
266
|
+
if type
|
267
|
+
@auth_provider = Strelka::AuthProvider.get_subclass( type )
|
268
|
+
elsif type.nil?
|
269
|
+
@auth_provider ||= Strelka::AuthProvider.get_subclass( DEFAULT_AUTH_PROVIDER )
|
270
|
+
end
|
271
|
+
|
272
|
+
return @auth_provider
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
### Register a function to call after the user successfully authenticates to check
|
277
|
+
### for authorization or other criteria. The arguments to the function depend on
|
278
|
+
### which authentication plugin is used. Returning +true+ from this function will
|
279
|
+
### cause authorization to succeed, while returning a false value causes it to fail
|
280
|
+
### with a FORBIDDEN response. If no callback is set, and the provider doesn't
|
281
|
+
### provide authorization
|
282
|
+
def authz_callback( callable=nil, &block )
|
283
|
+
if callable
|
284
|
+
@authz_callback = callable
|
285
|
+
elsif block
|
286
|
+
@authz_callback = block
|
287
|
+
end
|
288
|
+
|
289
|
+
return @authz_callback
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
### Returns +true+ if there are any criteria for determining whether or
|
294
|
+
### not a request needs auth.
|
295
|
+
def has_auth_criteria?
|
296
|
+
return self.has_positive_auth_criteria? || self.has_negative_auth_criteria?
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
### Returns +true+ if the app has been set up so that only some methods
|
301
|
+
### require auth.
|
302
|
+
def has_positive_auth_criteria?
|
303
|
+
return !self.positive_auth_criteria.empty?
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
### Returns +true+ if the app has been set up so that all methods but
|
308
|
+
### ones that match declared criteria require auth.
|
309
|
+
def has_negative_auth_criteria?
|
310
|
+
return !self.negative_auth_criteria.empty?
|
311
|
+
end
|
312
|
+
|
313
|
+
|
314
|
+
### Constrain auth to apply only to requests which match the given +criteria+,
|
315
|
+
### and/or the given +block+. The +criteria+ are either Strings or Regexps
|
316
|
+
### which are tested against
|
317
|
+
### {the request's #app_path}[rdoc-ref:Strelka::HTTPRequest#app_path]. The block
|
318
|
+
### should return a true-ish value if the request should undergo authentication
|
319
|
+
### and authorization.
|
320
|
+
### *NOTE:* using this declaration inverts the default security policy of
|
321
|
+
### restricting access to all requests.
|
322
|
+
def require_auth_for( *criteria, &block )
|
323
|
+
if self.has_negative_auth_criteria?
|
324
|
+
raise ScriptError,
|
325
|
+
"defining both positive and negative auth criteria is unsupported."
|
326
|
+
end
|
327
|
+
|
328
|
+
criteria << '' if criteria.empty?
|
329
|
+
block ||= Proc.new { true }
|
330
|
+
|
331
|
+
criteria.each do |pattern|
|
332
|
+
self.positive_auth_criteria[ pattern ] = block
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
|
337
|
+
### Contrain auth to apply to all requests *except* those that match the
|
338
|
+
### given +criteria+.
|
339
|
+
def no_auth_for( *criteria, &block )
|
340
|
+
if self.has_positive_auth_criteria?
|
341
|
+
raise ScriptError,
|
342
|
+
"defining both positive and negative auth criteria is unsupported."
|
343
|
+
end
|
344
|
+
|
345
|
+
criteria << '' if criteria.empty?
|
346
|
+
block ||= Proc.new { true }
|
347
|
+
|
348
|
+
criteria.each do |pattern|
|
349
|
+
self.negative_auth_criteria[ pattern ] = block
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
end # module ClassMethods
|
355
|
+
|
356
|
+
|
357
|
+
### Extension callback -- extend the HTTPRequest class with Auth
|
358
|
+
### support when this plugin is loaded.
|
359
|
+
def self::included( object )
|
360
|
+
Strelka.log.debug "Extending Request with Auth mixin"
|
361
|
+
Strelka::HTTPRequest.class_eval { include Strelka::HTTPRequest::Auth }
|
362
|
+
super
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
### Add an AuthProvider instance to the app.
|
367
|
+
def initialize( * )
|
368
|
+
super
|
369
|
+
@auth_provider = self.class.auth_provider.new( self )
|
370
|
+
end
|
371
|
+
|
372
|
+
|
373
|
+
######
|
374
|
+
public
|
375
|
+
######
|
376
|
+
|
377
|
+
# The instance of (a subclass of) Strelka::AuthProvider that provides authentication
|
378
|
+
# logic for the app.
|
379
|
+
attr_reader :auth_provider
|
380
|
+
|
381
|
+
|
382
|
+
### Check authentication and authorization for requests that need it before
|
383
|
+
### sending them on.
|
384
|
+
def handle_request( request, &block )
|
385
|
+
self.log.debug "AuthProvider: %p" % [ self.auth_provider ]
|
386
|
+
|
387
|
+
self.authenticate_and_authorize( request ) if self.request_should_auth?( request )
|
388
|
+
|
389
|
+
super
|
390
|
+
end
|
391
|
+
|
392
|
+
|
393
|
+
#########
|
394
|
+
protected
|
395
|
+
#########
|
396
|
+
|
397
|
+
### Returns +true+ if the given +request+ requires authentication.
|
398
|
+
def request_should_auth?( request )
|
399
|
+
self.log.debug "Checking to see if Auth(entication/orization) should be applied for %s" %
|
400
|
+
[ request.app_path ]
|
401
|
+
|
402
|
+
# If there are positive criteria, return true if the request matches any of them,
|
403
|
+
# or false if they don't
|
404
|
+
if self.class.has_positive_auth_criteria?
|
405
|
+
criteria = self.class.positive_auth_criteria
|
406
|
+
self.log.debug " checking %d positive auth criteria" % [ criteria.length ]
|
407
|
+
return criteria.any? do |pattern, block|
|
408
|
+
self.log.debug " %p -> %p" % [ pattern, block ]
|
409
|
+
self.request_matches_criteria( request, pattern, &block )
|
410
|
+
end
|
411
|
+
|
412
|
+
# If there are negative criteria, return false if the request matches any of them,
|
413
|
+
# or true if they don't
|
414
|
+
elsif self.class.has_negative_auth_criteria?
|
415
|
+
criteria = self.class.negative_auth_criteria
|
416
|
+
self.log.debug " checking %d negative auth criteria" % [ criteria.length ]
|
417
|
+
return !criteria.any? do |pattern, block|
|
418
|
+
self.log.debug " %p -> %p" % [ pattern, block ]
|
419
|
+
self.request_matches_criteria( request, pattern, &block )
|
420
|
+
end
|
421
|
+
|
422
|
+
else
|
423
|
+
self.log.debug " no auth criteria; default to requiring auth"
|
424
|
+
return true
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
|
429
|
+
### Returns +true+ if there are positive auth criteria and the +request+ matches
|
430
|
+
### at least one of them.
|
431
|
+
def request_matches_criteria( request, pattern )
|
432
|
+
case pattern
|
433
|
+
when Regexp
|
434
|
+
self.log.debug " matching app_path with regexp: %p" % [ pattern ]
|
435
|
+
matchdata = pattern.match( request.app_path ) or return false
|
436
|
+
self.log.debug " calling the block"
|
437
|
+
return yield( request, matchdata )
|
438
|
+
|
439
|
+
when String
|
440
|
+
self.log.debug " matching app_path prefix: %p" % [ pattern ]
|
441
|
+
request.app_path.start_with?( pattern ) or return false
|
442
|
+
self.log.debug " calling the block"
|
443
|
+
return yield( request )
|
444
|
+
|
445
|
+
else
|
446
|
+
raise ScriptError, "don't know how to match a request with a %p" % [ pattern.class ]
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
|
451
|
+
### Process authentication and authorization for the specified +request+.
|
452
|
+
def authenticate_and_authorize( request )
|
453
|
+
credentials = self.provide_authentication( request )
|
454
|
+
request.authenticated_user = credentials
|
455
|
+
self.provide_authorization( credentials, request )
|
456
|
+
end
|
457
|
+
|
458
|
+
|
459
|
+
### If the AuthProvider does authentication, try to extract authenticated credentials
|
460
|
+
### from the +request+ and return them, throwing a :finish with
|
461
|
+
### a properly-constructed 401 (Auth required) response if that fails.
|
462
|
+
def provide_authentication( request )
|
463
|
+
provider = self.auth_provider
|
464
|
+
self.log.info "Authenticating request using provider: %p" % [ provider ]
|
465
|
+
return provider.authenticate( request )
|
466
|
+
end
|
467
|
+
|
468
|
+
|
469
|
+
### Process authorization for the given +credentials+ and +request+.
|
470
|
+
def provide_authorization( credentials, request )
|
471
|
+
provider = self.auth_provider
|
472
|
+
callback = self.class.authz_callback
|
473
|
+
|
474
|
+
self.log.info "Authorizing using credentials: %p, callback: %p" % [ credentials, callback ]
|
475
|
+
provider.authorize( credentials, request, &callback )
|
476
|
+
end
|
477
|
+
|
478
|
+
end # module Strelka::App::Auth
|
479
|
+
|
480
|
+
|
data/lib/strelka/app/sessions.rb
CHANGED
@@ -85,9 +85,7 @@ module Strelka::App::Sessions
|
|
85
85
|
Strelka::Constants
|
86
86
|
|
87
87
|
# Default options to pass to the session object
|
88
|
-
DEFAULT_OPTIONS = {
|
89
|
-
:cookie_name => 'strelka-session',
|
90
|
-
}
|
88
|
+
DEFAULT_OPTIONS = {}
|
91
89
|
|
92
90
|
# Configurability API -- specify which section of the config this class gets
|
93
91
|
config_key :sessions
|
@@ -101,7 +99,7 @@ module Strelka::App::Sessions
|
|
101
99
|
singleton_attr_writer :session_class
|
102
100
|
|
103
101
|
|
104
|
-
# Class methods and instance variables to add to classes with
|
102
|
+
# Class methods and instance variables to add to classes with sessions.
|
105
103
|
module ClassMethods # :nodoc:
|
106
104
|
|
107
105
|
# The namespace of the session that will be exposed to instances of this
|
@@ -135,7 +133,11 @@ module Strelka::App::Sessions
|
|
135
133
|
if config
|
136
134
|
self.session_class = Strelka::Session.get_subclass( config[:session_class] ) if
|
137
135
|
config.key?( :session_class )
|
138
|
-
|
136
|
+
if config[:options]
|
137
|
+
options.merge!( config[:options] ) do |key, oldval, newval|
|
138
|
+
oldval.merge( newval )
|
139
|
+
end
|
140
|
+
end
|
139
141
|
else
|
140
142
|
self.session_class = Strelka::Session.get_subclass( :default )
|
141
143
|
end
|
@@ -150,6 +152,7 @@ module Strelka::App::Sessions
|
|
150
152
|
def self::included( object )
|
151
153
|
Strelka.log.debug "Extending Request with Session mixin"
|
152
154
|
Strelka::HTTPRequest.class_eval { include Strelka::HTTPRequest::Session }
|
155
|
+
Strelka.log.debug "Extending Response with Session mixin"
|
153
156
|
Strelka::HTTPResponse.class_eval { include Strelka::HTTPResponse::Session }
|
154
157
|
super
|
155
158
|
end
|
@@ -169,7 +172,6 @@ module Strelka::App::Sessions
|
|
169
172
|
return super
|
170
173
|
end
|
171
174
|
|
172
|
-
|
173
175
|
end # module Strelka::App::Sessions
|
174
176
|
|
175
177
|
|
@@ -10,7 +10,82 @@ require 'strelka/app' unless defined?( Strelka::App )
|
|
10
10
|
require 'strelka/app/plugins'
|
11
11
|
|
12
12
|
|
13
|
-
#
|
13
|
+
# A templated content-generation plugin for Strelka::Apps. It uses the
|
14
|
+
# Inversion[http://deveiate.org/projects/Inversion] templating system.
|
15
|
+
#
|
16
|
+
# It adds:
|
17
|
+
#
|
18
|
+
# * a preloaded/cached template table
|
19
|
+
# * a mechanism for fetching templates from the table
|
20
|
+
# * a global layout template which is automatically wrapped around responses
|
21
|
+
#
|
22
|
+
# == Usage
|
23
|
+
#
|
24
|
+
# To use it, just load the <tt>:templating</tt> plugin in your app:
|
25
|
+
#
|
26
|
+
# plugins :templating
|
27
|
+
#
|
28
|
+
# and declare one or more templates that your application will use:
|
29
|
+
#
|
30
|
+
# templates :console => 'views/console.tmpl',
|
31
|
+
# :proctable => 'partials/proctable.tmpl'
|
32
|
+
#
|
33
|
+
# Then, inside your app, you can fetch a copy of one or more of the templates and
|
34
|
+
# return it as the reponse:
|
35
|
+
#
|
36
|
+
# def handle_request( req )
|
37
|
+
# super do
|
38
|
+
# res = request.response
|
39
|
+
#
|
40
|
+
# proctable = template :proctable
|
41
|
+
# proctable.processes = ProcessList.fetch
|
42
|
+
#
|
43
|
+
# tmpl = template :console
|
44
|
+
# tmpl.message = "Everything's up."
|
45
|
+
# tmpl.proctable = proctable
|
46
|
+
# res.body = tmpl
|
47
|
+
#
|
48
|
+
# return res
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# You can also just return the template if you don't need to do anything else to the
|
53
|
+
# response.
|
54
|
+
#
|
55
|
+
# When returning a template, either in the body of the response or directly, it will
|
56
|
+
# automatically set a few attributes for commonly-used objects:
|
57
|
+
#
|
58
|
+
# request :: The current Strelka::HTTPRequest
|
59
|
+
# strelka_version :: Strelka.version_string( true )
|
60
|
+
# mongrel2_version :: Mongrel2.version_string( true )
|
61
|
+
# route :: If the :routing plugin is loaded, this will be set to the
|
62
|
+
# 'routing_info' of the chosen route. See
|
63
|
+
# Strelka::Router#add_route for details.
|
64
|
+
#
|
65
|
+
# If your app will *only* be loading and returning a template without doing anything
|
66
|
+
# with it, you can return just its name:
|
67
|
+
#
|
68
|
+
# def handle_request( req )
|
69
|
+
# super { :console }
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# It will be loaded, set as the response body, and the above common objects added to it.
|
73
|
+
#
|
74
|
+
# === Layouts
|
75
|
+
#
|
76
|
+
# Very often, you'll want your app to share a common page layout. To accomplish this, you
|
77
|
+
# can declare a special layout template:
|
78
|
+
#
|
79
|
+
# layout 'layout.tmpl'
|
80
|
+
#
|
81
|
+
# Any template that you return will be set as the 'body' attribute of this layout
|
82
|
+
# template, and the layout rendered as the body of the response.
|
83
|
+
#
|
84
|
+
# Note that if you want any of the "common objects" from above with a layout template,
|
85
|
+
# you must use the <tt><?import ?></tt> directive to import them:
|
86
|
+
#
|
87
|
+
# <?import request, strelka_version, route ?>
|
88
|
+
#
|
14
89
|
module Strelka::App::Templating
|
15
90
|
include Strelka::Constants
|
16
91
|
extend Strelka::App::Plugin
|
@@ -20,7 +95,7 @@ module Strelka::App::Templating
|
|
20
95
|
|
21
96
|
|
22
97
|
# Class methods to add to classes with templating.
|
23
|
-
module ClassMethods
|
98
|
+
module ClassMethods
|
24
99
|
|
25
100
|
# The map of template names to template file paths.
|
26
101
|
@template_map = {}
|
@@ -97,21 +172,7 @@ module Strelka::App::Templating
|
|
97
172
|
|
98
173
|
|
99
174
|
### Intercept responses on the way back out and turn them into a Mongrel2::HTTPResponse
|
100
|
-
### with a String for its entity body.
|
101
|
-
###
|
102
|
-
### 1. A Mongrel2::Response with an Inversion::Template as its body.
|
103
|
-
### 2. An Inversion::Template by itself.
|
104
|
-
### 3. A Symbol that matches one of the keys of the registered templates.
|
105
|
-
###
|
106
|
-
### In all three of these cases, the return value will be a Mongrel2::Response with a
|
107
|
-
### body set to the rendered value of the template in question, and with its status
|
108
|
-
### set to '200 OK' unless it is already set to something else.
|
109
|
-
###
|
110
|
-
### If there is a registered layout template, and any of the three cases is true, the
|
111
|
-
### layout template is loaded, its #body attributes set to the content template,
|
112
|
-
### and its rendered output set as the body of the response instead.
|
113
|
-
###
|
114
|
-
### Every other response is returned without modification.
|
175
|
+
### with a String for its entity body.
|
115
176
|
def handle_request( request, &block )
|
116
177
|
response = super
|
117
178
|
|
data/lib/strelka/app.rb
CHANGED
@@ -239,7 +239,6 @@ class Strelka::App < Mongrel2::Handler
|
|
239
239
|
### for the plugin system. Without being overridden or extended by plugins, this
|
240
240
|
### method just returns the default Mongrel2::HTTPRequest#response.
|
241
241
|
def handle_request( request, &block )
|
242
|
-
self.log.debug "Strelka::App#handle_request"
|
243
242
|
if block
|
244
243
|
return super( request, &block )
|
245
244
|
else
|
@@ -302,15 +301,17 @@ class Strelka::App < Mongrel2::Handler
|
|
302
301
|
### Abort the current execution and return a response with the specified
|
303
302
|
### http_status code immediately. The specified +message+ will be logged,
|
304
303
|
### and will be included in any message that is returned as part of the
|
305
|
-
### response. The +
|
306
|
-
def finish_with( http_status, message,
|
307
|
-
status_info =
|
304
|
+
### response. The +headers+ hash will be used to set response headers.
|
305
|
+
def finish_with( http_status, message, headers={} )
|
306
|
+
status_info = { :status => http_status, :message => message, :headers => headers }
|
308
307
|
throw :finish, status_info
|
309
308
|
end
|
310
309
|
|
311
310
|
|
312
311
|
### Create a response to specified +request+ based on the specified +status_code+
|
313
312
|
### and +message+.
|
313
|
+
### :TODO: Document and test the :content_type status_info field.
|
314
|
+
### :TODO: Implement a way to set headers from the status_info.
|
314
315
|
def prepare_status_response( request, status_info )
|
315
316
|
status_code, message = status_info.values_at( :status, :message )
|
316
317
|
self.log.info "Non-OK response: %d (%s)" % [ status_code, message ]
|
@@ -322,10 +323,17 @@ class Strelka::App < Mongrel2::Handler
|
|
322
323
|
# Some status codes allow explanatory text to be returned; some forbid it. Append the
|
323
324
|
# message for those that allow one.
|
324
325
|
unless request.verb == :HEAD || HTTP::BODILESS_HTTP_RESPONSE_CODES.include?( status_code )
|
325
|
-
response.content_type =
|
326
|
+
response.content_type = 'text/plain'
|
326
327
|
response.puts( message )
|
327
328
|
end
|
328
329
|
|
330
|
+
# Now assign any headers to the response that are part of the status
|
331
|
+
if status_info.key?( :headers )
|
332
|
+
status_info[:headers].each do |hdr, value|
|
333
|
+
response.headers[ hdr ] = value
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
329
337
|
return response
|
330
338
|
end
|
331
339
|
|