rack_warden 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +2 -0
  3. data/Gemfile +13 -10
  4. data/README.md +115 -53
  5. data/config.ru +1 -0
  6. data/lib/rack_warden.rb +33 -5
  7. data/lib/rack_warden/app.rb +73 -58
  8. data/lib/rack_warden/core_patches.rb +20 -0
  9. data/lib/rack_warden/env.rb +27 -0
  10. data/lib/rack_warden/frameworks.rb +34 -36
  11. data/lib/rack_warden/frameworks/rack.rb +36 -0
  12. data/lib/rack_warden/frameworks/rails.rb +29 -9
  13. data/lib/rack_warden/frameworks/sinatra.rb +15 -11
  14. data/lib/rack_warden/helpers.rb +197 -29
  15. data/lib/rack_warden/mail.rb +26 -0
  16. data/lib/rack_warden/models.rb +79 -40
  17. data/lib/rack_warden/models/user.rb +180 -22
  18. data/lib/rack_warden/routes.rb +159 -83
  19. data/lib/rack_warden/sinatra/decompile.rb +127 -0
  20. data/lib/rack_warden/sinatra/json.rb +131 -0
  21. data/lib/rack_warden/sinatra/namespace.rb +285 -0
  22. data/lib/rack_warden/sinatra/respond_with.rb +277 -0
  23. data/lib/rack_warden/version.rb +1 -1
  24. data/lib/rack_warden/views/rw_account_widget.html.erb +8 -0
  25. data/lib/rack_warden/views/rw_activation.email.erb +3 -0
  26. data/lib/rack_warden/views/rw_admin.html.erb +7 -5
  27. data/lib/rack_warden/views/rw_dbinfo.html.erb +5 -4
  28. data/lib/rack_warden/views/rw_error.html.erb +1 -0
  29. data/lib/rack_warden/views/rw_flash_widget.html.erb +12 -0
  30. data/lib/rack_warden/views/rw_index.html.erb +1 -1
  31. data/lib/rack_warden/views/rw_layout.html.erb +13 -19
  32. data/lib/rack_warden/views/rw_layout_admin.html.erb +6 -6
  33. data/lib/rack_warden/views/rw_login.html.erb +18 -5
  34. data/lib/rack_warden/views/rw_new_user.html.erb +22 -6
  35. data/lib/rack_warden/views/rw_protected.xml.erb +10 -0
  36. data/lib/rack_warden/views/rw_session.html.erb +34 -0
  37. data/lib/rack_warden/warden.rb +161 -30
  38. data/rack_warden.gemspec +16 -13
  39. metadata +84 -29
@@ -0,0 +1,277 @@
1
+ require 'rack_warden/sinatra/json'
2
+ require 'sinatra/base'
3
+ require 'erb'
4
+
5
+ module RackWarden
6
+ App.logger.debug "RW loading RespondWith"
7
+
8
+ #
9
+ # = Sinatra::RespondWith
10
+ #
11
+ # These extensions let Sinatra automatically choose what template to render or
12
+ # action to perform depending on the request's Accept header.
13
+ #
14
+ # Example:
15
+ #
16
+ # # Without Sinatra::RespondWith
17
+ # get '/' do
18
+ # data = { :name => 'example' }
19
+ # request.accept.each do |type|
20
+ # case type.to_s
21
+ # when 'text/html'
22
+ # halt haml(:index, :locals => data)
23
+ # when 'text/json'
24
+ # halt data.to_json
25
+ # when 'application/atom+xml'
26
+ # halt nokogiri(:'index.atom', :locals => data)
27
+ # when 'application/xml', 'text/xml'
28
+ # halt nokogiri(:'index.xml', :locals => data)
29
+ # when 'text/plain'
30
+ # halt 'just an example'
31
+ # end
32
+ # end
33
+ # error 406
34
+ # end
35
+ #
36
+ # # With Sinatra::RespondWith
37
+ # get '/' do
38
+ # respond_with :index, :name => 'example' do |f|
39
+ # f.txt { 'just an example' }
40
+ # end
41
+ # end
42
+ #
43
+ # Both helper methods +respond_to+ and +respond_with+ let you define custom
44
+ # handlers like the one above for +text/plain+. +respond_with+ additionally
45
+ # takes a template name and/or an object to offer the following default
46
+ # behavior:
47
+ #
48
+ # * If a template name is given, search for a template called
49
+ # +name.format.engine+ (+index.xml.nokogiri+ in the above example).
50
+ # * If a template name is given, search for a templated called +name.engine+
51
+ # for engines known to result in the requested format (+index.haml+).
52
+ # * If a file extension associated with the mime type is known to Sinatra, and
53
+ # the object responds to +to_extension+, call that method and use the result
54
+ # (+data.to_json+).
55
+ #
56
+ # == Security
57
+ #
58
+ # Since methods are triggered based on client input, this can lead to security
59
+ # issues (but not as severe as those might appear in the first place: keep in
60
+ # mind that only known file extensions are used). You should limit
61
+ # the possible formats you serve.
62
+ #
63
+ # This is possible with the +provides+ condition:
64
+ #
65
+ # get '/', :provides => [:html, :json, :xml, :atom] do
66
+ # respond_with :index, :name => 'example'
67
+ # end
68
+ #
69
+ # However, since you have to set +provides+ for every route, this extension
70
+ # adds an app global (class method) `respond_to`, that lets you define content
71
+ # types for all routes:
72
+ #
73
+ # respond_to :html, :json, :xml, :atom
74
+ # get('/a') { respond_with :index, :name => 'a' }
75
+ # get('/b') { respond_with :index, :name => 'b' }
76
+ #
77
+ # == Custom Types
78
+ #
79
+ # Use the +on+ method for defining actions for custom types:
80
+ #
81
+ # get '/' do
82
+ # respond_to do |f|
83
+ # f.xml { nokogiri :index }
84
+ # f.on('application/custom') { custom_action }
85
+ # f.on('text/*') { data.to_s }
86
+ # f.on('*/*') { "matches everything" }
87
+ # end
88
+ # end
89
+ #
90
+ # Definition order does not matter.
91
+ module RespondWith
92
+ class Format
93
+ def initialize(app)
94
+ @app, @map, @generic, @default = app, {}, {}, nil
95
+ end
96
+
97
+ def on(type, &block)
98
+ # WBR
99
+ @app.logger.debug "RW respond_with.on type: #{type}"
100
+ @app.logger.debug "RW respond_with.on block: #{block}"
101
+ @app.logger.debug "RW respond_with.on @app.params: #{@app.params}"
102
+ @app.logger.debug "RW respond_with.on @app.settings.mime_types(type): #{@app.settings.mime_types(type)}"
103
+
104
+ @app.settings.mime_types(type).each do |mime|
105
+ case mime
106
+ when '*/*' then @default = block
107
+ when /^([^\/]+)\/\*$/ then @generic[$1] = block
108
+ else @map[mime] = block
109
+ end
110
+ end
111
+ end
112
+
113
+ def finish
114
+ yield self if block_given?
115
+ # WBR - adds format/extension of uri to front of mime-types.
116
+ # You must capture the format string in the params with your route declaration.
117
+ format_type = @app.settings.mime_types(@app.params['format'])[0]
118
+ @app.logger.debug "RW respond_with.finish format_type (WBR): #{format_type}, #{format_type.class}"
119
+ mime_type = format_type ||
120
+ @app.content_type ||
121
+ @app.request.preferred_type(@map.keys) ||
122
+ @app.request.preferred_type ||
123
+ 'text/html'
124
+ type = mime_type.split(/\s*;\s*/, 2).first
125
+ @app.logger.debug "RW respond_with.finish type: #{type}"
126
+ @app.logger.debug "RW respond_with.finish @map: #{@map}"
127
+ @app.logger.debug "RW respond_with.finish @default: #{@default}"
128
+ @app.logger.debug "RW respond_with.finish @generic: #{@generic}"
129
+ handlers = [@map[type], @generic[type[/^[^\/]+/]], @default].compact
130
+ handlers.each do |block|
131
+ if result = block.call(type)
132
+ @app.content_type mime_type
133
+ @app.halt result
134
+ end
135
+ end
136
+ @app.halt 406
137
+ end
138
+
139
+ def method_missing(method, *args, &block)
140
+ return super if args.any? or block.nil? or not @app.mime_type(method)
141
+ on(method, &block)
142
+ end
143
+ end
144
+
145
+ module Helpers
146
+ include Sinatra::JSON
147
+
148
+ def respond_with(template, object = nil, &block)
149
+ object, template = template, nil unless Symbol === template
150
+ format = Format.new(self)
151
+ format.on "*/*" do |type|
152
+ exts = settings.ext_map[type]
153
+ exts << :xml if type.end_with? '+xml'
154
+ # WBR - not necessary, since the hack of #finish method above.
155
+ #exts.unshift params[:format].to_sym if params[:format]
156
+ logger.debug "RW respond_with format: #{params[:format]}"
157
+
158
+ logger.debug "RW respond_with exts: #{exts.inspect}"
159
+ if template
160
+ args = template_cache.fetch(type, template) { template_for(template, exts) }
161
+ if args.any?
162
+ locals = { :object => object }
163
+ locals.merge! object.to_hash if object.respond_to? :to_hash
164
+
165
+ renderer = args.first
166
+ options = args[1..-1] + [{:locals => locals}]
167
+
168
+ halt send(renderer, *options)
169
+ end
170
+ end
171
+ if object
172
+ exts.each do |ext|
173
+ halt json(object) if ext == :json
174
+ next unless object.respond_to? method = "to_#{ext}"
175
+ halt(*object.send(method))
176
+ end
177
+ end
178
+ false
179
+ end
180
+ format.finish(&block)
181
+ end
182
+
183
+ def respond_to(&block)
184
+ Format.new(self).finish(&block)
185
+ end
186
+
187
+ private
188
+
189
+ def template_for(name, exts)
190
+ logger.debug "RW respond_with#template_for name, exts: #{name}, #{exts}"
191
+ # in production this is cached, so don't worry too much about runtime
192
+ possible = []
193
+ settings.template_engines[:all].each do |engine|
194
+ exts.each { |ext| possible << [engine, "#{name}.#{ext}"] }
195
+ end
196
+ exts.each do |ext|
197
+ settings.template_engines[ext].each { |e| possible << [e, name] }
198
+ end
199
+ logger.debug "RW respond_with#template_for possible: #{possible.inspect}"
200
+ possible.each do |engine, template|
201
+ # not exactly like Tilt[engine], but does not trigger a require
202
+ klass = Tilt.mappings[Tilt.normalize(engine)].first
203
+ logger.debug "RW respond_with#template_for klass : #{klass}"
204
+ find_template(settings.views, template, klass) do |file|
205
+ #logger.debug "RW respond_with#template_for find_template file: #{file}"
206
+ #logger.debug "RW respond_with#template_for find_template engine: #{engine.inspect}"
207
+ #logger.debug "RW respond_with#template_for find_template template: #{template.inspect}"
208
+ next unless File.exist? file
209
+ return settings.rendering_method(engine) << template.to_sym
210
+ end
211
+ end
212
+
213
+ [] # nil or false would not be cached
214
+ end
215
+ end
216
+
217
+ def remap_extensions
218
+ ext_map.clear
219
+ Rack::Mime::MIME_TYPES.each { |e,t| ext_map[t] << e[1..-1].to_sym }
220
+ ext_map['text/javascript'] << 'js'
221
+ ext_map['text/xml'] << 'xml'
222
+ end
223
+
224
+ def mime_type(*)
225
+ result = super
226
+ remap_extensions
227
+ result
228
+ end
229
+
230
+ def respond_to(*formats)
231
+ if formats.any?
232
+ @respond_to ||= []
233
+ @respond_to.concat formats
234
+ elsif @respond_to.nil? and superclass.respond_to? :respond_to
235
+ superclass.respond_to
236
+ else
237
+ @respond_to
238
+ end
239
+ end
240
+
241
+ def rendering_method(engine)
242
+ return [engine] if Sinatra::Templates.method_defined? engine
243
+ return [:mab] if engine.to_sym == :markaby
244
+ [:render, :engine]
245
+ end
246
+
247
+ private
248
+
249
+ def compile!(verb, path, block, options = {})
250
+ options[:provides] ||= respond_to if respond_to
251
+ super
252
+ end
253
+
254
+ ENGINES = {
255
+ :css => [:less, :sass, :scss],
256
+ :xml => [:builder, :nokogiri],
257
+ :js => [:coffee],
258
+ :json => [:yajl],
259
+ :html => [:erb, :erubis, :haml, :slim, :liquid, :radius, :mab, :markdown,
260
+ :textile, :rdoc],
261
+ :all => Sinatra::Templates.instance_methods.map(&:to_sym) + [:mab] -
262
+ [:find_template, :markaby]
263
+ }
264
+
265
+ ENGINES.default = []
266
+
267
+ def self.registered(base)
268
+ base.set :ext_map, Hash.new { |h,k| h[k] = [] }
269
+ base.set :template_engines, ENGINES.dup
270
+ base.remap_extensions
271
+ base.helpers Helpers
272
+ end
273
+ end
274
+
275
+ #register RespondWith
276
+ #Delegator.delegate :respond_to
277
+ end
@@ -1,3 +1,3 @@
1
1
  module RackWarden
2
- VERSION = "0.0.9"
2
+ VERSION = "0.0.10"
3
3
  end
@@ -0,0 +1,8 @@
1
+ <span class="rw-account-widget">
2
+ <% if logged_in? %>
3
+ Logged in as <b><a href="<%=url_for('/')%>"><%=current_user.username%></a></b> |
4
+ <a href="<%=url_for('/logout')%>">Logout</a>
5
+ <% else %>
6
+ <a href="<%=url_for('/login')%>">Login</a>
7
+ <% end %>
8
+ </span>
@@ -0,0 +1,3 @@
1
+ Activate your new account with this link
2
+
3
+ http://localhost:4567/auth/activate/<%=RackWarden::App.uri_encode(user.activation_code)%>
@@ -5,18 +5,20 @@
5
5
  <table>
6
6
  <thead>
7
7
  <tr>
8
- <th>Email</th>
9
- <th>User</th>
10
8
  <th>Id</th>
9
+ <th>User</th>
10
+ <th>Email</th>
11
+ <th>Activated</th>
11
12
  </tr>
12
13
  <tbody>
13
14
  <% RackWarden::User.all(:limit=>100).each do |user| %>
14
15
  <tr>
15
- <td><%=user.email%></td>
16
- <td><%=user.username%></td>
17
16
  <td><%=user.id%></td>
17
+ <td><%=user.username%></td>
18
+ <td><%=user.email%></td>
19
+ <td><%=user.activated_at%></td>
18
20
  </tr>
19
21
  <% end %>
20
22
  </tbody>
21
23
  </table>
22
- </p>
24
+ </p>
@@ -3,7 +3,8 @@
3
3
  </p>
4
4
 
5
5
  <p>
6
- <%= DataMapper.repository.adapter.select('select * from SQLITE_MASTER').to_yaml.gsub(/\n|\r/, '<br>').gsub(/ /, '&nbsp;&nbsp;') %>
7
- <br><br>
8
- <%= DataMapper.repository.adapter.to_yaml.gsub(/\n|\r/, '<br>') %>
9
- </p>
6
+ <%#= DataMapper.repository.adapter.select('select * from SQLITE_MASTER').to_yaml.to_html %>
7
+ <%#= DataMapper.repository.adapter.to_yaml.gsub(/\n|\r/, '<br>') %>
8
+ <%= DataMapper.repository(settings.repository_name).adapter.options.dup.tap{|o| o.delete(:password); o.delete('password')}.to_yaml.to_html %>
9
+ </p>
10
+ <br>
@@ -0,0 +1 @@
1
+ <h2>Oops! We encountered an error</h2>
@@ -0,0 +1,12 @@
1
+ <% if defined? :flash %>
2
+ <% if flash.rw_success %>
3
+ <div style="color:green;">
4
+ <%= flash.rw_success %>
5
+ </div>
6
+ <% end %>
7
+ <% if flash.rw_error %>
8
+ <div style="color:red;">
9
+ <%= flash.rw_error %>
10
+ </div>
11
+ <% end %>
12
+ <% end %>
@@ -1,3 +1,3 @@
1
1
  <p>
2
- <h3>Warden authentication for rack based apps<h3>
2
+ <h4>Warden authentication for rack based apps<h3>
3
3
  </p>
@@ -4,38 +4,32 @@
4
4
  <title>Rack Warden</title>
5
5
 
6
6
  <style>
7
- body {
8
- margin: 10px auto;
9
- width: 800px;
10
- }
7
+ body {
8
+ margin: 10px auto;
9
+ width: 800px;
10
+ }
11
11
  </style>
12
12
 
13
13
  <script src='https://www.google.com/recaptcha/api.js'></script>
14
14
  </head>
15
15
  <body>
16
+ <br>
16
17
  <h1>Rack Warden</h1>
17
18
 
18
19
  <p>
19
- <a href="<%=url('/auth/login', false)%>">Log In</a> |
20
- <a href="<%=url('/', false)%>">Home</a> |
21
- <a href="<%=url('/auth/protected', false)%>">Test Protected Page</a> |
22
- <a href="<%=url('/auth/logout', false)%>">Log Out</a>
20
+ <a href="<%=url('/', false)%>">Main Site</a> |
21
+ <a href="<%=url_for('/protected')%>">Test Protected Page</a> |
22
+ <%= erb :'rw_account_widget.html', :layout=>false %>
23
23
  </p>
24
24
 
25
- <% if flash(:rwarden)[:success] %>
26
- <div style="color:green;">
27
- <%= flash(:rwarden)[:success] %>
28
- </div>
29
- <% end %>
30
-
31
- <% if flash(:rwarden)[:error] %>
32
- <div style="color:red;">
33
- <%= flash(:rwarden)[:error] %>
34
- </div>
35
- <% end %>
25
+ <%= erb :'rw_flash_widget.html', :layout=>false %>
36
26
 
37
27
  <div id="content">
28
+ <br>
29
+ <br>
38
30
  <%= yield if block_given? %>
31
+ <br>
32
+ <br>
39
33
  </div>
40
34
  </body>
41
35
  </html>
@@ -1,12 +1,12 @@
1
1
  <!-- A partial layout for admin. -->
2
2
  <%# @section_title = "RackWarden Admin" %>
3
- <h2>RackWarden Admin</h2>
3
+ <h2>Account Management</h2>
4
4
  <p>
5
- <a href="<%=url('/auth/login', false)%>">Log In</a> |
6
- <a href="<%=url('/auth', false)%>">Home</a> |
7
- <a href="<%=url('/auth/admin', false)%>">User Accounts</a> |
8
- <a href="<%=url('/auth/dbinfo', false)%>">Database Info</a> |
9
- <a href="<%=url('/auth/logout', false)%>">Log Out</a>
5
+ <a href="<%=url_for('/')%>">RackWarden Home</a> |
6
+ <a href="<%=url_for('/admin')%>">User Accounts</a> |
7
+ <a href="<%=url_for('/dbinfo')%>">Database Info</a> |
8
+ <a href="<%=url_for('/sessinfo')%>">Session Info</a> |
9
+ <%=account_widget%>
10
10
  </p>
11
11
 
12
12
  <br>
@@ -1,9 +1,22 @@
1
- <% @section_title = "Login" %>
2
- <form action="<%=url('/auth/login', false)%>" method="post">
3
- <p>Username or Email <input type="text" name="user[username]" /></p>
4
- <p>Password <input type="password" name="user[password]" /></p>
1
+ <h3>Login</h3>
2
+ <form action="<%=url_for('/login')%>" method="post" class="rw">
3
+ <style scoped="scoped">
4
+ .rw label {font-size: .95em; color: grey; display: block;}
5
+ .rw input {font-size: 1.15em; margin-bottom: 20px; padding:7px;}
6
+ </style>
7
+
8
+ <label for="user_username">Username or Email</label>
9
+ <input type="text" name="user[username]" id="user_username"/>
10
+
11
+ <label for="user_password">Password</label>
12
+ <input type="password" name="user[password]" id="user_password" />
13
+
14
+ <label for="user_remember_me">Remember me
15
+ <input name="user[remember_me]" type="checkbox" value="1" id="user_remember_me" />
16
+ </label>
17
+
5
18
  <input type="submit" value="Log In" />
6
19
  <% if settings.allow_public_signup %>
7
- or <a href="<%=url('/auth/new', false)%>">Create a new account</a>
20
+ or <a href="<%=url_for('/new')%>">Create a new account</a>
8
21
  <% end %>
9
22
  </form>