rack_warden 0.0.9 → 0.0.10
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.
- checksums.yaml +8 -8
- data/.gitignore +2 -0
- data/Gemfile +13 -10
- data/README.md +115 -53
- data/config.ru +1 -0
- data/lib/rack_warden.rb +33 -5
- data/lib/rack_warden/app.rb +73 -58
- data/lib/rack_warden/core_patches.rb +20 -0
- data/lib/rack_warden/env.rb +27 -0
- data/lib/rack_warden/frameworks.rb +34 -36
- data/lib/rack_warden/frameworks/rack.rb +36 -0
- data/lib/rack_warden/frameworks/rails.rb +29 -9
- data/lib/rack_warden/frameworks/sinatra.rb +15 -11
- data/lib/rack_warden/helpers.rb +197 -29
- data/lib/rack_warden/mail.rb +26 -0
- data/lib/rack_warden/models.rb +79 -40
- data/lib/rack_warden/models/user.rb +180 -22
- data/lib/rack_warden/routes.rb +159 -83
- data/lib/rack_warden/sinatra/decompile.rb +127 -0
- data/lib/rack_warden/sinatra/json.rb +131 -0
- data/lib/rack_warden/sinatra/namespace.rb +285 -0
- data/lib/rack_warden/sinatra/respond_with.rb +277 -0
- data/lib/rack_warden/version.rb +1 -1
- data/lib/rack_warden/views/rw_account_widget.html.erb +8 -0
- data/lib/rack_warden/views/rw_activation.email.erb +3 -0
- data/lib/rack_warden/views/rw_admin.html.erb +7 -5
- data/lib/rack_warden/views/rw_dbinfo.html.erb +5 -4
- data/lib/rack_warden/views/rw_error.html.erb +1 -0
- data/lib/rack_warden/views/rw_flash_widget.html.erb +12 -0
- data/lib/rack_warden/views/rw_index.html.erb +1 -1
- data/lib/rack_warden/views/rw_layout.html.erb +13 -19
- data/lib/rack_warden/views/rw_layout_admin.html.erb +6 -6
- data/lib/rack_warden/views/rw_login.html.erb +18 -5
- data/lib/rack_warden/views/rw_new_user.html.erb +22 -6
- data/lib/rack_warden/views/rw_protected.xml.erb +10 -0
- data/lib/rack_warden/views/rw_session.html.erb +34 -0
- data/lib/rack_warden/warden.rb +161 -30
- data/rack_warden.gemspec +16 -13
- 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
|
data/lib/rack_warden/version.rb
CHANGED
@@ -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
|
-
|
7
|
-
<br
|
8
|
-
<%= DataMapper.repository.adapter.
|
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>
|
@@ -4,38 +4,32 @@
|
|
4
4
|
<title>Rack Warden</title>
|
5
5
|
|
6
6
|
<style>
|
7
|
-
|
8
|
-
|
9
|
-
|
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('/
|
20
|
-
<a href="<%=
|
21
|
-
|
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
|
-
|
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>
|
3
|
+
<h2>Account Management</h2>
|
4
4
|
<p>
|
5
|
-
<a href="<%=
|
6
|
-
<a href="<%=
|
7
|
-
<a href="<%=
|
8
|
-
<a href="<%=
|
9
|
-
|
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
|
-
|
2
|
-
<form action="<%=
|
3
|
-
|
4
|
-
|
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="<%=
|
20
|
+
or <a href="<%=url_for('/new')%>">Create a new account</a>
|
8
21
|
<% end %>
|
9
22
|
</form>
|