strelka 0.0.1.pre.244 → 0.0.1.pre.252

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data.tar.gz.sig +0 -0
  2. data/ChangeLog +57 -3
  3. data/Manifest.txt +3 -4
  4. data/Rakefile +1 -1
  5. data/bin/strelka +4 -7
  6. data/examples/.env +4 -0
  7. data/examples/Procfile +8 -0
  8. data/examples/apps/auth-demo +3 -5
  9. data/examples/apps/auth-demo2 +10 -9
  10. data/{data/strelka → examples}/apps/hello-world +1 -2
  11. data/examples/apps/sessions-demo +2 -2
  12. data/examples/config.yml +12 -1
  13. data/examples/gen-config.rb +5 -6
  14. data/examples/templates/auth-form.tmpl +4 -0
  15. data/examples/templates/auth-success.tmpl +3 -1
  16. data/lib/strelka.rb +15 -21
  17. data/lib/strelka/app.rb +53 -31
  18. data/lib/strelka/app/auth.rb +260 -132
  19. data/lib/strelka/app/sessions.rb +25 -31
  20. data/lib/strelka/authprovider/basic.rb +33 -9
  21. data/lib/strelka/authprovider/hostaccess.rb +1 -1
  22. data/lib/strelka/constants.rb +0 -11
  23. data/lib/strelka/cookie.rb +17 -1
  24. data/lib/strelka/httpresponse/negotiation.rb +1 -1
  25. data/lib/strelka/session/db.rb +49 -25
  26. data/lib/strelka/session/default.rb +39 -19
  27. data/spec/lib/helpers.rb +3 -24
  28. data/spec/strelka/app/auth_spec.rb +461 -177
  29. data/spec/strelka/app/sessions_spec.rb +7 -26
  30. data/spec/strelka/app_spec.rb +3 -3
  31. data/spec/strelka/authprovider/basic_spec.rb +4 -4
  32. data/spec/strelka/httprequest/session_spec.rb +1 -1
  33. data/spec/strelka/httpresponse/session_spec.rb +1 -1
  34. data/spec/strelka/session/db_spec.rb +6 -1
  35. data/spec/strelka/session/default_spec.rb +3 -3
  36. metadata +7 -8
  37. metadata.gz.sig +2 -2
  38. data/examples/apps/ws-echo +0 -17
  39. data/lib/strelka/logging.rb +0 -301
  40. data/spec/strelka/logging_spec.rb +0 -74
@@ -64,16 +64,18 @@ require 'strelka/httpresponse/session'
64
64
  # # it uses
65
65
  # sessions:
66
66
  # session_class: default
67
- # options:
67
+ #
68
+ # defaultsession:
68
69
  # cookie_name: acme-session
69
70
  #
70
- # # Load a (hypothetical) Sequel-backed session type and point it
71
+ # # Use the database-backed session type and point it
71
72
  # # at a database
72
73
  # sessions:
73
- # session_class: sequel
74
- # options:
75
- # db: "postgres://pg.example.com/db01"
76
- # table: sessions
74
+ # session_class: db
75
+ #
76
+ # dbsession:
77
+ # connect: "postgres://pg.example.com/db01"
78
+ # table_name: sessions
77
79
  #
78
80
  # The +type+ value will be used to look up the class (see Strelka::Session
79
81
  # for more information about how this works), and the +options+ section
@@ -97,15 +99,6 @@ module Strelka::App::Sessions
97
99
  run_after :templating, :filters, :parameters
98
100
 
99
101
 
100
- # Default options to pass to the session object
101
- DEFAULT_OPTIONS = {}
102
-
103
-
104
- ##
105
- # What session class to use (Class object)
106
- singleton_attr_writer :session_class
107
-
108
-
109
102
  # Class methods and instance variables to add to classes with sessions.
110
103
  module ClassMethods # :nodoc:
111
104
 
@@ -132,33 +125,34 @@ module Strelka::App::Sessions
132
125
  end # module ClassMethods
133
126
 
134
127
 
128
+ # Default options to pass to the session object
129
+ CONFIG_DEFAULTS = {
130
+ session_class: 'default',
131
+ }
132
+
135
133
 
136
- ### Get the configured session class (Strelka::Session subclass)
137
- def self::session_class
138
- @session_class ||= Strelka::Session.get_subclass( :default )
139
- end
134
+ ##
135
+ # What session class to use (String, Symbol, or Class); passed to
136
+ # Strelka::Session.create.
137
+ singleton_attr_reader :session_class
140
138
 
141
139
 
142
140
  ### Configurability API -- set up session type and options with values from
143
141
  ### the +config+.
144
142
  def self::configure( config=nil )
145
- options = DEFAULT_OPTIONS.dup
146
-
147
143
  # Figure out which session class is going to be used, or choose a default one
148
144
  if config
149
- self.session_class = Strelka::Session.get_subclass( config[:session_class] ) if
150
- config.key?( :session_class )
151
- if config[:options]
152
- options.merge!( config[:options] ) do |key, oldval, newval|
153
- oldval.merge( newval )
154
- end
155
- end
145
+ self.session_class = config[:session_class]
156
146
  else
157
- self.session_class = Strelka::Session.get_subclass( :default )
147
+ self.session_class = CONFIG_DEFAULTS[:session_class]
158
148
  end
159
149
 
160
- # Configure the session class if it can be
161
- self.session_class.configure( options ) if self.session_class.respond_to?( :configure )
150
+ end
151
+
152
+
153
+ ### Get the configured session class (Strelka::Session subclass)
154
+ def self::session_class=( newclass )
155
+ @session_class = Strelka::Session.get_subclass( newclass )
162
156
  end
163
157
 
164
158
 
@@ -1,7 +1,7 @@
1
1
  # -*- ruby -*-
2
2
  # vim: set nosta noet ts=4 sw=4:
3
3
 
4
- require 'loggability'
4
+ require 'openssl'
5
5
  require 'configurability'
6
6
 
7
7
  require 'strelka' unless defined?( Strelka )
@@ -31,17 +31,26 @@ require 'strelka/mixins'
31
31
  # kmurgen: "MZj9+VhZ8C9+aJhmwp+kWBL76Vs="
32
32
  #
33
33
  class Strelka::AuthProvider::Basic < Strelka::AuthProvider
34
- extend Loggability,
35
- Configurability,
34
+ extend Configurability,
36
35
  Strelka::MethodUtilities
37
36
  include Strelka::Constants
38
37
 
39
38
  # Configurability API - set the section of the config
40
- config_key :auth
39
+ config_key :basicauth
41
40
 
41
+ # Configurability API -- configuration defaults
42
+ CONFIG_DEFAULTS = {
43
+ realm: nil,
44
+ users: {},
45
+ }
46
+
47
+ # The amount of work to do while encrypting -- higher number == more work == less suceptable
48
+ # to brute-force attacks
49
+ ENCRYPT_ITERATIONS = 20_000
50
+
51
+ # The Digest class to use when encrypting passwords
52
+ DIGEST_CLASS = OpenSSL::Digest::SHA256
42
53
 
43
- @users = nil
44
- @realm = nil
45
54
 
46
55
  ##
47
56
  # The Hash of users and their SHA1+Base64'ed passwords
@@ -54,11 +63,12 @@ class Strelka::AuthProvider::Basic < Strelka::AuthProvider
54
63
 
55
64
  ### Configurability API -- configure the auth provider instance.
56
65
  def self::configure( config=nil )
57
- if config
66
+ if config && config[:realm]
58
67
  self.log.debug "Configuring Basic authprovider: %p" % [ config ]
59
- self.realm = config['realm'] if config['realm']
60
- self.users = config['users'] if config['users']
68
+ self.realm = config[:realm]
69
+ self.users = config[:users]
61
70
  else
71
+ self.log.warn "No 'basicauth' config section; using the (empty) defaults"
62
72
  self.realm = nil
63
73
  self.users = {}
64
74
  end
@@ -132,4 +142,18 @@ class Strelka::AuthProvider::Basic < Strelka::AuthProvider
132
142
  finish_with( HTTP::AUTH_REQUIRED, "Requires authentication.", www_authenticate: header )
133
143
  end
134
144
 
145
+
146
+ ### (undocumented)
147
+ def encrypt( pass, salt=nil )
148
+ salt ||= OpenSSL::Random.random_bytes( 16 ) #store this with the generated value
149
+ iter = ENCRYPT_ITERATIONS
150
+ digest = DIGEST_CLASS.new
151
+ len = digest.digest_length
152
+
153
+ value = OpenSSL::PKCS5.pbkdf2_hmac( pass, salt, iter, len, digest )
154
+
155
+ return [ value, salt ].join( ':' )
156
+ end
157
+
158
+
135
159
  end # class Strelka::AuthProvider::Basic
@@ -45,7 +45,7 @@ class Strelka::AuthProvider::HostAccess < Strelka::AuthProvider
45
45
  self.allowed_netblocks = DEFAULT_ALLOWED_NETBLOCKS
46
46
 
47
47
  # Register this instance with Configurability
48
- config_key :auth
48
+ config_key :hostaccess
49
49
  end
50
50
 
51
51
 
@@ -11,17 +11,6 @@ module Strelka::Constants
11
11
  # Import Mongrel2's constants, too
12
12
  include Mongrel2::Constants
13
13
 
14
- # The data directory in the project if that exists, otherwise the gem datadir
15
- DEFAULT_DATADIR = if ENV['STRELKA_DATADIR']
16
- Pathname( ENV['STRELKA_DATADIR'] )
17
- elsif File.directory?( 'data/strelka' )
18
- Pathname( 'data/strelka' )
19
- elsif path = Gem.datadir('strelka')
20
- Pathname( path )
21
- else
22
- raise ScriptError, "can't find the data directory!"
23
- end
24
-
25
14
  # Extend Mongrel2's HTTP constants collection
26
15
  module HTTP
27
16
  include Mongrel2::Constants::HTTP
@@ -155,7 +155,23 @@ class Strelka::Cookie
155
155
  #################################################################
156
156
 
157
157
  ### Create a new Strelka::Cookie object with the specified +name+ and
158
- ### +values+.
158
+ ### +values+. Valid options are:
159
+ ###
160
+ ### [\version]
161
+ ### The cookie version. 0 (the default) is fine for most uses
162
+ ### [\domain]
163
+ ### The domain the cookie belongs to.
164
+ ### [\path]
165
+ ### The path the cookie applies to.
166
+ ### [\secure]
167
+ ### The cookie's 'secure' flag.
168
+ ### [\expires]
169
+ ### The cookie's expiration (a Time object). See expires= for valid
170
+ ### values.
171
+ ### [\max_age]
172
+ ### The lifetime of the cookie, in seconds.
173
+ ### [\comment]
174
+ ### Cookie comment; see #comment= for details.
159
175
  def initialize( name, values, options={} )
160
176
  values = [ values ] unless values.is_a?( Array )
161
177
  @name = name
@@ -334,7 +334,7 @@ module Strelka::HTTPResponse::Negotiation
334
334
  STRINGIFIERS.key?( mimetype ) && !new_body.is_a?( String )
335
335
 
336
336
  self.body = new_body
337
- self.content_type = mimetype
337
+ self.content_type = mimetype.dup # :TODO: Why is this frozen?
338
338
  self.status ||= HTTP::OK
339
339
 
340
340
  return true
@@ -15,29 +15,43 @@ require 'strelka/session/default'
15
15
  # any database that Sequel supports. It defaults to non-persistent, in memory Sqlite.
16
16
  #
17
17
  class Strelka::Session::Db < Strelka::Session::Default
18
- extend Loggability,
18
+ extend Configurability,
19
19
  Forwardable,
20
20
  Strelka::MethodUtilities
21
21
 
22
- # Loggability API -- set up logging under the 'strelka' log host
23
- log_to :strelka
24
22
 
23
+ ##
24
+ # Configurability API -- use the 'dbsession' section of the config
25
+ config_key :dbsession
26
+
27
+
28
+ # Configuration defaults
29
+ CONFIG_DEFAULTS = {
30
+ connect: nil,
31
+ table_name: 'sessions',
32
+ cookie_name: 'strelka-session',
33
+ cookie_options: {
34
+ expires: "+1d",
35
+ }
36
+ }
25
37
 
26
38
  # Class-instance variables
27
39
  @table_name = :sessions
28
40
  @db = nil
29
41
  @dataset = nil
30
- @cookie_options = {
31
- :name => 'strelka-session'
32
- }
42
+
33
43
 
34
44
  ##
35
45
  # The Sequel dataset connection
36
- singleton_attr_reader :db
46
+ singleton_attr_accessor :db
37
47
 
38
48
  ##
39
49
  # The Sequel dataset for the sessions table
40
- singleton_attr_reader :dataset
50
+ singleton_attr_accessor :dataset
51
+
52
+ ##
53
+ # The name of the table to use for storing sessions
54
+ singleton_attr_accessor :table_name
41
55
 
42
56
  ##
43
57
  # The configured session cookie parameters
@@ -51,21 +65,31 @@ class Strelka::Session::Db < Strelka::Session::Default
51
65
  ### Configure the session class with the given +options+, which should be a
52
66
  ### Hash or an object that has a Hash-like interface.
53
67
  ###
54
- ### Valid options:
55
- ### cookie -> A hash that contains valid Strelka::Cookie options
56
- ### connect -> The Sequel connection string
57
- ### table_name -> The name of the sessions table
58
- def self::configure( options={} )
59
- options ||= {}
60
-
61
- self.cookie_options.merge!( options[:cookie] ) if options[:cookie]
62
- @table_name = options[:table_name] || :sessions
63
-
64
- @db = options[ :connect ].nil? ?
65
- Mongrel2::Config.in_memory_db :
66
- Sequel.connect( options[:connect] )
67
- @db.logger = Loggability[ Mongrel2 ].proxy_for( @db )
68
+ ### Valid options (in addition to those ):
69
+ ###
70
+ ### [cookie_name]::
71
+ ### The name of the cookie to use for the session ID
72
+ ### [cookie_options]::
73
+ ### Options to pass to Strelka::Cookie's constructor.
74
+ ### [connect]::
75
+ ### The Sequel connection string; if nil, an in-memory DB will be used.
76
+ ### [table_name]::
77
+ ### The name of the sessions table. Defaults to 'sessions'.
78
+ def self::configure( options=nil )
79
+ super
80
+
81
+ if options
82
+ self.table_name = options[:table_name]
83
+ self.db = options[ :connect ].nil? ?
84
+ Mongrel2::Config.in_memory_db :
85
+ Sequel.connect( options[:connect] )
86
+ else
87
+ self.table_name = CONFIG_DEFAULTS[:table_name]
88
+ self.db = Mongrel2::Config.in_memory_db
89
+ end
68
90
 
91
+ self.db.logger = Loggability[ Mongrel2 ].proxy_for( self.db )
92
+ self.db.sql_log_level = :debug
69
93
  self.initialize_sessions_table
70
94
  end
71
95
 
@@ -74,12 +98,12 @@ class Strelka::Session::Db < Strelka::Session::Default
74
98
  ### attribute to a Sequel dataset on the configured DB table.
75
99
  ###
76
100
  def self::initialize_sessions_table
77
- if self.db.table_exists?( @table_name )
101
+ if self.db.table_exists?( self.table_name )
78
102
  self.log.debug "Using existing sessions table for %p" % [ db ]
79
103
 
80
104
  else
81
105
  self.log.debug "Creating new sessions table for %p" % [ db ]
82
- self.db.create_table( @table_name ) do
106
+ self.db.create_table( self.table_name ) do
83
107
  text :session_id, :index => true
84
108
  text :session
85
109
  timestamp :created
@@ -88,7 +112,7 @@ class Strelka::Session::Db < Strelka::Session::Default
88
112
  end
89
113
  end
90
114
 
91
- @dataset = self.db[ @table_name ]
115
+ self.dataset = self.db[ self.table_name.to_sym ]
92
116
  end
93
117
 
94
118
 
@@ -12,45 +12,64 @@ require 'strelka/session'
12
12
  require 'strelka/mixins'
13
13
 
14
14
  # Default session class -- simple in-memory, cookie-based base-bones session.
15
- #
16
- # == Hash Interface
17
- #
18
- # The following methods are delegated to the inner Hash via the #namespaced_hash method:
19
- # #[], #[]=, #delete, #key?
20
15
  class Strelka::Session::Default < Strelka::Session
21
- extend Loggability,
16
+ extend Configurability,
22
17
  Forwardable,
23
18
  Strelka::MethodUtilities,
24
19
  Strelka::Delegation
25
20
  include Strelka::DataUtilities
26
21
 
22
+
23
+ ##
24
+ # Configurability API -- use the 'defaultsession' section of the config
25
+ config_key :defaultsession
26
+
27
27
  # Default configuration
28
- DEFAULT_COOKIE_OPTIONS = {
29
- :name => 'strelka-session'
28
+ CONFIG_DEFAULTS = {
29
+ cookie_name: 'strelka-session',
30
+ cookie_options: {
31
+ expires: "+1d",
32
+ }
30
33
  }.freeze
31
34
 
32
35
 
33
36
  # Class-instance variables
34
- @cookie_options = DEFAULT_COOKIE_OPTIONS.dup
35
- @sessions = {}
37
+ @sessions = {}
38
+ @cookie_name = CONFIG_DEFAULTS[:cookie_name]
39
+ @cookie_options = {}
36
40
 
37
41
  ##
38
42
  # In-memory session store
39
43
  singleton_attr_reader :sessions
40
44
 
41
45
  ##
42
- # The configured session cookie parameters
46
+ # The name of the session cookie
47
+ singleton_attr_accessor :cookie_name
48
+
49
+ ##
50
+ # Session cookie options; see {Strelka::Cookie}[rdoc-ref://Strelka::Cookie.new]
51
+ # for available options.
43
52
  singleton_attr_accessor :cookie_options
44
53
 
45
54
 
46
55
  ### Configure the session class with the given +options+, which should be a
47
- ### Hash or an object that has a Hash-like interface. Sets cookie options
48
- ### for the session if the +:cookie+ key is set.
49
- def self::configure( options=nil )
50
- if options
51
- self.cookie_options.merge!( options[:cookie] ) if options[:cookie]
56
+ ### Hash or an object that has a Hash-like interface.
57
+ ###
58
+ ### Valid keys:
59
+ ###
60
+ ### [\cookie_name]
61
+ ### The name of the cookie that will be used for persisting the session key.
62
+ ### [\cookie_options]
63
+ ### A hash of options for the session cookie; see
64
+ ### {Strelka::Cookie}[rdoc-ref://Strelka::Cookie.new]
65
+ ### for available options.
66
+ def self::configure( config=nil )
67
+ if config
68
+ self.cookie_name = config[:cookie_name]
69
+ self.cookie_options = config[:cookie_options]
52
70
  else
53
- self.cookie_options = DEFAULT_COOKIE_OPTIONS.dup
71
+ self.cookie_name = CONFIG_DEFAULTS[:cookie_name]
72
+ self.cookie_options = CONFIG_DEFAULTS[:cookie_options].dup
54
73
  end
55
74
  end
56
75
 
@@ -77,7 +96,7 @@ class Strelka::Session::Default < Strelka::Session
77
96
  ### Try to fetch a session ID from the specified +request+, returning +nil+
78
97
  ### if there isn't one.
79
98
  def self::get_existing_session_id( request )
80
- cookie = request.cookies[ self.cookie_options[:name] ] or return nil
99
+ cookie = request.cookies[ self.cookie_name ] or return nil
81
100
 
82
101
  if cookie.value =~ /^([[:xdigit:]]+)$/i
83
102
  return $1.untaint
@@ -141,6 +160,7 @@ class Strelka::Session::Default < Strelka::Session
141
160
  # to the application.
142
161
  attr_reader :namespace
143
162
 
163
+ ##
144
164
  # Delegate Hash methods to whichever Hash is the current namespace
145
165
  def_method_delegators :namespaced_hash, :[], :[]=, :delete, :key?
146
166
 
@@ -160,7 +180,7 @@ class Strelka::Session::Default < Strelka::Session
160
180
  self.log.debug "Adding session cookie to the request."
161
181
 
162
182
  session_cookie = Strelka::Cookie.new(
163
- self.class.cookie_options[ :name ],
183
+ self.class.cookie_name,
164
184
  self.session_id,
165
185
  self.class.cookie_options
166
186
  )