pager-restful_open_id_authentication 1.0.20080507
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.
- data/README +63 -0
- data/Rakefile +22 -0
- data/generators/open_id_authenticated/USAGE +1 -0
- data/generators/open_id_authenticated/open_id_authenticated_generator.rb +215 -0
- data/generators/open_id_authenticated/templates/activation.rhtml +3 -0
- data/generators/open_id_authenticated/templates/authenticated_system.rb +120 -0
- data/generators/open_id_authenticated/templates/authenticated_test_helper.rb +113 -0
- data/generators/open_id_authenticated/templates/controller.rb +94 -0
- data/generators/open_id_authenticated/templates/fixtures.yml +17 -0
- data/generators/open_id_authenticated/templates/functional_test.rb +85 -0
- data/generators/open_id_authenticated/templates/helper.rb +2 -0
- data/generators/open_id_authenticated/templates/login.rhtml +20 -0
- data/generators/open_id_authenticated/templates/migration.rb +45 -0
- data/generators/open_id_authenticated/templates/model.rb +93 -0
- data/generators/open_id_authenticated/templates/model_controller.rb +30 -0
- data/generators/open_id_authenticated/templates/model_functional_test.rb +72 -0
- data/generators/open_id_authenticated/templates/model_helper.rb +2 -0
- data/generators/open_id_authenticated/templates/notifier.rb +25 -0
- data/generators/open_id_authenticated/templates/notifier_test.rb +31 -0
- data/generators/open_id_authenticated/templates/observer.rb +11 -0
- data/generators/open_id_authenticated/templates/open_id_form.rhtml +14 -0
- data/generators/open_id_authenticated/templates/signup.rhtml +22 -0
- data/generators/open_id_authenticated/templates/signup_notification.rhtml +8 -0
- data/generators/open_id_authenticated/templates/unit_test.rb +101 -0
- data/install.rb +1 -0
- data/lib/controller_methods.rb +137 -0
- data/lib/open_id_store.rb +69 -0
- data/rails/init.rb +15 -0
- metadata +81 -0
data/README
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
RESTful OpenID Authentication 0.1
|
2
|
+
===================
|
3
|
+
|
4
|
+
A Ruby on Rails plugin for OpenID authentication and profile exchange
|
5
|
+
|
6
|
+
Developed by EastMedia (http://www.eastmedia.com) for VeriSign
|
7
|
+
Contact Matt Pelletier - matt AT eastmedia.com
|
8
|
+
|
9
|
+
Thanks to atmos for working on an extension of restful_authentication that piggy-backed
|
10
|
+
the original open_id_consumer plugin. This plugin merges some of those ideas with others
|
11
|
+
and merged them with the open_id_consumer plugin.
|
12
|
+
|
13
|
+
|
14
|
+
WHAT IS IT?
|
15
|
+
-----------
|
16
|
+
|
17
|
+
Installing this plugin allows you to extend your Rails application with OpenID authentication and profile exchange.
|
18
|
+
|
19
|
+
|
20
|
+
PRE-REQUISITES
|
21
|
+
--------------
|
22
|
+
|
23
|
+
* JanRain's Yadis and OpenID 1.1 libraries in Ruby.
|
24
|
+
* These can be obtained using 'gem install ruby-openid'
|
25
|
+
* Requires you have an OpenID account, such as from one of the following:
|
26
|
+
* http://pip.verisignlabs.com
|
27
|
+
* http://www.myopenid.com
|
28
|
+
* Rails 1.2.2 (for the singular resource routes. otherwise 1.2.1 should be fine)
|
29
|
+
* SimplyRestful plugin from Rails
|
30
|
+
* ./script/plugin install http://svn.rubonrails.org/rails/plugins/simply_restful
|
31
|
+
|
32
|
+
|
33
|
+
INSTALLATION
|
34
|
+
------------
|
35
|
+
|
36
|
+
To install you need to run the generator script. This script will:
|
37
|
+
* Create a migration to create tables for the user and OpenID-related tables
|
38
|
+
* Generate two controllers (one for the model and one for the session)
|
39
|
+
* Generate the user model
|
40
|
+
|
41
|
+
./script/generate open_id_authenticated USERMODEL CONTROLLERNAME
|
42
|
+
|
43
|
+
Where USERMODEL would be 'User' and CONTROLLERNAME would be 'Session' by default
|
44
|
+
|
45
|
+
You should add RESTful routes (this is also recommended in the generator output).
|
46
|
+
For example, if your model is 'User' and your login controller is 'Session':
|
47
|
+
|
48
|
+
map.resources :users
|
49
|
+
map.resource :session, :collection => { :begin => :post, :complete => :get }
|
50
|
+
|
51
|
+
|
52
|
+
EDUCATION
|
53
|
+
---------
|
54
|
+
|
55
|
+
On the Web:
|
56
|
+
http://www.openidenabled.com/
|
57
|
+
http://www.openidenabled.com/openid/simple-registration-extension/
|
58
|
+
|
59
|
+
On IRC:
|
60
|
+
Freenode: #openid and ##identity
|
61
|
+
|
62
|
+
More to come
|
63
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the restful_open_id_authentication plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the restful_open_id_authentication plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'RestfulOpenIDAuthentication'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
./script/generate open_id_authenticated USERMODEL CONTROLLERNAME
|
@@ -0,0 +1,215 @@
|
|
1
|
+
class OpenIdAuthenticatedGenerator < Rails::Generator::NamedBase
|
2
|
+
attr_reader :controller_name,
|
3
|
+
:controller_class_path,
|
4
|
+
:controller_file_path,
|
5
|
+
:controller_class_nesting,
|
6
|
+
:controller_class_nesting_depth,
|
7
|
+
:controller_class_name,
|
8
|
+
:controller_singular_name,
|
9
|
+
:controller_plural_name
|
10
|
+
alias_method :controller_file_name, :controller_singular_name
|
11
|
+
alias_method :controller_table_name, :controller_plural_name
|
12
|
+
attr_reader :model_controller_name,
|
13
|
+
:model_controller_class_path,
|
14
|
+
:model_controller_file_path,
|
15
|
+
:model_controller_class_nesting,
|
16
|
+
:model_controller_class_nesting_depth,
|
17
|
+
:model_controller_class_name,
|
18
|
+
:model_controller_singular_name,
|
19
|
+
:model_controller_plural_name
|
20
|
+
alias_method :model_controller_file_name, :model_controller_singular_name
|
21
|
+
alias_method :model_controller_table_name, :model_controller_plural_name
|
22
|
+
|
23
|
+
def initialize(runtime_args, runtime_options = {})
|
24
|
+
super
|
25
|
+
|
26
|
+
@controller_name = args.shift || 'session'
|
27
|
+
@model_controller_name = @name.pluralize
|
28
|
+
|
29
|
+
# sessions controller
|
30
|
+
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
|
31
|
+
@controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
|
32
|
+
|
33
|
+
if @controller_class_nesting.empty?
|
34
|
+
@controller_class_name = @controller_class_name_without_nesting
|
35
|
+
else
|
36
|
+
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# model controller
|
40
|
+
base_name, @model_controller_class_path, @model_controller_file_path, @model_controller_class_nesting, @model_controller_class_nesting_depth = extract_modules(@model_controller_name)
|
41
|
+
@model_controller_class_name_without_nesting, @model_controller_singular_name, @model_controller_plural_name = inflect_names(base_name)
|
42
|
+
|
43
|
+
if @model_controller_class_nesting.empty?
|
44
|
+
@model_controller_class_name = @model_controller_class_name_without_nesting
|
45
|
+
else
|
46
|
+
@model_controller_class_name = "#{@model_controller_class_nesting}::#{@model_controller_class_name_without_nesting}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def manifest
|
51
|
+
recorded_session = record do |m|
|
52
|
+
# Check for class naming collisions.
|
53
|
+
m.class_collisions controller_class_path, "#{controller_class_name}Controller", # Sessions Controller
|
54
|
+
"#{controller_class_name}Helper"
|
55
|
+
m.class_collisions model_controller_class_path, "#{model_controller_class_name}Controller", # Model Controller
|
56
|
+
"#{model_controller_class_name}Helper"
|
57
|
+
m.class_collisions class_path, "#{class_name}", "#{class_name}Notifier", "#{class_name}NotifierTest", "#{class_name}Observer"
|
58
|
+
m.class_collisions [], 'AuthenticatedSystem', 'AuthenticatedTestHelper'
|
59
|
+
|
60
|
+
# Controller, helper, views, and test directories.
|
61
|
+
m.directory File.join('app/models', class_path)
|
62
|
+
m.directory File.join('app/controllers', controller_class_path)
|
63
|
+
m.directory File.join('app/controllers', model_controller_class_path)
|
64
|
+
m.directory File.join('app/helpers', controller_class_path)
|
65
|
+
m.directory File.join('app/views', controller_class_path, controller_file_name)
|
66
|
+
m.directory File.join('app/views', class_path, "#{file_name}_notifier")
|
67
|
+
m.directory File.join('test/functional', controller_class_path)
|
68
|
+
m.directory File.join('app/controllers', model_controller_class_path)
|
69
|
+
m.directory File.join('app/helpers', model_controller_class_path)
|
70
|
+
m.directory File.join('app/views', model_controller_class_path, model_controller_file_name)
|
71
|
+
m.directory File.join('test/functional', model_controller_class_path)
|
72
|
+
m.directory File.join('test/unit', class_path)
|
73
|
+
|
74
|
+
m.template 'model.rb',
|
75
|
+
File.join('app/models',
|
76
|
+
class_path,
|
77
|
+
"#{file_name}.rb")
|
78
|
+
|
79
|
+
if options[:include_activation]
|
80
|
+
%w( notifier observer ).each do |model_type|
|
81
|
+
m.template "#{model_type}.rb", File.join('app/models',
|
82
|
+
class_path,
|
83
|
+
"#{file_name}_#{model_type}.rb")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
m.template 'controller.rb',
|
88
|
+
File.join('app/controllers',
|
89
|
+
controller_class_path,
|
90
|
+
"#{controller_file_name}_controller.rb")
|
91
|
+
|
92
|
+
m.template 'model_controller.rb',
|
93
|
+
File.join('app/controllers',
|
94
|
+
model_controller_class_path,
|
95
|
+
"#{model_controller_file_name}_controller.rb")
|
96
|
+
|
97
|
+
m.template 'authenticated_system.rb',
|
98
|
+
File.join('lib', 'authenticated_system.rb')
|
99
|
+
|
100
|
+
m.template 'authenticated_test_helper.rb',
|
101
|
+
File.join('lib', 'authenticated_test_helper.rb')
|
102
|
+
|
103
|
+
m.template 'functional_test.rb',
|
104
|
+
File.join('test/functional',
|
105
|
+
controller_class_path,
|
106
|
+
"#{controller_file_name}_controller_test.rb")
|
107
|
+
|
108
|
+
m.template 'model_functional_test.rb',
|
109
|
+
File.join('test/functional',
|
110
|
+
model_controller_class_path,
|
111
|
+
"#{model_controller_file_name}_controller_test.rb")
|
112
|
+
|
113
|
+
m.template 'helper.rb',
|
114
|
+
File.join('app/helpers',
|
115
|
+
controller_class_path,
|
116
|
+
"#{controller_file_name}_helper.rb")
|
117
|
+
|
118
|
+
m.template 'model_helper.rb',
|
119
|
+
File.join('app/helpers',
|
120
|
+
model_controller_class_path,
|
121
|
+
"#{model_controller_file_name}_helper.rb")
|
122
|
+
|
123
|
+
m.template 'unit_test.rb',
|
124
|
+
File.join('test/unit',
|
125
|
+
class_path,
|
126
|
+
"#{file_name}_test.rb")
|
127
|
+
|
128
|
+
if options[:include_activation]
|
129
|
+
m.template 'notifier_test.rb', File.join('test/unit', class_path, "#{file_name}_notifier_test.rb")
|
130
|
+
end
|
131
|
+
|
132
|
+
m.template 'fixtures.yml',
|
133
|
+
File.join('test/fixtures',
|
134
|
+
"#{table_name}.yml")
|
135
|
+
|
136
|
+
# Controller templates
|
137
|
+
m.template 'login.rhtml', File.join('app/views', controller_class_path, controller_file_name, "new.rhtml")
|
138
|
+
m.template 'open_id_form.rhtml', File.join('app/views', controller_class_path, controller_file_name, "_open_id_form.rhtml")
|
139
|
+
m.template 'signup.rhtml', File.join('app/views', model_controller_class_path, model_controller_file_name, "new.rhtml")
|
140
|
+
|
141
|
+
if options[:include_activation]
|
142
|
+
# Mailer templates
|
143
|
+
%w( activation signup_notification ).each do |action|
|
144
|
+
m.template "#{action}.rhtml",
|
145
|
+
File.join('app/views', "#{file_name}_notifier", "#{action}.rhtml")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
unless options[:skip_migration]
|
150
|
+
m.migration_template 'migration.rb', 'db/migrate', :assigns => {
|
151
|
+
:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
|
152
|
+
}, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
action = nil
|
157
|
+
action = $0.split("/")[1]
|
158
|
+
case action
|
159
|
+
when "generate"
|
160
|
+
puts
|
161
|
+
puts ("-" * 70)
|
162
|
+
puts "Don't forget to:"
|
163
|
+
puts
|
164
|
+
puts " - add restful routes in config/routes.rb"
|
165
|
+
puts " map.resources :#{model_controller_file_name}"
|
166
|
+
puts " map.resource :#{controller_file_name}, :collection => { :begin => :post, :complete => :get }"
|
167
|
+
if options[:include_activation]
|
168
|
+
puts " map.activate '/activate/:activation_code', :controller => '#{model_controller_file_name}', :action => 'activate'"
|
169
|
+
puts
|
170
|
+
puts " - add an observer to config/environment.rb"
|
171
|
+
puts " config.active_record.observers = :#{file_name}_observer"
|
172
|
+
end
|
173
|
+
puts
|
174
|
+
puts "Try these for some familiar login URLs if you like:"
|
175
|
+
puts
|
176
|
+
puts " map.signup '/signup', :controller => '#{model_controller_file_name}', :action => 'new'"
|
177
|
+
puts " map.login '/login', :controller => '#{controller_file_name}', :action => 'new'"
|
178
|
+
puts " map.logout '/logout', :controller => '#{controller_file_name}', :action => 'destroy'"
|
179
|
+
puts
|
180
|
+
puts ("-" * 70)
|
181
|
+
puts
|
182
|
+
when "destroy"
|
183
|
+
puts
|
184
|
+
puts ("-" * 70)
|
185
|
+
puts
|
186
|
+
puts "Thanks for using restful_open_id_authentication"
|
187
|
+
puts
|
188
|
+
puts "Don't forget to comment out the observer line in environment.rb"
|
189
|
+
puts " (This was optional so it may not even be there)"
|
190
|
+
puts " # config.active_record.observers = :#{file_name}_observer"
|
191
|
+
puts
|
192
|
+
puts ("-" * 70)
|
193
|
+
puts
|
194
|
+
else
|
195
|
+
puts
|
196
|
+
end
|
197
|
+
|
198
|
+
recorded_session
|
199
|
+
end
|
200
|
+
|
201
|
+
protected
|
202
|
+
# Override with your own usage banner.
|
203
|
+
def banner
|
204
|
+
"Usage: #{$0} authenticated ModelName [ControllerName]"
|
205
|
+
end
|
206
|
+
|
207
|
+
def add_options!(opt)
|
208
|
+
opt.separator ''
|
209
|
+
opt.separator 'Options:'
|
210
|
+
opt.on("--skip-migration",
|
211
|
+
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
|
212
|
+
opt.on("--include-activation",
|
213
|
+
"Generate signup 'activation code' confirmation via email") { |v| options[:include_activation] = v }
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module AuthenticatedSystem
|
2
|
+
protected
|
3
|
+
# Returns true or false if the user is logged in.
|
4
|
+
# Preloads @current_<%= file_name %> with the user model if they're logged in.
|
5
|
+
def logged_in?
|
6
|
+
current_<%= file_name %> != :false
|
7
|
+
end
|
8
|
+
|
9
|
+
# Accesses the current <%= file_name %> from the session.
|
10
|
+
def current_<%= file_name %>
|
11
|
+
@current_<%= file_name %> ||= (session[:<%= file_name %>] && <%= class_name %>.find_by_id(session[:<%= file_name %>])) || :false
|
12
|
+
end
|
13
|
+
|
14
|
+
# Store the given <%= file_name %> in the session.
|
15
|
+
def current_<%= file_name %>=(new_<%= file_name %>)
|
16
|
+
session[:<%= file_name %>] = (new_<%= file_name %>.nil? || new_<%= file_name %>.is_a?(Symbol)) ? nil : new_<%= file_name %>.id
|
17
|
+
@current_<%= file_name %> = new_<%= file_name %>
|
18
|
+
end
|
19
|
+
|
20
|
+
# Check if the <%= file_name %> is authorized.
|
21
|
+
#
|
22
|
+
# Override this method in your controllers if you want to restrict access
|
23
|
+
# to only a few actions or if you want to check if the <%= file_name %>
|
24
|
+
# has the correct rights.
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
#
|
28
|
+
# # only allow nonbobs
|
29
|
+
# def authorize?
|
30
|
+
# current_<%= file_name %>.login != "bob"
|
31
|
+
# end
|
32
|
+
def authorized?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
# Filter method to enforce a login requirement.
|
37
|
+
#
|
38
|
+
# To require logins for all actions, use this in your controllers:
|
39
|
+
#
|
40
|
+
# before_filter :login_required
|
41
|
+
#
|
42
|
+
# To require logins for specific actions, use this in your controllers:
|
43
|
+
#
|
44
|
+
# before_filter :login_required, :only => [ :edit, :update ]
|
45
|
+
#
|
46
|
+
# To skip this in a subclassed controller:
|
47
|
+
#
|
48
|
+
# skip_before_filter :login_required
|
49
|
+
#
|
50
|
+
def login_required
|
51
|
+
username, passwd = get_auth_data
|
52
|
+
self.current_<%= file_name %> ||= <%= class_name %>.authenticate(username, passwd) || :false if username && passwd
|
53
|
+
logged_in? && authorized? ? true : access_denied
|
54
|
+
end
|
55
|
+
|
56
|
+
# Redirect as appropriate when an access request fails.
|
57
|
+
#
|
58
|
+
# The default action is to redirect to the login screen.
|
59
|
+
#
|
60
|
+
# Override this method in your controllers if you want to have special
|
61
|
+
# behavior in case the <%= file_name %> is not authorized
|
62
|
+
# to access the requested action. For example, a popup window might
|
63
|
+
# simply close itself.
|
64
|
+
def access_denied
|
65
|
+
respond_to do |accepts|
|
66
|
+
accepts.html do
|
67
|
+
store_location
|
68
|
+
redirect_to :controller => '<%= controller_file_name %>', :action => 'new'
|
69
|
+
end
|
70
|
+
accepts.xml do
|
71
|
+
headers["Status"] = "Unauthorized"
|
72
|
+
headers["WWW-Authenticate"] = %(Basic realm="Web Password")
|
73
|
+
render :text => "Could't authenticate you", :status => '401 Unauthorized'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
false
|
77
|
+
end
|
78
|
+
|
79
|
+
# Store the URI of the current request in the session.
|
80
|
+
#
|
81
|
+
# We can return to this location by calling #redirect_back_or_default.
|
82
|
+
def store_location
|
83
|
+
session[:return_to] = request.request_uri
|
84
|
+
end
|
85
|
+
|
86
|
+
# Redirect to the URI stored by the most recent store_location call or
|
87
|
+
# to the passed default.
|
88
|
+
def redirect_back_or_default(default)
|
89
|
+
session[:return_to] ? redirect_to_url(session[:return_to]) : redirect_to(default)
|
90
|
+
session[:return_to] = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
# Inclusion hook to make #current_<%= file_name %> and #logged_in?
|
94
|
+
# available as ActionView helper methods.
|
95
|
+
def self.included(base)
|
96
|
+
base.send :helper_method, :current_<%= file_name %>, :logged_in?
|
97
|
+
end
|
98
|
+
|
99
|
+
# When called with before_filter :login_from_cookie will check for an :auth_token
|
100
|
+
# cookie and log the user back in if apropriate
|
101
|
+
def login_from_cookie
|
102
|
+
return unless cookies[:auth_token] && !logged_in?
|
103
|
+
user = <%= class_name %>.find_by_remember_token(cookies[:auth_token])
|
104
|
+
if user && user.remember_token?
|
105
|
+
user.remember_me
|
106
|
+
self.current_<%= file_name %> = user
|
107
|
+
cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
|
108
|
+
flash[:notice] = "Logged in successfully"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
@@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
|
114
|
+
# gets BASIC auth info
|
115
|
+
def get_auth_data
|
116
|
+
auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
|
117
|
+
auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
|
118
|
+
return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module AuthenticatedTestHelper
|
2
|
+
# Sets the current <%= file_name %> in the session from the <%= file_name %> fixtures.
|
3
|
+
def login_as(<%= file_name %>)
|
4
|
+
@request.session[:<%= file_name %>] = <%= file_name %> ? <%= table_name %>(<%= file_name %>).id : nil
|
5
|
+
end
|
6
|
+
|
7
|
+
def content_type(type)
|
8
|
+
@request.env['Content-Type'] = type
|
9
|
+
end
|
10
|
+
|
11
|
+
def accept(accept)
|
12
|
+
@request.env["HTTP_ACCEPT"] = accept
|
13
|
+
end
|
14
|
+
|
15
|
+
def authorize_as(user)
|
16
|
+
if user
|
17
|
+
@request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64("#{users(user).login}:test")}"
|
18
|
+
accept 'application/xml'
|
19
|
+
content_type 'application/xml'
|
20
|
+
else
|
21
|
+
@request.env["HTTP_AUTHORIZATION"] = nil
|
22
|
+
accept nil
|
23
|
+
content_type nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# http://project.ioni.st/post/217#post-217
|
28
|
+
#
|
29
|
+
# def test_new_publication
|
30
|
+
# assert_difference(Publication, :count) do
|
31
|
+
# post :create, :publication => {...}
|
32
|
+
# # ...
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
def assert_difference(object, method = nil, difference = 1)
|
37
|
+
initial_value = object.send(method)
|
38
|
+
yield
|
39
|
+
assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def assert_no_difference(object, method, &block)
|
43
|
+
assert_difference object, method, 0, &block
|
44
|
+
end
|
45
|
+
|
46
|
+
# Assert the block redirects to the login
|
47
|
+
#
|
48
|
+
# assert_requires_login(:bob) { |c| c.get :edit, :id => 1 }
|
49
|
+
#
|
50
|
+
def assert_requires_login(login = nil)
|
51
|
+
yield HttpLoginProxy.new(self, login)
|
52
|
+
end
|
53
|
+
|
54
|
+
def assert_http_authentication_required(login = nil)
|
55
|
+
yield XmlLoginProxy.new(self, login)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reset!(*instance_vars)
|
59
|
+
instance_vars = [:controller, :request, :response] unless instance_vars.any?
|
60
|
+
instance_vars.collect! { |v| "@#{v}".to_sym }
|
61
|
+
instance_vars.each do |var|
|
62
|
+
instance_variable_set(var, instance_variable_get(var).class.new)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class BaseLoginProxy
|
68
|
+
attr_reader :controller
|
69
|
+
attr_reader :options
|
70
|
+
def initialize(controller, login)
|
71
|
+
@controller = controller
|
72
|
+
@login = login
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def authenticated
|
77
|
+
raise NotImplementedError
|
78
|
+
end
|
79
|
+
|
80
|
+
def check
|
81
|
+
raise NotImplementedError
|
82
|
+
end
|
83
|
+
|
84
|
+
def method_missing(method, *args)
|
85
|
+
@controller.reset!
|
86
|
+
authenticate
|
87
|
+
@controller.send(method, *args)
|
88
|
+
check
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class HttpLoginProxy < BaseLoginProxy
|
93
|
+
protected
|
94
|
+
def authenticate
|
95
|
+
@controller.login_as @login if @login
|
96
|
+
end
|
97
|
+
|
98
|
+
def check
|
99
|
+
@controller.assert_redirected_to :controller => 'sessions', :action => 'new'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class XmlLoginProxy < BaseLoginProxy
|
104
|
+
protected
|
105
|
+
def authenticate
|
106
|
+
@controller.accept 'application/xml'
|
107
|
+
@controller.authorize_as @login if @login
|
108
|
+
end
|
109
|
+
|
110
|
+
def check
|
111
|
+
@controller.assert_response 401
|
112
|
+
end
|
113
|
+
end
|