ramaze 2010.06.18 → 2011.01

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 (95) hide show
  1. data/.gitignore +1 -0
  2. data/MANIFEST +9 -16
  3. data/README.md +37 -30
  4. data/Rakefile +5 -1
  5. data/TODO.md +19 -0
  6. data/doc/AUTHORS +5 -1
  7. data/doc/CHANGELOG +3553 -3272
  8. data/doc/tutorial/todolist.html +1512 -1512
  9. data/examples/app/blog/app.rb +2 -0
  10. data/examples/app/todolist/controller/init.rb +1 -2
  11. data/examples/app/wiktacular/mkd/main/2007-07-20_19-21-12.mkd +1 -1
  12. data/examples/app/wiktacular/mkd/main/2007-07-20_19-23-10.mkd +1 -1
  13. data/examples/app/wiktacular/mkd/main/2007-07-20_19-45-07.mkd +1 -1
  14. data/examples/app/wiktacular/mkd/main/current.mkd +1 -1
  15. data/examples/app/wiktacular/mkd/testing/2007-07-20_16-43-46.mkd +1 -1
  16. data/examples/app/wiktacular/mkd/testing/2007-07-20_19-43-50.mkd +2 -2
  17. data/examples/app/wiktacular/mkd/testing/2007-07-21_18-47-08.mkd +16 -16
  18. data/examples/app/wiktacular/mkd/testing/2007-07-21_18-47-54.mkd +16 -16
  19. data/examples/app/wiktacular/mkd/testing/current.mkd +16 -16
  20. data/lib/proto/model/init.rb +1 -1
  21. data/lib/proto/public/js/jquery.js +2034 -1095
  22. data/lib/proto/start.rb +2 -0
  23. data/lib/proto/view/index.xhtml +3 -3
  24. data/lib/ramaze.rb +1 -2
  25. data/lib/ramaze/cache.rb +1 -0
  26. data/lib/ramaze/cache/sequel.rb +131 -37
  27. data/lib/ramaze/controller.rb +1 -0
  28. data/lib/ramaze/gestalt.rb +75 -46
  29. data/lib/ramaze/helper.rb +1 -0
  30. data/lib/ramaze/helper/auth.rb +38 -4
  31. data/lib/ramaze/helper/blue_form.rb +498 -78
  32. data/lib/ramaze/helper/cache.rb +2 -2
  33. data/lib/ramaze/helper/csrf.rb +225 -0
  34. data/lib/ramaze/helper/erector.rb +67 -9
  35. data/lib/ramaze/helper/flash.rb +4 -2
  36. data/lib/ramaze/helper/gestalt.rb +2 -0
  37. data/lib/ramaze/helper/gravatar.rb +1 -1
  38. data/lib/ramaze/helper/localize.rb +4 -0
  39. data/lib/ramaze/helper/send_file.rb +30 -0
  40. data/lib/ramaze/helper/thread.rb +5 -0
  41. data/lib/ramaze/helper/user.rb +4 -3
  42. data/lib/ramaze/helper/xhtml.rb +87 -8
  43. data/lib/ramaze/log.rb +13 -0
  44. data/lib/ramaze/log/analogger.rb +15 -5
  45. data/lib/ramaze/log/growl.rb +28 -13
  46. data/lib/ramaze/log/hub.rb +12 -4
  47. data/lib/ramaze/log/informer.rb +28 -11
  48. data/lib/ramaze/log/knotify.rb +7 -2
  49. data/lib/ramaze/log/logger.rb +12 -4
  50. data/lib/ramaze/log/logging.rb +40 -14
  51. data/lib/ramaze/log/rotatinginformer.rb +47 -23
  52. data/lib/ramaze/log/syslog.rb +37 -31
  53. data/lib/ramaze/log/xosd.rb +7 -4
  54. data/lib/ramaze/middleware_compiler.rb +2 -2
  55. data/lib/ramaze/snippets/fiber.rb +63 -63
  56. data/lib/ramaze/snippets/ramaze/lru_hash.rb +1 -1
  57. data/lib/ramaze/tool/bin.rb +1 -1
  58. data/lib/ramaze/version.rb +1 -1
  59. data/lib/ramaze/view.rb +4 -4
  60. data/lib/ramaze/view/erector.rb +88 -13
  61. data/ramaze.gemspec +65 -65
  62. data/spec/ramaze/bin/ramaze.rb +1 -1
  63. data/spec/ramaze/cache/localmemcache.rb +20 -12
  64. data/spec/ramaze/cache/sequel.rb +19 -19
  65. data/spec/ramaze/helper/blue_form.rb +549 -257
  66. data/spec/ramaze/helper/csrf.rb +109 -0
  67. data/spec/ramaze/helper/httpdigest.rb +31 -29
  68. data/spec/ramaze/helper/user.rb +1 -1
  69. data/spec/ramaze/helper/xhtml.rb +17 -0
  70. data/spec/ramaze/log/growl.rb +34 -0
  71. data/spec/ramaze/log/informer.rb +1 -0
  72. data/spec/ramaze/view/erector.rb +49 -71
  73. data/spec/ramaze/view/erector/external_view.erector +5 -0
  74. data/spec/ramaze/view/erector/index.erector +5 -0
  75. data/spec/ramaze/view/erector/layout.erector +13 -3
  76. data/spec/ramaze/view/erector/tables.erector +23 -0
  77. data/spec/ramaze/view/erector/view.erector +6 -0
  78. data/tasks/git.rake +2 -2
  79. metadata +133 -176
  80. data/examples/helpers/form_with_sequel.rb +0 -24
  81. data/examples/helpers/nitro_form.rb +0 -23
  82. data/lib/ramaze/helper/form.rb +0 -133
  83. data/lib/ramaze/helper/nitroform.rb +0 -14
  84. data/lib/ramaze/helper/pager.rb +0 -367
  85. data/lib/ramaze/helper/partial.rb +0 -100
  86. data/lib/ramaze/helper/sequel.rb +0 -55
  87. data/lib/ramaze/helper/sequel_form.rb +0 -284
  88. data/lib/vendor/etag.rb +0 -22
  89. data/spec/ramaze/helper/form.rb +0 -360
  90. data/spec/ramaze/helper/pager.rb +0 -96
  91. data/spec/ramaze/helper/sequel_form.rb +0 -94
  92. data/spec/ramaze/view/erector/external.erector +0 -1
  93. data/spec/ramaze/view/erector/invoke_helper_method.erector +0 -1
  94. data/spec/ramaze/view/erector/strict_xhtml.erector +0 -3
  95. data/spec/ramaze/view/erector/sum.erector +0 -1
@@ -66,11 +66,11 @@ module Ramaze
66
66
  # to do lazy computation of the cached value conveniently when using a
67
67
  # custom TTL or longer expressions that don't fit on one line with ||=.
68
68
  #
69
- # @usage Example to get the cache object directly
69
+ # @example to get the cache object directly
70
70
  #
71
71
  # count = cache_value[:count] ||= Article.count
72
72
  #
73
- # @usage Example with block
73
+ # @example with block
74
74
  #
75
75
  # count = cache_value(:count){ Article.count }
76
76
  # count = cache_value(:count, :ttl => 60){ Article.count }
@@ -0,0 +1,225 @@
1
+ require 'securerandom'
2
+ require 'digest'
3
+
4
+ module Ramaze
5
+ module Helper
6
+ ##
7
+ # A relatively basic yet useful helper that can be used to protect your application
8
+ # from CSRF attacks/exploits. Note that this helper merely generates the required data,
9
+ # and provides several methods. You still need to manually add the token to each form.
10
+ #
11
+ # The reason for this is because this is quite simple. Ramaze is meant as a framework that
12
+ # works with any given helper, ORM, template engine and so on. If we were to automatically
13
+ # load this helper and include (a perhaps more advanced) CSRF system that would mean that
14
+ # every form helper, official or third-party, would have to support that specific system.
15
+ # However, there's no need to panic as it's very easy to setup a basic anti CSRF system.
16
+ #
17
+ # == Usage
18
+ #
19
+ # In order to enable CSRF protection we need to do two things. Load the helper and create
20
+ # a before_all block in a controller. Take a look at the following code:
21
+ #
22
+ # class BaseController < Ramaze::Controller
23
+ # before_all do
24
+ # puts "Hello, before_all!"
25
+ # end
26
+ # end
27
+ #
28
+ # This would output "Hello, before_all!" to the console upon each request. Not very useful
29
+ # but it does show what the before_all block can do. On to actual CSRF related code!
30
+ #
31
+ # class BaseController < Ramaze::Controller
32
+ # before_all do
33
+ # csrf_protection :save do
34
+ # # ....
35
+ # end
36
+ # end
37
+ # end
38
+ #
39
+ # This example introduces an extra block that validates the current request.
40
+ # Whenever a user requests a controller that either extends BaseController or has it's own
41
+ # before_all block Ramaze will check if the current request data contains a CSRF token.
42
+ # Of course an if/end isn't very useful if it doesn't do anything, let's add some code.
43
+ #
44
+ # class BaseController < Ramaze::Controller
45
+ # before_all do
46
+ # csrf_protection :save do
47
+ # puts "Hello, unsafe data!"
48
+ # end
49
+ # end
50
+ # end
51
+ #
52
+ # The code above checks if the current method is "save" (or any other of the provided methods)
53
+ # and checks if an CSRF token is supplied if the method matches. Protected methods require
54
+ # a token in ALL HTTP requests (GET, POST, etc). While this may seem weird since GET is generally
55
+ # used for safe actions it actually makes sence. Ramaze stores both the POST and GET parameters in
56
+ # the request.params hash. While this makes it easy to work with POST/GET data this also makes it
57
+ # easier to spoof POST requests using a GET request, thus this helper protects ALL request methods.
58
+ #
59
+ # If you're a lazy person you can copy-paste the example below and adapt it to your needs.
60
+ #
61
+ # class BaseController < Ramaze::Controller
62
+ # before_all do
63
+ # csrf_protection :save do
64
+ # respond("The supplied CSRF token is invalid.", 401)
65
+ # end
66
+ # end
67
+ # end
68
+ #
69
+ # @author Yorick Peterse
70
+ #
71
+ module CSRF
72
+
73
+ ##
74
+ # Method that can be used to protect the specified methods against CSRF exploits.
75
+ # Each protected method will require the token to be stored in a field called "csrf_token".
76
+ # This method will then validate that token against the current token in the session.
77
+ #
78
+ # @author Yorick Peterse
79
+ # @param [Strings/Symbol] *methods Methods that will be protected/unprotected.
80
+ # @param [Block] Block that will be executed if the token is invalid.
81
+ # @example
82
+ #
83
+ # # Protect "create" and "save" against CSRF exploits
84
+ # before_all do
85
+ # csrf_protection :create, :save do
86
+ # respond("GET TO DA CHOPPA!", 401)
87
+ # end
88
+ # end
89
+ #
90
+ def csrf_protection *methods, &block
91
+ # Only protect the specified methods
92
+ if methods.include?(action.name) or methods.include?(action.name.to_sym)
93
+ # THINK: For now the field name is hard-coded to "csrf_token". While
94
+ # this is perfectly fine in most cases it might be a good idea
95
+ # to allow developers to change the name of this field (for whatever the reason).
96
+ if validate_csrf_token(request.params['csrf_token']) != true
97
+ yield
98
+ end
99
+ end
100
+ end
101
+
102
+ ##
103
+ # Generate a new token and create the session array that will be used to validate the client.
104
+ # The following items are stored in the session:
105
+ #
106
+ # * token: An unique hash that will be stored in each form
107
+ # * agent: The visitor's user agent
108
+ # * ip: The IP address of the visitor
109
+ # * time: Timestamp that indicates at what time the data was generated.
110
+ #
111
+ # Note that this method will be automatically called if no CSRF token exists.
112
+ #
113
+ # @author Yorick Peterse
114
+ # @param [Hash] Additional arguments that can be set such as the TTL.
115
+ # @return [Void]
116
+ #
117
+ def generate_csrf_token args = {}
118
+ # Default TTL is 15 minutes
119
+ if args[:ttl]
120
+ ttl = args[:ttl]
121
+ else
122
+ ttl = 900
123
+ end
124
+
125
+ # Generate all the required data
126
+ time = Time.new.to_i.to_s
127
+ number = SecureRandom.random_number(10000).to_s
128
+ base64 = SecureRandom.base64.to_s
129
+ token = Digest::SHA2.new(512).hexdigest(srand.to_s + rand.to_s + time + number + base64).to_s
130
+
131
+ # Get several details from the client such as the user agent, IP, etc
132
+ ip = request.env['REMOTE_ADDR']
133
+ agent = request.env['HTTP_USER_AGENT']
134
+ host = request.env['REMOTE_HOST']
135
+
136
+ # Time to store all the data
137
+ session[:_csrf] = {
138
+ :time => time.to_i,
139
+ :token => token,
140
+ :ip => ip,
141
+ :agent => agent,
142
+ :host => host,
143
+ :ttl => ttl
144
+ }
145
+
146
+ # Prevent this method from returning any value (it isn't needed anyway)
147
+ return
148
+ end
149
+
150
+ ##
151
+ # Retrieves the current value of the CSRF token.
152
+ #
153
+ # @author Yorick Peterse
154
+ # @return [String] The current CSRF token.
155
+ # @example
156
+ #
157
+ # form(@data, :method => :post) do |f|
158
+ # f.input_hidden :csrf_token, get_csrf_token()
159
+ # end
160
+ #
161
+ def get_csrf_token
162
+ if !session[:_csrf] or !self.validate_csrf_token(session[:_csrf][:token])
163
+ self.generate_csrf_token
164
+ end
165
+
166
+ # Land ho!
167
+ return session[:_csrf][:token]
168
+ end
169
+
170
+ ##
171
+ # Validates the request based on the current session date stored in _csrf.
172
+ # The following items are verified:
173
+ #
174
+ # * Do the user agent, ip and token match those supplied by the visitor?
175
+ # * Has the token been expired? (after 15 minutes).
176
+ #
177
+ # If any of these checks fail this method will return FALSE. It's your job to
178
+ # take action based on the results of this method.
179
+ #
180
+ # @author Yorick Peterse
181
+ # @param [String] input_token The CSRF token to validate.
182
+ # @return [Bool]
183
+ # @example
184
+ #
185
+ # before_all do
186
+ # if validate_csrf_token(request.params['csrf_token']) != true
187
+ # respond("Invalid CSRF token", 401)
188
+ # end
189
+ # end
190
+ #
191
+ def validate_csrf_token input_token
192
+ # Check if the CSRF data has been generated and generate it if this
193
+ # hasn't been done already (usually on the first request).
194
+ if !session[:_csrf] or session[:_csrf].empty?
195
+ self.generate_csrf_token
196
+ end
197
+
198
+ # Get several details from the client such as the user agent, IP, etc
199
+ ip = session[:_csrf][:ip]
200
+ agent = session[:_csrf][:agent]
201
+ host = session[:_csrf][:host]
202
+
203
+ # Get the current time and the time when the token was created
204
+ now = Time.new.to_i
205
+ token_time = session[:_csrf][:time]
206
+
207
+ # Mirror mirror on the wall, who's the most secure of them all?
208
+ results = Array.new
209
+ results.push( session[:_csrf][:token] == input_token )
210
+ results.push( (now - token_time) <= session[:_csrf][:ttl] )
211
+ results.push( host == request.env['REMOTE_HOST'] )
212
+ results.push( ip == request.env['REMOTE_ADDR'] )
213
+ results.push( agent == request.env['HTTP_USER_AGENT'] )
214
+
215
+ # Verify the results
216
+ if results.include?(false)
217
+ return false
218
+ else
219
+ return true
220
+ end
221
+ end
222
+
223
+ end # <-- End of CSRF module
224
+ end
225
+ end
@@ -5,15 +5,19 @@ require 'erector'
5
5
 
6
6
  module Ramaze
7
7
 
8
- # Allows you to use some shortcuts for Erector in your Controller.
9
-
10
- # use this inside your controller to directly build Erector
11
- # Refer to the Erector-documentation and testsuite for more examples.
12
- # Usage:
13
- # erector { h1 "Apples & Oranges" } #=> "<h1>Apples &amp; Oranges</h1>"
14
- # erector { h1(:class => 'fruits&floots'){ text 'Apples' } } #=> "<h1 class=\"fruits&amp;floots\">Apples</h1>"
15
-
16
8
  module Helper
9
+
10
+ ##
11
+ # Allows you to use some shortcuts for Erector in your Controller.
12
+ #
13
+ # use this inside your controller to directly build Erector
14
+ # Refer to the Erector-documentation and testsuite for more examples.
15
+ #
16
+ # @example
17
+ #
18
+ # erector { h1 "Apples & Oranges" } #=> "<h1>Apples &amp; Oranges</h1>"
19
+ # erector { h1(:class => 'fruits&floots'){ text 'Apples' } } #=> "<h1 class=\"fruits&amp;floots\">Apples</h1>"
20
+ #
17
21
  module Erector
18
22
  include ::Erector::Mixin
19
23
 
@@ -21,27 +25,81 @@ module Ramaze
21
25
  alias :raw! :rawtext
22
26
  alias :old_css :css
23
27
 
28
+ ##
29
+ # Method that generates a XHTML 1.0 Strict doctype.
30
+ #
31
+ # @example
32
+ #
33
+ # strict_html do
34
+ # head do
35
+ # title "Ramaze Rocks!"
36
+ # end
37
+ # body
38
+ # div do
39
+ #
40
+ # end
41
+ # end
42
+ # end
43
+ #
44
+ # @param [Hash] args Hash containing extra options such as the xml:lang and xmlns attribute.
45
+ # @param [Block] block Block that contains the inner data of the <html> element.
46
+ #
24
47
  def strict_xhtml(*args, &block)
25
48
  raw! '<?xml version="1.0" encoding="UTF-8"?>'
26
49
  raw! '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">'
27
50
  html(:xmlns => "http://www.w3.org/1999/xhtml", :"xml:lang" => "en", :lang => "en", &block)
28
51
  end
29
52
 
53
+ ##
54
+ # Generate a Javascript tag.
55
+ #
56
+ # @example
57
+ #
58
+ # js 'javascript/jquery.js'
59
+ #
60
+ # @param [String] src The full or relative path to the Javascript file.
61
+ #
30
62
  def js(src)
31
63
  script :src => src
32
64
  end
33
65
 
66
+ ##
67
+ # Generate a pair of conditional tags for a specific browser.
68
+ #
69
+ # @example
70
+ #
71
+ # ie_if 'IE' do
72
+ # ......
73
+ # end
74
+ #
75
+ # @param [String] expr The if expression, such as 'IE' or 'lte IE7'.
76
+ # @param [block] block Block that contains the data that needs to be loaded for the specified browser.
77
+ #
34
78
  def ie_if(expr, &block)
35
79
  raw! "<!--[if #{expr}]>"
36
80
  yield
37
81
  raw! "<![endif]-->"
38
82
  end
39
83
 
40
- # Diagnostics
84
+ ##
85
+ # Inspect the specified element.
86
+ #
87
+ # @param [String] elem The element to inspect.
88
+ #
41
89
  def inspect(elem)
42
90
  text elem.inspect
43
91
  end
44
92
 
93
+ ##
94
+ # Generate a stylesheet tag.
95
+ #
96
+ # @example
97
+ #
98
+ # css 'css/reset.css', :media => 'print'
99
+ #
100
+ # @param [String] href The path (either absolute or relative) to the CSS file.
101
+ # @param [Hash] args A hash containing additional arguments to add to the CSS tag.
102
+ #
45
103
  def css(href, args = {})
46
104
  attrs = {
47
105
  :rel => "stylesheet",
@@ -14,7 +14,7 @@ module Ramaze
14
14
  # On the first request, for example on registering:
15
15
  #
16
16
  # flash[:error] = "You should reconsider your username, it's taken already"
17
- # redirect R(self, :register)
17
+ # redirect r(:register)
18
18
  #
19
19
  # This is the request from the redirect:
20
20
  #
@@ -28,7 +28,9 @@ module Ramaze
28
28
 
29
29
  trait :flashbox => "<div class='flash' id='flash_%key'>%value</div>"
30
30
 
31
- # answers with Current.session.flash
31
+ ##
32
+ # Return the current value of Current.session.flash
33
+ #
32
34
  def flash
33
35
  Current.session.flash
34
36
  end
@@ -2,6 +2,8 @@ require 'ramaze/gestalt'
2
2
 
3
3
  module Ramaze
4
4
  module Helper
5
+ ##
6
+ # Gestalt is the custom HTML/XML builder for Ramaze, based on a very simple DSL it will build your markup.
5
7
  module Gestalt
6
8
  CACHE_G = {}
7
9
 
@@ -50,7 +50,7 @@ module Ramaze
50
50
  # Fall back to default if the given +email+ doesn't have an gravatar;
51
51
  # may be an absolute url, 'identicon', 'monsterid', or 'wavatar'
52
52
  #
53
- # @options opts [true, false] :force (false)
53
+ # @option opts [true, false] :force (false)
54
54
  # Force use of the default avatar, useful if you want to use only
55
55
  # identicons or the like
56
56
  #
@@ -3,6 +3,10 @@ require 'locale'
3
3
 
4
4
  module Ramaze
5
5
  module Helper
6
+ ##
7
+ # The localization helper can be used to output translated strings.
8
+ # This enables your application to use multiple language files for English, Dutch and so on.
9
+ #
6
10
  module Localize
7
11
  include Traited
8
12
 
@@ -0,0 +1,30 @@
1
+ module Ramaze
2
+ module Helper
3
+ ##
4
+ # The SendFile module can be used to stream a file to the user's computer.
5
+ # While the performance of the module isn't optimal it's convenient and relatively easy to use.
6
+ #
7
+ module SendFile
8
+ ##
9
+ # The send_file method streams the specified file to the user's browser.
10
+ #
11
+ # @param [String] filename The name or path to the file which will be streamed to the user.
12
+ # @param [String] content_type The type of file we're dealing with. For example, if we want to stream
13
+ # a JPG image we'd set this variable to 'image/jpg'.
14
+ # @param [String] content_disposition Value for the Content-Disposition header.
15
+ #
16
+ def send_file(filename, content_type = nil, content_disposition = nil)
17
+ content_type ||= Rack::Mime.mime_type(::File.extname(filename))
18
+ content_disposition ||= File.basename(filename)
19
+
20
+ response.body = ::File.open(filename, 'rb')
21
+ response['Content-Length'] = ::File.size(filename).to_s
22
+ response['Content-Type'] = content_type
23
+ response['Content-Disposition'] = content_disposition
24
+ response.status = 200
25
+
26
+ throw(:respond, response)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -3,6 +3,11 @@
3
3
 
4
4
  module Ramaze
5
5
  module Helper::Thread
6
+ ##
7
+ # The thread method executes the specified block in a new thread.
8
+ #
9
+ # @param [Block] block The block that contains the code that will be executed in the new thread.
10
+ #
6
11
  def thread &block
7
12
  parent_thread = Thread.current
8
13
  Thread.new do