strelka 0.0.1.pre.187 → 0.0.1.pre.193
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/ChangeLog +94 -26
- data/Manifest.txt +4 -2
- data/examples/apps/ws-echo +17 -0
- data/lib/strelka/app.rb +26 -24
- data/lib/strelka/app/auth.rb +2 -1
- data/lib/strelka/app/errors.rb +1 -1
- data/lib/strelka/app/filters.rb +1 -1
- data/lib/strelka/app/negotiation.rb +1 -1
- data/lib/strelka/app/parameters.rb +2 -2
- data/lib/strelka/app/restresources.rb +1 -1
- data/lib/strelka/app/routing.rb +3 -2
- data/lib/strelka/app/sessions.rb +3 -3
- data/lib/strelka/app/templating.rb +3 -3
- data/lib/strelka/authprovider.rb +2 -10
- data/lib/strelka/behavior/plugin.rb +3 -3
- data/lib/strelka/httprequest.rb +5 -2
- data/lib/strelka/httprequest/session.rb +3 -2
- data/lib/strelka/httpresponse/session.rb +8 -9
- data/lib/strelka/mixins.rb +15 -0
- data/lib/strelka/plugins.rb +257 -0
- data/lib/strelka/router/default.rb +27 -2
- data/lib/strelka/session.rb +20 -2
- data/lib/strelka/session/db.rb +20 -10
- data/lib/strelka/session/default.rb +41 -18
- data/spec/lib/helpers.rb +1 -1
- data/spec/strelka/app/auth_spec.rb +1 -1
- data/spec/strelka/app/errors_spec.rb +1 -1
- data/spec/strelka/app/filters_spec.rb +1 -1
- data/spec/strelka/app/negotiation_spec.rb +1 -1
- data/spec/strelka/app/parameters_spec.rb +1 -1
- data/spec/strelka/app/restresources_spec.rb +1 -1
- data/spec/strelka/app/routing_spec.rb +4 -1
- data/spec/strelka/app/sessions_spec.rb +63 -17
- data/spec/strelka/app/templating_spec.rb +1 -1
- data/spec/strelka/app_spec.rb +13 -5
- data/spec/strelka/httprequest/session_spec.rb +44 -23
- data/spec/strelka/httprequest_spec.rb +21 -0
- data/spec/strelka/httpresponse/session_spec.rb +143 -0
- data/spec/strelka/{app/plugins_spec.rb → plugins_spec.rb} +64 -53
- data/spec/strelka/router/default_spec.rb +15 -0
- data/spec/strelka/router/exclusive_spec.rb +14 -0
- data/spec/strelka/session/db_spec.rb +11 -0
- data/spec/strelka/session/default_spec.rb +10 -2
- metadata +119 -37
- metadata.gz.sig +0 -0
- data/lib/strelka/app/plugins.rb +0 -284
data/lib/strelka/session.rb
CHANGED
@@ -61,6 +61,14 @@ class Strelka::Session
|
|
61
61
|
end
|
62
62
|
|
63
63
|
|
64
|
+
### Fetch the session ID from the given +request, returning +nil+ if it doesn't
|
65
|
+
### have any session attributes. You should override this and provide the
|
66
|
+
### code which fetches the ID from the +request+.
|
67
|
+
def self::get_existing_session_id( request )
|
68
|
+
return nil
|
69
|
+
end
|
70
|
+
|
71
|
+
|
64
72
|
### Fetch the session ID from the given +request+, or create a new one if the
|
65
73
|
### request is +nil+ or doesn't have the necessary attributes. You should
|
66
74
|
### override this, as the default implementation just returns +nil+.
|
@@ -106,13 +114,23 @@ class Strelka::Session
|
|
106
114
|
end
|
107
115
|
|
108
116
|
|
117
|
+
### Return +true+ if the given +request+ has a valid session token, and that
|
118
|
+
### token corresponds to an existing session ID. You should override this if
|
119
|
+
### there's a cheaper way to check for an existing session than just calling
|
120
|
+
### ::load_session_data.
|
121
|
+
def self::has_session_for?( request )
|
122
|
+
id = self.get_existing_session_id( request ) or return false
|
123
|
+
return true if self.load( id )
|
124
|
+
end
|
125
|
+
|
126
|
+
|
109
127
|
#################################################################
|
110
128
|
### I N S T A N C E M E T H O D S
|
111
129
|
#################################################################
|
112
130
|
|
113
131
|
### Set up a new instance with the given +session_id+ and +initial_values+.
|
114
|
-
def initialize( session_id, initial_values={} ) # :notnew:
|
115
|
-
@session_id = session_id
|
132
|
+
def initialize( session_id=nil, initial_values={} ) # :notnew:
|
133
|
+
@session_id = session_id || self.class.get_session_id
|
116
134
|
end
|
117
135
|
|
118
136
|
|
data/lib/strelka/session/db.rb
CHANGED
@@ -15,7 +15,8 @@ require 'strelka/session/default'
|
|
15
15
|
#
|
16
16
|
class Strelka::Session::Db < Strelka::Session::Default
|
17
17
|
include Strelka::Loggable
|
18
|
-
extend Forwardable
|
18
|
+
extend Forwardable,
|
19
|
+
Strelka::MethodUtilities
|
19
20
|
|
20
21
|
# Class-instance variables
|
21
22
|
@table_name = :sessions
|
@@ -25,16 +26,17 @@ class Strelka::Session::Db < Strelka::Session::Default
|
|
25
26
|
:name => 'strelka-session'
|
26
27
|
}
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
##
|
30
|
+
# The Sequel dataset connection
|
31
|
+
singleton_attr_reader :db
|
31
32
|
|
32
|
-
|
33
|
-
|
33
|
+
##
|
34
|
+
# The Sequel dataset for the sessions table
|
35
|
+
singleton_attr_reader :dataset
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
##
|
38
|
+
# The configured session cookie parameters
|
39
|
+
singleton_attr_accessor :cookie_options
|
38
40
|
|
39
41
|
|
40
42
|
########################################################################
|
@@ -94,7 +96,7 @@ class Strelka::Session::Db < Strelka::Session::Default
|
|
94
96
|
|
95
97
|
|
96
98
|
### Save the given +data+ associated with the +session_id+ to the DB.
|
97
|
-
def self::save_session_data( session_id, data )
|
99
|
+
def self::save_session_data( session_id, data={} )
|
98
100
|
self.db.transaction do
|
99
101
|
self.delete_session_data( session_id.to_s )
|
100
102
|
self.dataset.insert(
|
@@ -111,5 +113,13 @@ class Strelka::Session::Db < Strelka::Session::Default
|
|
111
113
|
self.dataset.filter( :session_id => session_id ).delete
|
112
114
|
end
|
113
115
|
|
116
|
+
|
117
|
+
### Return +true+ if the given +request+ has a session token which corresponds
|
118
|
+
### to an existing session key.
|
119
|
+
def self::has_session_for?( request )
|
120
|
+
id = self.get_existing_session_id( request ) or return false
|
121
|
+
return !self.dataset.filter( :session_id => id ).empty?
|
122
|
+
end
|
123
|
+
|
114
124
|
end # class Strelka::Session::Db
|
115
125
|
|
@@ -18,31 +18,38 @@ require 'strelka/mixins'
|
|
18
18
|
# #[], #[]=, #delete, #key?
|
19
19
|
class Strelka::Session::Default < Strelka::Session
|
20
20
|
extend Forwardable,
|
21
|
+
Strelka::MethodUtilities,
|
21
22
|
Strelka::Delegation
|
22
23
|
include Strelka::Loggable,
|
23
24
|
Strelka::DataUtilities
|
24
25
|
|
26
|
+
# Default configuration
|
27
|
+
DEFAULT_COOKIE_OPTIONS = {
|
28
|
+
:name => 'strelka-session'
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
|
25
32
|
# Class-instance variables
|
33
|
+
@cookie_options = DEFAULT_COOKIE_OPTIONS.dup
|
26
34
|
@sessions = {}
|
27
|
-
@cookie_options = {
|
28
|
-
:name => 'strelka-session'
|
29
|
-
}
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
##
|
37
|
+
# In-memory session store
|
38
|
+
singleton_attr_reader :sessions
|
34
39
|
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
##
|
41
|
+
# The configured session cookie parameters
|
42
|
+
singleton_attr_accessor :cookie_options
|
38
43
|
|
39
44
|
|
40
45
|
### Configure the session class with the given +options+, which should be a
|
41
46
|
### Hash or an object that has a Hash-like interface. Sets cookie options
|
42
47
|
### for the session if the +:cookie+ key is set.
|
43
|
-
def self::configure( options )
|
48
|
+
def self::configure( options=nil )
|
44
49
|
if options
|
45
50
|
self.cookie_options.merge!( options[:cookie] ) if options[:cookie]
|
51
|
+
else
|
52
|
+
self.cookie_options = DEFAULT_COOKIE_OPTIONS.dup
|
46
53
|
end
|
47
54
|
end
|
48
55
|
|
@@ -66,17 +73,35 @@ class Strelka::Session::Default < Strelka::Session
|
|
66
73
|
end
|
67
74
|
|
68
75
|
|
76
|
+
### Try to fetch a session ID from the specified +request+, returning +nil+
|
77
|
+
### if there isn't one.
|
78
|
+
def self::get_existing_session_id( request )
|
79
|
+
cookie = request.cookies[ self.cookie_options[:name] ] or return nil
|
80
|
+
|
81
|
+
if cookie.value =~ /^([[:xdigit:]]+)$/i
|
82
|
+
return $1.untaint
|
83
|
+
else
|
84
|
+
Strelka.log.warn "Request with a malformed session cookie: %p" % [ request ]
|
85
|
+
return nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
69
90
|
### Fetch the session ID from the given +request+, or create a new one if the
|
70
91
|
### request doesn't have the necessary attributes.
|
71
92
|
def self::get_session_id( request=nil )
|
72
|
-
id =
|
93
|
+
id = self.get_existing_session_id( request ) if request
|
94
|
+
return id || SecureRandom.hex
|
95
|
+
end
|
73
96
|
|
74
|
-
# Fetch and untaint the existing ID if it exists and looks valid
|
75
|
-
if request && (cookie = request.cookies[ self.cookie_options[:name] ])
|
76
|
-
id = $1.untaint if cookie.value =~ /^([[:xdigit:]]+)$/i
|
77
|
-
end
|
78
97
|
|
79
|
-
|
98
|
+
### Return +true+ if the given +request+ has a session token which corresponds
|
99
|
+
### to an existing session key.
|
100
|
+
def self::has_session_for?( request )
|
101
|
+
Strelka.log.debug "Checking request (%s/%d) for session." % [ request.sender_id, request.conn_id ]
|
102
|
+
id = self.get_existing_session_id( request ) or return false
|
103
|
+
Strelka.log.debug " got a session ID: %p" % [ id ]
|
104
|
+
return @sessions.key?( id )
|
80
105
|
end
|
81
106
|
|
82
107
|
|
@@ -86,8 +111,6 @@ class Strelka::Session::Default < Strelka::Session
|
|
86
111
|
|
87
112
|
### Create a new session store using the given +hash+ for initial values.
|
88
113
|
def initialize( session_id=nil, initial_values={} )
|
89
|
-
session_id ||= self.class.get_session_id
|
90
|
-
|
91
114
|
@hash = Hash.new {|h,k| h[k] = {} }
|
92
115
|
@hash.merge!( initial_values )
|
93
116
|
|
data/spec/lib/helpers.rb
CHANGED
@@ -13,7 +13,7 @@ require 'rspec'
|
|
13
13
|
require 'spec/lib/helpers'
|
14
14
|
|
15
15
|
require 'strelka'
|
16
|
-
require 'strelka/
|
16
|
+
require 'strelka/plugins'
|
17
17
|
require 'strelka/app/routing'
|
18
18
|
|
19
19
|
require 'strelka/behavior/plugin'
|
@@ -203,6 +203,7 @@ describe Strelka::App::Routing do
|
|
203
203
|
|
204
204
|
it "has its routes inherited by subclasses" do
|
205
205
|
@app.class_eval do
|
206
|
+
router :deep
|
206
207
|
get( '/info' ) {}
|
207
208
|
get( '/about' ) {}
|
208
209
|
get( '/origami' ) {}
|
@@ -220,6 +221,8 @@ describe Strelka::App::Routing do
|
|
220
221
|
subclass.routes.should include(
|
221
222
|
[ :GET, ['origami'], {action: subclass.instance_method(:GET_origami), options: {}} ]
|
222
223
|
)
|
224
|
+
|
225
|
+
subclass.routerclass.should == @app.routerclass
|
223
226
|
end
|
224
227
|
|
225
228
|
describe "that also uses the :parameters plugin" do
|
@@ -11,7 +11,7 @@ require 'rspec'
|
|
11
11
|
require 'spec/lib/helpers'
|
12
12
|
|
13
13
|
require 'strelka'
|
14
|
-
require 'strelka/
|
14
|
+
require 'strelka/plugins'
|
15
15
|
require 'strelka/app/sessions'
|
16
16
|
|
17
17
|
require 'strelka/behavior/plugin'
|
@@ -36,23 +36,50 @@ describe Strelka::App::Sessions do
|
|
36
36
|
it_should_behave_like( "A Strelka::App Plugin" )
|
37
37
|
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
describe "session-class loading" do
|
40
|
+
before( :all ) do
|
41
|
+
# First, hook the anonymous class up to the 'testing' name using the PluginFactory API
|
42
|
+
@test_session_class = Class.new( Strelka::Session ) do
|
43
|
+
class << self; attr_accessor :options; end
|
44
|
+
def self::configure( options )
|
45
|
+
@options = options
|
46
|
+
end
|
47
|
+
end
|
48
|
+
Strelka::Session.derivatives[ 'testing' ] = @test_session_class
|
49
|
+
end
|
43
50
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
Strelka::Session.derivatives[ 'testing' ] = test_session_class
|
51
|
+
after( :each ) do
|
52
|
+
Strelka::App::Sessions.instance_variable_set( :@session_class, nil )
|
53
|
+
end
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
|
55
|
+
it "has a default associated session class" do
|
56
|
+
Strelka::App::Sessions.session_class.should be_a( Class )
|
57
|
+
Strelka::App::Sessions.session_class.should < Strelka::Session
|
58
|
+
end
|
52
59
|
|
53
|
-
|
54
|
-
|
60
|
+
it "is can be configured to use a different session class" do
|
61
|
+
Strelka::App::Sessions.configure( :session_class => 'testing' )
|
62
|
+
Strelka::App::Sessions.session_class.should == @test_session_class
|
63
|
+
end
|
55
64
|
|
65
|
+
it "configures the configured session class with default options" do
|
66
|
+
Strelka::App::Sessions.configure( :session_class => 'testing' )
|
67
|
+
Strelka::App::Sessions.session_class.options.should == Strelka::App::Sessions::DEFAULT_OPTIONS
|
68
|
+
end
|
69
|
+
|
70
|
+
it "merges any config options for the configured session class" do
|
71
|
+
options = { 'cookie_name' => 'patience' }
|
72
|
+
Strelka::App::Sessions.configure( :session_class => 'testing', :options => options )
|
73
|
+
Strelka::App::Sessions.session_class.options.
|
74
|
+
should == Strelka::App::Sessions::DEFAULT_OPTIONS.merge( options )
|
75
|
+
end
|
76
|
+
|
77
|
+
it "uses the default session class if the config doesn't have a session section" do
|
78
|
+
Strelka::App::Sessions.configure
|
79
|
+
Strelka::App::Sessions.session_class.should be( Strelka::Session::Default )
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
56
83
|
|
57
84
|
describe "an including App" do
|
58
85
|
|
@@ -72,9 +99,11 @@ describe Strelka::App::Sessions do
|
|
72
99
|
super
|
73
100
|
end
|
74
101
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
102
|
+
def handle_request( req )
|
103
|
+
super do
|
104
|
+
req.session[ :test ] = 'session data'
|
105
|
+
req.response
|
106
|
+
end
|
78
107
|
end
|
79
108
|
end
|
80
109
|
end
|
@@ -107,6 +136,23 @@ describe Strelka::App::Sessions do
|
|
107
136
|
@app.session_namespace.should == :findizzle
|
108
137
|
end
|
109
138
|
|
139
|
+
it "extends the request and response classes" do
|
140
|
+
@app.install_plugins
|
141
|
+
Strelka::HTTPRequest.should < Strelka::HTTPRequest::Session
|
142
|
+
Strelka::HTTPResponse.should < Strelka::HTTPResponse::Session
|
143
|
+
end
|
144
|
+
|
145
|
+
it "sets the session namespace on requests" do
|
146
|
+
req = @request_factory.get( '/foom' )
|
147
|
+
res = @app.new.handle( req )
|
148
|
+
req.session_namespace.should == @app.default_appid
|
149
|
+
end
|
150
|
+
|
151
|
+
it "saves the session automatically" do
|
152
|
+
req = @request_factory.get( '/foom' )
|
153
|
+
res = @app.new.handle( req )
|
154
|
+
res.cookies.should include( Strelka::Session::Default.cookie_options[:name] )
|
155
|
+
end
|
110
156
|
|
111
157
|
end
|
112
158
|
|
data/spec/strelka/app_spec.rb
CHANGED
@@ -26,6 +26,7 @@ describe Strelka::App do
|
|
26
26
|
|
27
27
|
before( :all ) do
|
28
28
|
setup_logging( :fatal )
|
29
|
+
@initial_registry = Strelka::App.loaded_plugins.dup
|
29
30
|
@request_factory = Mongrel2::RequestFactory.new( route: '/mail' )
|
30
31
|
Mongrel2::Config.db = Mongrel2::Config.in_memory_db
|
31
32
|
Mongrel2::Config.init_database
|
@@ -39,6 +40,7 @@ describe Strelka::App do
|
|
39
40
|
end
|
40
41
|
|
41
42
|
before( :each ) do
|
43
|
+
Strelka::App.loaded_plugins.clear
|
42
44
|
@app = Class.new( Strelka::App ) do
|
43
45
|
def initialize( appid=TEST_APPID, sspec=TEST_SEND_SPEC, rspec=TEST_RECV_SPEC )
|
44
46
|
super
|
@@ -55,6 +57,7 @@ describe Strelka::App do
|
|
55
57
|
end
|
56
58
|
|
57
59
|
after( :all ) do
|
60
|
+
Strelka::App.loaded_plugins = @initial_registry
|
58
61
|
reset_logging()
|
59
62
|
end
|
60
63
|
|
@@ -174,7 +177,8 @@ describe Strelka::App do
|
|
174
177
|
|
175
178
|
# make a plugin that always 304s and install it
|
176
179
|
not_modified_plugin = Module.new do
|
177
|
-
|
180
|
+
def self::name; "Strelka::App::NotModified"; end
|
181
|
+
extend Strelka::Plugin
|
178
182
|
def handle_request( r )
|
179
183
|
finish_with( HTTP::NOT_MODIFIED, "Unchanged." )
|
180
184
|
fail "Shouldn't be reached."
|
@@ -193,7 +197,8 @@ describe Strelka::App do
|
|
193
197
|
it "creates a simple response body for status responses that can have them" do
|
194
198
|
# make an auth plugin that always denies requests
|
195
199
|
forbidden_plugin = Module.new do
|
196
|
-
|
200
|
+
def self::name; "Strelka::App::Forbidden"; end
|
201
|
+
extend Strelka::Plugin
|
197
202
|
def handle_request( r )
|
198
203
|
finish_with( HTTP::FORBIDDEN, "You aren't allowed to look at that." )
|
199
204
|
fail "Shouldn't be reached."
|
@@ -213,7 +218,8 @@ describe Strelka::App do
|
|
213
218
|
it "uses the specified content type for error responses" do
|
214
219
|
# make an auth plugin that always denies requests
|
215
220
|
forbidden_plugin = Module.new do
|
216
|
-
|
221
|
+
def self::name; "Strelka::App::Forbidden"; end
|
222
|
+
extend Strelka::Plugin
|
217
223
|
def handle_request( r )
|
218
224
|
finish_with( HTTP::FORBIDDEN, "You aren't allowed to look at that.",
|
219
225
|
:content_type => 'text/html' )
|
@@ -350,7 +356,8 @@ describe Strelka::App do
|
|
350
356
|
it "provides a plugin hook for plugins to manipulate the request before handling it" do
|
351
357
|
# make a fixup plugin that adds a custom x- header to the request
|
352
358
|
header_fixup_plugin = Module.new do
|
353
|
-
|
359
|
+
def self::name; "Strelka::App::HeaderFixup"; end
|
360
|
+
extend Strelka::Plugin
|
354
361
|
def fixup_request( r )
|
355
362
|
r.headers[:x_funted_by] = 'Cragnux/1.1.3'
|
356
363
|
super
|
@@ -375,7 +382,8 @@ describe Strelka::App do
|
|
375
382
|
it "provides a plugin hook for plugins to manipulate the response before it's returned to Mongrel2" do
|
376
383
|
# make a fixup plugin that adds a custom x- header to the response
|
377
384
|
header_fixup_plugin = Module.new do
|
378
|
-
|
385
|
+
def self::name; "Strelka::App::HeaderFixup"; end
|
386
|
+
extend Strelka::Plugin
|
379
387
|
def fixup_response( res )
|
380
388
|
res.headers.x_funted_by = 'Cragnux/1.1.3'
|
381
389
|
super
|