openid_fu_generator 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +48 -0
- data/README.txt +48 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +72 -0
- data/config/requirements.rb +15 -0
- data/lib/openid_fu.rb +10 -0
- data/lib/openid_fu/association.rb +12 -0
- data/lib/openid_fu/controller_methods.rb +97 -0
- data/lib/openid_fu/nonce.rb +5 -0
- data/lib/openid_fu/openid_ar_store.rb +62 -0
- data/lib/openid_fu/openid_attribute_types.rb +79 -0
- data/lib/openid_fu/openid_ax_ext.rb +12 -0
- data/lib/openid_fu/openid_setting.rb +5 -0
- data/lib/openid_fu/version.rb +9 -0
- data/openid_fu_generator.rb +246 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +17 -0
- data/templates/activation.html.erb +3 -0
- data/templates/authenticated_system.rb +155 -0
- data/templates/authenticated_test_helper.rb +94 -0
- data/templates/controller.rb +111 -0
- data/templates/fixtures.yml +15 -0
- data/templates/functional_test.rb +87 -0
- data/templates/helper.rb +2 -0
- data/templates/login.html.erb +20 -0
- data/templates/migration.rb +55 -0
- data/templates/model.rb +92 -0
- data/templates/model_controller.rb +30 -0
- data/templates/model_functional_test.rb +64 -0
- data/templates/model_helper.rb +2 -0
- data/templates/notifier.rb +25 -0
- data/templates/notifier_test.rb +31 -0
- data/templates/observer.rb +11 -0
- data/templates/open_id_form.html.erb +13 -0
- data/templates/restore_location.html.erb +30 -0
- data/templates/signup.html.erb +19 -0
- data/templates/signup_notification.html.erb +8 -0
- data/templates/unit_test.rb +94 -0
- data/test/test_helper.rb +4 -0
- data/test/test_openid_fu.rb +11 -0
- metadata +108 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
desc 'Release the website and new gem version'
|
2
|
+
task :deploy => [:check_version, :website, :release] do
|
3
|
+
puts "Remember to create SVN tag:"
|
4
|
+
puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
|
5
|
+
"svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
|
6
|
+
puts "Suggested comment:"
|
7
|
+
puts "Tagging release #{CHANGES}"
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
|
11
|
+
task :local_deploy => [:website_generate, :install_gem]
|
12
|
+
|
13
|
+
task :check_version do
|
14
|
+
unless ENV['VERSION']
|
15
|
+
puts 'Must pass a VERSION=x.y.z release version'
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
unless ENV['VERSION'] == VERS
|
19
|
+
puts "Please update your version.rb to match the release version, currently #{VERS}"
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Install the package as a gem, without generating documentation(ri/rdoc)'
|
25
|
+
task :install_gem_no_doc => [:clean, :package] do
|
26
|
+
sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri"
|
27
|
+
end
|
28
|
+
|
29
|
+
namespace :manifest do
|
30
|
+
desc 'Recreate Manifest.txt to include ALL files'
|
31
|
+
task :refresh do
|
32
|
+
`rake check_manifest | patch -p0 > Manifest.txt`
|
33
|
+
end
|
34
|
+
end
|
data/tasks/website.rake
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
desc 'Generate website files'
|
2
|
+
task :website_generate => :ruby_env do
|
3
|
+
(Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt|
|
4
|
+
sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
desc 'Upload website files to rubyforge'
|
9
|
+
task :website_upload do
|
10
|
+
host = "#{rubyforge_username}@rubyforge.org"
|
11
|
+
remote_dir = "/var/www/gforge-projects/#{PATH}/"
|
12
|
+
local_dir = 'website'
|
13
|
+
sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate and upload website files'
|
17
|
+
task :website => [:website_generate, :website_upload, :publish_docs]
|
@@ -0,0 +1,155 @@
|
|
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
|
+
return auto_login if request.xhr?
|
69
|
+
redirect_to :controller => '<%= controller_file_name %>',
|
70
|
+
:action => 'new', :open_id_claimed_id => params[:open_id_claimed_id]
|
71
|
+
end
|
72
|
+
accepts.xml do
|
73
|
+
headers["Status"] = "Unauthorized"
|
74
|
+
headers["WWW-Authenticate"] = %(Basic realm="Web Password")
|
75
|
+
render :text => "Could't authenticate you", :status => '401 Unauthorized'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
81
|
+
def auto_login
|
82
|
+
render :update do |page|
|
83
|
+
page << <<-"EOH"
|
84
|
+
(function(){
|
85
|
+
var input, form = document.createElement('form');
|
86
|
+
form.style.position = 'absolute';
|
87
|
+
form.style.left = form.style.top = '-1000px';
|
88
|
+
document.body.appendChild(form);
|
89
|
+
input = form.appendChild(document.createElement('input'));
|
90
|
+
input.name = 'open_id_claimed_id';
|
91
|
+
input.value = #{params[:open_id_claimed_id].to_json};
|
92
|
+
input = form.appendChild(document.createElement('input'));
|
93
|
+
input.name = 'remember_me';
|
94
|
+
input.value = #{params[:remember_me].to_json};
|
95
|
+
input = form.appendChild(document.createElement('input'));
|
96
|
+
input.name = 'authenticity_token';
|
97
|
+
input.value = $$('input[name="authenticity_token"]')[0].value;
|
98
|
+
form.action = #{begin_<%= controller_file_name %>_path.to_json};
|
99
|
+
form.method = 'POST';
|
100
|
+
form.submit();
|
101
|
+
})();
|
102
|
+
EOH
|
103
|
+
end
|
104
|
+
false
|
105
|
+
end
|
106
|
+
|
107
|
+
# Store the URI of the current request in the session.
|
108
|
+
#
|
109
|
+
# We can return to this location by calling #redirect_back_or_default.
|
110
|
+
def store_location
|
111
|
+
session[:stored_parameters] = params
|
112
|
+
session[:return_to] = request.request_uri
|
113
|
+
session[:return_to_method] = request.method
|
114
|
+
end
|
115
|
+
|
116
|
+
# Redirect to the URI stored by the most recent store_location call or
|
117
|
+
# to the passed default.
|
118
|
+
def redirect_back_or_default(default)
|
119
|
+
if session[:return_to]
|
120
|
+
render :action => 'restore_location'
|
121
|
+
else
|
122
|
+
redirect_to(default)
|
123
|
+
end
|
124
|
+
session[:return_to] = nil
|
125
|
+
session[:return_to_method] = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
# Inclusion hook to make #current_<%= file_name %> and #logged_in?
|
129
|
+
# available as ActionView helper methods.
|
130
|
+
def self.included(base)
|
131
|
+
base.send :helper_method, :current_<%= file_name %>, :logged_in?
|
132
|
+
end
|
133
|
+
|
134
|
+
# When called with before_filter :login_from_cookie will check for an :auth_token
|
135
|
+
# cookie and log the user back in if apropriate
|
136
|
+
def login_from_cookie
|
137
|
+
return unless cookies[:auth_token] && !logged_in?
|
138
|
+
user = <%= class_name %>.find_by_remember_token(cookies[:auth_token])
|
139
|
+
if user && user.remember_token?
|
140
|
+
user.remember_me
|
141
|
+
self.current_<%= file_name %> = user
|
142
|
+
cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
|
143
|
+
flash[:notice] = "Logged in successfully"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
@@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
|
149
|
+
# gets BASIC auth info
|
150
|
+
def get_auth_data
|
151
|
+
auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
|
152
|
+
auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
|
153
|
+
return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,94 @@
|
|
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
|
+
# Assert the block redirects to the login
|
28
|
+
#
|
29
|
+
# assert_requires_login(:bob) { |c| c.get :edit, :id => 1 }
|
30
|
+
#
|
31
|
+
def assert_requires_login(login = nil)
|
32
|
+
yield HttpLoginProxy.new(self, login)
|
33
|
+
end
|
34
|
+
|
35
|
+
def assert_http_authentication_required(login = nil)
|
36
|
+
yield XmlLoginProxy.new(self, login)
|
37
|
+
end
|
38
|
+
|
39
|
+
def reset!(*instance_vars)
|
40
|
+
instance_vars = [:controller, :request, :response] unless instance_vars.any?
|
41
|
+
instance_vars.collect! { |v| "@#{v}".to_sym }
|
42
|
+
instance_vars.each do |var|
|
43
|
+
instance_variable_set(var, instance_variable_get(var).class.new)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class BaseLoginProxy
|
49
|
+
attr_reader :controller
|
50
|
+
attr_reader :options
|
51
|
+
def initialize(controller, login)
|
52
|
+
@controller = controller
|
53
|
+
@login = login
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def authenticated
|
58
|
+
raise NotImplementedError
|
59
|
+
end
|
60
|
+
|
61
|
+
def check
|
62
|
+
raise NotImplementedError
|
63
|
+
end
|
64
|
+
|
65
|
+
def method_missing(method, *args)
|
66
|
+
@controller.reset!
|
67
|
+
authenticate
|
68
|
+
@controller.send(method, *args)
|
69
|
+
check
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class HttpLoginProxy < BaseLoginProxy
|
74
|
+
protected
|
75
|
+
def authenticate
|
76
|
+
@controller.login_as @login if @login
|
77
|
+
end
|
78
|
+
|
79
|
+
def check
|
80
|
+
@controller.assert_redirected_to :controller => 'sessions', :action => 'new'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class XmlLoginProxy < BaseLoginProxy
|
85
|
+
protected
|
86
|
+
def authenticate
|
87
|
+
@controller.accept 'application/xml'
|
88
|
+
@controller.authorize_as @login if @login
|
89
|
+
end
|
90
|
+
|
91
|
+
def check
|
92
|
+
@controller.assert_response 401
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# This controller handles the login/logout function of the site.
|
2
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
3
|
+
include OpenidFu::ControllerMethods
|
4
|
+
# XXX: Be sure to include AuthenticationSystem in Application Controller instead
|
5
|
+
include AuthenticatedSystem
|
6
|
+
|
7
|
+
# Obtain personal identity from OP. If OP accomodates the OpenID Attributes Exchange(AX) or SREG,
|
8
|
+
# then use AX or SREG, unfortunately if both OpenID Extensions aren't supported then input ones in RP.
|
9
|
+
#
|
10
|
+
# == Examples:
|
11
|
+
# # Email and nickname are required, gender and birth date are obtained if these are available.
|
12
|
+
# open_id_consumer :required => [:email, :nickname], :if_available => [:gender, :birth_date]
|
13
|
+
#
|
14
|
+
open_id_consumer :required => [:email, :nickname]
|
15
|
+
|
16
|
+
# XXX: If you want "remember me" functionality, add this before_filter to Application Controller
|
17
|
+
before_filter :login_from_cookie
|
18
|
+
|
19
|
+
rescue_from OpenID::DiscoveryFailure, :with => :begin_error
|
20
|
+
|
21
|
+
def new
|
22
|
+
@<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
|
23
|
+
end
|
24
|
+
|
25
|
+
def create
|
26
|
+
self.current_<%= file_name %> = <%= class_name %>.authenticate(params[:email], params[:password])
|
27
|
+
if logged_in?
|
28
|
+
if params[:remember_me] == "1"
|
29
|
+
self.current_<%= file_name %>.remember_me
|
30
|
+
cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
|
31
|
+
end
|
32
|
+
redirect_back_or_default('/')
|
33
|
+
flash[:notice] = "Logged in successfully"
|
34
|
+
else
|
35
|
+
flash[:notice] = "Login failed"
|
36
|
+
render :action => 'new'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy
|
41
|
+
self.current_<%= file_name %>.forget_me if logged_in?
|
42
|
+
cookies.delete :auth_token
|
43
|
+
reset_session
|
44
|
+
flash[:notice] = "You have been logged out."
|
45
|
+
redirect_back_or_default('/')
|
46
|
+
end
|
47
|
+
|
48
|
+
def begin
|
49
|
+
# If the URL was unusable (either because of network conditions, a server error,
|
50
|
+
# or that the response returned was not an OpenID identity page), the library
|
51
|
+
# will return HTTP_FAILURE or PARSE_ERROR. Let the user know that the URL is unusable.
|
52
|
+
case open_id_request.status
|
53
|
+
when OpenID::Consumer::SUCCESS
|
54
|
+
# The URL was a valid identity URL. Now we just need to send a redirect
|
55
|
+
# to the server using the redirect_url the library created for us.
|
56
|
+
|
57
|
+
# redirect to the server
|
58
|
+
redirect_to open_id_request.redirect_url((request.protocol + request.host_with_port + "/"), complete_<%= controller_file_name %>_url)
|
59
|
+
else
|
60
|
+
flash[:error] = "Unable to find OpenID server for <q>#{params[:open_id_claimed_id]}</q>"
|
61
|
+
render :action => :new
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def begin_error
|
66
|
+
flash[:error] =
|
67
|
+
"Unable to find OpenID server for <q>#{params[:open_id_claimed_id]}</q>"
|
68
|
+
render :action => :new
|
69
|
+
end
|
70
|
+
|
71
|
+
def complete
|
72
|
+
case open_id_response.status
|
73
|
+
when OpenID::Consumer::FAILURE
|
74
|
+
# In the case of failure, if info is non-nil, it is the URL that we were verifying.
|
75
|
+
# We include it in the error message to help the user figure out what happened.
|
76
|
+
flash[:notice] = if open_id_response.identity_url
|
77
|
+
"Verification of #{open_id_response.identity_url} failed. "
|
78
|
+
else
|
79
|
+
"Verification failed. "
|
80
|
+
end
|
81
|
+
flash[:notice] += open_id_response.msg.to_s
|
82
|
+
when OpenID::Consumer::SUCCESS
|
83
|
+
# Success means that the transaction completed without error. If info is nil,
|
84
|
+
# it means that the user cancelled the verification.
|
85
|
+
flash[:notice] = "You have successfully verified #{open_id_response.identity_url} as your identity."
|
86
|
+
if open_id_fields.any?
|
87
|
+
@<%= file_name %> = <%= class_name %>.find_by_claimed_id(open_id_response.identity_url)
|
88
|
+
@<%= file_name %> ||= <%= class_name %>.new(:claimed_id => open_id_response.identity_url)
|
89
|
+
@<%= file_name %>.email = open_id_fields['email'] if open_id_fields['email']
|
90
|
+
@<%= file_name %>.nickname = open_id_fields['nickname'] if open_id_fields['nickname']
|
91
|
+
if @<%= file_name %>.save
|
92
|
+
self.current_<%= file_name %> = @<%= file_name %>
|
93
|
+
if params[:remember_me] == "1"
|
94
|
+
self.current_<%= file_name %>.remember_me
|
95
|
+
cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
|
96
|
+
end
|
97
|
+
flash[:notice] = "You have successfully verified #{open_id_response.identity_url} as your identity."
|
98
|
+
return redirect_back_or_default('/')
|
99
|
+
else
|
100
|
+
flash[:notice] = @<%= file_name %>.errors.full_messages.join('<br />')
|
101
|
+
render :action => 'new' and return
|
102
|
+
end
|
103
|
+
end
|
104
|
+
when OpenID::Consumer::CANCEL
|
105
|
+
flash[:notice] = "Verification cancelled."
|
106
|
+
else
|
107
|
+
flash[:notice] = "Unknown response status: #{open_id_response.status}"
|
108
|
+
end
|
109
|
+
redirect_to :action => 'new'
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
quentin:
|
2
|
+
id: 1
|
3
|
+
email: quentin@example.com
|
4
|
+
salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
|
5
|
+
crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
|
6
|
+
created_at: <%%= 5.days.ago.to_s :db %>
|
7
|
+
<% if options[:include_activation] %> activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9b <% end %>
|
8
|
+
<% if options[:include_activation] %> activated_at: <%%= 5.days.ago.to_s :db %> <% end %>
|
9
|
+
aaron:
|
10
|
+
id: 2
|
11
|
+
email: aaron@example.com
|
12
|
+
salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
|
13
|
+
crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
|
14
|
+
created_at: <%%= 1.days.ago.to_s :db %>
|
15
|
+
<% if options[:include_activation] %> activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9a <% end %>
|