]> git.openstreetmap.org Git - rails.git/commitdiff
Merge branch 'master' into openstreetbugs
authorTom Hughes <tom@compton.nu>
Wed, 22 Jun 2011 21:36:43 +0000 (22:36 +0100)
committerTom Hughes <tom@compton.nu>
Wed, 22 Jun 2011 21:36:43 +0000 (22:36 +0100)
64 files changed:
app/controllers/amf_controller.rb
app/controllers/application_controller.rb
app/controllers/user_controller.rb
app/helpers/user_helper.rb
app/models/user.rb
app/models/user_sweeper.rb
app/views/user/account.html.erb
app/views/user/login.html.erb
app/views/user/new.html.erb
app/views/user/terms.html.erb
config/example.application.yml
config/initializers/openid.rb [new file with mode: 0644]
config/locales/en.yml
config/locales/is.yml
db/migrate/20100516124737_add_open_id.rb [new file with mode: 0644]
public/images/aol.png [new file with mode: 0644]
public/images/google.png [new file with mode: 0644]
public/images/myopenid.png [new file with mode: 0644]
public/images/openid.png [new file with mode: 0644]
public/images/openid_input.png [new file with mode: 0644]
public/images/openid_small.png [new file with mode: 0644]
public/images/wordpress.png [new file with mode: 0644]
public/images/yahoo.png [new file with mode: 0644]
public/potlatch2/locales/de_DE.swf
public/potlatch2/locales/en_GB.swf
public/potlatch2/locales/en_US.swf
public/potlatch2/locales/fr_FR.swf
public/potlatch2/locales/pl_PL.swf
public/potlatch2/potlatch2.swf
public/potlatch2/stylesheets/core_interactive.css
public/potlatch2/stylesheets/core_landuse.css
public/potlatch2/stylesheets/core_relations.css
public/potlatch2/stylesheets/core_ways.css
public/potlatch2/stylesheets/enhanced.css
public/potlatch2/stylesheets/network.css
public/potlatch2/stylesheets/opencyclemap.css
public/potlatch2/stylesheets/potlatch.css
public/stylesheets/common.css
public/stylesheets/small.css
test/fixtures/users.yml
test/integration/client_application_test.rb
test/integration/user_blocks_test.rb
test/integration/user_creation_test.rb
test/integration/user_diaries_test.rb
test/integration/user_login_test.rb [new file with mode: 0644]
test/integration/user_roles_test.rb
test/test_helper.rb
vendor/gems/rots-0.2.1/.specification [new file with mode: 0644]
vendor/gems/rots-0.2.1/AUTHORS [new file with mode: 0644]
vendor/gems/rots-0.2.1/README [new file with mode: 0644]
vendor/gems/rots-0.2.1/Rakefile [new file with mode: 0644]
vendor/gems/rots-0.2.1/bin/rots [new file with mode: 0755]
vendor/gems/rots-0.2.1/lib/rots.rb [new file with mode: 0644]
vendor/gems/rots-0.2.1/lib/rots/identity_page_app.rb [new file with mode: 0644]
vendor/gems/rots-0.2.1/lib/rots/server_app.rb [new file with mode: 0644]
vendor/gems/rots-0.2.1/lib/rots/test_helper.rb [new file with mode: 0644]
vendor/gems/rots-0.2.1/rots.gemspec [new file with mode: 0644]
vendor/gems/rots-0.2.1/spec/server_app_spec.rb [new file with mode: 0644]
vendor/gems/rots-0.2.1/spec/spec_helper.rb [new file with mode: 0644]
vendor/plugins/open_id_authentication/CHANGELOG [new file with mode: 0644]
vendor/plugins/open_id_authentication/README [new file with mode: 0644]
vendor/plugins/open_id_authentication/init.rb [new file with mode: 0644]
vendor/plugins/open_id_authentication/lib/open_id_authentication.rb [new file with mode: 0644]
vendor/plugins/session-persistence/lib/session_persistence.rb

index 0b04f0f3569b2610c143409f5feb2ca0f6c130ec..078823cbb3dc72ec7826461954ce8b1cfd1a0793 100644 (file)
@@ -186,6 +186,7 @@ class AmfController < ApplicationController
       user = getuser(usertoken)
       if !user then return -1,"You are not logged in, so Potlatch can't write any changes to the database." end
       unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
 
       if cstags
         if !tags_ok(cstags) then return -1,"One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1." end
@@ -573,6 +574,8 @@ class AmfController < ApplicationController
       user = getuser(usertoken)
       if !user then return -1,"You are not logged in, so the relation could not be saved." end
       unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
+
       if !tags_ok(tags) then return -1,"One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1." end
       tags = strip_non_xml_chars tags
 
@@ -661,7 +664,10 @@ class AmfController < ApplicationController
       user = getuser(usertoken)
       if !user then return -1,"You are not logged in, so the way could not be saved." end
       unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
+
       if pointlist.length < 2 then return -2,"Server error - way is only #{points.length} points long." end
+
       if !tags_ok(attributes) then return -1,"One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1." end
       attributes = strip_non_xml_chars attributes
 
@@ -767,6 +773,8 @@ class AmfController < ApplicationController
       user = getuser(usertoken)
       if !user then return -1,"You are not logged in, so the point could not be saved." end
       unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
+
       if !tags_ok(tags) then return -1,"One of the tags is invalid. Linux users may need to upgrade to Flash Player 10.1." end
       tags = strip_non_xml_chars tags
 
@@ -850,6 +858,7 @@ class AmfController < ApplicationController
       user = getuser(usertoken)
       unless user then return -1,"You are not logged in, so the way could not be deleted." end
       unless user.active_blocks.empty? then return -1,t('application.setup_user_auth.blocked') end
+      if REQUIRE_TERMS_AGREED and user.terms_agreed.nil? then return -1,"You must accept the contributor terms before you can edit." end
       
       way_id = way_id.to_i
       nodeversions = {}
index 6c19b3a529c26a4c463dcc350034280649adfb27..619ef981a61ba8260248368f681b04a6136e246a 100644 (file)
@@ -11,7 +11,7 @@ class ApplicationController < ActionController::Base
       @user = User.find(session[:user], :conditions => {:status => ["active", "confirmed", "suspended"]})
 
       if @user.status == "suspended"
-        session[:user] = nil
+        session.delete(:user)
         session_expires_automatically
 
         redirect_to :controller => "user", :action => "suspended"
@@ -50,7 +50,7 @@ class ApplicationController < ActionController::Base
     # method, otherwise an OAuth token was used, which has to be checked.
     unless current_token.nil?
       unless current_token.read_attribute(cap)
-        render :text => "OAuth token doesn't have that capability.", :status => :forbidden
+        report_error "OAuth token doesn't have that capability.", :forbidden
         return false
       end
     end
@@ -61,11 +61,14 @@ class ApplicationController < ActionController::Base
   def require_cookies
     if request.cookies["_osm_session"].to_s == ""
       if params[:cookie_test].nil?
+        session[:cookie_test] = true
         redirect_to params.merge(:cookie_test => "true")
         return false
       else
         flash.now[:warning] = t 'application.require_cookies.cookies_needed'
       end
+    else
+      session.delete(:cookie_test)
     end
   end
 
@@ -81,6 +84,11 @@ class ApplicationController < ActionController::Base
   end
   def require_allow_write_api
     require_capability(:allow_write_api)
+
+    if REQUIRE_TERMS_AGREED and @user.terms_agreed.nil?
+      report_error "You must accept the contributor terms before you can edit.", :forbidden
+      return false
+    end
   end
   def require_allow_read_gpx
     require_capability(:allow_read_gpx)
index 97b0de73c79fdf4d85676069df17a816a6989fd3..b337dc04c6cd8fd50014344c8ec4ae5fdb1b3a73 100644 (file)
@@ -27,22 +27,53 @@ class UserController < ApplicationController
       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] and not params[:user][:openid_url].empty?
+          # 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
@@ -94,10 +125,11 @@ class UserController < ApplicationController
       @user.languages = request.user_preferred_languages
       @user.terms_agreed = Time.now.getutc
       @user.terms_seen = true
+      @user.openid_url = nil if @user.openid_url and @user.openid_url.empty?
       
       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
@@ -136,22 +168,25 @@ class UserController < ApplicationController
         @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]
@@ -217,46 +252,26 @@ 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[: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'
@@ -272,9 +287,9 @@ class UserController < ApplicationController
         if token
           token.destroy
         end
-        session[:token] = nil
+        session.delete(:token)
       end
-      session[:user] = nil
+      session.delete(:user)
       session_expires_automatically
       if params[:referer]
         redirect_to params[:referer]
@@ -476,6 +491,175 @@ 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 = 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
+          # Guard against not getting any extension data
+          sreg = Hash.new if sreg.nil?
+          ax = Hash.new if ax.nil?
+
+          # 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.
index 0147c3fe6aabbf787808646d70952bb2f0bf1eed..8686d5a035c80751081aad9d3f3c1a1b89b2d037 100644 (file)
@@ -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
index d2535bbd48f2c41e585abe4135176f8434e19765..0b2a902dfac734f71d18c183cefe5863e579bbe1 100644 (file)
@@ -23,13 +23,14 @@ 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
-  validates_email_format_of :new_email, :allow_blank => true
-  validates_format_of :display_name, :with => /^[^\/;.,?]*$/
-  validates_format_of :display_name, :with => /^\S/, :message => "has leading whitespace"
-  validates_format_of :display_name, :with => /\S$/, :message => "has trailing whitespace"
+  validates_email_format_of :email, :if => Proc.new { |u| u.email_changed? }
+  validates_email_format_of :new_email, :allow_blank => true, :if => Proc.new { |u| u.new_email_changed? }
+  validates_format_of :display_name, :with => /^[^\/;.,?]*$/, :if => Proc.new { |u| u.display_name_changed? }
+  validates_format_of :display_name, :with => /^\S/, :message => "has leading whitespace", :if => Proc.new { |u| u.display_name_changed? }
+  validates_format_of :display_name, :with => /\S$/, :message => "has trailing whitespace", :if => Proc.new { |u| u.display_name_changed? }
   validates_numericality_of :home_lat, :allow_nil => true
   validates_numericality_of :home_lon, :allow_nil => true
   validates_numericality_of :home_zoom, :only_integer => true, :allow_nil => true
index d2fd983f76221d56215eab4c315763ffda02e374..7e420ae40b42684d4acdbf0dde025ad243dc2c57 100644 (file)
@@ -15,13 +15,18 @@ private
     if old_record and
         (new_record.nil? or
          old_record.visible? != new_record.visible? or
-         old_record.display_name != new_record.display_name)
+         old_record.display_name != new_record.display_name or
+         old_record.image != new_record.image)
       old_record.diary_entries.each do |entry|
         expire_action(:controller => 'diary_entry', :action => 'view', :display_name => old_record.display_name, :id => entry.id)
         expire_action(:controller => 'diary_entry', :action => 'list', :language => entry.language_code, :display_name => nil)
         expire_action(:controller => 'diary_entry', :action => 'rss', :language => entry.language_code, :display_name => nil)
       end
 
+      old_record.diary_comments.each do |comment|
+        expire_action(:controller => 'diary_entry', :action => 'view', :display_name => comment.diary_entry.user.display_name, :id => comment.diary_entry.id)
+      end
+
       expire_action(:controller => 'diary_entry', :action => 'list', :language => nil, :display_name => nil)
       expire_action(:controller => 'diary_entry', :action => 'list', :language => nil, :display_name => old_record.display_name)
 
index abece879f7622bb2362c36372b69f17acf5cfe73..d18971f0a18b68f546d10015ef68af542e1d7fd5 100644 (file)
     <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, {:id => "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>
index 7cc4b6f24ad455c960ab175ec263de289789dcff..2d90ba7cfd1d5a69ea8eced427b210a04b576ed4 100644 (file)
@@ -1,25 +1,80 @@
 <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 => "", :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"><%= t 'user.login.email or username' %></td><td><%= text_field_tag "username", params[:username], :size => 28, :maxlength => 255, :tabindex => 1 %></td></tr>
+        <tr><td class="fieldName"><%= t 'user.login.password' %></td><td><%= password_field_tag "password", "", :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">
+        <tr>
+          <td>
+            <%=
+              link_to_function(image_tag("openid.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].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>
+          <td><%= openid_button "myopenid", "myopenid.com" %></td>
+          <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
+%>
index 66d8826c2d4fcc0ed227a4208edecabb534b2704..5ff4ed02863cc3b57fc85757e2c848c1a3c1fef7 100644 (file)
@@ -2,37 +2,89 @@
 
 <% 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">&nbsp;<!--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">&nbsp;<!--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">&nbsp;<!--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">&nbsp;<!--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">&nbsp;<!--vertical spacer--></td></tr>
+
+    <tr>
+      <td class="fieldName"><%= t 'user.new.openid', :logo => openid_logo %></td>
+      <td><%= text_field(:user, :openid_url, { :id => "openid_url", :size => 50, :maxlength => 255, :tabindex => 4, :value => params[:openid], :class => "openid_url" }) %></td>
+    </tr>
+
+    <tr><td colspan="2">&nbsp;<!--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" >&nbsp;<!--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 %>
index cd352ab6fde9af90e80c5b9f68e17ae6535fc14f..e4644e8151758a6180acc98d07ced0db70d0863f 100644 (file)
   </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") %>
index dea87c247216ba59be7f2c60884fd67874448abf..eef48a7bbe5b6eca55d15ec93e7cd7c3d6553627 100644 (file)
@@ -71,6 +71,8 @@ standard_settings: &standard_settings
   #potlatch2_key: ""
   # Whether to require users to view the CTs before continuing to edit...
   require_terms_seen: false
+  # Whether to require users to agree to the CTs before editing
+  require_terms_agreed: false
 
 development:
   <<: *standard_settings
diff --git a/config/initializers/openid.rb b/config/initializers/openid.rb
new file mode 100644 (file)
index 0000000..966164b
--- /dev/null
@@ -0,0 +1 @@
+OpenIdAuthentication.store = :file
index c619d449d9050550b369a016bf7fce040d0b6da1..5b6d575d3f158e9cc65e91dcbeabf1845f3f7168 100644 (file)
@@ -1067,6 +1067,9 @@ en:
       <ul id="contributors">
           <li><strong>Australia</strong>: Contains suburb data based
           on Australian Bureau of Statistics data.</li>
+          <li><strong>Austria</strong>: Contains data from
+          <a href="http://data.wien.gv.at/">Stadt Wien</a> under
+          <a href="http://creativecommons.org/licenses/by/3.0/at/deed.de">CC-BY</a>.</li>
           <li><strong>Canada</strong>: Contains data from
           GeoBase&reg;, GeoGratis (&copy; Department of Natural
           Resources Canada), CanVec (&copy; Department of Natural
@@ -1534,15 +1537,15 @@ en:
     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.
@@ -1551,6 +1554,28 @@ en:
       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"
@@ -1583,8 +1608,21 @@ en:
       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!"
@@ -1674,6 +1712,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."
index 66a78861dde60cb20dbf03d9d8269fe820e35fe6..dfdacf9e9c95e182d588f905f2b4334ecc185bcc 100644 (file)
@@ -1011,6 +1011,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:"
@@ -1070,6 +1072,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á
@@ -1101,6 +1125,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á <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.
diff --git a/db/migrate/20100516124737_add_open_id.rb b/db/migrate/20100516124737_add_open_id.rb
new file mode 100644 (file)
index 0000000..2fb9ee5
--- /dev/null
@@ -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/aol.png b/public/images/aol.png
new file mode 100644 (file)
index 0000000..917028b
Binary files /dev/null and b/public/images/aol.png differ
diff --git a/public/images/google.png b/public/images/google.png
new file mode 100644 (file)
index 0000000..8d52074
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 (file)
index 0000000..5e410bb
Binary files /dev/null and b/public/images/myopenid.png differ
diff --git a/public/images/openid.png b/public/images/openid.png
new file mode 100644 (file)
index 0000000..dd6f65e
Binary files /dev/null and b/public/images/openid.png differ
diff --git a/public/images/openid_input.png b/public/images/openid_input.png
new file mode 100644 (file)
index 0000000..b5aa49d
Binary files /dev/null and b/public/images/openid_input.png differ
diff --git a/public/images/openid_small.png b/public/images/openid_small.png
new file mode 100644 (file)
index 0000000..83bb302
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 (file)
index 0000000..21e22ed
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 (file)
index 0000000..7bd439d
Binary files /dev/null and b/public/images/yahoo.png differ
index 65ee3395a0e7d4ff614b750cbb7c8ed475fc269d..c60ca7e63fbb316086fa8bdd7d28331e48e472b8 100644 (file)
Binary files a/public/potlatch2/locales/de_DE.swf and b/public/potlatch2/locales/de_DE.swf differ
index 25526e728ad701469db6349fbcd9df5a3a2ea1dd..27610068489a45f1237f0bf185b8525931a959da 100644 (file)
Binary files a/public/potlatch2/locales/en_GB.swf and b/public/potlatch2/locales/en_GB.swf differ
index d8bf4e54d2b3c068da12880ca65c88c09f1cec54..de138a009b2559197779363f40755c59368ba7e0 100644 (file)
Binary files a/public/potlatch2/locales/en_US.swf and b/public/potlatch2/locales/en_US.swf differ
index 1e874699321a8902c9f79561ea99a7b6f8678dba..b24b3ffe90b897bca10d8256ee6e691d5937dab8 100644 (file)
Binary files a/public/potlatch2/locales/fr_FR.swf and b/public/potlatch2/locales/fr_FR.swf differ
index d65619e5eba8aa4007487c7fda85914071b2e66e..32cc5c15f82333ccf670d9c6bfc0f81e8967d93a 100644 (file)
Binary files a/public/potlatch2/locales/pl_PL.swf and b/public/potlatch2/locales/pl_PL.swf differ
index 461e6b7cd29a8c3a8f43582ef010c26b8c887aea..3940ffbb5e97db2cf36c2a528c8fa3ac40d08e3a 100644 (file)
Binary files a/public/potlatch2/potlatch2.swf and b/public/potlatch2/potlatch2.swf differ
index 0e4ae87119cad05a1c65993ad0e0751b19870beb..0c02f6117da02723879b5d2dc8eaa28a37bf0c37 100644 (file)
@@ -4,10 +4,12 @@ relation[type=restriction] node { z-index:11; icon-image: icons/restriction.png;
 
 /* Interactive way behaviour */
 
-way :hover     { z-index: 2; width: eval('_width+10'); color: #ffff99; }
-way :selected { z-index: 2; width: eval('_width+10'); color: yellow; opacity: 0.7;}
-way :restrictfrom { z-index: -1; width: eval('_width+10'); color: red; opacity: 0.7;}
-way :restrictto { z-index: -1; width: eval('_width+10'); color: blue; opacity: 0.7;}
+way::highlight :hover  { z-index: 2; width: eval('_width+10'); color: #ffff99; }
+way::highlight :hover :background { color: lightcyan; }
+way::highlight :selected { z-index: 2; width: eval('_width+10'); color: yellow; opacity: 0.7;}
+way::highlight :selected :background { color: cyan; }
+way::highlight :restrictfrom { z-index: -1; width: eval('_width+10'); color: red; opacity: 0.7;}
+way::highlight :restrictto { z-index: -1; width: eval('_width+10'); color: blue; opacity: 0.7;}
 /*way !:drawn !:hasTags{ z-index:10; width: 0.5; color: red; }*/
 way !:drawn { z-index:10; width: 1; color: #333333; }
 way :tiger { casing-color: #ff00ff;}
@@ -20,5 +22,6 @@ node !:drawn :poi { z-index: 2; icon-image: circle; icon-width: 4; color: green;
 node !:drawn :hasTags { z-index: 9; icon-image: circle; icon-width: 4; color: black; }
 node :hasTags :selectedway { z-index: 9; icon-image: square; icon-width: 8; color: black; layer: 5; }
 node !:drawn :selectedway { z-index: 9; icon-image: square; icon-width: 8; color: red; casing-color: #cc0000; casing-width: 1; layer: 5; }
-node :selected { z-index: 1; icon-image: square; icon-width: eval('_width+10'); color: yellow; interactive: no; layer: 5; }
-node :junction :selectedway { z-index: 8; icon-image: square; icon-width: 12; casing-color: black; casing-width: 1; layer: 5; }
+node::selectedNode :selected { z-index: 1; icon-image: square; icon-width: eval('_width+10'); color: yellow; interactive: no; layer: 5; }
+node::selectedNode :selected :background { color: cyan; }
+node::junctionNode :junction :selectedway { z-index: 8; icon-image: square; icon-width: 12; casing-color: black; casing-width: 1; layer: 5; }
index 7eeb11738ff4289a1b978098e1f0f75970b3320c..a113cc7e13c9658375df7ec5e909e282b8b15b60 100644 (file)
@@ -28,10 +28,10 @@ way[leisure=pitch] :area                                    { z-index: 6; color: #88bb44; width: 2; fill-color:
 way[landuse=recreation_ground] :area        { color: green; fill-color: green; set .area_small_name;}
 way[amenity=parking] :area                  { color: #bbaa66;    width: 1; fill-color: #bbaa66;    fill-opacity: 0.2; }
 way[public_transport=pay_scale_area] :area  { color: gray;    width: 1; fill-color: gray;    fill-opacity: 0.1; }
-way[man_made=pier]                                                     { z-index: 4; color: #777; width: 3; casing-color: black; casing-width: 5;}
+way[man_made=pier]                                                     { z-index: 4; color: #777; width: 3; casing-color: black; casing-width: 1;}
 way[man_made=pier][floating=yes]                       { dashes: 4,2; casing-color: #444;}
 way[leisure=marina] :area                                      { color: pink; fill-color: pink; fill-opacity: 0.4; set .area_small_name;}
-way[leisure=slipway]                                           { color: grey; width: 3; casing-color: blue; casing-width: 7; }
+way[leisure=slipway]                                           { color: grey; width: 3; casing-color: blue; casing-width: 2; }
 way[leisure=golf_course] :area { color: #44ee22; width: 2; fill-color: #44ee22; fill-opacity: 0.2; set .area_small_name;}
 way[boundary]                               { color: #000066; width: 2; opacity: 0.6; dashes: 24,4, 4, 4; z-index: 4;}
 /* Perhaps should be filled, on lower zooms. */
index d1b2d5f6d7e8fef8848980a049a61173ec1ab865..7756abe64b1979633b0bd1c34e7db6b3a07aaae6 100644 (file)
@@ -1,7 +1,7 @@
 /* Route relations */
 
-relation[type=route] way { z-index: 1; width: 13; color: blue; opacity: 0.3; }
-relation[type=route][route=bicycle][network=ncn] way { z-index: 1; width: 12; color: red; opacity: 0.3; }
-relation[type=route][route=bicycle][network=rcn] way { z-index: 1; width: 12; color: cyan; opacity: 0.3; }
-relation[type=route][route=bicycle][network=lcn] way { z-index: 1; width: 12; color: blue; opacity: 0.3; }
-relation[type=route][route=foot] way { z-index: 1; width: 10; color: #80ff80; opacity: 0.6; }
+relation[type=route] way::route { z-index: 1; width: 13; color: blue; opacity: 0.3; }
+relation[type=route][route=bicycle][network=ncn] way::route { z-index: 1; width: 12; color: red; opacity: 0.3; }
+relation[type=route][route=bicycle][network=rcn] way::route { z-index: 1; width: 12; color: cyan; opacity: 0.3; }
+relation[type=route][route=bicycle][network=lcn] way::route { z-index: 1; width: 12; color: blue; opacity: 0.3; }
+relation[type=route][route=foot] way::route { z-index: 1; width: 10; color: #80ff80; opacity: 0.6; }
index 9e087f59cb2cd85e048b3490e7601b0dc009f21b..b7c2823325581730bfdf7a4ee476c6750b794d53 100644 (file)
@@ -7,23 +7,23 @@ way[highway=primary],way[highway=primary_link],
 way[highway=secondary],way[highway=secondary_link],
 way[highway=tertiary],way[highway=tertiary_link],
 way[highway=residential],way[highway=unclassified]  { text: name; text-color: black; font-size: 10; text-position: line; text-halo-color: white; text-halo-radius: 2; }
-way[highway=motorway],way[highway=motorway_link]    { z-index: 9; color: #809BC0; width: 7; casing-color: black; casing-width: 8; }
-way[highway=trunk],way[highway=trunk_link]          { z-index: 9; color: #7FC97F; width: 7; casing-color: black; casing-width: 8; }
-way[highway=primary],way[highway=primary_link]      { z-index: 8; color: #E46D71; width: 7; casing-color: black; casing-width: 8; }
-way[highway=secondary],way[highway=secondary_link]  { z-index: 7; color: #FDBF6F; width: 7; casing-width: 8; }
-way[highway=tertiary]                               { z-index: 6; color: #FEFECB; width: 5; casing-width: 7; }
-way[highway=unclassified]                           { z-index: 6; color: #D0D0D0; width: 5; casing-width: 7; }
-way[highway=tertiary_link]                          { z-index: 5; color: #FEFECB; width: 4; casing-width: 5; }
-way[highway=residential]                            { z-index: 5; color: #E8E8E8; width: 5; casing-color: gray; casing-width: 7; }
-way[highway=service][service!=parking_aisle]        { color: white; width: 3; casing-color: gray; casing-width: 5; }
-way[highway=service][service=parking_aisle]         { color: white; width: 1; casing-color: #aaaaaa; casing-width: 2; }
-way[highway=service][service=alley]                 { color: white; width: 2; dashes: 6,2; casing-color: black; casing-width: 4; }
-way[highway=road]                                   { color: gray; width: 5; casing-color: white; casing-width: 7; }
-way[highway=living_street]                          { z-index: 5; color: #ddffee; width: 3; casing-color: #555555; casing-width: 4; }
+way[highway=motorway],way[highway=motorway_link]    { z-index: 9; color: #809BC0; width: 7; casing-color: black; casing-width: 1; }
+way[highway=trunk],way[highway=trunk_link]          { z-index: 9; color: #7FC97F; width: 7; casing-color: black; casing-width: 1; }
+way[highway=primary],way[highway=primary_link]      { z-index: 8; color: #E46D71; width: 7; casing-color: black; casing-width: 1; }
+way[highway=secondary],way[highway=secondary_link]  { z-index: 7; color: #FDBF6F; width: 7; casing-width: 1; }
+way[highway=tertiary]                               { z-index: 6; color: #FEFECB; width: 5; casing-width: 1; }
+way[highway=unclassified]                           { z-index: 6; color: #D0D0D0; width: 5; casing-width: 1; }
+way[highway=tertiary_link]                          { z-index: 5; color: #FEFECB; width: 4; casing-width: 1; }
+way[highway=residential]                            { z-index: 5; color: #E8E8E8; width: 5; casing-color: gray; casing-width: 1; }
+way[highway=service][service!=parking_aisle]        { color: white; width: 3; casing-color: gray; casing-width: 1; }
+way[highway=service][service=parking_aisle]         { color: white; width: 1; casing-color: #aaaaaa; casing-width: 1; }
+way[highway=service][service=alley]                 { color: white; width: 2; dashes: 6,2; casing-color: black; casing-width: 1; }
+way[highway=road]                                   { color: gray; width: 5; casing-color: white; casing-width: 1; }
+way[highway=living_street]                          { z-index: 5; color: #ddffee; width: 3; casing-color: #555555; casing-width: 1; }
 
 /* Road areas */
 
-way[highway=pedestrian] !:area { color: #ddddee; width: 5; casing-color: #555555; casing-width: 6; casing-dashes: 2,4;}
+way[highway=pedestrian] !:area { color: #ddddee; width: 5; casing-color: #555555; casing-width: 1; casing-dashes: 2,4;}
 way[highway=pedestrian] :area  { color: #555555; width: 1; fill-color: #ddddee; fill-opacity: 0.8; }
 
 /* Paths */
@@ -35,29 +35,28 @@ way[highway=bridleway] { z-index:9; color: #996644; width: 2; dashes: 4, 2, 2, 2
 way[highway=track]     { color: #996644; width: 2; dashes: 4, 2; set .path;}
 way[highway=path]      { color: brown; width: 2; dashes: 2, 2; set .path;}
 way[highway=cycleway]  { color: blue; width: 2; dashes: 4, 2; set .path;}
-way[railway=tram]      { z-index: 11; color: #999999; width: 2; casing-color: black; casing-width: 6; }
+way[railway=tram]      { z-index: 11; color: #999999; width: 2; casing-color: black; casing-width: 2; }
 way .path              { text:name; text-color: black; text-position: offset; text-offset: 5;}
 
 /* Under construction */
 
 way[highway=proposed] { color: #88ffff; width: 6; dashes: 8, 4; }
-way[highway=construction] { color: #ffffbb; width: 6; dashes: 8, 4; casing-color: #0000aa; casing-width: 8; casing-dashes: 8,4;}
-way[construction=rail] 
-       { z-index: 6; color: black; width: 5; dashes: 6, 6, 4, 8;}
-       { z-index: 7; color: white; width: 3; dashes: 6,18; }
+way[highway=construction] { color: #ffffbb; width: 6; dashes: 8, 4; casing-color: #0000aa; casing-width: 1; casing-dashes: 8,4;}
+way[construction=rail] { z-index: 6; color: black; width: 5; dashes: 6, 6, 4, 8;}
+way[construction=rail]::inner { z-index: 7; color: white; width: 3; dashes: 6,18; }
 
 /* Railways */
 
-way[railway=rail]
-       { z-index: 6; color: black; width: 5; }
-       { z-index: 7; color: white; width: 3; dashes: 12,12; }
+way[railway=rail] { z-index: 6; color: black; width: 5; }
+way[railway=rail]::dashes { z-index: 7; color: white; width: 3; dashes: 12,12; }
+
 way[railway=platform] { color:black; width: 2; }
-way[railway=subway]
-    { z-index: 6; color: #444444; width: 5; }
-    { z-index: 7; color: white; width: 3; dashes: 8,8; }
-way[railway=disused],way[railway=abandoned]
-    { z-index: 6; color: #444400; width: 3; dashes: 17, 2, 5, 0; }
-    { z-index: 7; color: #999999; width: 2; dashes: 12,12; }
+
+way[railway=subway] { z-index: 6; color: #444444; width: 5; }
+way[railway=subway]::dashes  { z-index: 7; color: white; width: 3; dashes: 8,8; }
+
+way[railway=disused],way[railway=abandoned] { z-index: 6; color: #444400; width: 3; dashes: 17, 2, 5, 0; }
+way[railway=disused]::dashes,way[railway=abandoned]::dashes { z-index: 7; color: #999999; width: 2; dashes: 12,12; }
 
 /* Waterways */        
 
@@ -70,25 +69,26 @@ way[waterway][tunnel=yes]                {dashes: 8,4;}
 /* Aeroways */
 
 way[aeroway=aerodrome] :area
-    { z-index: 3; color: #bb44bb; width: 3; casing-color: #66066;  casing-width: 4;  }
+    { z-index: 3; color: #bb44bb; width: 3; casing-color: #66066;  casing-width: 1;  }
 way|z-15[aeroway=aerodrome] :area
     { z-index: 3; fill-color: #bb99bb;  fill-opacity: 0.5;}
-way[aeroway=taxiway] !:area { z-index: 8; color: #999999; width: 3; casing-color: #aa66aa; casing-width: 6; } 
+way[aeroway=taxiway] !:area { z-index: 8; color: #999999; width: 3; casing-color: #aa66aa; casing-width: 2; }
 way[aeroway=taxiway]  :area { z-index: 8; color: #bb99bb; width: 3; fill-color: #ccaacc; } 
-way|z17-[aeroway=runway] !:area 
-    { z-index: 9; color: black; width: 11; casing-color: #aa66aa; casing-width: 12; } 
-    { z-index: 10; color: white; width: 9;  dashes: 0, 20, 4, 76; } 
-    { z-index: 11; color: black; width: 7; } 
-    { z-index: 12; color: white; width: 5;  dashes: 0, 20, 4, 76; } 
-    { z-index: 13; color: black; width: 3; } 
-    { z-index: 14; color: white; width: 1;  dashes: 4, 16; } 
-way|z15-16[aeroway=runway] !:area 
-    { z-index: 9; color: black; width: 5;  } 
-    { z-index: 12; color: white; width: 5;  dashes: 0, 20, 4, 76; } 
-    { z-index: 13; color: black; width: 3; } 
-    { z-index: 14; color: white; width: 1;  dashes: 4, 16; } 
-way|z-14[aeroway=runway] !:area 
-    { z-index: 9; color: #444444; width: 3;  } 
+
+way|z17-[aeroway=runway] !:area { z-index: 9; color: black; width: 11; casing-color: #aa66aa; casing-width: 1; }
+way|z17-[aeroway=runway]::aa !:area { z-index: 10; color: white; width: 9;  dashes: 0, 20, 4, 76; }
+way|z17-[aeroway=runway]::bb !:area { z-index: 11; color: black; width: 7; }
+way|z17-[aeroway=runway]::cc !:area { z-index: 12; color: white; width: 5;  dashes: 0, 20, 4, 76; }
+way|z17-[aeroway=runway]::dd !:area { z-index: 13; color: black; width: 3; }
+way|z17-[aeroway=runway]::ee !:area { z-index: 14; color: white; width: 1;  dashes: 4, 16; }
+
+way|z15-16[aeroway=runway] !:area { z-index: 9; color: black; width: 5;  }
+way|z15-16[aeroway=runway]::aa !:area { z-index: 12; color: white; width: 5;  dashes: 0, 20, 4, 76; }
+way|z15-16[aeroway=runway]::bb !:area { z-index: 13; color: black; width: 3; }
+way|z15-16[aeroway=runway]::cc !:area { z-index: 14; color: white; width: 1;  dashes: 4, 16; }
+
+way|z-14[aeroway=runway] !:area { z-index: 9; color: #444444; width: 3;  }
+
 way[aeroway=runway] :area { z-index: 9; color: black; width: 3; fill-color: #775577; } 
 way[aeroway=apron] :area { z-index: 4; color: #cc66cc; width: 1; fill-color: #ddaadd; fill-opacity: 0.5;} 
 
@@ -99,27 +99,25 @@ way[barrier=fence] {color: #000000; width: 1; dashes: 8,4,2,4; }
 
 /* Power */
 
-way[power=line] {color: darkgray; width: 3; dashes: 12,2; casing-color: black; casing-width: 8; casing-dashes: 4, 38;}
-way[power=minor_line] {color: gray; width: 2; dashes: 2,4; casing-width: 8; casing-color: white; casing-dashes: 2,22;}
+way[power=line] {color: darkgray; width: 3; dashes: 12,2; casing-color: black; casing-width: 2; casing-dashes: 4, 38;}
+way[power=minor_line] {color: gray; width: 2; dashes: 2,4; casing-width: 3; casing-color: white; casing-dashes: 2,22;}
 way[power=station] :area { color: black; width: 2; fill-color: #666666; fill-opacity: 0.6; set .area_small_name;}
 way[power=generator] :area { color: black; width: 2; fill-color: #444444; fill-opacity: 0.6; set .area_small_name;}
 
 /* Leisure */
 
-way[golf=hole] {color: darkgreen; width: 5; casing-color: green; casing-width: 10; }
+way[golf=hole] {color: darkgreen; width: 5; casing-color: green; casing-width: 2; }
 way[leisure=sports_centre] :area { color: #66ddcc; fill-color: #66ddcc; set .area_small_name; }
 
 
 /* Physical decoration */
 
-way[bridge=yes], way[bridge=viaduct], way[bridge=suspension]
-    { z-index: 4; color: white; width: eval('_width+3'); }
-    { z-index: 3; color: black; width: eval('_width+6'); }
-way[tunnel=yes][!waterway]
-    { z-index: 4; color: white; width: eval('_width+2'); }
-    { z-index: 3; color: black; width: eval('_width+6'); dashes: 4,4; }
+way[bridge=yes]::bridge1, way[bridge=viaduct]::bridge1, way[bridge=suspension]::bridge1 { z-index: 4; color: white; width: eval('_width+3'); }
+way[bridge=yes]::bridge2, way[bridge=viaduct]::bridge2, way[bridge=suspension]::bridge2 { z-index: 3; color: black; width: eval('_width+6'); }
+way[tunnel=yes][!waterway]::bridge1 { z-index: 4; color: white; width: eval('_width+2'); }
+way[tunnel=yes][!waterway]::bridge2 { z-index: 3; color: black; width: eval('_width+6'); dashes: 4,4; }
 
 /* Attribute decoration */
 
-way[oneway=yes], way[junction=roundabout] { z-index: 15; color: #444444; width: 2; dashes: 15,35; line-style: arrows; }
-way[oneway=-1] { z-index: 15; color: #444444; width: 2; dashes: 15,35; line-style: arrows-reversed; }
+way[oneway=yes]::arrows, way[junction=roundabout]::arrows { z-index: 15; color: #444444; width: 2; dashes: 15,35; line-style: arrows; }
+way[oneway=-1]::arrows { z-index: 15; color: #444444; width: 2; dashes: 15,35; line-style: arrows-reversed; }
index 61315b3950b6395e1385ba970d2c3b5ddfb02774..23966e004cf3703bf479bcf2bc7ea9caaeb39800 100644 (file)
 
 /* Access */
 
-way[access=private],way[access=no] { z-index: 10; color: red; width: eval('_width+2'); dashes: 2,5;}
-way[access=permissive] { z-index: 10; color: green; width: eval('_width+2'); dashes: 1,3;}
+way[access=private]::access,way[access=no]::access { z-index: 10; color: red; width: eval('_width+2'); dashes: 2,5;}
+way[access=permissive]::access { z-index: 10; color: green; width: eval('_width+2'); dashes: 1,3;}
 
 /* Physical */
 
-way[embankment=yes], way[cutting=yes]
+way[embankment=yes]::hatches, way[cutting=yes]::hatches
     { z-index: 3; opacity: 0.5; color: grey; width: eval('_width+5'); dashes: 2, 2; }
 
 /* Interactive behaviour */
@@ -28,8 +28,8 @@ way .area_small_name {text-color: black; font-size: 9; text: name; text-halo: #f
 
 /* Direction on selected ways */
 
-way[highway][!oneway][junction!=roundabout]:selected,
-way[aerial_way]:selected { z-index: 14; color: #999922; width: 2; dashes: 3,60; line-style: arrows; }
-way[waterway]:selected { z-index: 14; color: #4444CC; width: 2; dashes: 5,60; line-style: arrows; }
-way[railway] :selected{ z-index: 14; color: #999999; width: 3; dashes: 4,92; line-style: arrows; }
+way[highway][!oneway][junction!=roundabout]::direction :selected,
+way[aerial_way]::direction :selected { z-index: 14; color: #999922; width: 2; dashes: 3,60; line-style: arrows; }
+way[waterway]::direction :selected { z-index: 14; color: #4444CC; width: 2; dashes: 5,60; line-style: arrows; }
+way[railway]::direction :selected{ z-index: 14; color: #999999; width: 3; dashes: 4,92; line-style: arrows; }
 
index a8f72a6c0b410514173543947cc44ae1a181ec19..d2a60a9fb4b9f883084c70f54daf17dcdf4907d8 100644 (file)
 
 /* Access */
 
-way[access=private],way[access=no] { z-index: 10; color: red; width: eval('_width+2'); dashes: 2,5;}
-way[access=permissive] { z-index: 10; color: green; width: eval('_width+2'); dashes: 1,3;}
+way[access=private]::access,way[access=no]::access { z-index: 10; color: red; width: eval('_width+2'); dashes: 2,5;}
+way[access=permissive]::access { z-index: 10; color: green; width: eval('_width+2'); dashes: 1,3;}
 
 /* Physical */
 
-way[embankment=yes], way[cutting=yes]
+way[embankment=yes]::hatches, way[cutting=yes]::hatches
     { z-index: 3; opacity: 0.5; color: grey; width: eval('_width+5'); dashes: 2, 2; }
 
 /* Interactive behaviour */
@@ -28,8 +28,8 @@ way .area_small_name {text-color: black; font-size: 9; text: name; text-halo: #f
 
 /* Direction on selected ways */
 
-way[highway][!oneway][junction!=roundabout]:selected,
-way[aerial_way]:selected { z-index: 14; color: #999922; width: 2; dashes: 3,60; line-style: arrows; }
-way[waterway]:selected { z-index: 14; color: #4444CC; width: 2; dashes: 5,60; line-style: arrows; }
-way[railway] :selected{ z-index: 14; color: #999999; width: 3; dashes: 4,92; line-style: arrows; }
+way[highway][!oneway][junction!=roundabout]::direction :selected,
+way[aerial_way]::direction :selected { z-index: 14; color: #999922; width: 2; dashes: 3,60; line-style: arrows; }
+way[waterway]::direction :selected { z-index: 14; color: #4444CC; width: 2; dashes: 5,60; line-style: arrows; }
+way[railway]::direction :selected{ z-index: 14; color: #999999; width: 3; dashes: 4,92; line-style: arrows; }
 
index 194d9101d63323c2b1efbc8e9582ac7f62ca27c4..1270ba26cb15b68096510c46de6562461a20776d 100644 (file)
@@ -27,17 +27,17 @@ way[highway=primary],way[highway=primary_link],
 way[highway=secondary],way[highway=secondary_link],
 way[highway=tertiary],way[highway=tertiary_link],
 way[highway=residential]                             { text: name; text-color: black; font-size: 7; text-position: line;}*/
-way[highway=motorway],way[highway=motorway_link]    { z-index: 9; color: #bfbfcf; width: 7; casing-color: #506077; casing-width: 9; }
-way[highway=trunk],way[highway=trunk_link]          { z-index: 9; color: #c8d8c8; width: 7; casing-color: #477147; casing-width: 9; }
-way[highway=primary],way[highway=primary_link]      { z-index: 8; color: #d8c8c8; width: 7; casing-color: #8d4346; casing-width: 9; }
-way[highway=secondary],way[highway=secondary_link]  { z-index: 7; color: #eeeec9; width: 7; casing-color: #a37b48; casing-width: 9; }
-way[highway=tertiary],way[highway=unclassified]     { z-index: 6; color: #eeeec9; width: 5; casing-color: #999999; casing-width: 7; }
-way[highway=residential]                            { z-index: 5; color: white; width: 5; casing-color: #999; casing-width: 7; }
-way[highway=service]                                { color: white; width: 3; casing-color: #999; casing-width: 5; }
+way[highway=motorway],way[highway=motorway_link]    { z-index: 9; color: #bfbfcf; width: 7; casing-color: #506077; casing-width: 1; }
+way[highway=trunk],way[highway=trunk_link]          { z-index: 9; color: #c8d8c8; width: 7; casing-color: #477147; casing-width: 1; }
+way[highway=primary],way[highway=primary_link]      { z-index: 8; color: #d8c8c8; width: 7; casing-color: #8d4346; casing-width: 1; }
+way[highway=secondary],way[highway=secondary_link]  { z-index: 7; color: #eeeec9; width: 7; casing-color: #a37b48; casing-width: 1; }
+way[highway=tertiary],way[highway=unclassified]     { z-index: 6; color: #eeeec9; width: 5; casing-color: #999999; casing-width: 1; }
+way[highway=residential]                            { z-index: 5; color: white; width: 5; casing-color: #999; casing-width: 1; }
+way[highway=service]                                { color: white; width: 3; casing-color: #999; casing-width: 1; }
 
 /* Pedestrian precincts need to be treated carefully. Only closed-loops with an explicit
 area=yes tag should be filled. The below doesn't yet work as intended. */
-way[highway=pedestrian] !:area { color: #ddddee; width: 5; casing-color: #555555; casing-width: 6; }
+way[highway=pedestrian] !:area { color: #ddddee; width: 5; casing-color: #555555; casing-width: 1; }
 way[highway=pedestrian] :area  { color: #555555; width: 1; fill-color: #ddddee; fill-opacity: 0.8; }
 
 way[highway=steps]     { color: #be6c6c; width: 2; dashes: 4, 2; }
@@ -107,26 +107,22 @@ node[barrier=cattle_grid] { icon-image: icons/cattle_grid.png; }*/
        
 /* We can stack styles at different z-index (depth) */
 
-way[railway=rail]
-       { z-index: 6; color: #444444; width: 5; } 
-       { z-index: 7; color: white; width: 3; dashes: 12,12; }
+way[railway=rail] { z-index: 6; color: #444444; width: 5; }
+way[railway=rail]::dashes { z-index: 7; color: white; width: 3; dashes: 12,12; }
 way[railway=platform] { color:black; width: 2; }
-way[railway=subway]
-        { z-index: 6; color: #444444; width: 5; }
-        { z-index: 7; color: white; width: 3; dashes: 8,8; }
+way[railway=subway] { z-index: 6; color: #444444; width: 5; }
+way[railway=subway]::dashes { z-index: 7; color: white; width: 3; dashes: 8,8; }
 
 /* Bridge */
-way[bridge=yes], way[bridge=viaduct], way[bridge=suspension]
-    { z-index: 4; color: white; width: eval('_width+3'); }
-    { z-index: 3; color: black; width: eval('_width+6'); }
+way[bridge=yes]::bridge1, way[bridge=viaduct]::bridge1, way[bridge=suspension]::bridge1  { z-index: 4; color: white; width: eval('_width+3'); }
+way[bridge=yes]::bridge2, way[bridge=viaduct]::bridge2, way[bridge=suspension]::bridge2  { z-index: 3; color: black; width: eval('_width+6'); }
     
 /* Tunnel */
-way[tunnel=yes]
-    { z-index: 4; color: white; width: eval('_width+2'); }
-    { z-index: 3; color: black; width: eval('_width+6'); dashes: 4,4; }
+way[tunnel=yes]::tunnel1 { z-index: 4; color: white; width: eval('_width+2'); }
+way[tunnel=yes]::tunnel2 { z-index: 3; color: black; width: eval('_width+6'); dashes: 4,4; }
 
 /* Oneway */
-way[oneway=yes] { z-index: 10; color: #6c70d5; width: 2; dashes: 10,30; line-style: arrows; }
+way[oneway=yes]::arrows { z-index: 10; color: #6c70d5; width: 2; dashes: 10,30; line-style: arrows; }
 
 
 /* Change the road colour based on dynamically set "highlighted" tag (see earlier) */
@@ -136,24 +132,16 @@ way .highlighted { color: pink; }
 /* Interactive editors may choose different behaviour when a user mouses-over or selects
    an object. Potlatch 2 supports these but the stand-alone Halcyon viewer does not */
 
-way :hover     { z-index: 2; width: eval('_width+10'); color: #ffff99; }
-way :selected { z-index: 2; width: eval('_width+10'); color: yellow; opacity: 0.7;}
-way !:drawn { z-index:10; width: 0.5; color: gray; } 
+@import("stylesheets/core_interactive.css");
 
-node :selectedway { z-index: 9; icon-image: square; icon-width: 8; color: red; casing-color: #cc0000; casing-width: 1;}
-node :hoverway { z-index: 9; icon-image: square; icon-width: 7; color: blue; }
-node !:drawn :poi { z-index: 2; icon-image: circle; icon-width: 3; color: lightsteelblue; casing-color: black; casing-width: 1; }
-node :selected { z-index: 1; icon-image: square; icon-width: eval('_width+10'); color: yellow; }
-node :junction :selectedway { z-index: 8; icon-image: square; icon-width: 12; casing-color: black; casing-width: 1; }
-       
 /* Descendant selectors provide an easy way to style relations: this example means "any way
    which is part of a relation whose type=route". */
 
-relation[type=route] way { z-index: 1; width: 17; color: yellow; opacity: 0.3; }
-relation[type=route][route=bicycle][network=ncn] way { z-index: 1; width: 12; color: red; opacity: 0.3; }
-relation[type=route][route=bicycle][network=rcn] way { z-index: 1; width: 12; color: cyan; opacity: 0.3; }
-relation[type=route][route=bicycle][network=lcn] way { z-index: 1; width: 12; color: blue; opacity: 0.3; }
-relation[type=route][route=bicycle][network=mtb] way { z-index: 1; width: 12; color: #48a448; opacity: 0.3; }
+relation[type=route] way::routeline { z-index: 1; width: 17; color: yellow; opacity: 0.3; }
+relation[type=route][route=bicycle][network=ncn] way::routeline { z-index: 1; width: 12; color: red; opacity: 0.3; }
+relation[type=route][route=bicycle][network=rcn] way::routeline { z-index: 1; width: 12; color: cyan; opacity: 0.3; }
+relation[type=route][route=bicycle][network=lcn] way::routeline { z-index: 1; width: 12; color: blue; opacity: 0.3; }
+relation[type=route][route=bicycle][network=mtb] way::routeline { z-index: 1; width: 12; color: #48a448; opacity: 0.3; }
 
 
 
index 625d5462f5140a9c7264bf13e9e2cacdfd4c0a1e..34fd3bc4a23dd3867c117c6eb5577350d29b6ba3 100644 (file)
 way .area_small_name {text-color: black; font-size: 9; text: name; text-halo: #ffffaa; text-halo-radius: 2; text-position: center;}
 @import("stylesheets/core_interactive.css");
 
+/* Test rendering for licence status */
+
+way[_status=no]::status       { z-index: 0; width: 20; color: red; }
+way[_status=partial]::status  { z-index: 0; width: 20; color: red; opacity: 0.4; }
+way[_status=unsure]::status   { z-index: 0; width: 20; color: orange; opacity: 0.4; }
+node[_status=no]::status      { z-index: 0; icon-image: square; icon-width: 15; color: red; }
+node[_status=partial]::status { z-index: 0; icon-image: square; icon-width: 15; color: red; opacity: 0.4; }
+node[_status=unsure]::status  { z-index: 0; icon-image: square; icon-width: 15; color: orange; opacity: 0.4; }
index 8b387e1d34dbc5b1555b24d5a28836e36e94b7a5..89fa39b31f59358d24247b552deaf27545f564c8 100644 (file)
@@ -723,6 +723,21 @@ table.browse_details th {
   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;
@@ -927,6 +942,11 @@ input[type="submit"] {
   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 {
@@ -969,3 +989,10 @@ abbr.geo {
 .table1 { 
   background: #fff;
 }
+
+/* Rules for OpenID logo */
+
+.openid_logo {
+  vertical-align: text-bottom;
+  border: 0;
+}
index f4d51c868453d43c9e9edc781b262f8d9ab908d0..353cf6fd06f65d9b2e3b43056cc2b2cb850dce3a 100644 (file)
@@ -126,16 +126,28 @@ h1 {
 
 /* Rules for the login form */
 
-#loginForm input#user_email {
+#login_login input#user_email {
   width: 100%;
   max-width: 18em;
 }
 
-#loginForm input#user_password {
+#login_login input#user_password {
   width: 100%;
   max-width: 18em;
 }
 
+#login_login input#openid_url {
+  width: 100%;
+  max-width: 18em;
+}
+
+#login_openid_buttons td {
+  padding-left: 2px;
+  padding-right: 2px;
+  padding-top: 2px;
+  padding-bottom: 2px;
+}
+
 /* Rules for the profile page */
 
 .user_map {
index 98517fd2aa86f9b24134f0ce560027349c93324a..ba66d87953e29c3349a903737d3d73c75af5c7eb 100644 (file)
@@ -84,3 +84,14 @@ terms_not_seen_user:
   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
index 8e08cbda0f9da15d8df3d46bb5896b519285a8d1..80da36deb38edb975e5753dcbf465c24fe9adf3a 100644 (file)
@@ -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
index b4ca49022ec056c8b6fbccb6b283e5a4082b3640..7003d769251c2a97dbe0578a35bf627e60255c41 100644 (file)
@@ -39,7 +39,7 @@ class UserBlocksTest < ActionController::IntegrationTest
     # 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
index 01a7ca649dab7c2d8669d20aa8d722b1382fb602..9cb5e895ccf4993f672f036d6da0352261ed6d4b 100644 (file)
@@ -3,6 +3,10 @@ require File.dirname(__FILE__) + '/../test_helper'
 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}
@@ -94,8 +98,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 +135,99 @@ 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
+
+  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
index b686fbac9132d858d6165c3ef33127418efee62a..fab05894fe924a6b55f48629c6bec2d0fa291339 100644 (file)
@@ -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 (file)
index 0000000..f76f3ca
--- /dev/null
@@ -0,0 +1,91 @@
+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
index 0691edc8ea0e763436cc5ca7e0e218ede1022d66..8bf06374fc41601ed914b0197d2d51712cc26dd4 100644 (file)
@@ -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
index 6a618c8d3e6073bf24e1f2c7bbf77fd61269a4ba..dc29287f51a0f399f7aeb4263d86391d3cae4f50 100644 (file)
@@ -3,6 +3,43 @@ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
 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
@@ -145,6 +182,44 @@ class ActiveSupport::TestCase
   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
diff --git a/vendor/gems/rots-0.2.1/.specification b/vendor/gems/rots-0.2.1/.specification
new file mode 100644 (file)
index 0000000..68c7f20
--- /dev/null
@@ -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 (file)
index 0000000..ad2dd89
--- /dev/null
@@ -0,0 +1,2 @@
+* Roman Gonzalez <romanandreg@gmail.com>
+* Anibal Rojas <anibal@rojas.net.ve>
\ 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 (file)
index 0000000..5369cc6
--- /dev/null
@@ -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 <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.
diff --git a/vendor/gems/rots-0.2.1/Rakefile b/vendor/gems/rots-0.2.1/Rakefile
new file mode 100644 (file)
index 0000000..7dd59be
--- /dev/null
@@ -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 (executable)
index 0000000..27178d8
--- /dev/null
@@ -0,0 +1,99 @@
+#!/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
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 (file)
index 0000000..644416c
--- /dev/null
@@ -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 (file)
index 0000000..09d70db
--- /dev/null
@@ -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
+<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
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 (file)
index 0000000..e08595c
--- /dev/null
@@ -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'}, 
+          ["<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
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 (file)
index 0000000..a7a91de
--- /dev/null
@@ -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 (file)
index 0000000..0de2410
--- /dev/null
@@ -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 (file)
index 0000000..f76d689
--- /dev/null
@@ -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 == "<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
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 (file)
index 0000000..3dae012
--- /dev/null
@@ -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 (file)
index 0000000..0c8971e
--- /dev/null
@@ -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 (file)
index 0000000..fe2b37e
--- /dev/null
@@ -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 %>
+    <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&hellip;" %>
+    </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
diff --git a/vendor/plugins/open_id_authentication/init.rb b/vendor/plugins/open_id_authentication/init.rb
new file mode 100644 (file)
index 0000000..84ec11f
--- /dev/null
@@ -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 (file)
index 0000000..ed875cd
--- /dev/null
@@ -0,0 +1,129 @@
+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
index 50e30c0f131fac8055ad372a780a495055a627fe..2fac6033257c89b8fe9762e19e0351aa0cb42c9d 100644 (file)
@@ -22,9 +22,8 @@ module SessionPersistence
   
   def _persist_session
     if session[session_persistence_key]
-      request.session_options = request.session_options.dup
+      request.session_options[:expires] = Time.now + session[session_persistence_key]
       request.session_options[:expire_after] = session[session_persistence_key]
-      request.session_options.freeze
     end
   end
 end