render :update do |page|
page.replace_html "contributorTerms", :partial => "terms"
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
else
+ session[:referer] = params[:referer]
+
@title = t 'user.terms.title'
@user = User.new(params[:user]) if params[:user]
+ if params[:user] and 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
if @user.invalid?
if @user.new_record?
+ # Something is wrong with a new user, so rerender the form
render :action => :new
else
+ # Error in existing user, so go to account settings
flash[:errors] = @user.errors
redirect_to :action => :account, :display_name => @user.display_name
end
elsif @user.terms_agreed?
+ # Already agreed to terms, so just show settings
redirect_to :action => :account, :display_name => @user.display_name
+ elsif params[:user] and params[:user][:openid_url]
+ # Verify OpenID before moving on
+ session[:new_user] = @user
+ openid_verify(params[:user][:openid_url], @user)
end
else
+ # Not logged in, so redirect to the login page
redirect_to :action => :login, :referer => request.request_uri
end
end
if @user.save
flash[:notice] = t 'user.new.flash create success message', :email => @user.email
- Notifier.deliver_signup_confirm(@user, @user.tokens.create(:referer => params[:referer]))
+ Notifier.deliver_signup_confirm(@user, @user.tokens.create(:referer => session.delete(:referer)))
session[:token] = @user.tokens.create.token
redirect_to :action => 'login', :referer => params[:referer]
else
@user.preferred_editor = params[:user][:preferred_editor]
end
- if @user.save
- set_locale
-
- 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'
+ @user.openid_url = nil if params[:user][:openid_url].empty?
- 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
+ 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
else
if flash[:errors]
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[:username] or using_open_id?
+ session[:remember_me] ||= params[:remember_me]
+ session[:referer] ||= params[:referer]
- 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 user
- session[:user] = user.id
- session_expires_after 1.month if params[:remember_me]
-
- target = params[:referer] || url_for(:controller => :site, :action => :index)
-
- # The user is logged in, so decide where to send them:
- #
- # - If they haven't seen the contributor terms, send them there.
- # - If they have a block on them, show them that.
- # - If they were referred to the login, send them back there.
- # - Otherwise, send them to the home page.
- if REQUIRE_TERMS_SEEN and not user.terms_seen
- redirect_to :controller => :user, :action => :terms, :referer => target
- elsif user.blocked_on_view
- redirect_to user.blocked_on_view, :referer => target
- else
- redirect_to target
- end
- elsif user = User.authenticate(:username => email_or_display_name, :password => pass, :pending => true)
- flash.now[:error] = t 'user.login.account not active', :reconfirm => url_for(:action => 'confirm_resend', :display_name => user.display_name)
- 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
elsif flash[:notice].nil?
flash.now[:notice] = t 'user.login.notice'
private
+ ##
+ # handle password authentication
+ def password_authentication(username, password)
+ if user = User.authenticate(:username => username, :password => password)
+ successful_login(user)
+ elsif user = User.authenticate(:username => username, :password => password, :pending => true)
+ failed_login t('user.login.account not active', :reconfirm => url_for(:action => 'confirm_resend', :display_name => user.display_name))
+ 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)
+ required = nil
+ else
+ required = [:nickname, :email, "http://axschema.org/namePerson/friendly", "http://axschema.org/contact/email"]
+ end
+
+ # Start the authentication
+ authenticate_with_open_id(openid_expand_url(openid_url), :required => required) do |result, identity_url, sreg, ax|
+ 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.
+ nickname = sreg["nickname"] || ax["http://axschema.org/namePerson/friendly"]
+ email = sreg["email"] || ax["http://axschema.org/contact/email"]
+ redirect_to :controller => 'user', :action => 'new', :nickname => nickname, :email => 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]
+
+ target = session[:referer] || url_for(:controller => :site, :action => :index)
+
+ # The user is logged in, so decide where to send them:
+ #
+ # - If they haven't seen the contributor terms, send them there.
+ # - If they have a block on them, show them that.
+ # - If they were referred to the login, send them back there.
+ # - Otherwise, send them to the home page.
+ if REQUIRE_TERMS_SEEN and not user.terms_seen
+ redirect_to :controller => :user, :action => :terms, :referer => target
+ elsif user.blocked_on_view
+ redirect_to user.blocked_on_view, :referer => target
+ else
+ redirect_to target
+ 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.
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
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, :if => Proc.new { |u| u.email_changed? }
<td><%= f.password_field :pass_crypt_confirmation, {:value => '', :size => 30, :maxlength => 255, :autocomplete => :off} %></td>
</tr>
+ <tr>
+ <td class="fieldName" ><%= t 'user.account.openid.openid' %></td>
+ <td><%= f.text_field :openid_url, {:class => "openid_url"} %> <span class="minorNote">(<a href="<%= t 'user.account.openid.link' %>" target="_new"><%= t 'user.account.openid.link text' %></a>)</span></td>
+ </tr>
+
<tr>
<td class="fieldName" valign="top"><%= t 'user.account.public editing.heading' %></td>
<td>
<div id="login_wrapper">
+
<div id="login_login">
<h1><%= t 'user.login.heading' %></h1>
- <p><%= t 'user.login.already have' %></p>
-
- <% form_tag :action => 'login' do %>
+ <% form_tag({ :action => "login" }, { :id => "login_form" }) do %>
<%= hidden_field_tag('referer', h(params[:referer])) %>
+
+ <p><%= t 'user.login.with username' %></p>
+
<table id="loginForm">
<tr><td class="fieldName"><%= t 'user.login.email or username' %></td><td><%= text_field('user', 'email',{:value => params[:username], :size => 28, :maxlength => 255, :tabindex => 1}) %></td></tr>
<tr><td class="fieldName"><%= t 'user.login.password' %></td><td><%= password_field('user', 'password',{:value => "", :size => 28, :maxlength => 255, :tabindex => 2}) %> <span class="minorNote">(<%= link_to t('user.login.lost password link'), :controller => 'user', :action => 'lost_password' %>)</span></td></tr>
<tr><td class="fieldName"><label for="remember_me"><%= t 'user.login.remember' %></label></td><td><%= check_box_tag "remember_me", "yes", false, :tabindex => 3 %></td></tr>
</table>
<%= submit_tag t('user.login.login_button'), :tabindex => 3 %>
+
+ <br clear="all" />
+
+ <p><%= t 'user.login.with openid' %></p>
+
+ <table id="login_openid_buttons_wide">
+ <tr>
+ <td>
+ <%=
+ 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[:login_openid_buttons_wide].hide
+ page[:login_openid_url].show
+ page[:login_openid_submit].show
+ end
+ %>
+ </td>
+ <td><%= openid_button "google", "gmail.com" %></td>
+ <td><%= openid_button "yahoo", "me.yahoo.com" %></td>
+ </tr>
+ <tr>
+ <td><%= openid_button "myopenid", "myopenid.com" %></td>
+ <td><%= openid_button "wordpress", "wordpress.com" %></td>
+ <td><%= openid_button "aol", "aol.com" %></td>
+ </tr>
+ </table>
+
+ <table id="login_openid_buttons_narrow">
+ <tr>
+ <td>
+ <%=
+ 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[:login_openid_buttons_narrow].hide
+ page[:login_openid_url].show
+ page[:login_openid_submit].show
+ end
+ %>
+ </td>
+ <td><%= openid_button "google", "gmail.com" %></td>
+ </tr>
+ <tr>
+ <td><%= openid_button "yahoo", "me.yahoo.com" %></td>
+ <td><%= openid_button "myopenid", "myopenid.com" %></td>
+ </tr>
+ <tr>
+ <td><%= openid_button "wordpress", "wordpress.com" %></td>
+ <td><%= openid_button "aol", "aol.com" %></td>
+ </tr>
+ </table>
+
+ <table>
+ <tr id="login_openid_url">
+ <td class="fieldName nowrap">
+ <%= t 'user.login.openid', :logo => openid_logo %>
+ </td>
+ <td>
+ <%= text_field_tag("openid_url", "", { :size => 28, :maxlength => 255, :tabindex => 3, :class => "openid_url" }) %>
+ <span class="minorNote">(<a href="<%= t 'user.account.openid.link' %>" target="_new"><%= t 'user.account.openid.link text' %></a>)</span>
+ </td>
+ </tr>
+ <tr>
+ <td class="fieldName nowrap" id="remember_me_label"><label for="remember_me"><%= t 'user.login.remember' %></label></td>
+ <td width="100%"><%= check_box_tag "remember_me", "yes", false, :tabindex => 5 %></td>
+ </tr>
+ </table>
+
+ <%= submit_tag t('user.login.login_button'), :tabindex => 6, :id => "login_openid_submit" %>
<% end %>
+
<br clear="all" />
</div>
+
<div id="login_signup">
<h2><%= t 'user.login.new to osm' %></h2>
<p><%= t 'user.login.to make changes' %></p>
<p><%= t 'user.login.create account minute' %></p>
<p><%= button_to t('user.login.register now'), :action => :new, :referer => params[:referer] %></p>
- <br clear="all" />
+
+ <br clear="both">
</div>
+
</div>
+
+<%=
+ update_page_tag do |page|
+ page[:login_openid_url].hide
+ page[:login_openid_submit].hide
+ end
+%>
<% if Acl.find_by_address(request.remote_ip, :conditions => {:k => "no_account_creation"}) %>
-<p><%= t 'user.new.no_auto_account_create' %>
-</p>
+<p><%= t 'user.new.no_auto_account_create' %></p>
-<p><%= t 'user.new.contact_webmaster' %>
-</p>
+<p><%= t 'user.new.contact_webmaster' %></p>
<% else %>
-<p><%= t 'user.new.fill_form' %>
-</p>
+<p><%= t 'user.new.fill_form' %></p>
<%= error_messages_for 'user' %>
<% form_tag :action => 'terms' do %>
-<%= hidden_field_tag('referer', h(params[:referer])) unless params[:referer].nil? %>
-<table id="signupForm">
- <tr><td class="fieldName"><%= t 'user.new.email address' %></td><td><%= text_field('user', 'email',{:size => 50, :maxlength => 255, :tabindex => 1}) %></td></tr>
- <tr><td class="fieldName"><%= t 'user.new.confirm email address' %></td><td><%= text_field('user', 'email_confirmation',{:size => 50, :maxlength => 255, :tabindex => 2}) %></td></tr>
- <tr><td></td><td><span class="minorNote"><%= t 'user.new.not displayed publicly' %></span></td></tr>
- <tr><td colspan="2"> <!--vertical spacer--></td></tr>
- <tr><td class="fieldName"><%= t 'user.new.display name' %></td><td><%= text_field('user', 'display_name',{:size => 30, :maxlength => 255, :tabindex => 3}) %></td></tr>
- <tr><td></td><td><span class="minorNote"><%= t 'user.new.display name description' %></span></td></tr>
- <tr><td colspan="2"> <!--vertical spacer--></td></tr>
- <tr><td class="fieldName"><%= t 'user.new.password' %></td><td><%= password_field('user', 'pass_crypt',{:size => 30, :maxlength => 255, :tabindex => 4}) %></td></tr>
- <tr><td class="fieldName"><%= t 'user.new.confirm password' %></td><td><%= password_field('user', 'pass_crypt_confirmation',{:size => 30, :maxlength => 255, :tabindex => 5}) %></td></tr>
-
- <tr><td colspan="2"> <!--vertical spacer--></td></tr>
- <tr><td></td><td align="right"><input type="submit" value="<%= t'user.new.continue' %>" tabindex="6"></td></tr>
-</table>
+ <%= hidden_field_tag('referer', h(@referer)) unless @referer.nil? %>
+
+ <table id="signupForm">
+ <tr>
+ <td class="fieldName"><%= t 'user.new.email address' %></td>
+ <td><%= text_field(:user, :email, { :size => 50, :maxlength => 255, :tabindex => 1, :value => params[:email] }) %></td>
+ </tr>
+ <tr>
+ <td class="fieldName"><%= t 'user.new.confirm email address' %></td>
+ <td><%= text_field(:user, :email_confirmation, { :size => 50, :maxlength => 255, :tabindex => 2, :value => params[:email] }) %></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td><span class="minorNote"><%= t 'user.new.not displayed publicly' %></span></td>
+ </tr>
+
+ <tr><td colspan="2"> <!--vertical spacer--></td></tr>
+
+ <tr>
+ <td class="fieldName"><%= t 'user.new.display name' %></td>
+ <td><%= text_field(:user, :display_name, { :size => 30, :maxlength => 255, :tabindex => 3, :value => params[:nickname] }) %></td></tr>
+ <tr>
+ <td></td>
+ <td><span class="minorNote"><%= t 'user.new.display name description' %></span></td>
+ </tr>
+
+ <tr id="openid_spacer"><td colspan="2"> <!--vertical spacer--></td></tr>
+
+ <tr id="openid_url">
+ <td class="fieldName"><%= t 'user.new.openid', :logo => openid_logo %></td>
+ <td><%= text_field(:user, :openid_url, { :size => 50, :maxlength => 255, :tabindex => 4, :value => params[:openid], :class => "openid_url" }) %></td>
+ </tr>
+
+ <tr><td colspan="2"> <!--vertical spacer--></td></tr>
+
+ <tr>
+ <td class="fieldName"><%= t 'user.new.password' %></td>
+ <td><%= password_field(:user, :pass_crypt, { :size => 30, :maxlength => 255, :tabindex => 5 }) %></td>
+ </tr>
+ <tr>
+ <td class="fieldName"><%= t 'user.new.confirm password' %></td>
+ <td><%= password_field(:user, :pass_crypt_confirmation, { :size => 30, :maxlength => 255, :tabindex => 6 }) %></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>
+ <span id="openid_prompt" class="minorNote"><%= link_to_function(t('user.new.use openid', :logo => openid_logo)) { |page| page.hide 'openid_prompt'; page.show 'openid_spacer', 'openid_url', 'openid_note' } %></span>
+ <span id="openid_note" class="minorNote"><%= t 'user.new.openid no password' %></span>
+ </td>
+ </tr>
+
+ <tr><td colspan="2" > <!--vertical spacer--></td></tr>
+
+ <tr>
+ <td></td>
+ <td align="right"><%= submit_tag t('user.new.continue'), :tabindex => 6 %></td>
+ </tr>
+ </table>
<% 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 %>
</p>
<p>
<%= hidden_field_tag('referer', h(params[:referer])) unless params[:referer].nil? %>
- <% if params[:user] %>
+ <% if @user.new_record? %>
<%= hidden_field('user', 'email') %>
<%= hidden_field('user', 'email_confirmation') %>
<%= hidden_field('user', 'display_name') %>
<%= hidden_field('user', 'pass_crypt') %>
<%= hidden_field('user', 'pass_crypt_confirmation') %>
+ <%= hidden_field('user', 'openid_url') %>
<% end %>
<div id="buttons">
<%= submit_tag(t('user.terms.decline'), :name => "decline", :id => "decline") %>
--- /dev/null
+OpenIdAuthentication.store = :file
login:
title: "Login"
heading: "Login"
- please login: "Please login or %{create_user_link}."
- create_account: "create an account"
email or username: "Email Address or Username:"
password: "Password:"
+ openid: "%{logo} OpenID:"
remember: "Remember me:"
lost password link: "Lost your password?"
login_button: "Login"
register now: Register now
- already have: Already have an OpenStreetMap account? Please login.
+ with username: "Already have an OpenStreetMap account? Please login with your username and password:"
+ with openid: "Alternatively please use your OpenID to login:"
new to osm: New to OpenStreetMap?
to make changes: To make changes to the OpenStreetMap data, you must have an account.
create account minute: Create an account. It only takes a minute.
webmaster: webmaster
auth failure: "Sorry, could not log in with those details."
notice: "<a href=\"http://www.osmfoundation.org/wiki/License/We_Are_Changing_The_License\">Find out more about OpenStreetMap's upcoming license change</a> (<a href=\"http://wiki.openstreetmap.org/wiki/ODbL/We_Are_Changing_The_License\">translations</a>) (<a href=\"http://wiki.openstreetmap.org/wiki/Talk:ODbL/Upcoming\">discussion</a>)"
+ 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 OpenID
+ alt: Login with an OpenID URL
+ google:
+ title: Login with Google
+ alt: Login with a Google OpenID
+ yahoo:
+ title: Login with Yahoo
+ alt: Login with a Yahoo OpenID
+ myopenid:
+ title: Login with myOpenID
+ alt: Login with a myOpenID OpenID
+ wordpress:
+ title: Login with Wordpress
+ alt: Login with a Wordpress OpenID
+ aol:
+ title: Login with AOL
+ alt: Login with an AOL OpenID
logout:
title: "Logout"
heading: "Logout from OpenStreetMap"
not displayed publicly: 'Not displayed publicly (see <a href="http://wiki.openstreetmap.org/wiki/Privacy_Policy" title="wiki privacy policy including section on email addresses">privacy policy</a>)'
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: |
+ <p>Your OpenID is not associated with a OpenStreetMap account yet.</p>
+ <ul>
+ <li>If you are new to OpenStreetMap, please create a new account using the form below.</li>
+ <li>
+ If you already have an account, you can login to your account
+ using your username and password and then associate the account
+ with your OpenID in your user settings.
+ </li>
+ </ul>
continue: Continue
flash create success message: "Thanks for signing up. We've sent a confirmation note to %{email} and as soon as you confirm your account you'll be able to get mapping.<br /><br />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 accepted: "Thanks for accepting the new contributor terms!"
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."
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:"
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á
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á <a href="http://wiki.openstreetmap.org/index.php?uselang=is&title=Privacy_Policy" title="Meðferð persónuupplýsinga, þ.á.m. netfanga">meðferð persónuupplýsinga</a>)
password: "Lykilorð:"
+ openID associate: "Tengja OpenID við þennan aðgang"
+ openID: "OpenID:"
+ openID description: '(Valfrjálst) Ef þú ert með <a href="http://wiki.openstreetmap.org/wiki/openID">OpenID</a> 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.
+ <ul>
+ <li>Ef þú ert ekki með OpenStreetMap aðgang getur þú búið til nýjan aðgang hér fyrir neðan.</li>
+ <li>
+ Ef þú ert þegar með aðgang skaltu innskrá þig með
+ honum. Svo getur þú tengt OpenID við aðganginn þinn á
+ stillingarsíðunni.
+ </li>
+ </ul>
+ 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.
--- /dev/null
+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
margin-top: 5px;
}
+table#login_openid_buttons {
+ padding-bottom: 10px;
+}
+
+#login_openid_buttons td {
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+
+#login_openid_buttons img {
+ border: 0;
+}
+
#login_signup form.button-to div {
margin: 0px;
padding: 0px;
border: 1px solid black;
}
+input.openid_url {
+ background: url('../images/openid_input.png') repeat-y left white;
+ padding-left: 16px;
+}
+
/* Rules for user images */
img.user_image {
.table1 {
background: #fff;
}
+
+/* Rules for OpenID logo */
+
+.openid_logo {
+ vertical-align: text-bottom;
+ border: 0;
+}
.olControlPanZoom {
display: none;
}
+
+/* Rules for the login form */
+
+#login_openid_buttons_narrow {
+ display: none;
+}
max-width: 18em;
}
+#login_openid_buttons_wide {
+ display: none;
+}
+
/* Rules for the profile page */
.user_map {
display_name: not_agreed
data_public: true
terms_seen: false
+
+openid_user:
+ id: 8
+ 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
+ terms_seen: true
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
# revoke the ban
get '/login'
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
class UserCreationTest < ActionController::IntegrationTest
fixtures :users
+ def setup
+ openid_setup
+ end
+
def test_create_user_form
I18n.available_locales.each do |locale|
get '/user/new', {}, {"accept_language" => locale.to_s}
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
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
+
+ def test_user_create_openid_failure
+ new_email = "newtester-openid2@osm.org"
+ display_name = "new_tester-openid2"
+ password = "testtest2"
+ assert_difference('User.count',0) do
+ assert_difference('ActionMailer::Base.deliveries.size',0) do
+ post "/user/terms",
+ {:user => { :email => new_email, :email_confirmation => new_email, :display_name => display_name, :openid_url => "http://localhost:1123/john.doe?openid.failure=newuser", :pass_crypt => "", :pass_crypt_confirmation => ""}}
+ assert_response :redirect
+ res = openid_request(@response.redirected_to)
+ post '/user/terms', res
+ assert_response :success
+ assert_template 'user/new'
+ end
+ end
+
+ ActionMailer::Base.deliveries.clear
+ end
+
+ def test_user_create_openid_redirect
+ new_email = "redirect_tester_openid@osm.org"
+ display_name = "redirect_tester_openid"
+ password = ""
+ # nothing special about this page, just need a protected page to redirect back to.
+ referer = "/traces/mine"
+ 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 => ""}, :referer => referer }
+ assert_response :redirect
+ res = openid_request(@response.location)
+ post '/user/terms', res
+ assert_response :success
+ assert_template 'terms'
+ post_via_redirect "/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 => "testtest", :pass_crypt_confirmation => "testtest"} }
+ end
+ end
+
+ # Check the e-mail
+ register_email = ActionMailer::Base.deliveries.first
+
+ assert_equal register_email.to[0], new_email
+ # Check that the confirm account url is correct
+ confirm_regex = Regexp.new("/user/redirect_tester_openid/confirm\\?confirm_string=([a-zA-Z0-9]*)")
+ assert_match(confirm_regex, register_email.body)
+ confirm_string = confirm_regex.match(register_email.body)[1]
+
+ # Check the page
+ assert_response :success
+ assert_template 'login'
+
+ ActionMailer::Base.deliveries.clear
+
+ # Go to the confirmation page
+ get 'user/confirm', { :confirm_string => confirm_string }
+ assert_response :success
+ assert_template 'user/confirm'
+
+ post 'user/confirm', { :confirm_string => confirm_string, :confirm_action => 'submit' }
+ assert_response :redirect # to trace/mine in original referrer
+ follow_redirect!
+ assert_response :redirect # but it not redirects to /user/<display_name>/traces
+ follow_redirect!
+ assert_response :success
+ assert_template "trace/list.html.erb"
+ end
end
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
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+class UserLoginTest < ActionController::IntegrationTest
+ fixtures :users
+
+ def setup
+ openid_setup
+ end
+
+ 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
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
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
require 'test_help'
load 'composite_primary_keys/fixtures.rb'
+# This monkey patch is to make tests where a rack module alters
+# the response work with rails 2 - it can be dropped when we move
+# to rails 3.
+module ActionController
+ module Integration
+ class Session
+ def process_with_capture(method, path, parameters = nil, headers = nil)
+ status = process_without_capture(method, path, parameters, headers)
+ @controller = ActionController::Base.last_controller
+ @request = @controller.request
+ @response.session = @controller.response.session
+ @response.template = @controller.response.template
+ @response.redirected_to = @response.location
+ status
+ end
+
+ alias_method_chain :process, :capture
+ end
+
+ module ControllerCapture
+ module ClassMethods
+ mattr_accessor :last_controller
+
+ def clear_last_instantiation!
+ self.last_controller = nil
+ end
+
+ def new_with_capture(*args)
+ controller = new_without_capture(*args)
+ self.last_controller ||= controller
+ controller
+ end
+ end
+ end
+ end
+end
+
class ActiveSupport::TestCase
# Transactional fixtures accelerate your tests by wrapping each test method
# in a transaction that's rolled back on completion. This ensures that the
def assert_no_missing_translations(msg="")
assert_select "span[class=translation_missing]", false, "Missing translation #{msg}"
end
+
+ # Set things up for OpenID testing
+ def openid_setup
+ begin
+ # Test if the ROTS (Ruby OpenID Test Server) is already running
+ rots_response = Net::HTTP.get_response(URI.parse("http://localhost:1123/"))
+ rescue
+ # It isn't, so start a new instance.
+ rots = IO.popen(RAILS_ROOT + "/vendor/gems/rots-0.2.1/bin/rots --silent")
+
+ # Wait for up to 30 seconds for the server to start and respond before continuing
+ for i in (1 .. 30)
+ begin
+ sleep 1
+ rots_response = Net::HTTP.get_response(URI.parse("http://localhost:1123/"))
+ # If the rescue block doesn't fire, ROTS is up and running and we can continue
+ break
+ rescue
+ # If the connection failed, do nothing and repeat the loop
+ end
+ end
+
+ # Arrange to kill the process when we exit - note that we need
+ # to kill it really har due to a bug in ROTS
+ Kernel.at_exit do
+ Process.kill("KILL", rots.pid)
+ end
+ end
+ 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
--- /dev/null
+--- !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
--- /dev/null
+* Roman Gonzalez <romanandreg@gmail.com>
+* Anibal Rojas <anibal@rojas.net.ve>
\ No newline at end of file
--- /dev/null
+= 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 <romanandreg@gmail.com>
+
+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.
--- /dev/null
+# 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
--- /dev/null
+#!/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
+ if server_options[:verbose]
+ Rack::Handler::WEBrick.run server, :Port => server_options[:port]
+ else
+ Rack::Handler::WEBrick.run server, :Port => server_options[:port], :AccessLog => [], :Logger => WEBrick::Log::new("/dev/null", 7)
+ end
+end
--- /dev/null
+module Rots
+
+ def self.release
+ "0.2.1"
+ end
+
+end
+
+require "rots/server_app"
+require "rots/identity_page_app"
+require "rots/test_helper"
--- /dev/null
+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
+<html>
+ <head>
+ <link rel="openid2.provider" href="#{op_endpoint}" />
+ <link rel="openid.server" href="#{op_endpoint}" />
+ </head>
+ <body>
+ <h1>This is #{@config['identity']} identity page</h1>
+ </body>
+</html>
+ 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
--- /dev/null
+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'},
+ ["<html><body><h1>ROTS => This is an OpenID endpoint</h1></body></html>"] ]
+ 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
--- /dev/null
+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
--- /dev/null
+# -*- 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
--- /dev/null
+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 == "<html><body><h1>ROTS => This is an OpenID endpoint</h1></body></html>"
+ 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
--- /dev/null
+$:.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
--- /dev/null
+* 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
--- /dev/null
+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 %>
+ <p>
+ <label for="name">Username:</label>
+ <%= text_field_tag "name" %>
+ </p>
+
+ <p>
+ <label for="password">Password:</label>
+ <%= password_field_tag %>
+ </p>
+
+ <p>
+ ...or use:
+ </p>
+
+ <p>
+ <label for="openid_identifier">OpenID:</label>
+ <%= text_field_tag "openid_identifier" %>
+ </p>
+
+ <p>
+ <%= submit_tag 'Sign in', :disable_with => "Signing in…" %>
+ </p>
+ <% 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
--- /dev/null
+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
--- /dev/null
+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),
+ OpenID::AX::FetchResponse.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