pager-restful_open_id_authentication 1.0.20080507
Sign up to get free protection for your applications and to get access to all the features.
- 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
|