From: Tom Hughes Date: Wed, 16 Jun 2010 20:18:44 +0000 (+0100) Subject: Merge branch 'master' into openid X-Git-Tag: live~6824 X-Git-Url: https://git.openstreetmap.org/rails.git/commitdiff_plain/6a3cadb52d010b333a4acea8942bc4c13394e236?hp=b43f9c47145a14b547a900d9559f9aa3fd0895d1 Merge branch 'master' into openid --- diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index 0957853ce..aae059252 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -20,8 +20,6 @@ class UserController < ApplicationController def terms @title = t 'user.new.title' - @user = User.new(params[:user]) - @legale = params[:legale] || OSM.IPToCountry(request.remote_ip) || APP_CONFIG['default_legale'] @text = OSM.legal_text_for_country(@legale) @@ -29,8 +27,46 @@ class UserController < ApplicationController render :update do |page| page.replace_html "contributorTerms", :partial => "terms" end - elsif @user.invalid? - render :action => 'new' + elsif params[:user] + session[:referer] = params[:referer] + + @user = User.new(params[:user]) + + if params[:user][:openid_url] and @user.pass_crypt.empty? + # We are creating an account with OpenID and no password + # was specified so create a random one + @user.pass_crypt = ActiveSupport::SecureRandom.base64(16) + @user.pass_crypt_confirmation = @user.pass_crypt + end + + if @user.valid? + if params[:user][:openid_url].nil? or + params[:user][:openid_url].empty? + # No OpenID so just move on to the terms + render :action => 'terms' + else + # Verify OpenID before moving on + session[:new_user] = @user + openid_verify(params[:user][:openid_url], @user) + end + else + # Something is wrong, so rerender the form + render :action => 'new' + end + elsif using_open_id? + # The redirect from the OpenID provider reenters here + # again and we need to pass the parameters through to + # the open_id_authentication function + @user = session.delete(:new_user) + + openid_verify(nil, @user) do |user| + end + + if @user.openid_url.nil? or @user.invalid? + render :action => 'new' + else + render :action => 'terms' + end end end @@ -53,7 +89,7 @@ class UserController < ApplicationController if @user.save flash[:notice] = t 'user.new.flash create success message' - Notifier.deliver_signup_confirm(@user, @user.tokens.create(:referer => params[:referer])) + Notifier.deliver_signup_confirm(@user, @user.tokens.create(:referer => session.delete(:referer))) redirect_to :action => 'login' else render :action => 'new' @@ -85,29 +121,25 @@ class UserController < ApplicationController @user.home_lat = params[:user][:home_lat] @user.home_lon = params[:user][:home_lon] - if @user.save - set_locale + @user.openid_url = nil if params[:user][:openid_url].empty? - if @user.new_email.nil? or @user.new_email.empty? - flash[:notice] = t 'user.account.flash update success' - else - flash[:notice] = t 'user.account.flash update success confirm needed' - - begin - Notifier.deliver_email_confirm(@user, @user.tokens.create) - rescue - # Ignore errors sending email - end - end - - redirect_to :action => "account", :display_name => @user.display_name + if params[:user][:openid_url].length > 0 and + params[:user][:openid_url] != @user.openid_url + # If the OpenID has changed, we want to check that it is a + # valid OpenID and one the user has control over before saving + # it as a password equivalent for the user. + session[:new_user] = @user + openid_verify(params[:user][:openid_url], @user) + else + update_user(@user) end - else - if flash[:errors] - flash[:errors].each do |attr,msg| - attr = "new_email" if attr == "email" - @user.errors.add(attr,msg) - end + elsif using_open_id? + # The redirect from the OpenID provider reenters here + # again and we need to pass the parameters through to + # the open_id_authentication function + @user = session.delete(:new_user) + openid_verify(nil, @user) do |user| + update_user(user) end end end @@ -166,42 +198,29 @@ class UserController < ApplicationController def new @title = t 'user.new.title' - - # The user is logged in already, so don't show them the signup - # page, instead send them to the home page - redirect_to :controller => 'site', :action => 'index' if session[:user] + @referer = params[:referer] || session[:referer] + + if session[:user] + # The user is logged in already, so don't show them the signup + # page, instead send them to the home page + redirect_to :controller => 'site', :action => 'index' + elsif not params['openid'].nil? + flash.now[:notice] = t 'user.new.openid association' + end end def login - @title = t 'user.login.title' - - if params[:user] - email_or_display_name = params[:user][:email] - pass = params[:user][:password] - user = User.authenticate(:username => email_or_display_name, :password => pass) + if params[:username] or using_open_id? + session[:remember_me] ||= params[:remember_me] + session[:referer] ||= params[:referer] - if user - session[:user] = user.id - session_expires_after 1.month if params[:remember_me] - - # The user is logged in, if the referer param exists, redirect - # them to that unless they've also got a block on them, in - # which case redirect them to the block so they can clear it. - if user.blocked_on_view - redirect_to user.blocked_on_view, :referrer => params[:referrer] - elsif params[:referer] - redirect_to params[:referer] - else - redirect_to :controller => 'site', :action => 'index' - end - elsif User.authenticate(:username => email_or_display_name, :password => pass, :pending => true) - flash.now[:error] = t 'user.login.account not active' - elsif User.authenticate(:username => email_or_display_name, :password => pass, :suspended => true) - webmaster = link_to t('user.login.webmaster'), "mailto:webmaster@openstreetmap.org" - flash.now[:error] = t 'user.login.account suspended', :webmaster => webmaster + if using_open_id? + openid_authentication(params[:openid_url]) else - flash.now[:error] = t 'user.login.auth failure' + password_authentication(params[:username], params[:password]) end + else + @title = t 'user.login.title' end end @@ -375,6 +394,162 @@ class UserController < ApplicationController private + ## + # handle password authentication + def password_authentication(username, password) + if user = User.authenticate(:username => username, :password => password) + successful_login(user) + elsif User.authenticate(:username => username, :password => password, :pending => true) + failed_login t('user.login.account not active') + elsif User.authenticate(:username => username, :password => password, :suspended => true) + webmaster = link_to t('user.login.webmaster'), "mailto:webmaster@openstreetmap.org" + failed_login t('user.login.account suspended', :webmaster => webmaster) + else + failed_login t('user.login.auth failure') + end + end + + ## + # handle OpenID authentication + def openid_authentication(openid_url) + # If we don't appear to have a user for this URL then ask the + # provider for some extra information to help with signup + if openid_url and User.find_by_openid_url(openid_url) + optional = nil + else + optional = [:nickname, :email] + end + + # Start the authentication + authenticate_with_open_id(openid_expand_url(openid_url), :optional => optional) do |result, identity_url, registration| + if result.successful? + # We need to use the openid url passed back from the OpenID provider + # rather than the one supplied by the user, as these can be different. + # + # For example, you can simply enter yahoo.com in the login box rather + # than a user specific url. Only once it comes back from the provider + # provider do we know the unique address for the user. + if user = User.find_by_openid_url(identity_url) + case user.status + when "pending" then + failed_login t('user.login.account not active') + when "active", "confirmed" then + successful_login(user) + when "suspended" then + webmaster = link_to t('user.login.webmaster'), "mailto:webmaster@openstreetmap.org" + failed_login t('user.login.account suspended', :webmaster => webmaster) + else + failed_login t('user.login.auth failure') + end + else + # We don't have a user registered to this OpenID, so redirect + # to the create account page with username and email filled + # in if they have been given by the OpenID provider through + # the simple registration protocol. + redirect_to :controller => 'user', :action => 'new', :nickname => registration['nickname'], :email => registration['email'], :openid => identity_url + end + elsif result.missing? + failed_login t('user.login.openid missing provider') + elsif result.invalid? + failed_login t('user.login.openid invalid') + else + failed_login t('user.login.auth failure') + end + end + end + + ## + # verify an OpenID URL + def openid_verify(openid_url, user) + user.openid_url = openid_url + + authenticate_with_open_id(openid_expand_url(openid_url)) do |result, identity_url| + if result.successful? + # We need to use the openid url passed back from the OpenID provider + # rather than the one supplied by the user, as these can be different. + # + # For example, you can simply enter yahoo.com in the login box rather + # than a user specific url. Only once it comes back from the provider + # provider do we know the unique address for the user. + user.openid_url = identity_url + yield user + elsif result.missing? + flash.now[:error] = t 'user.login.openid missing provider' + elsif result.invalid? + flash.now[:error] = t 'user.login.openid invalid' + else + flash.now[:error] = t 'user.login.auth failure' + end + end + end + + ## + # special case some common OpenID providers by applying heuristics to + # try and come up with the correct URL based on what the user entered + def openid_expand_url(openid_url) + if openid_url.nil? + return nil + elsif openid_url.match(/(.*)gmail.com(\/?)$/) or openid_url.match(/(.*)googlemail.com(\/?)$/) + # Special case gmail.com as it is potentially a popular OpenID + # provider and, unlike yahoo.com, where it works automatically, Google + # have hidden their OpenID endpoint somewhere obscure this making it + # somewhat less user friendly. + return 'https://www.google.com/accounts/o8/id' + else + return openid_url + end + end + + ## + # process a successful login + def successful_login(user) + session[:user] = user.id + + session_expires_after 1.month if session[:remember_me] + + if user.blocked_on_view + redirect_to user.blocked_on_view, :referer => params[:referer] + elsif session[:referer] + redirect_to session[:referer] + else + redirect_to :controller => 'site', :action => 'index' + end + + session.delete(:remember_me) + session.delete(:referer) + end + + ## + # process a failed login + def failed_login(message) + flash[:error] = message + + redirect_to :action => 'login', :referer => session[:referer] + + session.delete(:remember_me) + session.delete(:referer) + end + + ## + # update a user's details + def update_user(user) + if user.save + set_locale + + if user.new_email.nil? or user.new_email.empty? + flash.now[:notice] = t 'user.account.flash update success' + else + flash.now[:notice] = t 'user.account.flash update success confirm needed' + + begin + Notifier.deliver_email_confirm(user, user.tokens.create) + rescue + # Ignore errors sending email + end + end + end + end + ## # require that the user is a administrator, or fill out a helpful error message # and return them to the user page. diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb index 0147c3fe6..8686d5a03 100644 --- a/app/helpers/user_helper.rb +++ b/app/helpers/user_helper.rb @@ -1,2 +1,16 @@ module UserHelper + def openid_logo + image_tag "openid_small.png", :alt => t('user.login.openid_logo_alt'), :class => "openid_logo" + end + + def openid_button(name, url) + link_to_function( + image_tag("#{name}.png", :alt => t("user.login.openid_providers.#{name}.alt")), + nil, + :title => t("user.login.openid_providers.#{name}.title") + ) do |page| + page[:login_form][:openid_url][:value] = url + page[:login_form].submit() + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index 31b0f27a0..f99ad3b40 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,6 +23,7 @@ class User < ActiveRecord::Base validates_confirmation_of :pass_crypt#, :message => ' must match the confirmation password' validates_uniqueness_of :display_name, :allow_nil => true validates_uniqueness_of :email + validates_uniqueness_of :openid_url, :allow_nil => true validates_length_of :pass_crypt, :within => 8..255 validates_length_of :display_name, :within => 3..255, :allow_nil => true validates_email_format_of :email diff --git a/app/views/user/account.html.erb b/app/views/user/account.html.erb index 85e9aebef..ba5b652ac 100644 --- a/app/views/user/account.html.erb +++ b/app/views/user/account.html.erb @@ -27,6 +27,11 @@ <%= f.password_field :pass_crypt_confirmation, {:value => '', :size => 30, :maxlength => 255, :autocomplete => :off} %> + + <%= t 'user.account.openid.openid' %> + <%= f.text_field :openid_url, {:class => "openid_url"} %> (<%= t 'user.account.openid.link text' %>) + + <%= t 'user.account.public editing.heading' %> diff --git a/app/views/user/login.html.erb b/app/views/user/login.html.erb index cf7f4a819..1cdb8511f 100644 --- a/app/views/user/login.html.erb +++ b/app/views/user/login.html.erb @@ -2,13 +2,85 @@

<%= t 'user.login.please login', :create_user_link => link_to(t('user.login.create_account'), :controller => 'user', :action => 'new', :referer => params[:referer]) %>

-<% form_tag :action => 'login' do %> -<%= hidden_field_tag('referer', h(params[:referer])) %> - - - - - - -
<%= t 'user.login.email or username' %><%= text_field('user', 'email',{:value => "", :size => 28, :maxlength => 255, :tabindex => 1}) %>
<%= t 'user.login.password' %><%= password_field('user', 'password',{:value => "", :size => 28, :maxlength => 255, :tabindex => 2}) %> (<%= link_to t('user.login.lost password link'), :controller => 'user', :action => 'lost_password' %>)
<%= check_box_tag "remember_me", "yes", false, :tabindex => 3 %>
 
<%= submit_tag t('user.login.login_button'), :tabindex => 3 %>
+<% form_tag({ :action => "login" }, { :id => "login_form" }) do %> + <%= hidden_field_tag('referer', h(params[:referer])) %> + +
+
+

<%= t 'user.login.username_heading' %>

+ + + + + + + + + + + + + + + + + +
<%= text_field_tag("username", "", { :size => 28, :maxlength => 255, :tabindex => 1 }) %>
<%= password_field_tag("password", "", { :size => 28, :maxlength => 255, :tabindex => 2 }) %>
(<%= link_to t('user.login.lost password link'), :controller => 'user', :action => 'lost_password' %>)
<%= check_box_tag "remember_me", "yes", false, :tabindex => 3 %>
+ + <%= submit_tag t('user.login.login_button'), :tabindex => 4 %> +
+ +
+
+ +
+

<%= t 'user.login.openid_heading' %>

+
+ <%= + link_to_function(image_tag("openid_large.png", :alt => t("user.login.openid_providers.openid.title")), nil, :title => t("user.login.openid_providers.openid.title")) do |page| + page[:login_form][:openid_url].value = "http://" + page[:openid_buttons].hide + page[:openid_url].show + page[:openid_url_hint].show + page[:openid_submit].show + page[:remember_me_label].removeClassName("nowrap") + end + %> + <%= openid_button "yahoo", "yahoo.com" %> + <%= openid_button "google", "gmail.com" %> + <%= openid_button "myopenid", "myopenid.com" %> + <%= openid_button "wordpress", "wordpress.com" %> + <%= openid_button "myspace", "myspace.com" %> +
+ + + + + + + + + + + + + + +
+ <%= t 'user.login.openid', :logo => openid_logo %> + <%= text_field_tag("openid_url", "", { :size => 28, :maxlength => 255, :tabindex => 3, :class => "openid_url" }) %>
+ (<%= t 'user.account.openid.link text' %>) +
<%= check_box_tag "remember_me", "yes", false, :tabindex => 5 %>
+ + <%= submit_tag t('user.login.login_button'), :tabindex => 6, :id => "openid_submit" %> +
+
<% end %> + +<%= + update_page_tag do |page| + page[:openid_url].hide + page[:openid_url_hint].hide + page[:openid_submit].hide + end +%> diff --git a/app/views/user/new.html.erb b/app/views/user/new.html.erb index 66d8826c2..4e3f4428d 100644 --- a/app/views/user/new.html.erb +++ b/app/views/user/new.html.erb @@ -2,37 +2,89 @@ <% if Acl.find_by_address(request.remote_ip, :conditions => {:k => "no_account_creation"}) %> -

<%= t 'user.new.no_auto_account_create' %> -

+

<%= t 'user.new.no_auto_account_create' %>

-

<%= t 'user.new.contact_webmaster' %> -

+

<%= t 'user.new.contact_webmaster' %>

<% else %> -

<%= t 'user.new.fill_form' %> -

+

<%= t 'user.new.fill_form' %>

<%= error_messages_for 'user' %> <% form_tag :action => 'terms' do %> -<%= hidden_field_tag('referer', h(params[:referer])) unless params[:referer].nil? %> - - - - - - - - - - - - - -
<%= t 'user.new.email address' %><%= text_field('user', 'email',{:size => 50, :maxlength => 255, :tabindex => 1}) %>
<%= t 'user.new.confirm email address' %><%= text_field('user', 'email_confirmation',{:size => 50, :maxlength => 255, :tabindex => 2}) %>
<%= t 'user.new.not displayed publicly' %>
 
<%= t 'user.new.display name' %><%= text_field('user', 'display_name',{:size => 30, :maxlength => 255, :tabindex => 3}) %>
<%= t 'user.new.display name description' %>
 
<%= t 'user.new.password' %><%= password_field('user', 'pass_crypt',{:size => 30, :maxlength => 255, :tabindex => 4}) %>
<%= t 'user.new.confirm password' %><%= password_field('user', 'pass_crypt_confirmation',{:size => 30, :maxlength => 255, :tabindex => 5}) %>
 
+ <%= hidden_field_tag('referer', h(@referer)) unless @referer.nil? %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
<%= t 'user.new.email address' %><%= text_field(:user, :email, { :size => 50, :maxlength => 255, :tabindex => 1, :value => params[:email] }) %>
<%= t 'user.new.confirm email address' %><%= text_field(:user, :email_confirmation, { :size => 50, :maxlength => 255, :tabindex => 2, :value => params[:email] }) %>
<%= t 'user.new.not displayed publicly' %>
 
<%= t 'user.new.display name' %><%= text_field(:user, :display_name, { :size => 30, :maxlength => 255, :tabindex => 3, :value => params[:nickname] }) %>
<%= t 'user.new.display name description' %>
 
<%= t 'user.new.openid', :logo => openid_logo %><%= text_field(:user, :openid_url, { :size => 50, :maxlength => 255, :tabindex => 4, :value => params[:openid], :class => "openid_url" }) %>
 
<%= t 'user.new.password' %><%= password_field(:user, :pass_crypt, { :size => 30, :maxlength => 255, :tabindex => 5 }) %>
<%= t 'user.new.confirm password' %><%= password_field(:user, :pass_crypt_confirmation, { :size => 30, :maxlength => 255, :tabindex => 6 }) %>
+ <%= link_to_function(t('user.new.use openid', :logo => openid_logo)) { |page| page.hide 'openid_prompt'; page.show 'openid_spacer', 'openid_url', 'openid_note' } %> + <%= t 'user.new.openid no password' %> +
 
<%= submit_tag t('user.new.continue'), :tabindex => 6 %>
<% end %> +<%= + update_page_tag do |page| + if params[:openid] or (@user and @user.openid_url) + page[:openid_prompt].hide + else + page[:openid_spacer].hide + page[:openid_url].hide + page[:openid_note].hide + end + end +%> + <%= javascript_include_tag 'https://ethnio.com/remotes/62786' %> <% end %> diff --git a/app/views/user/terms.html.erb b/app/views/user/terms.html.erb index 049e07ca7..ae801bc11 100644 --- a/app/views/user/terms.html.erb +++ b/app/views/user/terms.html.erb @@ -38,6 +38,7 @@ <%= hidden_field('user', 'display_name') %> <%= hidden_field('user', 'pass_crypt') %> <%= hidden_field('user', 'pass_crypt_confirmation') %> + <%= hidden_field('user', 'openid_url') %>
<%= submit_tag(t('user.terms.decline'), :name => "decline", :id => "decline") %> <%= submit_tag(t('user.terms.agree'), :name => "agree", :id => "agree") %> diff --git a/config/initializers/openid.rb b/config/initializers/openid.rb new file mode 100644 index 000000000..966164b3e --- /dev/null +++ b/config/initializers/openid.rb @@ -0,0 +1 @@ +OpenIdAuthentication.store = :file diff --git a/config/locales/en.yml b/config/locales/en.yml index 5d42f3c5d..832610d60 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1491,6 +1491,9 @@ en: create_account: "create an account" email or username: "Email Address or Username:" password: "Password:" + openid: "{{logo}} OpenID:" + username_heading: "Login with username and password:" + openid_heading: "Login with OpenID:" remember: "Remember me:" lost password link: "Lost your password?" login_button: "Login" @@ -1498,6 +1501,28 @@ en: account suspended: Sorry, your account has been suspended due to suspicious activity.
Please contact the {{webmaster}} if you wish to discuss this. webmaster: webmaster auth failure: "Sorry, could not log in with those details." + openid missing provider: "Sorry, could not contact your OpenID provider" + openid invalid: "Sorry, your OpenID seems to be malformed" + openid_logo_alt: "Log in with an OpenID" + openid_providers: + openid: + title: Login with an OpenID URL + alt: Login with an OpenID URL + yahoo: + title: Login with a Yahoo! OpenID + alt: Login with a Yahoo! OpenID + google: + title: Login with a Google OpenID + alt: Login with a Google OpenID + myopenid: + title: Login with a myOpenID OpenID + alt: Login with a myOpenID OpenID + wordpress: + title: Login with a Wordpress.com OpenID + alt: Login with a Wordpress.com OpenID + myspace: + title: Login with a MySpace OpenID + alt: Login with a MySpace OpenID logout: title: "Logout" heading: "Logout from OpenStreetMap" @@ -1530,8 +1555,21 @@ en: not displayed publicly: 'Not displayed publicly (see privacy policy)' display name: "Display Name:" display name description: "Your publicly displayed username. You can change this later in the preferences." + openid: "{{logo}} OpenID:" password: "Password:" confirm password: "Confirm Password:" + use openid: "Alternatively, use {{logo}} OpenID to login" + openid no password: "With OpenID a password is not required, but some extra tools or server may still need one." + openid association: | +

Your OpenID is not associated with a OpenStreetMap account yet.

+ continue: Continue flash create success message: "User was successfully created. Check your email for a confirmation note, and you will be mapping in no time :-)

Please note that you will not be able to login until you've received and confirmed your email address.

If you use an antispam system which sends confirmation requests then please make sure you whitelist webmaster@openstreetmap.org as we are unable to reply to any confirmation requests." terms: @@ -1612,6 +1650,10 @@ en: current email address: "Current Email Address:" new email address: "New Email Address:" email never displayed publicly: "(never displayed publicly)" + openid: + openid: "OpenID:" + link: "http://wiki.openstreetmap.org/wiki/OpenID" + link text: "what is this?" public editing: heading: "Public editing:" enabled: "Enabled. Not anonymous and can edit data." diff --git a/config/locales/is.yml b/config/locales/is.yml index f16d87a9a..93e00ed50 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -1021,6 +1021,8 @@ is: current email address: "Núverandi netfang:" delete image: Eyða þessari mynd email never displayed publicly: (aldrei sýnt opinberlega) + openid: + link text: "hvað er openID?" flash update success: Stillingarnar þínar voru uppfærðar. flash update success confirm needed: Stillingarnar þínar voru uppfærðar. Póstur var sendur á netfangið þitt sem þú þarft að bregðast við til að netfangið þitt verði staðfest. home location: "Staðsetning:" @@ -1081,6 +1083,28 @@ is: remember: "Muna innskráninguna:" title: Innskrá webmaster: vefstjóra + openid_heading: "Innskráning með OpenID:" + username_heading: "Innskráning með OpenStreetMap aðgang:" + openid_logo_alt: "Innskrá með OpenID" + openid_providers: + openid: + title: Innskrá með OpenID slóð + alt: Innskrá með OpenID slóð + yahoo: + title: Innsrká með Yahoo! OpenID + alt: Innsrká með Yahoo! OpenID + google: + title: Innsrká með Google OpenID + alt: Innsrká með Google OpenID + myopenid: + title: Innsrká með myOpenID OpenID + alt: Innsrká með myOpenID OpenID + wordpress: + title: Innsrká með Wordpress.com OpenID + alt: Innsrká með Wordpress.com OpenID + myspace: + title: Innsrká með MySpace OpenID + alt: Innsrká með MySpace OpenID logout: heading: Útskrá logout_button: Útskrá @@ -1112,6 +1136,21 @@ is: no_auto_account_create: Því miður getum við eki búið til reikning fyrir þig sjálfkrafa. not displayed publicly: Ekki sýnt opinberlega (sjá meðferð persónuupplýsinga) password: "Lykilorð:" + openID associate: "Tengja OpenID við þennan aðgang" + openID: "OpenID:" + openID description: '(Valfrjálst) Ef þú ert með OpenID getur þú tengt það við nýja aðganginn þinn.' + openID nopassword: "Með OpenID þarft þú ekki að gefa upp lykilorð við innskráningu. Í stað þess notar þú OpenID." + openID association: | + Þetta OpenID er ekki tengt við neinn OpenStreetMap aðgang. + + signup: Nýskrá title: Nýskrá no_such_user: body: Það er ekki til notandi með nafninu {{user}}. Kannski slóstu nafnið rangt inn eða fylgdir ógildum tengli. diff --git a/db/migrate/20100516124737_add_open_id.rb b/db/migrate/20100516124737_add_open_id.rb new file mode 100644 index 000000000..2fb9ee5b4 --- /dev/null +++ b/db/migrate/20100516124737_add_open_id.rb @@ -0,0 +1,11 @@ +class AddOpenId < ActiveRecord::Migration + def self.up + add_column :users, :openid_url, :string + add_index :users, [:openid_url], :name => "user_openid_url_idx", :unique => true + end + + def self.down + remove_index :users, :name => "user_openid_url_idx" + remove_column :users, :openid_url + end +end diff --git a/public/images/google.png b/public/images/google.png new file mode 100644 index 000000000..9ae3bd863 Binary files /dev/null and b/public/images/google.png differ diff --git a/public/images/myopenid.png b/public/images/myopenid.png new file mode 100644 index 000000000..78e4562e4 Binary files /dev/null and b/public/images/myopenid.png differ diff --git a/public/images/myspace.png b/public/images/myspace.png new file mode 100644 index 000000000..2fefe48af Binary files /dev/null and b/public/images/myspace.png differ diff --git a/public/images/openid_input.png b/public/images/openid_input.png new file mode 100644 index 000000000..b5aa49dfa Binary files /dev/null and b/public/images/openid_input.png differ diff --git a/public/images/openid_large.png b/public/images/openid_large.png new file mode 100644 index 000000000..6f8bf63dd Binary files /dev/null and b/public/images/openid_large.png differ diff --git a/public/images/openid_small.png b/public/images/openid_small.png new file mode 100644 index 000000000..83bb30282 Binary files /dev/null and b/public/images/openid_small.png differ diff --git a/public/images/wordpress.png b/public/images/wordpress.png new file mode 100644 index 000000000..901549859 Binary files /dev/null and b/public/images/wordpress.png differ diff --git a/public/images/yahoo.png b/public/images/yahoo.png new file mode 100644 index 000000000..0a54c8ae6 Binary files /dev/null and b/public/images/yahoo.png differ diff --git a/public/stylesheets/common.css b/public/stylesheets/common.css index 2590c2d5f..9d4ecb445 100644 --- a/public/stylesheets/common.css +++ b/public/stylesheets/common.css @@ -583,6 +583,37 @@ hr { margin-top: 10px; } +/* Rules for the login form */ + +.loginBox { + float: left; + border-style: solid; + border-width: 1px; + padding-left: 10px; + padding-right: 10px; + padding-bottom: 10px; +} + +.loginBox table { + width: 100%; +} + +.loginBox img { + border: 0; +} + +.loginBox #openid_buttons img { + vertical-align: middle; +} + +.loginBox input[type="submit"] { + float: right; +} + +#openid_buttons { + margin-bottom: 20px; +} + /* Rules for the account confirmation page */ div#contributorTerms { @@ -759,6 +790,11 @@ input[type="submit"] { border: 1px solid black; } +input.openid_url { + background: url('../images/openid_input.png') repeat-y left; + padding-left: 16px; +} + /* Rules for user images */ img.user_image { @@ -801,3 +837,10 @@ abbr.geo { .table1 { background: #fff; } + +/* Rules for OpenID logo */ + +.openid_logo { + vertical-align: text-bottom; + border: 0; +} diff --git a/public/stylesheets/large.css b/public/stylesheets/large.css index a1efa8583..458099add 100644 --- a/public/stylesheets/large.css +++ b/public/stylesheets/large.css @@ -18,3 +18,11 @@ .olControlPanZoom { display: none; } + +/* Rules for the login form */ + +.loginBox { + width: 400px; + height: 200px; + margin-bottom: 40px; +} diff --git a/public/stylesheets/small.css b/public/stylesheets/small.css index b11aebf69..c709bcba9 100644 --- a/public/stylesheets/small.css +++ b/public/stylesheets/small.css @@ -100,12 +100,21 @@ h1 { /* Rules for the login form */ -#loginForm input#user_email { +.loginBox { + width: 90%; +} + +.loginBox input#user_email { + width: 100%; + max-width: 18em; +} + +.loginBox input#user_password { width: 100%; max-width: 18em; } -#loginForm input#user_password { +.loginBox input#user_openid_url { width: 100%; max-width: 18em; } diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index eb3c6ef6d..9802783fd 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -68,3 +68,13 @@ administrator_user: creation_time: "2008-05-01 01:23:45" display_name: administrator data_public: true + +openid_user: + id: 7 + email: openid-user@example.com + status: active + pass_crypt: <%= Digest::MD5.hexdigest('test') %> + creation_time: "2008-05-01 01:23:45" + display_name: openIDuser + data_public: true + openid_url: http://localhost:1123/john.doe?openid.success=true diff --git a/test/integration/client_application_test.rb b/test/integration/client_application_test.rb index 8e08cbda0..80da36deb 100644 --- a/test/integration/client_application_test.rb +++ b/test/integration/client_application_test.rb @@ -12,7 +12,7 @@ class ClientApplicationTest < ActionController::IntegrationTest assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" follow_redirect! assert_response :success - post '/login', {'user[email]' => "test@example.com", 'user[password]' => "test", :referer => '/user/test2'} + post '/login', {'username' => "test@example.com", 'password' => "test", :referer => '/user/test2'} assert_response :redirect follow_redirect! assert_response :success diff --git a/test/integration/user_blocks_test.rb b/test/integration/user_blocks_test.rb index ecd1d37c5..942a94302 100644 --- a/test/integration/user_blocks_test.rb +++ b/test/integration/user_blocks_test.rb @@ -42,7 +42,7 @@ class UserBlocksTest < ActionController::IntegrationTest assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" follow_redirect! assert_response :success - post '/login', {'user[email]' => moderator.email, 'user[password]' => "test", :referer => "/blocks/#{block.id}/revoke"} + post '/login', {'username' => moderator.email, 'password' => "test", :referer => "/blocks/#{block.id}/revoke"} assert_response :redirect follow_redirect! assert_response :success diff --git a/test/integration/user_creation_test.rb b/test/integration/user_creation_test.rb index 77d73552a..3a9795bc7 100644 --- a/test/integration/user_creation_test.rb +++ b/test/integration/user_creation_test.rb @@ -94,8 +94,12 @@ class UserCreationTest < ActionController::IntegrationTest referer = "/traces/mine" assert_difference('User.count') do assert_difference('ActionMailer::Base.deliveries.size', 1) do - post_via_redirect "/user/save", + post "/user/terms", {:user => { :email => new_email, :email_confirmation => new_email, :display_name => display_name, :pass_crypt => password, :pass_crypt_confirmation => password}, :referer => referer } + assert_response :success + assert_template 'terms' + post_via_redirect "/user/save", + {:user => { :email => new_email, :email_confirmation => new_email, :display_name => display_name, :pass_crypt => password, :pass_crypt_confirmation => password} } end end @@ -127,4 +131,33 @@ class UserCreationTest < ActionController::IntegrationTest assert_response :success assert_template "trace/list.html.erb" end + + def test_user_create_openid_success + new_email = "newtester-openid@osm.org" + display_name = "new_tester-openid" + password = "testtest" + assert_difference('User.count') do + assert_difference('ActionMailer::Base.deliveries.size', 1) do + post "/user/terms", + {:user => { :email => new_email, :email_confirmation => new_email, :display_name => display_name, :openid_url => "http://localhost:1123/john.doe?openid.success=newuser", :pass_crypt => "", :pass_crypt_confirmation => ""}} + assert_response :redirect + res = openid_request(@response.redirected_to) + post '/user/terms', res + assert_response :success + assert_template 'terms' + post '/user/save', + {:user => { :email => new_email, :email_confirmation => new_email, :display_name => display_name, :openid_url => "http://localhost:1123/john.doe?openid.success=newuser", :pass_crypt => password, :pass_crypt_confirmation => password}} + assert_response :redirect + follow_redirect! + end + end + + # Check the page + assert_response :success + assert_template 'login' + + ActionMailer::Base.deliveries.clear + end + + end diff --git a/test/integration/user_diaries_test.rb b/test/integration/user_diaries_test.rb index b686fbac9..fab05894f 100644 --- a/test/integration/user_diaries_test.rb +++ b/test/integration/user_diaries_test.rb @@ -11,7 +11,7 @@ class UserDiariesTest < ActionController::IntegrationTest assert_response :success assert_template 'user/login' # We can now login - post '/login', {'user[email]' => "test@openstreetmap.org", 'user[password]' => "test", :referer => '/diary/new'} + post '/login', {'username' => "test@openstreetmap.org", 'password' => "test", :referer => '/diary/new'} assert_response :redirect #print @response.body # Check that there is some payload alerting the user to the redirect diff --git a/test/integration/user_login_test.rb b/test/integration/user_login_test.rb new file mode 100644 index 000000000..a272f7bad --- /dev/null +++ b/test/integration/user_login_test.rb @@ -0,0 +1,87 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class UserLoginTest < ActionController::IntegrationTest + fixtures :users + + def test_login_openid_success + get '/login' + assert_response :redirect + assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" + follow_redirect! + assert_response :success + post '/login', {'openid_url' => "http://localhost:1123/john.doe?openid.success=true", :referer => "/browse"} + assert_response :redirect + + res = openid_request(@response.redirected_to) + res2 = post '/login', res + + assert_response :redirect + follow_redirect! + assert_response :success + assert_template 'changeset/list' + end + + def test_login_openid_cancel + get '/login' + assert_response :redirect + assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" + follow_redirect! + assert_response :success + post '/login', {'openid_url' => "http://localhost:1123/john.doe", :referer => "/diary"} + assert_response :redirect + + res = openid_request(@response.redirected_to) + post '/login', res + + assert_response :redirect + follow_redirect! + assert_response :success + assert_template 'login' + end + + def test_login_openid_invalid_provider + get '/login' + assert_response :redirect + assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" + follow_redirect! + assert_response :success + #Use a different port that doesn't have the OpenID provider running on to test an invalid openID + post '/login', {'openid_url' => "http://localhost:1124/john.doe", :referer => "/diary"} + assert_response :redirect + follow_redirect! + assert_response :success + assert_template 'login' + end + + def test_login_openid_invalid_url + get '/login' + assert_response :redirect + assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" + follow_redirect! + assert_response :success + #Use a url with an invalid protocol to make sure it handles that correctly too + post '/login', {'openid_url' => "htt://localhost:1123/john.doe", :referer => "/diary"} + assert_response :redirect + follow_redirect! + assert_response :success + assert_template 'login' + end + + def test_login_openid_unknown + get '/login' + assert_response :redirect + assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" + follow_redirect! + assert_response :success + post '/login', {'openid_url' => "http://localhost:1123/john.doe?openid.success=true_somethingelse", :referer => "/diary"} + assert_response :redirect + + res = openid_request(@response.redirected_to) + res2 = post '/login', res + + assert_response :redirect + follow_redirect! + assert_response :success + assert_template 'user/new' + end +end diff --git a/test/integration/user_roles_test.rb b/test/integration/user_roles_test.rb index 0691edc8e..8bf06374f 100644 --- a/test/integration/user_roles_test.rb +++ b/test/integration/user_roles_test.rb @@ -22,7 +22,7 @@ class UserRolesControllerTest < ActionController::IntegrationTest assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" follow_redirect! assert_response :success - post '/login', {'user[email]' => users(user).email, 'user[password]' => "test", :referer => "/"} + post '/login', {'username' => users(user).email, 'password' => "test", :referer => "/"} assert_response :redirect follow_redirect! assert_response :success @@ -40,7 +40,7 @@ class UserRolesControllerTest < ActionController::IntegrationTest assert_redirected_to "controller" => "user", "action" => "login", "cookie_test" => "true" follow_redirect! assert_response :success - post '/login', {'user[email]' => users(user).email, 'user[password]' => "test", :referer => "/"} + post '/login', {'username' => users(user).email, 'password' => "test", :referer => "/"} assert_response :redirect follow_redirect! assert_response :success diff --git a/test/test_helper.rb b/test/test_helper.rb index 4972ee6d6..a83a89f2a 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -141,6 +141,15 @@ class ActiveSupport::TestCase def assert_no_missing_translations(msg="") assert_select "span[class=translation_missing]", false, "Missing translation #{msg}" end + + def openid_request(openid_request_uri) + openid_response = Net::HTTP.get_response(URI.parse(openid_request_uri)) + openid_response_uri = URI(openid_response['Location']) + openid_response_qs = Rack::Utils.parse_query(openid_response_uri.query) + + return openid_response_qs + end + # Add more helper methods to be used by all tests here... end diff --git a/vendor/gems/rots-0.2.1/.specification b/vendor/gems/rots-0.2.1/.specification new file mode 100644 index 000000000..68c7f206d --- /dev/null +++ b/vendor/gems/rots-0.2.1/.specification @@ -0,0 +1,112 @@ +--- !ruby/object:Gem::Specification +name: rots +version: !ruby/object:Gem::Version + prerelease: false + segments: + - 0 + - 2 + - 1 + version: 0.2.1 +platform: ruby +authors: +- Roman Gonzalez +autorequire: +bindir: bin +cert_chain: [] + +date: 2010-05-11 00:00:00 +01:00 +default_executable: rots +dependencies: +- !ruby/object:Gem::Dependency + name: rspec + prerelease: false + requirement: &id001 !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + segments: + - 0 + version: "0" + type: :development + version_requirements: *id001 +- !ruby/object:Gem::Dependency + name: rack + prerelease: false + requirement: &id002 !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + segments: + - 0 + version: "0" + type: :development + version_requirements: *id002 +- !ruby/object:Gem::Dependency + name: ruby-openid + prerelease: false + requirement: &id003 !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + segments: + - 0 + version: "0" + type: :development + version_requirements: *id003 +description: | + Ruby OpenID Test Server (ROST) provides a basic OpenID server made in top of the Rack gem. + With this small server, you can make dummy OpenID request for testing purposes, + the success of the response will depend on a parameter given on the url of the authentication request. + +email: romanandreg@gmail.com +executables: +- rots +extensions: [] + +extra_rdoc_files: +- README +files: +- AUTHORS +- README +- Rakefile +- bin/rots +- lib/rots.rb +- lib/rots/identity_page_app.rb +- lib/rots/server_app.rb +- lib/rots/test_helper.rb +- rots.gemspec +- spec/server_app_spec.rb +- spec/spec_helper.rb +has_rdoc: true +homepage: http://github.com/roman +licenses: [] + +post_install_message: +rdoc_options: [] + +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + segments: + - 0 + version: "0" +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + segments: + - 0 + version: "0" +requirements: [] + +rubyforge_project: rots +rubygems_version: 1.3.6 +signing_key: +specification_version: 3 +summary: an OpenID server for making tests of OpenID clients implementations +test_files: +- spec/server_app_spec.rb +- spec/spec_helper.rb diff --git a/vendor/gems/rots-0.2.1/AUTHORS b/vendor/gems/rots-0.2.1/AUTHORS new file mode 100644 index 000000000..ad2dd8944 --- /dev/null +++ b/vendor/gems/rots-0.2.1/AUTHORS @@ -0,0 +1,2 @@ +* Roman Gonzalez +* Anibal Rojas \ No newline at end of file diff --git a/vendor/gems/rots-0.2.1/README b/vendor/gems/rots-0.2.1/README new file mode 100644 index 000000000..5369cc67b --- /dev/null +++ b/vendor/gems/rots-0.2.1/README @@ -0,0 +1,64 @@ += Ruby OpenID Test Server (ROTS), a dummy OpenID server that makes consumer tests dead easy. + +ROTS is a minimal implementation of an OpenID server, developed on top of the Rack middleware, this +server provides an easy to use interface to make testing OpenID consumers really easy. + +== No more mocks + +Have you always wanted to test the authentication of an OpenID consumer implementation, but find your self +in a point where is to hard to mock? A lot of people have been there. + +With ROTS, you only need to specify an identity url provided by the dummy server, passing with it a flag +saying that you want the authentication to be successful. It handles SREG extensions as well. + +== How does it works + +When you install the ROTS gem, a binary called rots is provided for starting the server (for more +info about what options you have when executing this file, check the -h option). + +By default, rots will have a test user called "John Doe", with an OpenID identity "john.doe". +If you want to use your own test user name, you can specify a config file to rots. The +default configuration file looks like this: + +# Default configuration file +identity: john.doe +sreg: + nickname: jdoe + fullname: John Doe + email: jhon@doe.com + dob: 1985-09-21 + gender: M + +You can specify a new config file using the option --config. + +== Getting Started + +The best way to get started, is running the rots server, and then starting to execute your OpenID consumer tests/specs. You just have to specify the identity url of your test user, if you want the OpenID response be successful just add the openid.success=true flag to the user identity url. If you don't specify the flag it +will return a cancel response instead. + +Example: + +it "should authenticate with OpenID" do + post("/consumer_openid_login", 'identity_url' => 'http://localhost:1132/john.doe?openid.success=true') +end + +== Copyright + +Copyright (C) 2009 Roman Gonzalez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/gems/rots-0.2.1/Rakefile b/vendor/gems/rots-0.2.1/Rakefile new file mode 100644 index 000000000..7dd59befb --- /dev/null +++ b/vendor/gems/rots-0.2.1/Rakefile @@ -0,0 +1,125 @@ +# Rakefile for Rack. -*-ruby-*- +require 'rake/rdoctask' +require 'rake/testtask' +require 'spec/rake/spectask' + + +desc "Run all the tests" +task :default => [:spec] + +desc "Do predistribution stuff" +task :predist => [:changelog, :rdoc] + + +desc "Make an archive as .tar.gz" +task :dist => [:fulltest, :predist] do + sh "git archive --format=tar --prefix=#{release}/ HEAD^{tree} >#{release}.tar" + sh "pax -waf #{release}.tar -s ':^:#{release}/:' RDOX SPEC ChangeLog doc" + sh "gzip -f -9 #{release}.tar" +end + +# Helper to retrieve the "revision number" of the git tree. +def git_tree_version + #if File.directory?(".git") + # @tree_version ||= `git describe`.strip.sub('-', '.') + # @tree_version << ".0" unless @tree_version.count('.') == 2 + #else + $: << "lib" + require 'rots' + @tree_version = Rots.release + #end + @tree_version +end + +def gem_version + git_tree_version.gsub(/-.*/, '') +end + +def release + "ruby-openid-tester-#{git_tree_version}" +end + +def manifest + `git ls-files`.split("\n") +end + +desc "Generate a ChangeLog" +task :changelog do + File.open("ChangeLog", "w") do |out| + `git log -z`.split("\0").map do |chunk| + author = chunk[/Author: (.*)/, 1].strip + date = chunk[/Date: (.*)/, 1].strip + desc, detail = $'.strip.split("\n", 2) + detail ||= "" + detail.rstrip! + out.puts "#{date} #{author}" + out.puts " * #{desc.strip}" + out.puts detail unless detail.empty? + out.puts + end + end +end + + +begin + require 'rubygems' + + require 'rake' + require 'rake/clean' + require 'rake/packagetask' + require 'rake/gempackagetask' + require 'fileutils' +rescue LoadError + # Too bad. +else + spec = Gem::Specification.new do |s| + s.name = "rots" + s.version = gem_version + s.platform = Gem::Platform::RUBY + s.summary = "an OpenID server for making tests of OpenID clients implementations" + + s.description = <<-EOF +Ruby OpenID Test Server (ROST) provides a basic OpenID server made in top of the Rack gem. +With this small server, you can make dummy OpenID request for testing purposes, +the success of the response will depend on a parameter given on the url of the authentication request. + EOF + + s.files = manifest + s.bindir = 'bin' + s.executables << 'rots' + s.require_path = 'lib' + s.has_rdoc = true + s.extra_rdoc_files = ['README'] + s.test_files = Dir['spec/*_spec.rb'] + + s.author = 'Roman Gonzalez' + s.email = 'romanandreg@gmail.com' + s.homepage = 'http://github.com/roman' + s.rubyforge_project = 'rots' + + s.add_development_dependency 'rspec' + s.add_development_dependency 'rack' + s.add_development_dependency 'ruby-openid', '~> 2.0.0' + end + + Rake::GemPackageTask.new(spec) do |p| + p.gem_spec = spec + p.need_tar = false + p.need_zip = false + end +end + +Spec::Rake::SpecTask.new do |t| +end + +desc "Generate RDoc documentation" +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.options << '--line-numbers' << '--inline-source' << + '--main' << 'README' << + '--title' << 'ROTS Documentation' << + '--charset' << 'utf-8' + rdoc.rdoc_dir = "doc" + rdoc.rdoc_files.include 'README' + rdoc.rdoc_files.include('lib/ruby_openid_test_server.rb') + rdoc.rdoc_files.include('lib/ruby_openid_test_server/*.rb') +end diff --git a/vendor/gems/rots-0.2.1/bin/rots b/vendor/gems/rots-0.2.1/bin/rots new file mode 100755 index 000000000..726c65581 --- /dev/null +++ b/vendor/gems/rots-0.2.1/bin/rots @@ -0,0 +1,95 @@ +#!/usr/bin/env ruby +# -*- ruby -*- + +$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) +require "rubygems" +require "optparse" +require "rack" +require "yaml" +require "rots" + +server_options = { + :debugger => false, + :port => 1123, + :verbose => true, + :storage => File.join('.', 'tmp', 'rots'), + :config => <<-DEFAULT_CONFIG +# Default configuration file +identity: john.doe +sreg: + nickname: jdoe + fullname: John Doe + email: jhon@doe.com + dob: 1985-09-21 + gender: M + + DEFAULT_CONFIG +} + +opts = OptionParser.new do |opts| + opts.banner = "Usage: rots [options]" + + opts.separator "" + opts.separator "Options:" + + opts.on("-p", "--port PORT", + "use PORT (default: 1123)") do |port| + server_options[:port] = port + end + + opts.on("-s", "--storage PATH", + "use PATH as the OpenID Server storage path (default: ./tmp/rots)") do |storage_path| + server_options[:storage] = storage_path + end + + opts.on("-c", "--config FILE.yaml", + "server configuration YAML file") do |config_path| + abort "\x1B[31mConfiguration file #{config_path} not found\x1B[0m" unless File.exists?(config_path) + server_options[:config] = File.new(config_path) + end + + opts.on("-s", "--silent", + "If specified, the server will be in silent mode") do + server_options[:verbose] = false + end + + opts.on("-d", "--debugger") do + server_options[:debugger] = true + end + + opts.separator "" + opts.separator "Common options:" + + opts.on_tail("-h", "--help", "Show this help message") do + puts opts + exit + end + +end + +opts.parse!(ARGV) + +config = YAML.load(server_options[:config]) + +require "ruby-debug" if server_options[:debugger] + +server = Rack::Builder.new do + use Rack::Lint + if server_options[:verbose] + use Rack::CommonLogger, STDOUT + use Rack::ShowExceptions + end + map ("/%s" % config['identity']) do + run Rots::IdentityPageApp.new(config, server_options) + end + map "/server" do + run Rots::ServerApp.new(config, server_options) + end +end + +puts "\x1B[32mRunning OpenID Test server on port 1123\x1B[0m" if server_options[:verbose] +begin + Rack::Handler::Mongrel.run server, :Port => server_options[:port] +rescue LoadError + Rack::Handler::WEBrick.run server, :Port => server_options[:port] +end diff --git a/vendor/gems/rots-0.2.1/lib/rots.rb b/vendor/gems/rots-0.2.1/lib/rots.rb new file mode 100644 index 000000000..644416ce6 --- /dev/null +++ b/vendor/gems/rots-0.2.1/lib/rots.rb @@ -0,0 +1,11 @@ +module Rots + + def self.release + "0.2.1" + end + +end + +require "rots/server_app" +require "rots/identity_page_app" +require "rots/test_helper" diff --git a/vendor/gems/rots-0.2.1/lib/rots/identity_page_app.rb b/vendor/gems/rots-0.2.1/lib/rots/identity_page_app.rb new file mode 100644 index 000000000..09d70dbb2 --- /dev/null +++ b/vendor/gems/rots-0.2.1/lib/rots/identity_page_app.rb @@ -0,0 +1,36 @@ +require 'rack/request' +require 'rack/response' +require 'rack/utils' +require 'openid' + +class Rots::IdentityPageApp + + def initialize(config, server_options) + @server_options = server_options + @config = config + end + + def call(env) + @request = Rack::Request.new(env) + Rack::Response.new do |response| + response.write <<-HERE + + + + + + +

This is #{@config['identity']} identity page

+ + + HERE + end.finish + end + + def op_endpoint + "http://%s:%d/server/%s" % [@request.host, + @request.port, + (@request.params['openid.success'] ? '?openid.success=true' : '')] + end + +end \ No newline at end of file diff --git a/vendor/gems/rots-0.2.1/lib/rots/server_app.rb b/vendor/gems/rots-0.2.1/lib/rots/server_app.rb new file mode 100644 index 000000000..e08595cc2 --- /dev/null +++ b/vendor/gems/rots-0.2.1/lib/rots/server_app.rb @@ -0,0 +1,147 @@ +require 'openid' +require 'openid/extension' +require 'openid/extensions/sreg' +require 'openid/store/filesystem' +require 'openid/util' +require 'rack/request' +require 'rack/utils' +require 'fileutils' + + +module Rots + + class ServerApp + + attr_accessor :request,:openid_request, + :response, :openid_response, + :server + + def initialize(config, server_options) + @server_options = server_options + @sreg_fields = config['sreg'] + end + + def call(env) + on_openid_request(env) do + if !is_checkid_request? + @openid_response = @server.handle_request(@openid_request) + reply_consumer + elsif is_checkid_immediate? + process_immediate_checkid_request + else + process_checkid_request + end + end + end + + protected + + def on_openid_request(env) + create_wrappers(env) + if @openid_request.nil? + [200, {'Content-Type' => 'text/html'}, + ["

ROTS => This is an OpenID endpoint

"] ] + else + yield + end + end + + def create_wrappers(env) + @request = Rack::Request.new(env) + @server = OpenID::Server::Server.new(storage, op_endpoint) + @openid_request = @server.decode_request(@request.params) + @openid_sreg_request = OpenID::SReg::Request.from_openid_request(@openid_request) unless @openid_request.nil? + end + + def is_checkid_request? + @openid_request.is_a?(OpenID::Server::CheckIDRequest) + end + + def is_checkid_immediate? + @openid_request && @openid_request.immediate + end + + def process_immediate_checkid_request + # TODO: We should enable the user to configure + # if she wants immediate request support or not + url = OpenID::Util.append_args(@openid_request.return_to, + @request.params.merge('openid.mode' => 'setup_needed')) + redirect(url) + end + + def process_checkid_request + if checkid_request_is_valid? + return_successful_openid_response + else + return_cancel_openid_response + end + end + + def checkid_request_is_valid? + @request.params['openid.success'] == 'true' + end + + def return_successful_openid_response + @openid_response = @openid_request.answer(true) + process_sreg_extension + # TODO: Add support for SREG extension + @server.signatory.sign(@openid_response) if @openid_response.needs_signing + reply_consumer + end + + def process_sreg_extension + return if @openid_sreg_request.nil? + response = OpenID::SReg::Response.extract_response(@openid_sreg_request, @sreg_fields) + @openid_response.add_extension(response) + end + + def return_cancel_openid_response + redirect(@openid_request.cancel_url) + end + + def reply_consumer + web_response = @server.encode_response(@openid_response) + case web_response.code + when OpenID::Server::HTTP_OK + success(web_response.body) + when OpenID::Server::HTTP_REDIRECT + redirect(web_response.headers['location']) + else + bad_request + end + end + + def redirect(uri) + [ 303, {'Content-Length'=>'0', 'Content-Type'=>'text/plain', + 'Location' => uri}, + [] ] + end + + def bad_request() + [ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'}, + [] ] + end + + def storage + # create the folder if it doesn't exist + FileUtils.mkdir_p(@server_options[:storage]) unless File.exist?(@server_options[:storage]) + OpenID::Store::Filesystem.new(@server_options[:storage]) + end + + def success(text="") + Rack::Response.new(text).finish + end + + def op_endpoint + if @request.url =~ /(.*\?openid.success=true)/ + $1 + elsif @request.url =~ /([^?]*)/ + $1 + else + nil + end + end + + end + +end \ No newline at end of file diff --git a/vendor/gems/rots-0.2.1/lib/rots/test_helper.rb b/vendor/gems/rots-0.2.1/lib/rots/test_helper.rb new file mode 100644 index 000000000..a7a91de89 --- /dev/null +++ b/vendor/gems/rots-0.2.1/lib/rots/test_helper.rb @@ -0,0 +1,16 @@ +require "openid/consumer" +require "openid/consumer/checkid_request.rb" +require "net/http" + +module Rots::TestHelper + + def openid_request(openid_request_uri) + openid_response = Net::HTTP.get_response(URI.parse(openid_request_uri)) + openid_response_uri = URI(openid_response['Location']) + openid_response_qs = Rack::Utils.parse_query(openid_response_uri.query) + + { :url => openid_response_uri.to_s, + :query_params => openid_response_qs } + end + +end \ No newline at end of file diff --git a/vendor/gems/rots-0.2.1/rots.gemspec b/vendor/gems/rots-0.2.1/rots.gemspec new file mode 100644 index 000000000..0de241038 --- /dev/null +++ b/vendor/gems/rots-0.2.1/rots.gemspec @@ -0,0 +1,31 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = "rots" + s.version = '0.2.1' + s.platform = Gem::Platform::RUBY + s.summary = "an OpenID server for making tests of OpenID clients implementations" + + s.description = <<-EOF +Ruby OpenID Test Server (ROST) provides a basic OpenID server made in top of the Rack gem. +With this small server, you can make dummy OpenID request for testing purposes, +the success of the response will depend on a parameter given on the url of the authentication request. + EOF + + s.files = ["AUTHORS", "README", "Rakefile", "bin/rots", "lib/rots.rb", "lib/rots/identity_page_app.rb", "lib/rots/server_app.rb", "lib/rots/test_helper.rb","rots.gemspec", "spec/server_app_spec.rb", "spec/spec_helper.rb"] + s.bindir = 'bin' + s.executables << 'rots' + s.require_path = 'lib' + s.has_rdoc = true + s.extra_rdoc_files = ['README'] + s.test_files = ['spec/server_app_spec.rb', 'spec/spec_helper.rb'] + + s.author = 'Roman Gonzalez' + s.email = 'romanandreg@gmail.com' + s.homepage = 'http://github.com/roman' + s.rubyforge_project = 'rots' + + s.add_development_dependency 'rspec' + s.add_development_dependency 'rack' + s.add_development_dependency 'ruby-openid' +end diff --git a/vendor/gems/rots-0.2.1/spec/server_app_spec.rb b/vendor/gems/rots-0.2.1/spec/server_app_spec.rb new file mode 100644 index 000000000..f76d689dc --- /dev/null +++ b/vendor/gems/rots-0.2.1/spec/server_app_spec.rb @@ -0,0 +1,99 @@ +require File.join(File.dirname(__FILE__), 'spec_helper') + +# This is just a comment test + +describe Rots::ServerApp do + + describe "when the request is not an OpenID request" do + + it "should return a helpful message saying that is an OpenID endpoint" do + request = Rack::MockRequest.new(Rots::ServerApp.new({'sreg' => {}}, + {:storage => File.join(*%w(. tmp rots)) })) + response = request.get("/") + response.should be_ok + response.body.should == "

ROTS => This is an OpenID endpoint

" + end + + end + + describe "when the request is an OpenID request" do + + before(:each) do + @request = Rack::MockRequest.new(Rots::ServerApp.new({ + 'identity' => 'john.doe', + 'sreg' => { + 'email' => "john@doe.com", + 'nickname' => 'johndoe', + 'fullname' => "John Doe", + 'dob' => "1985-09-21", + 'gender' => "M" + }}, + {:storage => File.join(*%w(. tmp rots))} + )) + end + + + describe "and it is a check_id request" do + + describe "and is immediate" do + + it "should return an openid.mode equal to setup_needed" do + response = checkid_immediate(@request) + params = openid_params(response) + params['openid.mode'].should == 'setup_needed' + end + + end + + describe "and is not immediate" do + + describe "with a success flag" do + + it "should return an openid.mode equal to id_res" do + response = checkid_setup(@request, 'openid.success' => 'true') + params = openid_params(response) + params['openid.mode'].should == 'id_res' + end + + end + + describe "without a success flag" do + + it "should return an openid.mode equal to cancel" do + response = checkid_setup(@request) + params = openid_params(response) + params['openid.mode'].should == 'cancel' + end + + end + + describe "using SREG extension with a success flag" do + + it "should return an openid.mode equal to id_res" do + response = checkid_setup(@request, 'openid.success' => 'true') + params = openid_params(response) + params['openid.mode'].should == 'id_res' + end + + it "should return all the sreg fields" do + response = checkid_setup(@request, { + 'openid.success' => true, + 'openid.ns.sreg' => OpenID::SReg::NS_URI, + 'openid.sreg.required' => 'email,nickname,fullname', + 'openid.sreg.optional' => 'dob,gender' + }) + params = openid_params(response) + params['openid.sreg.email'].should == "john@doe.com" + params['openid.sreg.nickname'].should == 'johndoe' + params['openid.sreg.fullname'].should == "John Doe" + params['openid.sreg.dob'].should == "1985-09-21" + params['openid.sreg.gender'].should == "M" + end + + end + + end + end + end + +end \ No newline at end of file diff --git a/vendor/gems/rots-0.2.1/spec/spec_helper.rb b/vendor/gems/rots-0.2.1/spec/spec_helper.rb new file mode 100644 index 000000000..3dae012f3 --- /dev/null +++ b/vendor/gems/rots-0.2.1/spec/spec_helper.rb @@ -0,0 +1,73 @@ +$:.unshift(File.dirname(__FILE__), '..', 'lib') +require "rubygems" +require "spec" +require "rack" +require "rots" + +module Rots::RequestHelper + + def checkid_setup(request, params={}, with_associate=true) + assoc_handle = make_association(request) if with_associate + send_checkid(request, :setup, params, assoc_handle) + end + + def checkid_immediate(request, params={}, with_associate=true) + assoc_handle = make_association(request) if with_associate + send_checkid(request, :immediate, params, assoc_handle) + end + + def openid_params(response) + uri = URI(response.headers['Location']) + Rack::Utils.parse_query(uri.query) + end + + protected + + def send_checkid(request, mode, params={}, assoc_handle = nil) + params = self.send(:"checkid_#{mode}_params", params) + params.merge('openid.assoc_handle' => assoc_handle) if assoc_handle + qs = "/?" + Rack::Utils.build_query(params) + request.get(qs) + end + + def make_association(request) + associate_qs = Rack::Utils.build_query(associate_params) + response = request.post('/', :input => associate_qs) + parse_assoc_handle_from(response) + end + + def parse_assoc_handle_from(response) + response.body.split("\n")[0].match(/^assoc_handle:(.*)$/).captures[0] + end + + def checkid_setup_params(params = {}) + { + "openid.ns" => "http://specs.openid.net/auth/2.0", + "openid.mode" => "checkid_setup", + "openid.claimed_id" => 'john.doe', + "openid.identity" => 'john.doe', + "openid.return_to" => "http://www.google.com" + # need to specify the openid_handle by hand + }.merge!(params) + end + + def checkid_immediate_params(params = {}) + checkid_setup_params({'openid.mode' => 'checkid_immediate'}.merge!(params)) + end + + def associate_params + { + "openid.ns" => "http://specs.openid.net/auth/2.0", + "openid.mode" => "associate", + "openid.session_type" => "DH-SHA1", + "openid.assoc_type" => "HMAC-SHA1", + "openid.dh_consumer_public" => + "U672/RsDUNxAFFAXA+ShVh5LMD2CRdsoqdqhDCPUzfCNy2f44uTWuid/MZuGfJmiVA7QmxqM3GSb8EVq3SGK8eGEwwyzUtatqHidx72rfwAav5AUrZTnwSPQJyiCFrKNGmNhXdRJzcfzSkgaC3hVz2kpADzEevIExG6agns1sYY=" + } + end + +end + +Spec::Runner.configure do |config| + config.include Rots::RequestHelper +end \ No newline at end of file diff --git a/vendor/plugins/open_id_authentication/CHANGELOG b/vendor/plugins/open_id_authentication/CHANGELOG new file mode 100644 index 000000000..0c8971efd --- /dev/null +++ b/vendor/plugins/open_id_authentication/CHANGELOG @@ -0,0 +1,37 @@ +* Dump heavy lifting off to rack-openid gem. OpenIdAuthentication is just a simple controller concern. + +* Fake HTTP method from OpenID server since they only support a GET. Eliminates the need to set an extra route to match the server's reply. [Josh Peek] + +* OpenID 2.0 recommends that forms should use the field name "openid_identifier" rather than "openid_url" [Josh Peek] + +* Return open_id_response.display_identifier to the application instead of .endpoints.claimed_id. [nbibler] + +* Add Timeout protection [Rick] + +* An invalid identity url passed through authenticate_with_open_id will no longer raise an InvalidOpenId exception. Instead it will return Result[:missing] to the completion block. + +* Allow a return_to option to be used instead of the requested url [Josh Peek] + +* Updated plugin to use Ruby OpenID 2.x.x [Josh Peek] + +* Tied plugin to ruby-openid 1.1.4 gem until we can make it compatible with 2.x [DHH] + +* Use URI instead of regexps to normalize the URL and gain free, better matching #8136 [dkubb] + +* Allow -'s in #normalize_url [Rick] + +* remove instance of mattr_accessor, it was breaking tests since they don't load ActiveSupport. Fix Timeout test [Rick] + +* Throw a InvalidOpenId exception instead of just a RuntimeError when the URL can't be normalized [DHH] + +* Just use the path for the return URL, so extra query parameters don't interfere [DHH] + +* Added a new default database-backed store after experiencing trouble with the filestore on NFS. The file store is still available as an option [DHH] + +* Added normalize_url and applied it to all operations going through the plugin [DHH] + +* Removed open_id? as the idea of using the same input box for both OpenID and username has died -- use using_open_id? instead (which checks for the presence of params[:openid_url] by default) [DHH] + +* Added OpenIdAuthentication::Result to make it easier to deal with default situations where you don't care to do something particular for each error state [DHH] + +* Stop relying on root_url being defined, we can just grab the current url instead [DHH] \ No newline at end of file diff --git a/vendor/plugins/open_id_authentication/README b/vendor/plugins/open_id_authentication/README new file mode 100644 index 000000000..fe2b37e29 --- /dev/null +++ b/vendor/plugins/open_id_authentication/README @@ -0,0 +1,223 @@ +OpenIdAuthentication +==================== + +Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first: + + gem install ruby-openid + +To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb +from that gem. + +The specification used is http://openid.net/specs/openid-authentication-2_0.html. + + +Prerequisites +============= + +OpenID authentication uses the session, so be sure that you haven't turned that off. + +Alternatively, you can use the file-based store, which just relies on on tmp/openids being present in RAILS_ROOT. But be aware that this store only works if you have a single application server. And it's not safe to use across NFS. It's recommended that you use the database store if at all possible. To use the file-based store, you'll also have to add this line to your config/environment.rb: + + OpenIdAuthentication.store = :file + +This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations. +If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb. + +The plugin also expects to find a root_url method that points to the home page of your site. You can accomplish this by using a root route in config/routes.rb: + + map.root :controller => 'articles' + +This plugin relies on Rails Edge revision 6317 or newer. + + +Example +======= + +This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add +salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point, +not a destination. + +Note that the User model referenced in the simple example below has an 'identity_url' attribute. You will want to add the same or similar field to whatever +model you are using for authentication. + +Also of note is the following code block used in the example below: + + authenticate_with_open_id do |result, identity_url| + ... + end + +In the above code block, 'identity_url' will need to match user.identity_url exactly. 'identity_url' will be a string in the form of 'http://example.com' - +If you are storing just 'example.com' with your user, the lookup will fail. + +There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs. + + OpenIdAuthentication.normalize_url(user.identity_url) + +The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/' +It will also raise an InvalidOpenId exception if the URL is determined to not be valid. +Use the above code in your User model and validate OpenID URLs before saving them. + +config/routes.rb + + map.root :controller => 'articles' + map.resource :session + + +app/views/sessions/new.erb + + <% form_tag(session_url) do %> +

+ + <%= text_field_tag "name" %> +

+ +

+ + <%= password_field_tag %> +

+ +

+ ...or use: +

+ +

+ + <%= text_field_tag "openid_identifier" %> +

+ +

+ <%= submit_tag 'Sign in', :disable_with => "Signing in…" %> +

+ <% end %> + +app/controllers/sessions_controller.rb + class SessionsController < ApplicationController + def create + if using_open_id? + open_id_authentication + else + password_authentication(params[:name], params[:password]) + end + end + + + protected + def password_authentication(name, password) + if @current_user = @account.users.authenticate(params[:name], params[:password]) + successful_login + else + failed_login "Sorry, that username/password doesn't work" + end + end + + def open_id_authentication + authenticate_with_open_id do |result, identity_url| + if result.successful? + if @current_user = @account.users.find_by_identity_url(identity_url) + successful_login + else + failed_login "Sorry, no user by that identity URL exists (#{identity_url})" + end + else + failed_login result.message + end + end + end + + + private + def successful_login + session[:user_id] = @current_user.id + redirect_to(root_url) + end + + def failed_login(message) + flash[:error] = message + redirect_to(new_session_url) + end + end + + + +If you're fine with the result messages above and don't need individual logic on a per-failure basis, +you can collapse the case into a mere boolean: + + def open_id_authentication + authenticate_with_open_id do |result, identity_url| + if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url) + successful_login + else + failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})") + end + end + end + + +Simple Registration OpenID Extension +==================================== + +Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension + +You can support it in your app by changing #open_id_authentication + + def open_id_authentication(identity_url) + # Pass optional :required and :optional keys to specify what sreg fields you want. + # Be sure to yield registration, a third argument in the #authenticate_with_open_id block. + authenticate_with_open_id(identity_url, + :required => [ :nickname, :email ], + :optional => :fullname) do |result, identity_url, registration| + case result.status + when :missing + failed_login "Sorry, the OpenID server couldn't be found" + when :invalid + failed_login "Sorry, but this does not appear to be a valid OpenID" + when :canceled + failed_login "OpenID verification was canceled" + when :failed + failed_login "Sorry, the OpenID verification failed" + when :successful + if @current_user = @account.users.find_by_identity_url(identity_url) + assign_registration_attributes!(registration) + + if current_user.save + successful_login + else + failed_login "Your OpenID profile registration failed: " + + @current_user.errors.full_messages.to_sentence + end + else + failed_login "Sorry, no user by that identity URL exists" + end + end + end + end + + # registration is a hash containing the valid sreg keys given above + # use this to map them to fields of your user model + def assign_registration_attributes!(registration) + model_to_registration_mapping.each do |model_attribute, registration_attribute| + unless registration[registration_attribute].blank? + @current_user.send("#{model_attribute}=", registration[registration_attribute]) + end + end + end + + def model_to_registration_mapping + { :login => 'nickname', :email => 'email', :display_name => 'fullname' } + end + +Attribute Exchange OpenID Extension +=================================== + +Some OpenID providers also support the OpenID AX (attribute exchange) protocol for exchanging identity information between endpoints. See more: http://openid.net/specs/openid-attribute-exchange-1_0.html + +Accessing AX data is very similar to the Simple Registration process, described above -- just add the URI identifier for the AX field to your :optional or :required parameters. For example: + + authenticate_with_open_id(identity_url, + :required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration| + +This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate' + + + +Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license \ No newline at end of file diff --git a/vendor/plugins/open_id_authentication/init.rb b/vendor/plugins/open_id_authentication/init.rb new file mode 100644 index 000000000..84ec11fc4 --- /dev/null +++ b/vendor/plugins/open_id_authentication/init.rb @@ -0,0 +1,12 @@ +if Rails.version < '3' + config.gem 'rack-openid', :lib => 'rack/openid', :version => '>=0.2.1' +end + +require 'open_id_authentication' + +config.middleware.use OpenIdAuthentication + +config.after_initialize do + OpenID::Util.logger = Rails.logger + ActionController::Base.send :include, OpenIdAuthentication +end diff --git a/vendor/plugins/open_id_authentication/lib/open_id_authentication.rb b/vendor/plugins/open_id_authentication/lib/open_id_authentication.rb new file mode 100644 index 000000000..b35504976 --- /dev/null +++ b/vendor/plugins/open_id_authentication/lib/open_id_authentication.rb @@ -0,0 +1,128 @@ +require 'uri' +require 'openid' +require 'rack/openid' + +module OpenIdAuthentication + def self.new(app) + store = OpenIdAuthentication.store + if store.nil? + Rails.logger.warn "OpenIdAuthentication.store is nil. Using in-memory store." + end + + ::Rack::OpenID.new(app, OpenIdAuthentication.store) + end + + def self.store + @@store + end + + def self.store=(*store_option) + store, *parameters = *([ store_option ].flatten) + + @@store = case store + when :memory + require 'openid/store/memory' + OpenID::Store::Memory.new + when :file + require 'openid/store/filesystem' + OpenID::Store::Filesystem.new(Rails.root.join('tmp/openids')) + when :memcache + require 'memcache' + require 'openid/store/memcache' + OpenID::Store::Memcache.new(MemCache.new(parameters)) + else + store + end + end + + self.store = nil + + class Result + ERROR_MESSAGES = { + :missing => "Sorry, the OpenID server couldn't be found", + :invalid => "Sorry, but this does not appear to be a valid OpenID", + :canceled => "OpenID verification was canceled", + :failed => "OpenID verification failed", + :setup_needed => "OpenID verification needs setup" + } + + def self.[](code) + new(code) + end + + def initialize(code) + @code = code + end + + def status + @code + end + + ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } } + + def successful? + @code == :successful + end + + def unsuccessful? + ERROR_MESSAGES.keys.include?(@code) + end + + def message + ERROR_MESSAGES[@code] + end + end + + protected + # The parameter name of "openid_identifier" is used rather than + # the Rails convention "open_id_identifier" because that's what + # the specification dictates in order to get browser auto-complete + # working across sites + def using_open_id?(identifier = nil) #:doc: + identifier ||= open_id_identifier + !identifier.blank? || request.env[Rack::OpenID::RESPONSE] + end + + def authenticate_with_open_id(identifier = nil, options = {}, &block) #:doc: + identifier ||= open_id_identifier + + if request.env[Rack::OpenID::RESPONSE] + complete_open_id_authentication(&block) + else + begin_open_id_authentication(identifier, options, &block) + end + end + + private + def open_id_identifier + params[:openid_identifier] || params[:openid_url] + end + + def begin_open_id_authentication(identifier, options = {}) + options[:identifier] = identifier + value = Rack::OpenID.build_header(options) + response.headers[Rack::OpenID::AUTHENTICATE_HEADER] = value + head :unauthorized + end + + def complete_open_id_authentication + response = request.env[Rack::OpenID::RESPONSE] + identifier = response.display_identifier + + case response.status + when OpenID::Consumer::SUCCESS + yield Result[:successful], identifier, + OpenID::SReg::Response.from_success_response(response) + when :missing + yield Result[:missing], identifier, nil + when :invalid + yield Result[:invalid], identifier, nil + when OpenID::Consumer::CANCEL + yield Result[:canceled], identifier, nil + when OpenID::Consumer::FAILURE + yield Result[:failed], identifier, nil + when OpenID::Consumer::SETUP_NEEDED + yield Result[:setup_needed], response.setup_url, nil + end + end +end