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

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.
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
  )