mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-25 00:24:07 +01:00
Merge branch 'master' of git://github.com/bsag/tracks
This commit is contained in:
commit
cf4ea7c2cd
241 changed files with 31052 additions and 2040 deletions
|
|
@ -6,114 +6,45 @@ class LoginController < ApplicationController
|
||||||
skip_before_filter :login_required
|
skip_before_filter :login_required
|
||||||
before_filter :login_optional
|
before_filter :login_optional
|
||||||
before_filter :get_current_user
|
before_filter :get_current_user
|
||||||
open_id_consumer if openid_enabled?
|
|
||||||
|
|
||||||
def login
|
def login
|
||||||
@page_title = "TRACKS::Login"
|
if openid_enabled? && using_open_id?
|
||||||
@openid_url = cookies[:openid_url] if openid_enabled?
|
login_openid
|
||||||
case request.method
|
else
|
||||||
when :post
|
@page_title = "TRACKS::Login"
|
||||||
if @user = User.authenticate(params['user_login'], params['user_password'])
|
case request.method
|
||||||
session['user_id'] = @user.id
|
when :post
|
||||||
# If checkbox on login page checked, we don't expire the session after 1 hour
|
if @user = User.authenticate(params['user_login'], params['user_password'])
|
||||||
# of inactivity and we remember this user for future browser sessions
|
session['user_id'] = @user.id
|
||||||
session['noexpiry'] = params['user_noexpiry']
|
# If checkbox on login page checked, we don't expire the session after 1 hour
|
||||||
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
# of inactivity and we remember this user for future browser sessions
|
||||||
notify :notice, "Login successful: session #{msg}"
|
session['noexpiry'] = params['user_noexpiry']
|
||||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
||||||
unless should_expire_sessions?
|
notify :notice, "Login successful: session #{msg}"
|
||||||
@user.remember_me
|
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
||||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
unless should_expire_sessions?
|
||||||
|
@user.remember_me
|
||||||
|
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
||||||
|
end
|
||||||
|
redirect_back_or_home
|
||||||
|
return
|
||||||
|
else
|
||||||
|
@login = params['user_login']
|
||||||
|
notify :warning, "Login unsuccessful"
|
||||||
end
|
end
|
||||||
redirect_back_or_home
|
when :get
|
||||||
return
|
if User.no_users_yet?
|
||||||
else
|
redirect_to :controller => 'users', :action => 'new'
|
||||||
@login = params['user_login']
|
return
|
||||||
notify :warning, "Login unsuccessful"
|
end
|
||||||
end
|
end
|
||||||
when :get
|
respond_to do |format|
|
||||||
if User.no_users_yet?
|
format.html
|
||||||
redirect_to :controller => 'users', :action => 'new'
|
format.m { render :action => 'login_mobile.html.erb', :layout => 'mobile' }
|
||||||
return
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
respond_to do |format|
|
|
||||||
format.html
|
|
||||||
format.m { render :action => 'login_mobile.html.erb', :layout => 'mobile' }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def begin
|
|
||||||
# If the URL was unusable (either because of network conditions,
|
|
||||||
# a server error, or that the response returned was not an OpenID
|
|
||||||
# identity page), the library will return HTTP_FAILURE or PARSE_ERROR.
|
|
||||||
# Let the user know that the URL is unusable.
|
|
||||||
case open_id_response.status
|
|
||||||
when OpenID::SUCCESS
|
|
||||||
session['openid_url'] = params[:openid_url]
|
|
||||||
session['user_noexpiry'] = params[:user_noexpiry]
|
|
||||||
# The URL was a valid identity URL. Now we just need to send a redirect
|
|
||||||
# to the server using the redirect_url the library created for us.
|
|
||||||
|
|
||||||
# redirect to the server
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), open_id_complete_url) }
|
|
||||||
format.m { redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), formatted_open_id_complete_url(:format => 'm')) }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
notify :warning, "Unable to find openid server for <q>#{openid_url}</q>"
|
|
||||||
redirect_to_login
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def complete
|
|
||||||
openid_url = session['openid_url']
|
|
||||||
if openid_url.blank?
|
|
||||||
notify :error, "expected an openid_url"
|
|
||||||
end
|
|
||||||
|
|
||||||
case open_id_response.status
|
|
||||||
when OpenID::FAILURE
|
|
||||||
# In the case of failure, if info is non-nil, it is the
|
|
||||||
# URL that we were verifying. We include it in the error
|
|
||||||
# message to help the user figure out what happened.
|
|
||||||
if open_id_response.identity_url
|
|
||||||
msg = "Verification of #{openid_url}(#{open_id_response.identity_url}) failed. "
|
|
||||||
else
|
|
||||||
msg = "Verification failed. "
|
|
||||||
end
|
|
||||||
notify :error, open_id_response.msg.to_s + msg
|
|
||||||
|
|
||||||
when OpenID::SUCCESS
|
|
||||||
# Success means that the transaction completed without
|
|
||||||
# error. If info is nil, it means that the user cancelled
|
|
||||||
# the verification.
|
|
||||||
@user = User.find_by_open_id_url(openid_url)
|
|
||||||
unless (@user.nil?)
|
|
||||||
session['user_id'] = @user.id
|
|
||||||
session['noexpiry'] = session['user_noexpiry']
|
|
||||||
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
|
||||||
notify :notice, "You have successfully verified #{openid_url} as your identity. Login successful: session #{msg}"
|
|
||||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
|
||||||
unless should_expire_sessions?
|
|
||||||
@user.remember_me
|
|
||||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
|
||||||
end
|
|
||||||
cookies[:openid_url] = { :value => openid_url, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
|
||||||
redirect_back_or_home
|
|
||||||
else
|
|
||||||
notify :warning, "You have successfully verified #{openid_url} as your identity, but you do not have a Tracks account. Please ask your administrator to sign you up."
|
|
||||||
end
|
|
||||||
|
|
||||||
when OpenID::CANCEL
|
|
||||||
notify :warning, "Verification cancelled."
|
|
||||||
|
|
||||||
else
|
|
||||||
notify :warning, "Unknown response status: #{open_id_response.status}"
|
|
||||||
end
|
|
||||||
redirect_to_login unless performed?
|
|
||||||
end
|
|
||||||
|
|
||||||
def logout
|
def logout
|
||||||
@user.forget_me if logged_in?
|
@user.forget_me if logged_in?
|
||||||
cookies.delete :auth_token
|
cookies.delete :auth_token
|
||||||
|
|
@ -156,5 +87,31 @@ class LoginController < ApplicationController
|
||||||
def should_expire_sessions?
|
def should_expire_sessions?
|
||||||
session['noexpiry'] != "on"
|
session['noexpiry'] != "on"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def login_openid
|
||||||
|
# If checkbox on login page checked, we don't expire the session after 1 hour
|
||||||
|
# of inactivity and we remember this user for future browser sessions
|
||||||
|
session['noexpiry'] ||= params['user_noexpiry']
|
||||||
|
authenticate_with_open_id do |result, identity_url|
|
||||||
|
if result.successful?
|
||||||
|
if @user = User.find_by_identity_url(identity_url)
|
||||||
|
session['user_id'] = @user.id
|
||||||
|
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
||||||
|
notify :notice, "Login successful: session #{msg}"
|
||||||
|
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
||||||
|
unless should_expire_sessions?
|
||||||
|
@user.remember_me
|
||||||
|
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
||||||
|
end
|
||||||
|
redirect_back_or_home
|
||||||
|
else
|
||||||
|
notify :warning, "Sorry, no user by that identity URL exists (#{identity_url})"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
notify :warning, result.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
|
|
||||||
if openid_enabled?
|
|
||||||
open_id_consumer
|
|
||||||
before_filter :begin_open_id_auth, :only => :update_auth_type
|
|
||||||
end
|
|
||||||
|
|
||||||
before_filter :admin_login_required, :only => [ :index, :show, :destroy ]
|
before_filter :admin_login_required, :only => [ :index, :show, :destroy ]
|
||||||
skip_before_filter :login_required, :only => [ :new, :create ]
|
skip_before_filter :login_required, :only => [ :new, :create ]
|
||||||
prepend_before_filter :login_optional, :only => [ :new, :create ]
|
prepend_before_filter :login_optional, :only => [ :new, :create ]
|
||||||
|
|
@ -153,18 +147,25 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_auth_type
|
def update_auth_type
|
||||||
if (params[:user][:auth_type] == 'open_id') && openid_enabled?
|
if (params[:open_id_complete] || (params[:user][:auth_type] == 'open_id')) && openid_enabled?
|
||||||
case open_id_response.status
|
authenticate_with_open_id do |result, identity_url|
|
||||||
when OpenID::SUCCESS
|
if result.successful?
|
||||||
# The URL was a valid identity URL. Now we just need to send a redirect
|
# Success means that the transaction completed without
|
||||||
# to the server using the redirect_url the library created for us.
|
# error. If info is nil, it means that the user cancelled
|
||||||
session['openid_url'] = params[:openid_url]
|
# the verification.
|
||||||
|
@user.auth_type = 'open_id'
|
||||||
# redirect to the server
|
@user.identity_url = identity_url
|
||||||
redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), url_for(:action => 'complete'))
|
if @user.save
|
||||||
|
notify :notice, "You have successfully verified #{identity_url} as your identity and set your authentication type to Open ID."
|
||||||
|
else
|
||||||
|
debugger
|
||||||
|
notify :warning, "You have successfully verified #{identity_url} as your identity but there was a problem saving your authentication preferences."
|
||||||
|
end
|
||||||
|
redirect_to preferences_path
|
||||||
else
|
else
|
||||||
notify :warning, "Unable to find openid server for <q>#{openid_url}</q>"
|
notify :warning, result.message
|
||||||
redirect_to :action => 'change_auth_type'
|
redirect_to :action => 'change_auth_type'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -178,47 +179,6 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def complete
|
|
||||||
return unless openid_enabled?
|
|
||||||
openid_url = session['openid_url']
|
|
||||||
if openid_url.blank?
|
|
||||||
notify :error, "expected an openid_url"
|
|
||||||
end
|
|
||||||
case open_id_response.status
|
|
||||||
when OpenID::FAILURE
|
|
||||||
# In the case of failure, if info is non-nil, it is the
|
|
||||||
# URL that we were verifying. We include it in the error
|
|
||||||
# message to help the user figure out what happened.
|
|
||||||
if open_id_response.identity_url
|
|
||||||
msg = "Verification of #{openid_url}(#{open_id_response.identity_url}) failed. "
|
|
||||||
else
|
|
||||||
msg = "Verification failed. "
|
|
||||||
end
|
|
||||||
notify :error, open_id_response.msg.to_s + msg
|
|
||||||
|
|
||||||
when OpenID::SUCCESS
|
|
||||||
# Success means that the transaction completed without
|
|
||||||
# error. If info is nil, it means that the user cancelled
|
|
||||||
# the verification.
|
|
||||||
@user.auth_type = 'open_id'
|
|
||||||
@user.open_id_url = openid_url
|
|
||||||
if @user.save
|
|
||||||
notify :notice, "You have successfully verified #{openid_url} as your identity and set your authentication type to Open ID."
|
|
||||||
else
|
|
||||||
notify :warning, "You have successfully verified #{openid_url} as your identity but there was a problem saving your authentication preferences."
|
|
||||||
end
|
|
||||||
redirect_to preferences_path
|
|
||||||
|
|
||||||
when OpenID::CANCEL
|
|
||||||
notify :warning, "Verification cancelled."
|
|
||||||
|
|
||||||
else
|
|
||||||
notify :warning, "Unknown response status: #{open_id_response.status}"
|
|
||||||
end
|
|
||||||
redirect_to :action => 'change_auth_type' unless performed?
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def refresh_token
|
def refresh_token
|
||||||
@user.generate_token
|
@user.generate_token
|
||||||
@user.save!
|
@user.save!
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ module ApplicationHelper
|
||||||
|
|
||||||
# Replicates the link_to method but also checks request.request_uri to find
|
# Replicates the link_to method but also checks request.request_uri to find
|
||||||
# current page. If that matches the url, the link is marked id = "current"
|
# current page. If that matches the url, the link is marked id = "current"
|
||||||
#
|
#
|
||||||
def navigation_link(name, options = {}, html_options = nil, *parameters_for_method_reference)
|
def navigation_link(name, options = {}, html_options = nil, *parameters_for_method_reference)
|
||||||
if html_options
|
if html_options
|
||||||
html_options = html_options.stringify_keys
|
html_options = html_options.stringify_keys
|
||||||
|
|
@ -29,7 +29,7 @@ module ApplicationHelper
|
||||||
|
|
||||||
# Check due date in comparison to today's date Flag up date appropriately with
|
# Check due date in comparison to today's date Flag up date appropriately with
|
||||||
# a 'traffic light' colour code
|
# a 'traffic light' colour code
|
||||||
#
|
#
|
||||||
def due_date(due)
|
def due_date(due)
|
||||||
if due == nil
|
if due == nil
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -62,7 +62,7 @@ module ApplicationHelper
|
||||||
|
|
||||||
# Check due date in comparison to today's date Flag up date appropriately with
|
# Check due date in comparison to today's date Flag up date appropriately with
|
||||||
# a 'traffic light' colour code Modified method for mobile screen
|
# a 'traffic light' colour code Modified method for mobile screen
|
||||||
#
|
#
|
||||||
def due_date_mobile(due)
|
def due_date_mobile(due)
|
||||||
if due == nil
|
if due == nil
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -92,7 +92,7 @@ module ApplicationHelper
|
||||||
# Returns a count of next actions in the given context or project. The result
|
# Returns a count of next actions in the given context or project. The result
|
||||||
# is count and a string descriptor, correctly pluralised if there are no
|
# is count and a string descriptor, correctly pluralised if there are no
|
||||||
# actions or multiple actions
|
# actions or multiple actions
|
||||||
#
|
#
|
||||||
def count_undone_todos_phrase(todos_parent, string="actions")
|
def count_undone_todos_phrase(todos_parent, string="actions")
|
||||||
@controller.count_undone_todos_phrase(todos_parent, string)
|
@controller.count_undone_todos_phrase(todos_parent, string)
|
||||||
end
|
end
|
||||||
|
|
@ -143,5 +143,31 @@ module ApplicationHelper
|
||||||
page.replace 'flash', "<h4 id='flash' class='alert #{type}'>#{message}</h4>"
|
page.replace 'flash', "<h4 id='flash' class='alert #{type}'>#{message}</h4>"
|
||||||
page.visual_effect :fade, 'flash', :duration => fade_duration
|
page.visual_effect :fade, 'flash', :duration => fade_duration
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recurrence_time_span(rt)
|
||||||
|
case rt.ends_on
|
||||||
|
when "no_end_date"
|
||||||
|
return rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
|
||||||
|
when "ends_on_number_of_times"
|
||||||
|
return "for "+rt.number_of_occurences.to_s + " times"
|
||||||
|
when "ends_on_end_date"
|
||||||
|
starts = rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
|
||||||
|
ends = rt.end_date.nil? ? "" : " until " + format_date(rt.end_date)
|
||||||
|
return starts+ends
|
||||||
|
else
|
||||||
|
raise Exception.new, "unknown recurrence time span selection (#{rt.ends_on})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def recurrence_pattern_as_text(recurring_todo)
|
||||||
|
rt = recurring_todo.recurring_target_as_text
|
||||||
|
rp = recurring_todo.recurrence_pattern
|
||||||
|
# only add space if recurrence_pattern has content
|
||||||
|
rp = " " + rp if !rp.nil?
|
||||||
|
rts = recurrence_time_span(recurring_todo)
|
||||||
|
# only add space if recurrence_time_span has content
|
||||||
|
rts = " " + rts if !(rts == "")
|
||||||
|
return rt+rp+rts
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,4 @@
|
||||||
module RecurringTodosHelper
|
module RecurringTodosHelper
|
||||||
|
|
||||||
def recurrence_time_span(rt)
|
|
||||||
case rt.ends_on
|
|
||||||
when "no_end_date"
|
|
||||||
return rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
|
|
||||||
when "ends_on_number_of_times"
|
|
||||||
return "for "+rt.number_of_occurences.to_s + " times"
|
|
||||||
when "ends_on_end_date"
|
|
||||||
starts = rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
|
|
||||||
ends = rt.end_date.nil? ? "" : " until " + format_date(rt.end_date)
|
|
||||||
return starts+ends
|
|
||||||
else
|
|
||||||
raise Exception.new, "unknown recurrence time span selection (#{self.ends_on})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def recurrence_target(rt)
|
|
||||||
case rt.target
|
|
||||||
when 'due_date'
|
|
||||||
return "due"
|
|
||||||
when 'show_from_date'
|
|
||||||
return "show"
|
|
||||||
else
|
|
||||||
return "ERROR"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def recurring_todo_tag_list
|
def recurring_todo_tag_list
|
||||||
tags_except_starred = @recurring_todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
|
tags_except_starred = @recurring_todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,17 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
def recurring_target=(t)
|
def recurring_target=(t)
|
||||||
self.target = t
|
self.target = t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recurring_target_as_text
|
||||||
|
case self.target
|
||||||
|
when 'due_date'
|
||||||
|
return "due"
|
||||||
|
when 'show_from_date'
|
||||||
|
return "show"
|
||||||
|
else
|
||||||
|
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def recurring_show_days_before=(days)
|
def recurring_show_days_before=(days)
|
||||||
self.show_from_delta=days
|
self.show_from_delta=days
|
||||||
|
|
@ -361,6 +372,8 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
when 'show_from'
|
when 'show_from'
|
||||||
# so leave due date empty
|
# so leave due date empty
|
||||||
return nil
|
return nil
|
||||||
|
else
|
||||||
|
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -476,14 +489,14 @@ class RecurringTodo < ActiveRecord::Base
|
||||||
when 0 # specific day of the month
|
when 0 # specific day of the month
|
||||||
if start.mday >= day
|
if start.mday >= day
|
||||||
# there is no next day n in this month, search in next month
|
# there is no next day n in this month, search in next month
|
||||||
#
|
#
|
||||||
# start += n.months
|
# start += n.months
|
||||||
#
|
#
|
||||||
# The above seems to not work. Fiddle with timezone. Looks like we hit a
|
# The above seems to not work. Fiddle with timezone. Looks like we hit a
|
||||||
# bug in rails here where 2008-12-01 +0100 plus 1.month becomes
|
# bug in rails here where 2008-12-01 +0100 plus 1.month becomes
|
||||||
# 2008-12-31 +0100. For now, just calculate in UTC and convert back to
|
# 2008-12-31 +0100. For now, just calculate in UTC and convert back to
|
||||||
# local timezone.
|
# local timezone.
|
||||||
#
|
#
|
||||||
# TODO: recheck if future rails versions have this problem too
|
# TODO: recheck if future rails versions have this problem too
|
||||||
start = Time.utc(start.year, start.month, start.day)+n.months
|
start = Time.utc(start.year, start.month, start.day)+n.months
|
||||||
start = Time.zone.local(start.year, start.month, start.day)
|
start = Time.zone.local(start.year, start.month, start.day)
|
||||||
|
|
|
||||||
|
|
@ -116,11 +116,11 @@ class User < ActiveRecord::Base
|
||||||
validates_confirmation_of :password
|
validates_confirmation_of :password
|
||||||
validates_length_of :login, :within => 3..80
|
validates_length_of :login, :within => 3..80
|
||||||
validates_uniqueness_of :login, :on => :create
|
validates_uniqueness_of :login, :on => :create
|
||||||
validates_presence_of :open_id_url, :if => :using_openid?
|
validates_presence_of :identity_url, :if => :using_openid?
|
||||||
|
|
||||||
before_create :crypt_password, :generate_token
|
before_create :crypt_password, :generate_token
|
||||||
before_update :crypt_password
|
before_update :crypt_password
|
||||||
before_save :normalize_open_id_url
|
before_save :normalize_identity_url
|
||||||
|
|
||||||
#for will_paginate plugin
|
#for will_paginate plugin
|
||||||
cattr_accessor :per_page
|
cattr_accessor :per_page
|
||||||
|
|
@ -145,9 +145,9 @@ class User < ActiveRecord::Base
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_by_open_id_url(raw_open_id_url)
|
def self.find_by_identity_url(raw_identity_url)
|
||||||
normalized_open_id_url = normalize_open_id_url(raw_open_id_url)
|
normalized_identity_url = OpenIdAuthentication.normalize_url(raw_identity_url)
|
||||||
find(:first, :conditions => ['open_id_url = ?', normalized_open_id_url])
|
find(:first, :conditions => ['identity_url = ?', normalized_identity_url])
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.no_users_yet?
|
def self.no_users_yet?
|
||||||
|
|
@ -235,15 +235,8 @@ protected
|
||||||
crypted_password == sha1(pass)
|
crypted_password == sha1(pass)
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalize_open_id_url
|
def normalize_identity_url
|
||||||
return if open_id_url.nil?
|
return if identity_url.nil?
|
||||||
self.open_id_url = self.class.normalize_open_id_url(open_id_url)
|
self.identity_url = OpenIdAuthentication.normalize_url(identity_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.normalize_open_id_url(raw_open_id_url)
|
|
||||||
normalized = raw_open_id_url
|
|
||||||
normalized = "http://#{raw_open_id_url}" unless raw_open_id_url =~ /\:\/\//
|
|
||||||
normalized.downcase.chomp('/')
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
<% if show_openid_form %>
|
<% if show_openid_form %>
|
||||||
<div id="openid_auth_form" style="display:none">
|
<div id="openid_auth_form" style="display:none">
|
||||||
<% form_tag :action=> 'login', :action => 'begin' do %>
|
<% form_tag :action=> 'login' do %>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="openid_url">Identity URL:</label></td>
|
<td><label for="openid_url">Identity URL:</label></td>
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,7 @@
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<span class="todo.descr"><%= sanitize(recurring_todo.description) %></span> <%= recurring_todo_tag_list %>
|
<span class="todo.descr"><%= sanitize(recurring_todo.description) %></span> <%= recurring_todo_tag_list %>
|
||||||
<span class='recurrence_pattern'>
|
<span class='recurrence_pattern'>
|
||||||
<%
|
[<%= recurrence_pattern_as_text(@recurring_todo) %>]
|
||||||
rt = recurrence_target(recurring_todo)
|
|
||||||
rp = recurring_todo.recurrence_pattern
|
|
||||||
# only add space if recurrence_pattern has content
|
|
||||||
rp = " " + rp if !rp.nil?
|
|
||||||
rts = recurrence_time_span(recurring_todo)
|
|
||||||
# only add space if recurrence_time_span has content
|
|
||||||
rts = " " + rts if !(rts == "")
|
|
||||||
%>
|
|
||||||
[<%=rt%><%=rp%><%=rts%>]
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@
|
||||||
<% unless @todo.completed? %><span class="defer-container"><%= defer_link(1) %> <%= defer_link(7) %></span><% end %>
|
<% unless @todo.completed? %><span class="defer-container"><%= defer_link(1) %> <%= defer_link(7) %></span><% end %>
|
||||||
<%= date_span -%>
|
<%= date_span -%>
|
||||||
<span class="todo.descr"><%= h sanitize(todo.description) %></span>
|
<span class="todo.descr"><%= h sanitize(todo.description) %></span>
|
||||||
<%= link_to(image_tag("recurring16x16.png"), {:controller => "recurring_todos", :action => "index"}, :class => "recurring_icon") if @todo.from_recurring_todo? %>
|
<%= link_to(
|
||||||
|
image_tag("recurring16x16.png"),
|
||||||
|
{:controller => "recurring_todos", :action => "index"},
|
||||||
|
:class => "recurring_icon", :title => recurrence_pattern_as_text(@todo.recurring_todo)) if @todo.from_recurring_todo? %>
|
||||||
<%= tag_list %>
|
<%= tag_list %>
|
||||||
<%= deferred_due_date %>
|
<%= deferred_due_date %>
|
||||||
<%= project_and_context_links( parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %>
|
<%= project_and_context_links( parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %>
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ ActionController::Routing::Routes.draw do |map|
|
||||||
login.formatted_login 'login.:format', :action => 'login'
|
login.formatted_login 'login.:format', :action => 'login'
|
||||||
login.logout 'logout', :action => 'logout'
|
login.logout 'logout', :action => 'logout'
|
||||||
login.formatted_logout 'logout.:format', :action => 'logout'
|
login.formatted_logout 'logout.:format', :action => 'logout'
|
||||||
login.open_id_begin 'begin', :action => 'begin'
|
|
||||||
login.formatted_open_id_begin 'begin.:format', :action => 'begin'
|
|
||||||
login.open_id_complete 'complete', :action => 'complete'
|
|
||||||
login.formatted_open_id_complete 'complete.:format', :action => 'complete'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
map.resources :users,
|
map.resources :users,
|
||||||
|
|
@ -57,6 +53,7 @@ ActionController::Routing::Routes.draw do |map|
|
||||||
todos.mobile_abbrev 'm', :action => "index", :format => 'm'
|
todos.mobile_abbrev 'm', :action => "index", :format => 'm'
|
||||||
todos.mobile_abbrev_new 'm/new', :action => "new", :format => 'm'
|
todos.mobile_abbrev_new 'm/new', :action => "new", :format => 'm'
|
||||||
end
|
end
|
||||||
|
map.root :controller => 'todos' # Make OpenID happy because it needs #root_url defined
|
||||||
|
|
||||||
map.resources :notes
|
map.resources :notes
|
||||||
map.feeds 'feeds', :controller => 'feedlist', :action => 'index'
|
map.feeds 'feeds', :controller => 'feedlist', :action => 'index'
|
||||||
|
|
|
||||||
47
db/migrate/044_upgrade_open_id.rb
Normal file
47
db/migrate/044_upgrade_open_id.rb
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
class UpgradeOpenId < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :open_id_authentication_associations, :force => true do |t|
|
||||||
|
t.integer :issued, :lifetime
|
||||||
|
t.string :handle, :assoc_type
|
||||||
|
t.binary :server_url, :secret
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :open_id_authentication_nonces, :force => true do |t|
|
||||||
|
t.integer :timestamp, :null => false
|
||||||
|
t.string :server_url, :null => true
|
||||||
|
t.string :salt, :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_column :users, :identity_url, :string
|
||||||
|
|
||||||
|
drop_table :open_id_associations
|
||||||
|
drop_table :open_id_nonces
|
||||||
|
drop_table :open_id_settings
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :open_id_authentication_associations
|
||||||
|
drop_table :open_id_authentication_nonces
|
||||||
|
|
||||||
|
create_table "open_id_associations", :force => true do |t|
|
||||||
|
t.binary "server_url"
|
||||||
|
t.string "handle"
|
||||||
|
t.binary "secret"
|
||||||
|
t.integer "issued"
|
||||||
|
t.integer "lifetime"
|
||||||
|
t.string "assoc_type"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "open_id_nonces", :force => true do |t|
|
||||||
|
t.string "nonce"
|
||||||
|
t.integer "created"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "open_id_settings", :force => true do |t|
|
||||||
|
t.string "setting"
|
||||||
|
t.binary "value"
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_column :users, :identity_url
|
||||||
|
end
|
||||||
|
end
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,293 +0,0 @@
|
||||||
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {margin:0; padding:0}
|
|
||||||
table {border-collapse:collapse; border-spacing:0}
|
|
||||||
fieldset,img {border:0}
|
|
||||||
address,caption,cite,code,dfn,em,strong,th,var {font-style:normal; font-weight:normal}
|
|
||||||
ol,ul {list-style:none}
|
|
||||||
caption,th {text-align:left}
|
|
||||||
h1,h2,h3,h4,h5,h6 {font-size:100%; font-weight:normal}
|
|
||||||
q:before,q:after {content:''}
|
|
||||||
abbr,acronym {border:0}
|
|
||||||
body {font-family: "Lucida Grande", Verdana, Geneva, Arial, sans-serif; font-size: 80%; padding: 0px 10px; margin: 0px; background: #eee}
|
|
||||||
p {padding: 2px; font-size: 92%; line-height: 140%}
|
|
||||||
a, a:link, a:active, a:visited {color: #cc3334; text-decoration: none; padding-left: 1px; padding-right: 1px}
|
|
||||||
a:hover {color: #fff; background-color: #cc3334}
|
|
||||||
h1 {font-size: 304%; font-weight: bold}
|
|
||||||
h2 {font-size: 148%; font-weight: bold}
|
|
||||||
h3 {font-size: 129%; font-weight: bold}
|
|
||||||
img.edit_item {background-image: url(../images/edit_off.png); background-repeat: no-repeat; border: none;}
|
|
||||||
a:hover img.edit_item {background-image: url(../images/edit_on.png); background-color: transparent; background-repeat: no-repeat; border: none;}
|
|
||||||
img.delete_item {background-image: url(../images/delete_off.png); background-repeat: no-repeat; border: none;}
|
|
||||||
a:hover img.delete_item {background-image: url(../images/delete_on.png);background-color: transparent;background-repeat: no-repeat; border: none;}
|
|
||||||
img.starred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: 0px 0px;}
|
|
||||||
a:hover img.starred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -16px 0px;}
|
|
||||||
img.unstarred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -32px 0px;}
|
|
||||||
a:hover img.unstarred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -48px 0px;}
|
|
||||||
a.to_top {background: transparent url(../images/top_off.png) no-repeat;}
|
|
||||||
a.to_top:hover {background: transparent url(../images/top_on.png) no-repeat;}
|
|
||||||
a.up {background: transparent url(../images/up_off.png) no-repeat;}
|
|
||||||
a.up:hover {background: transparent url(../images/up_on.png) no-repeat;}
|
|
||||||
a.down {background: transparent url(../images/down_off.png) no-repeat;}
|
|
||||||
a.down:hover {background: transparent url(../images/down_on.png) no-repeat;}
|
|
||||||
a.to_bottom {background: transparent url(../images/bottom_off.png) no-repeat;}
|
|
||||||
a.to_bottom:hover {background: transparent url(../images/bottom_on.png) no-repeat;}
|
|
||||||
a.show_notes, a.link_to_notes {background-image: url(../images/notes_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
|
||||||
a.show_notes:hover, a.link_to_notes:hover {background-image: url(../images/notes_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
|
||||||
#content {margin-top: 90px}
|
|
||||||
#display_box {float: left; width: 55%; margin: 0px 10px 50px 15px}
|
|
||||||
#single_box {width: 60%; margin: 80px auto}
|
|
||||||
#full_width_display {float: left; width: 95%; margin: 0px 15px 90px 15px}
|
|
||||||
#display_box_projects {float: left; width: 95%; margin: 0px 15px 90px 15px}
|
|
||||||
#recurring_timespan, #recurring_target {border: none; clear: both}
|
|
||||||
#recurring_target {border-top-style: dotted; padding: 15px 0px 0px 0px}
|
|
||||||
#recurring_daily, #recurring_weekly, #recurring_monthly, #recurring_yearly, #recurring_edit_daily, #recurring_edit_weekly, #recurring_edit_monthly, #recurring_edit_yearly {border: none; border-top-style: dotted; clear: both; padding: 15px 0px 15px 0px}
|
|
||||||
#recurring_period_id, #recurring_edit_period_id {border: none; float: left; margin: 0px 50px 0px 0px; padding: 0px 25px 15px 50px; border-right-style: none}
|
|
||||||
#recurring_todo {width: 270px}
|
|
||||||
#recurring_todo_form_container {border-right-style: dotted; padding: 0px 50px 15px 0px; float: left}
|
|
||||||
#recurring_todo input, #recurring_todo textarea {width: 100%}
|
|
||||||
#overlay {visibility: hidden; position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; z-index: 102; text-align: center; background-image:url("../images/trans70.png")}
|
|
||||||
#overlay #new-recurring-todo, #overlay #edit-recurring-todo {width:750px; background-color: #fff; border:1px solid #000; padding: 15px; margin: 70px auto}
|
|
||||||
.recurring_container {padding: 0px 5px 0px 5px; border: 1px solid #999; margin: 0px 0px 0px 0px; background: #fff; text-align: left}
|
|
||||||
.recurring_submit_box {height: 25px; padding: 5px 0; text-align: center; clear: both; border: none}
|
|
||||||
#navcontainer {position: fixed; top: 48px; left: 0px}
|
|
||||||
#navlist {margin: 0; padding: 0 0 20px 5px}
|
|
||||||
#navlist ul, #navlist li {margin: 0; padding: 0; display: inline; list-style-type: none}
|
|
||||||
#navlist a:link, #navlist a:visited {float: left; line-height: 14px; font-weight: bold; margin: 0 10px 4px 10px; text-decoration: none; color: #eee}
|
|
||||||
#navlist a:link#current, #navlist a:visited#current, #navlist a:hover {border-bottom: 4px solid #CCC; padding-bottom: 2px; background: transparent; color: #CCC}
|
|
||||||
#navlist a:hover {color: #CCC}
|
|
||||||
#topbar {position: fixed; top: 0px; left: 0px; height: 68px; margin-bottom: 20px; clear: both; background-color: #000; filter: alpha(opacity=75); -moz-opacity: .75; opacity: .75; color: #eee; width: 100%; z-index:101}
|
|
||||||
body.stats #topbar {filter: alpha(opacity=100); -moz-opacity: 1; opacity: 1}
|
|
||||||
#date {float: left; width: 45%; padding-left: 15px; margin-top: 15px; margin-bottom: 5px; white-space: nowrap}
|
|
||||||
#date h1 {font-size: 152%}
|
|
||||||
#minilinks {text-align: right; position: fixed; right: 15px; top: 10px; font-size: 0.9em}
|
|
||||||
.container {padding: 0px 5px 0px 5px; border: 1px solid #999; margin: 0px 0px 15px 0px; background: #fff}
|
|
||||||
.completed {background: #eee}
|
|
||||||
.container h2 {background: #ccc; padding: 5px; margin-top: 0px; margin-left: -5px; margin-right: -5px; margin-bottom: 0px; color: #666; position:static}
|
|
||||||
.container_toggle img {height:20px; width:20px; border:0px}
|
|
||||||
h2 a, h2 a:link, h2 a:active, h2 a:visited {color: #666; text-decoration: none}
|
|
||||||
h2 a:hover {color: #cc3334; background-color: transparent; text-decoration: none}
|
|
||||||
div#input_box {margin: 0px 15px 0px 58%; padding: 0px 15px 0px 0px}
|
|
||||||
#input_box h2 {color: #999}
|
|
||||||
#input_box ul {list-style-type: circle; font-size: 0.9em;}
|
|
||||||
.show_from_input, .due_input, .project_input, .context_input {float:left}
|
|
||||||
.box {float: left; width: 20px}
|
|
||||||
div.item-container {padding: 2px 0px; line-height:20px; clear: both}
|
|
||||||
a.recurring_icon {vertical-align: middle; background-color: transparent}
|
|
||||||
a.icon {float: left; vertical-align: middle; background-color: transparent}
|
|
||||||
input.item-checkbox {float: left; margin-left: 10px; vertical-align: middle}
|
|
||||||
.description {margin-left: 85px; position:relative }
|
|
||||||
.stale_l1, .stale_l2, .stale_l3 {margin-left: 82px; padding-left: 3px}
|
|
||||||
.stale_l1 {background: #ffC}
|
|
||||||
.tools {margin-left: 25px; width: 40px; border-top: 1px solid #999}
|
|
||||||
#footer {clear: both; font-size: 85%; text-align: center; color: #999; margin: 20px 20px 5px 20px; padding: 0px}
|
|
||||||
.todo_notes {margin: 5px; padding: 3px; border: 1px solid #F5ED59; background: #FAF6AE; color: #666666}
|
|
||||||
.todo_notes p, .todo_notes li {padding: 1px; margin: 0px; font-size: 12px}
|
|
||||||
.todo_notes ul, .note_wrapper ul {list-style-type: disc; margin-left:20px}
|
|
||||||
.todo_notes ol {list-style-type: decimal; margin-left:20px}
|
|
||||||
div.note_wrapper {margin: 3px; padding: 2px}
|
|
||||||
div.note_wrapper p {display: inline}
|
|
||||||
div.note_footer {border-top: 1px solid #999; padding-top: 3px; font-style: italic; font-size: 0.9em; color: #666}
|
|
||||||
div.note_footer a, div.note_footer a:hover {border-top: none; padding-top: 0px; vertical-align: middle; background-color: transparent}
|
|
||||||
div.add_note_link {margin-top:12px; float: right}
|
|
||||||
div#project_status > div {padding: 10px}
|
|
||||||
#project_status span {margin-right:5px; background-color:white}
|
|
||||||
#project_status .active_state {font-weight:bold}
|
|
||||||
div#default_context > div{ padding:10px}
|
|
||||||
a.footer_link {color: #cc3334; font-style: normal;}
|
|
||||||
a.footer_link:hover {color: #fff; background-color: #cc3334 !important;}
|
|
||||||
span.tag {font-size: 0.8em; background-color: #CCE7FF; color: #000; padding: 1px; margin-right: 2px}
|
|
||||||
span.tag a, span.tag a:link, span.tag a:active, span.tag a:visited {color: #000}
|
|
||||||
span.tag a:hover {background-color: #99CCFF; color: #333}
|
|
||||||
div#message_holder {position: absolute; z-index: 100; left: 60%; top: 30px; right: 0px; margin: 0px}
|
|
||||||
h4.alert {font-size: 1em; margin: 0px; padding: 5px; text-align: center}
|
|
||||||
h4.warning {border: 1px solid #ED2E38; background-color: #F6979C; color: #000}
|
|
||||||
h4.error {color:#fff; background:#c00}
|
|
||||||
h4.notice {border: 1px solid #007E00; background-color: #c2ffc2; color: #007E00}
|
|
||||||
.project_completed {border: 1px solid #007E00; background-color: #c2ffc2; padding: 5px; color: #007E00; text-align: center}
|
|
||||||
.red {color: #fff; background: #f00; padding: 1px; font-size: 85%}
|
|
||||||
.amber {color: #fff; background: #ff6600; padding: 1px; font-size: 85%}
|
|
||||||
.orange {color: #fff; background: #FFA500; padding: 1px; font-size: 85%}
|
|
||||||
.green {color: #fff; background: #33cc00; padding: 1px; font-size: 85%}
|
|
||||||
.grey {color: #fff; background: #999; padding: 2px; font-size: 85%}
|
|
||||||
.info {color: #fff; background: #CCC; border: 1px solid #999; padding: 5px; text-align: center}
|
|
||||||
.highlight {background: #ffC; padding: 2px}
|
|
||||||
.stale_l1 {background: #ffC}
|
|
||||||
.stale_l2 {background: #ff6}
|
|
||||||
.stale_l3 {background: #ff0}
|
|
||||||
.badge {color: #fff; background: #f00; padding: 3px 5px; font-size: 12pt; margin: 10px 10px 0px 0px; height:26px}
|
|
||||||
ul {list-style-type: none}
|
|
||||||
#sidebar h3 {margin-top:15px; margin-bottom:5px}
|
|
||||||
#sidebar ul {margin-left: auto; list-style-position: inside}
|
|
||||||
li {font-size: 1.1em; padding: 3px 0px}
|
|
||||||
#sidebar .integrations-link {margin-top:10px; padding-top:10px; font-size: 0.8em}
|
|
||||||
.sortable_row {background: #fff; _background: transparent; padding: 4px 4px 4px 8px; margin: 2px 2px; border: 1px solid #ccc}
|
|
||||||
.edit-form {background: #ccc; padding: 0 10px; border-top: 1px solid #999; border-bottom: 1px solid #999; position:relative}
|
|
||||||
.label {text-align: right}
|
|
||||||
input {vertical-align: middle}
|
|
||||||
img.position, a:hover img.position {text-align: left; vertical-align: middle; background-color: transparent}
|
|
||||||
.data {text-align: left; margin-left: 20px; float: left}
|
|
||||||
div.buttons, div.buttons a, div.buttons a:hover {text-align: right; margin-right: 0px; vertical-align: middle; background-color: transparent}
|
|
||||||
div#list-active-projects, div#list-hidden-projects, div#list-completed-projects, div#list-contexts, div#projects-empty-nd {clear:right; border: 1px solid #999}
|
|
||||||
.project-state-group h2 {margin:20px 0px 8px 13px}
|
|
||||||
.search-result-group h2 {margin:20px 0px 8px 13px }
|
|
||||||
div.menu_sort {margin-top:-20px; float:right}
|
|
||||||
div.alpha_sort, div.tasks_sort,span.sort_separator {float:left}
|
|
||||||
.container td {border: none; padding-bottom: 5px}
|
|
||||||
.container form {border: none}
|
|
||||||
div.project_description {background: #eee; padding: 5px; margin-top: 0px; margin-left: -5px; margin-right: -5px; color: #666; font-style: italic; font-size: 12px; font-weight: normal}
|
|
||||||
#project-next-prev {text-align:right}
|
|
||||||
form {border: 1px solid #CCC; padding: 10px; margin: 0px}
|
|
||||||
.inline-form {border: none; padding: 3px}
|
|
||||||
.inline-form table {padding-right: 3px}
|
|
||||||
.inline-form table, .inline-form textarea#item_notes, .inline-form input#item_description {width: 100%}
|
|
||||||
.inline-form table td.label {width: 13ex}
|
|
||||||
#todo_new_action_container, #project_new_project_container, #context_new_container, #recurring_new_container {background: #ddd; width: 270px; padding: 5px 10px; background-color: #000; filter: alpha(opacity=75); -moz-opacity: .75; opacity: .75; color: #eee}
|
|
||||||
#recurring_new_container img {vertical-align: middle}
|
|
||||||
#project_new_project_filler {padding-top: 50px}
|
|
||||||
#todo_new_action_container input, #todo_new_action_container textarea, #project_new_project_container input, #project_new_project_container textarea, #context_new_container input {width: 100%}
|
|
||||||
input#go_to_project, input#context_hide {width: 5%}
|
|
||||||
#todo_new_action_container .show_from_input, #todo_new_action_container .due_input {width: 45%}
|
|
||||||
#todo_new_action_container .show_from_input {float: right}
|
|
||||||
#todo-form-new-action .submit_box, #project_form .submit_box, #context_form .submit_box {height: 25px; padding: 5px 0; text-align: center; clear: right}
|
|
||||||
.edit_todo_form .submit_box {height: 25px; padding: 5px 0; text-align: center; clear: right}
|
|
||||||
.edit_todo_form input, .edit_todo_form textarea {width:100%}
|
|
||||||
.edit_todo_form .Date {width:89%}
|
|
||||||
.edit_todo_form a.date_clear:hover {background: #CCCCCC}
|
|
||||||
.edit_todo_form .tag_list_label {clear:both}
|
|
||||||
.edit_todo_form .due_input, .edit_todo_form .show_from_input, .edit_todo_form .project_input, .edit_todo_form .context_input {width:48%}
|
|
||||||
.edit_todo_form .show_from_input, .edit_todo_form .context_input {float: right}
|
|
||||||
.edit_todo_form .submit_box input {width:120px}
|
|
||||||
.hide_form {text-align:right}
|
|
||||||
#todo-form-new-action label, .edit_todo_form label {display: block; padding-bottom: 3px}
|
|
||||||
form.button-to {border: none; padding: 0px; margin: 0px}
|
|
||||||
label {font-weight: bold; padding: 0px 0px}
|
|
||||||
input, select, textarea {margin: 0px 0px 5px 0px}
|
|
||||||
#feedicons-project, #feedicons-context {background-color: #D2D3D6; margin: 0px 0px 0px 60px; padding: 0px 0px 0px 5px; width: 70%}
|
|
||||||
.feed {font-family: verdana, sans-serif; font-size: 10px; font-weight:bold; text-decoration:none; color: white; background-color: #F60; border:1px solid; border-color: #FC9 #630 #330 #F96; padding:0px 3px 0px 3px; margin:0px}
|
|
||||||
.position {float: left; margin-top:2px}
|
|
||||||
.handle {color: #fff; background: #000; padding: 2px; font-size: 92%; cursor: move}
|
|
||||||
div.message {margin: 5px 0px; background: #FAF4B5; padding: 2px}
|
|
||||||
.message p {margin: 0px; padding: 0px; text-align: center; font-size: 1em; color: #666}
|
|
||||||
.fieldWithErrors {padding: 2px; background-color: red; display: table}
|
|
||||||
#errorExplanation {border: 2px solid #ff0000; padding: 7px; padding-bottom: 12px; margin: 10px auto 20px auto; background-color: #f0f0f0}
|
|
||||||
#errorExplanation h2 {text-align: left; font-weight: bold; padding: 5px 5px 5px 15px; font-size: 12px; margin: -7px; background-color: #c00; color: #fff}
|
|
||||||
#errorExplanation > p {color: #333; margin-top: 5px; margin-bottom: 0; padding: 5px}
|
|
||||||
#errorExplanation ul li {font-size: 1em; list-style-type: disc; list-style-position: inside; margin-left:7px; color: #333}
|
|
||||||
ul#prefs {list-style-type: disc; margin-left: 15px;}
|
|
||||||
#token_area, #authentication_area {text-align:center; margin-top:20px; margin-bottom:10px}
|
|
||||||
#token_area .description{ font-weight:bold}
|
|
||||||
#token_area form {width:100%; text-align:center}
|
|
||||||
.prefscontainer .actions {text-align:center; margin-bottom:20px}
|
|
||||||
.authtype_container .actions {margin-top:20px; margin-bottom:20px}
|
|
||||||
#feedlegend {padding: 2px; background-color: #D2D3D6; color: #666; padding: 5px 20px; text-align: left}
|
|
||||||
#feedlegend h3, #feedlegend dl, #feedlegend dt, #feedlegend dd {display: inline}
|
|
||||||
#feedlegend dt {margin-left: 15px}
|
|
||||||
#feedlegend dd {margin-left: 3px}
|
|
||||||
#feedlegend p {margin-bottom: 0px}
|
|
||||||
#feeds img.rss-icon {margin-bottom: -4px}
|
|
||||||
#feeds li {font-size:13px; font-family: "Lucida Grande", Verdana, Geneva, Arial, sans-serif}
|
|
||||||
#feeds li h4 {margin-top: 12px; margin-bottom: 4px; font-weight: normal; font-style:oblique}
|
|
||||||
input.open_id {background: url(../images/open-id-login-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; color: #000; padding-left: 18px; width:182px}
|
|
||||||
div.page_name_auto_complete {width: 100%; background: #fff; display: inline; z-index: 100}
|
|
||||||
div.page_name_auto_complete ul {border: 1px solid #888; margin: 0; padding: 0; width: 100%; list-style-type: none}
|
|
||||||
div.page_name_auto_complete ul li {margin: 0; padding: 2px; font-weight:bold; list-style-type: none; color: #000}
|
|
||||||
div.page_name_auto_complete ul li.selected {background-color: #ffb}
|
|
||||||
div.page_name_auto_complete ul strong.highlight {color: #800; margin: 0; padding: 0}
|
|
||||||
table.next_actions td {padding:5px 3px 2px 0px}
|
|
||||||
table.users_table {width: 100%; text-align: center; border: 1px solid #666; background-color: #fff; border-spacing: 0px}
|
|
||||||
.users_table th {color: #fff; background-color: #000;}
|
|
||||||
.users_table td {border: none;}
|
|
||||||
table.export_table {border: 1px solid #666; background-color: #fff; border-spacing: 0px}
|
|
||||||
.export_table th {color: #fff; background-color: #000;}
|
|
||||||
.export_table td {border: 1px; padding: 5px 0px 5px 5px;}
|
|
||||||
.widgets a, .widgets button{ display:block; float: left; margin:0px 7px 0 0; background-color:#f5f5f5; border:1px solid #dedede; border-top:1px solid #eee; border-left:1px solid #eee; font-family:"Lucida Grande", Verdana, Geneva, Arial, sans-serif; font-size:80%; line-height:100%; text-decoration:none; font-weight:bold; color:#565656; cursor:pointer; padding:3px 5px 7px 5px}
|
|
||||||
.widgets button{ width:auto; overflow:visible; padding:3px 5px 5px 5px}
|
|
||||||
.widgets button[type]{ padding:3px 5px 4px 5px; line-height:15px}
|
|
||||||
.widgets button img, .widgets a img{ margin:0 3px -3px 0 !important; padding:0; border:none; width:16px; height:16px}
|
|
||||||
.widgets a:hover{ background-color:#dff4ff; border:1px solid #c2e1ef; color:#336699}
|
|
||||||
.widgets a:active{ background-color:#6299c5; border:1px solid #6299c5; color:#fff}
|
|
||||||
button.positive, .widgets a.positive{ color: #498111}
|
|
||||||
.widgets a.positive:hover, button.positive:hover{ background-color:#E6EFC2; border:1px solid #C6D880; color:#529214}
|
|
||||||
.widgets a.positive:active{ background-color:#529214; border:1px solid #529214; color:#fff}
|
|
||||||
.widgets a.negative, button.negative{ color:#d12f19}
|
|
||||||
.widgets a.negative:hover, button.negative:hover{ background:#fbe3e4; border:1px solid #fbc2c4; color:#d12f19}
|
|
||||||
.widgets a.negative:active{ background-color:#d12f19; border:1px solid #d12f19; color:#fff}
|
|
||||||
.tracks__waiting {background-image:url('../images/waiting.gif'); background-repeat:no-repeat; background-position:center center; background-color:white}
|
|
||||||
.bigWaiting {background-image:url('../images/bigWaiting.gif'); background-repeat:no-repeat; background-position:center 20%; background-color:white}
|
|
||||||
.blackWaiting {background-image:url('../images/blackWaiting.gif'); background-repeat:no-repeat; background-position:center center; background-color:black}
|
|
||||||
.bigBlackWaiting {background-image:url('../images/bigBlackWaiting.gif'); background-repeat:no-repeat; background-position:center center; background-color:black}
|
|
||||||
.stats_content .open-flash-chart, .stats_content .stats_module {float: left; width: 450px; margin-right:20px; padding-bottom:20px}
|
|
||||||
.stats_content h2 {clear:both; margin-top:15px; margin-bottom:15px}
|
|
||||||
body.integrations h2 {margin-top:40px; padding-top:20px; margin-bottom:10px; border-top:1px solid #ccc}
|
|
||||||
body.integrations p, body.integrations li {font-size:1.0em}
|
|
||||||
body.integrations li {list-style-type: disc; list-style-position: inside; margin-left:30px}
|
|
||||||
body.integrations textarea {margin:10px; padding:3px; width:80%; background-color:#ddd}
|
|
||||||
.defer-container {float:right}
|
|
||||||
.defer-container a:hover {background-color: inherit}
|
|
||||||
div.calendar {position: relative}
|
|
||||||
.calendar, .calendar table {border: 1px solid #556; font-size: 11px; color: #000; cursor: default; background: #eef; z-index: 110; font-family: tahoma,verdana,sans-serif}
|
|
||||||
.calendar .button {text-align: center; padding: 2px}
|
|
||||||
.calendar .nav {background: #778 url(../images/menuarrow.gif) no-repeat 100% 100%}
|
|
||||||
.calendar thead .title {font-weight: bold; text-align: center; background: #fff; color: #000; padding: 2px}
|
|
||||||
.calendar thead .headrow {background: #778; color: #fff}
|
|
||||||
.calendar thead .daynames {background: #bdf}
|
|
||||||
.calendar thead .name {border-bottom: 1px solid #556; padding: 2px; text-align: center; color: #000}
|
|
||||||
.calendar thead .weekend {color: #a66}
|
|
||||||
.calendar thead .hilite {background-color: #aaf; color: #000; border: 1px solid #04f; padding: 1px}
|
|
||||||
.calendar thead .active {background-color: #77c; padding: 2px 0px 0px 2px}
|
|
||||||
.calendar tbody .day {width: 2em; color: #456; text-align: right; padding: 2px 4px 2px 2px}
|
|
||||||
.calendar tbody .day.othermonth {font-size: 80%; color: #bbb}
|
|
||||||
.calendar tbody .day.othermonth.oweekend {color: #fbb}
|
|
||||||
.calendar table .wn {padding: 2px 3px 2px 2px; border-right: 1px solid #000; background: #bdf}
|
|
||||||
.calendar tbody .rowhilite td {background: #def}
|
|
||||||
.calendar tbody .rowhilite td.wn {background: #eef}
|
|
||||||
.calendar tbody td.hilite {background: #def; padding: 1px 3px 1px 1px; border: 1px solid #bbb}
|
|
||||||
.calendar tbody td.active {background: #cde; padding: 2px 2px 0px 2px}
|
|
||||||
.calendar tbody td.selected {font-weight: bold; border: 1px solid #000; padding: 1px 3px 1px 1px; background: #fff; color: #000}
|
|
||||||
.calendar tbody td.weekend {color: #a66}
|
|
||||||
.calendar tbody td.today {font-weight: bold; color: #00f}
|
|
||||||
.calendar tbody .disabled {color: #999}
|
|
||||||
.calendar tbody .emptycell {visibility: hidden}
|
|
||||||
.calendar tbody .emptyrow {display: none}
|
|
||||||
.calendar tfoot .footrow {text-align: center; background: #556; color: #fff}
|
|
||||||
.calendar tfoot .ttip {background: #fff; color: #445; border-top: 1px solid #556; padding: 1px}
|
|
||||||
.calendar tfoot .hilite {background: #aaf; border: 1px solid #04f; color: #000; padding: 1px}
|
|
||||||
.calendar tfoot .active {background: #77c; padding: 2px 0px 0px 2px}
|
|
||||||
.calendar .combo {position: absolute; display: none; top: 0px; left: 0px; width: 4em; cursor: default; border: 1px solid #655; background: #def; color: #000; font-size: 90%; z-index: 100}
|
|
||||||
.calendar .combo .label, .calendar .combo .label-IEfix {text-align: center; padding: 1px}
|
|
||||||
.calendar .combo .label-IEfix {width: 4em}
|
|
||||||
.calendar .combo .hilite {background: #acf}
|
|
||||||
.calendar .combo .active {border-top: 1px solid #46a; border-bottom: 1px solid #46a; background: #eef; font-weight: bold}
|
|
||||||
.calendar td.time {border-top: 1px solid #000; padding: 1px 0px; text-align: center; background-color: #f4f0e8}
|
|
||||||
.calendar td.time .hour, .calendar td.time .minute, .calendar td.time .ampm {padding: 0px 3px 0px 4px; border: 1px solid #889; font-weight: bold; background-color: #fff}
|
|
||||||
.calendar td.time .ampm {text-align: center}
|
|
||||||
.calendar td.time .colon {padding: 0px 2px 0px 3px; font-weight: bold}
|
|
||||||
.calendar td.time span.hilite {border-color: #000; background-color: #667; color: #fff}
|
|
||||||
.calendar td.time span.active {border-color: #f00; background-color: #000; color: #0f0}
|
|
||||||
b.niftycorners,b.niftyfill{display:block}
|
|
||||||
b.niftycorners *{display:block;height: 1px;line-height:1px;font-size: 1px; overflow:hidden;border-style:solid;border-width: 0 1px}
|
|
||||||
b.r1{margin: 0 3px;border-width: 0 2px}
|
|
||||||
b.r2{margin: 0 2px}
|
|
||||||
b.r3{margin: 0 1px}
|
|
||||||
b.r4{height: 2px}
|
|
||||||
b.rb1{margin: 0 8px;border-width:0 2px}
|
|
||||||
b.rb2{margin: 0 6px;border-width:0 2px}
|
|
||||||
b.rb3{margin: 0 5px}
|
|
||||||
b.rb4{margin: 0 4px}
|
|
||||||
b.rb5{margin: 0 3px}
|
|
||||||
b.rb6{margin: 0 2px}
|
|
||||||
b.rb7{margin: 0 1px;height:2px}
|
|
||||||
b.rb8{margin: 0;height:2px}
|
|
||||||
b.rs1{margin: 0 1px}
|
|
||||||
b.t1{border-width: 0 5px}
|
|
||||||
b.t2{border-width: 0 3px}
|
|
||||||
b.t3{border-width: 0 2px}
|
|
||||||
b.t4{height: 2px}
|
|
||||||
b.tb1{border-width: 0 10px}
|
|
||||||
b.tb2{border-width: 0 8px}
|
|
||||||
b.tb3{border-width: 0 6px}
|
|
||||||
b.tb4{border-width: 0 5px}
|
|
||||||
b.tb5{border-width: 0 4px}
|
|
||||||
b.tb6{border-width: 0 3px}
|
|
||||||
b.tb7{border-width: 0 2px;height:2px}
|
|
||||||
b.tb8{border-width: 0 1px;height:2px}
|
|
||||||
b.ts1{border-width: 0 2px}
|
|
||||||
|
|
@ -326,18 +326,18 @@ class UserTest < Test::Rails::TestCase
|
||||||
assert_nil users(:other_user).remember_token
|
assert_nil users(:other_user).remember_token
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_normalizes_open_id_url_on_save
|
def test_normalizes_identity_url_on_save
|
||||||
['www.johndoe.com', 'WWW.JOHNDOE.COM', 'http://www.johndoe.com/', 'http://www.johndoe.com'].each do |initial|
|
['www.johndoe.com', 'WWW.JOHNDOE.COM', 'http://www.johndoe.com/', 'http://www.johndoe.com'].each do |initial|
|
||||||
assert_open_id_url_normalized_on_save initial, 'http://www.johndoe.com'
|
assert_identity_url_normalized_on_save initial, 'http://www.johndoe.com/'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_normalizes_open_id_url_on_find
|
def test_normalizes_identity_url_on_find
|
||||||
u = users(:other_user)
|
u = users(:other_user)
|
||||||
u.open_id_url = 'http://www.johndoe.com'
|
u.identity_url = 'http://www.johndoe.com'
|
||||||
u.save
|
u.save
|
||||||
['www.johndoe.com', 'WWW.JOHNDOE.COM', 'http://www.johndoe.com/', 'http://www.johndoe.com'].each do |raw_open_id_url|
|
['www.johndoe.com', 'WWW.JOHNDOE.COM', 'http://www.johndoe.com/', 'http://www.johndoe.com'].each do |raw_identity_url|
|
||||||
assert_equal u.id, User.find_by_open_id_url(raw_open_id_url).id
|
assert_equal u.id, User.find_by_identity_url(raw_identity_url).id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -348,11 +348,11 @@ class UserTest < Test::Rails::TestCase
|
||||||
User.create({ :login => 'quire', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
|
User.create({ :login => 'quire', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_open_id_url_normalized_on_save(initial, expected)
|
def assert_identity_url_normalized_on_save(initial, expected)
|
||||||
u = users(:other_user)
|
u = users(:other_user)
|
||||||
u.open_id_url = initial
|
u.identity_url = initial
|
||||||
u.save
|
u.save
|
||||||
assert_equal expected, u.open_id_url
|
assert_equal expected, u.identity_url
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
289
vendor/gems/ruby-openid-2.1.2/.specification
vendored
Normal file
289
vendor/gems/ruby-openid-2.1.2/.specification
vendored
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
--- !ruby/object:Gem::Specification
|
||||||
|
name: ruby-openid
|
||||||
|
version: !ruby/object:Gem::Version
|
||||||
|
version: 2.1.2
|
||||||
|
platform: ruby
|
||||||
|
authors:
|
||||||
|
- JanRain, Inc
|
||||||
|
autorequire: openid
|
||||||
|
bindir: bin
|
||||||
|
cert_chain:
|
||||||
|
date: 2008-06-27 00:00:00 -04:00
|
||||||
|
default_executable:
|
||||||
|
dependencies: []
|
||||||
|
|
||||||
|
description:
|
||||||
|
email: openid@janrain.com
|
||||||
|
executables: []
|
||||||
|
|
||||||
|
extensions: []
|
||||||
|
|
||||||
|
extra_rdoc_files:
|
||||||
|
- README
|
||||||
|
- INSTALL
|
||||||
|
- LICENSE
|
||||||
|
- UPGRADE
|
||||||
|
files:
|
||||||
|
- examples/README
|
||||||
|
- examples/active_record_openid_store
|
||||||
|
- examples/rails_openid
|
||||||
|
- examples/discover
|
||||||
|
- examples/active_record_openid_store/lib
|
||||||
|
- examples/active_record_openid_store/test
|
||||||
|
- examples/active_record_openid_store/init.rb
|
||||||
|
- examples/active_record_openid_store/README
|
||||||
|
- examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb
|
||||||
|
- examples/active_record_openid_store/XXX_upgrade_open_id_store.rb
|
||||||
|
- examples/active_record_openid_store/lib/association.rb
|
||||||
|
- examples/active_record_openid_store/lib/nonce.rb
|
||||||
|
- examples/active_record_openid_store/lib/open_id_setting.rb
|
||||||
|
- examples/active_record_openid_store/lib/openid_ar_store.rb
|
||||||
|
- examples/active_record_openid_store/test/store_test.rb
|
||||||
|
- examples/rails_openid/app
|
||||||
|
- examples/rails_openid/components
|
||||||
|
- examples/rails_openid/config
|
||||||
|
- examples/rails_openid/db
|
||||||
|
- examples/rails_openid/doc
|
||||||
|
- examples/rails_openid/lib
|
||||||
|
- examples/rails_openid/log
|
||||||
|
- examples/rails_openid/public
|
||||||
|
- examples/rails_openid/script
|
||||||
|
- examples/rails_openid/test
|
||||||
|
- examples/rails_openid/vendor
|
||||||
|
- examples/rails_openid/Rakefile
|
||||||
|
- examples/rails_openid/README
|
||||||
|
- examples/rails_openid/app/controllers
|
||||||
|
- examples/rails_openid/app/helpers
|
||||||
|
- examples/rails_openid/app/models
|
||||||
|
- examples/rails_openid/app/views
|
||||||
|
- examples/rails_openid/app/controllers/application.rb
|
||||||
|
- examples/rails_openid/app/controllers/login_controller.rb
|
||||||
|
- examples/rails_openid/app/controllers/server_controller.rb
|
||||||
|
- examples/rails_openid/app/controllers/consumer_controller.rb
|
||||||
|
- examples/rails_openid/app/helpers/application_helper.rb
|
||||||
|
- examples/rails_openid/app/helpers/login_helper.rb
|
||||||
|
- examples/rails_openid/app/helpers/server_helper.rb
|
||||||
|
- examples/rails_openid/app/views/layouts
|
||||||
|
- examples/rails_openid/app/views/login
|
||||||
|
- examples/rails_openid/app/views/server
|
||||||
|
- examples/rails_openid/app/views/consumer
|
||||||
|
- examples/rails_openid/app/views/layouts/server.rhtml
|
||||||
|
- examples/rails_openid/app/views/login/index.rhtml
|
||||||
|
- examples/rails_openid/app/views/server/decide.rhtml
|
||||||
|
- examples/rails_openid/app/views/consumer/index.rhtml
|
||||||
|
- examples/rails_openid/config/environments
|
||||||
|
- examples/rails_openid/config/database.yml
|
||||||
|
- examples/rails_openid/config/boot.rb
|
||||||
|
- examples/rails_openid/config/environment.rb
|
||||||
|
- examples/rails_openid/config/routes.rb
|
||||||
|
- examples/rails_openid/config/environments/development.rb
|
||||||
|
- examples/rails_openid/config/environments/production.rb
|
||||||
|
- examples/rails_openid/config/environments/test.rb
|
||||||
|
- examples/rails_openid/doc/README_FOR_APP
|
||||||
|
- examples/rails_openid/lib/tasks
|
||||||
|
- examples/rails_openid/public/images
|
||||||
|
- examples/rails_openid/public/javascripts
|
||||||
|
- examples/rails_openid/public/stylesheets
|
||||||
|
- examples/rails_openid/public/dispatch.cgi
|
||||||
|
- examples/rails_openid/public/404.html
|
||||||
|
- examples/rails_openid/public/500.html
|
||||||
|
- examples/rails_openid/public/dispatch.fcgi
|
||||||
|
- examples/rails_openid/public/dispatch.rb
|
||||||
|
- examples/rails_openid/public/favicon.ico
|
||||||
|
- examples/rails_openid/public/robots.txt
|
||||||
|
- examples/rails_openid/public/images/openid_login_bg.gif
|
||||||
|
- examples/rails_openid/public/javascripts/controls.js
|
||||||
|
- examples/rails_openid/public/javascripts/dragdrop.js
|
||||||
|
- examples/rails_openid/public/javascripts/effects.js
|
||||||
|
- examples/rails_openid/public/javascripts/prototype.js
|
||||||
|
- examples/rails_openid/script/performance
|
||||||
|
- examples/rails_openid/script/process
|
||||||
|
- examples/rails_openid/script/console
|
||||||
|
- examples/rails_openid/script/about
|
||||||
|
- examples/rails_openid/script/breakpointer
|
||||||
|
- examples/rails_openid/script/destroy
|
||||||
|
- examples/rails_openid/script/generate
|
||||||
|
- examples/rails_openid/script/plugin
|
||||||
|
- examples/rails_openid/script/runner
|
||||||
|
- examples/rails_openid/script/server
|
||||||
|
- examples/rails_openid/script/performance/benchmarker
|
||||||
|
- examples/rails_openid/script/performance/profiler
|
||||||
|
- examples/rails_openid/script/process/spawner
|
||||||
|
- examples/rails_openid/script/process/reaper
|
||||||
|
- examples/rails_openid/script/process/spinner
|
||||||
|
- examples/rails_openid/test/fixtures
|
||||||
|
- examples/rails_openid/test/functional
|
||||||
|
- examples/rails_openid/test/mocks
|
||||||
|
- examples/rails_openid/test/unit
|
||||||
|
- examples/rails_openid/test/test_helper.rb
|
||||||
|
- examples/rails_openid/test/functional/login_controller_test.rb
|
||||||
|
- examples/rails_openid/test/functional/server_controller_test.rb
|
||||||
|
- examples/rails_openid/test/mocks/development
|
||||||
|
- examples/rails_openid/test/mocks/test
|
||||||
|
- lib/openid
|
||||||
|
- lib/hmac
|
||||||
|
- lib/openid.rb
|
||||||
|
- lib/openid/cryptutil.rb
|
||||||
|
- lib/openid/extras.rb
|
||||||
|
- lib/openid/urinorm.rb
|
||||||
|
- lib/openid/util.rb
|
||||||
|
- lib/openid/trustroot.rb
|
||||||
|
- lib/openid/message.rb
|
||||||
|
- lib/openid/yadis
|
||||||
|
- lib/openid/consumer
|
||||||
|
- lib/openid/fetchers.rb
|
||||||
|
- lib/openid/dh.rb
|
||||||
|
- lib/openid/kvform.rb
|
||||||
|
- lib/openid/association.rb
|
||||||
|
- lib/openid/store
|
||||||
|
- lib/openid/kvpost.rb
|
||||||
|
- lib/openid/extensions
|
||||||
|
- lib/openid/protocolerror.rb
|
||||||
|
- lib/openid/server.rb
|
||||||
|
- lib/openid/extension.rb
|
||||||
|
- lib/openid/consumer.rb
|
||||||
|
- lib/openid/yadis/htmltokenizer.rb
|
||||||
|
- lib/openid/yadis/parsehtml.rb
|
||||||
|
- lib/openid/yadis/filters.rb
|
||||||
|
- lib/openid/yadis/xrds.rb
|
||||||
|
- lib/openid/yadis/accept.rb
|
||||||
|
- lib/openid/yadis/constants.rb
|
||||||
|
- lib/openid/yadis/discovery.rb
|
||||||
|
- lib/openid/yadis/xri.rb
|
||||||
|
- lib/openid/yadis/xrires.rb
|
||||||
|
- lib/openid/yadis/services.rb
|
||||||
|
- lib/openid/consumer/html_parse.rb
|
||||||
|
- lib/openid/consumer/idres.rb
|
||||||
|
- lib/openid/consumer/associationmanager.rb
|
||||||
|
- lib/openid/consumer/discovery.rb
|
||||||
|
- lib/openid/consumer/discovery_manager.rb
|
||||||
|
- lib/openid/consumer/checkid_request.rb
|
||||||
|
- lib/openid/consumer/responses.rb
|
||||||
|
- lib/openid/store/filesystem.rb
|
||||||
|
- lib/openid/store/interface.rb
|
||||||
|
- lib/openid/store/nonce.rb
|
||||||
|
- lib/openid/store/memory.rb
|
||||||
|
- lib/openid/extensions/sreg.rb
|
||||||
|
- lib/openid/extensions/ax.rb
|
||||||
|
- lib/openid/extensions/pape.rb
|
||||||
|
- lib/hmac/hmac.rb
|
||||||
|
- lib/hmac/sha1.rb
|
||||||
|
- lib/hmac/sha2.rb
|
||||||
|
- test/data
|
||||||
|
- test/test_association.rb
|
||||||
|
- test/test_urinorm.rb
|
||||||
|
- test/testutil.rb
|
||||||
|
- test/test_util.rb
|
||||||
|
- test/test_message.rb
|
||||||
|
- test/test_cryptutil.rb
|
||||||
|
- test/test_extras.rb
|
||||||
|
- test/util.rb
|
||||||
|
- test/test_trustroot.rb
|
||||||
|
- test/test_parsehtml.rb
|
||||||
|
- test/test_fetchers.rb
|
||||||
|
- test/test_dh.rb
|
||||||
|
- test/test_kvform.rb
|
||||||
|
- test/test_openid_yadis.rb
|
||||||
|
- test/test_linkparse.rb
|
||||||
|
- test/test_stores.rb
|
||||||
|
- test/test_filters.rb
|
||||||
|
- test/test_xrds.rb
|
||||||
|
- test/test_nonce.rb
|
||||||
|
- test/test_accept.rb
|
||||||
|
- test/test_kvpost.rb
|
||||||
|
- test/test_associationmanager.rb
|
||||||
|
- test/discoverdata.rb
|
||||||
|
- test/test_server.rb
|
||||||
|
- test/test_yadis_discovery.rb
|
||||||
|
- test/test_sreg.rb
|
||||||
|
- test/test_idres.rb
|
||||||
|
- test/test_ax.rb
|
||||||
|
- test/test_xri.rb
|
||||||
|
- test/test_xrires.rb
|
||||||
|
- test/test_discover.rb
|
||||||
|
- test/test_consumer.rb
|
||||||
|
- test/test_pape.rb
|
||||||
|
- test/test_checkid_request.rb
|
||||||
|
- test/test_discovery_manager.rb
|
||||||
|
- test/test_responses.rb
|
||||||
|
- test/test_extension.rb
|
||||||
|
- test/data/test_xrds
|
||||||
|
- test/data/urinorm.txt
|
||||||
|
- test/data/n2b64
|
||||||
|
- test/data/trustroot.txt
|
||||||
|
- test/data/dh.txt
|
||||||
|
- test/data/test1-parsehtml.txt
|
||||||
|
- test/data/linkparse.txt
|
||||||
|
- test/data/accept.txt
|
||||||
|
- test/data/test_discover
|
||||||
|
- test/data/example-xrds.xml
|
||||||
|
- test/data/test1-discover.txt
|
||||||
|
- test/data/test_xrds/ref.xrds
|
||||||
|
- test/data/test_xrds/README
|
||||||
|
- test/data/test_xrds/delegated-20060809-r1.xrds
|
||||||
|
- test/data/test_xrds/delegated-20060809-r2.xrds
|
||||||
|
- test/data/test_xrds/delegated-20060809.xrds
|
||||||
|
- test/data/test_xrds/no-xrd.xml
|
||||||
|
- test/data/test_xrds/not-xrds.xml
|
||||||
|
- test/data/test_xrds/prefixsometimes.xrds
|
||||||
|
- test/data/test_xrds/sometimesprefix.xrds
|
||||||
|
- test/data/test_xrds/spoof1.xrds
|
||||||
|
- test/data/test_xrds/spoof2.xrds
|
||||||
|
- test/data/test_xrds/spoof3.xrds
|
||||||
|
- test/data/test_xrds/status222.xrds
|
||||||
|
- test/data/test_xrds/valid-populated-xrds.xml
|
||||||
|
- test/data/test_xrds/=j3h.2007.11.14.xrds
|
||||||
|
- test/data/test_xrds/subsegments.xrds
|
||||||
|
- test/data/test_discover/openid2_xrds.xml
|
||||||
|
- test/data/test_discover/openid.html
|
||||||
|
- test/data/test_discover/openid2.html
|
||||||
|
- test/data/test_discover/openid2_xrds_no_local_id.xml
|
||||||
|
- test/data/test_discover/openid_1_and_2.html
|
||||||
|
- test/data/test_discover/openid_1_and_2_xrds.xml
|
||||||
|
- test/data/test_discover/openid_and_yadis.html
|
||||||
|
- test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml
|
||||||
|
- test/data/test_discover/openid_no_delegate.html
|
||||||
|
- test/data/test_discover/yadis_0entries.xml
|
||||||
|
- test/data/test_discover/yadis_2_bad_local_id.xml
|
||||||
|
- test/data/test_discover/yadis_2entries_delegate.xml
|
||||||
|
- test/data/test_discover/yadis_2entries_idp.xml
|
||||||
|
- test/data/test_discover/yadis_another_delegate.xml
|
||||||
|
- test/data/test_discover/yadis_idp.xml
|
||||||
|
- test/data/test_discover/yadis_idp_delegate.xml
|
||||||
|
- test/data/test_discover/yadis_no_delegate.xml
|
||||||
|
- NOTICE
|
||||||
|
- CHANGELOG
|
||||||
|
- README
|
||||||
|
- INSTALL
|
||||||
|
- LICENSE
|
||||||
|
- UPGRADE
|
||||||
|
- admin/runtests.rb
|
||||||
|
has_rdoc: true
|
||||||
|
homepage: http://openidenabled.com/ruby-openid/
|
||||||
|
post_install_message:
|
||||||
|
rdoc_options:
|
||||||
|
- --main
|
||||||
|
- README
|
||||||
|
require_paths:
|
||||||
|
- lib
|
||||||
|
required_ruby_version: !ruby/object:Gem::Requirement
|
||||||
|
requirements:
|
||||||
|
- - ">"
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: 0.0.0
|
||||||
|
version:
|
||||||
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
||||||
|
requirements:
|
||||||
|
- - ">="
|
||||||
|
- !ruby/object:Gem::Version
|
||||||
|
version: "0"
|
||||||
|
version:
|
||||||
|
requirements: []
|
||||||
|
|
||||||
|
rubyforge_project:
|
||||||
|
rubygems_version: 1.2.0
|
||||||
|
signing_key:
|
||||||
|
specification_version: 1
|
||||||
|
summary: A library for consuming and serving OpenID identities.
|
||||||
|
test_files:
|
||||||
|
- admin/runtests.rb
|
||||||
78
vendor/gems/ruby-openid-2.1.2/CHANGELOG
vendored
Normal file
78
vendor/gems/ruby-openid-2.1.2/CHANGELOG
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
Fri Jun 27 15:39:14 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
tagged 2.1.2
|
||||||
|
|
||||||
|
Fri Jun 27 15:38:05 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* update version to 2.1.2
|
||||||
|
|
||||||
|
Fri Jun 27 15:01:35 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* util: remove call to srand
|
||||||
|
|
||||||
|
From the Ruby FAQ:
|
||||||
|
|
||||||
|
9.2 How do random number seeds work?
|
||||||
|
|
||||||
|
It depends. In Ruby versions prior to 1.5.2, the random number generator had
|
||||||
|
(by default) a constant seed, and so would produce the same series of numbers
|
||||||
|
each time a program was run. If you needed less deterministic behaviors, you
|
||||||
|
called srand to set up a less predictable seed.
|
||||||
|
|
||||||
|
Newer Rubys (Rubies?) have a different behavior. If rand is called without a
|
||||||
|
prior call to srand, Ruby will generate its own random(ish) seed. Successive
|
||||||
|
runs of a program that does not use srand will generate different sequences of
|
||||||
|
random numbers. To get the old, predictable, behavior (perhaps for testing),
|
||||||
|
call srand with a constant seed.
|
||||||
|
|
||||||
|
Fri Jun 27 13:34:43 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* LICENSE: htmltokenizer is (c) 2004 Ben Giddings
|
||||||
|
|
||||||
|
Fri Jun 27 13:32:09 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* Yadis.html_yadis_location: catch HTMLTokenizerError
|
||||||
|
|
||||||
|
Fri Jun 27 13:24:13 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* htmltokenizer: define HTMLTokenizerError to raise
|
||||||
|
|
||||||
|
Fri Jun 27 13:18:38 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* htmltokenizer: Don't raise OpenIDError from htmltokenizer (it's not in the OpenID module namespace) #255
|
||||||
|
|
||||||
|
Wed Jun 25 17:31:26 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* OpenID::Server::CheckIDRequest.answer: document return type
|
||||||
|
|
||||||
|
Wed Jun 25 17:06:35 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* TrustRoot.check_sanity: don't fail if the trust root is not parseable
|
||||||
|
|
||||||
|
Wed Jun 25 16:31:30 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* Message.from_http_response: accept 206 code
|
||||||
|
|
||||||
|
Wed Jun 25 14:14:05 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* move OpenID::VERSION definition in openid.rb, for #256
|
||||||
|
|
||||||
|
Wed Jun 25 13:55:18 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* Add admin/gettlds.py to ease updating of TLD list in trust root validation
|
||||||
|
|
||||||
|
Wed Jun 25 13:50:22 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* TrustRoot.TOP_LEVEL_DOMAINS: updated
|
||||||
|
|
||||||
|
Fri Jun 13 14:18:04 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* xrds.rb: fix stray colon
|
||||||
|
|
||||||
|
Fri Jun 13 13:41:58 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* Yadis::get_canonical_id: case-insensitive comparison
|
||||||
|
|
||||||
|
Porting a patch from =wil:
|
||||||
|
|
||||||
|
1. There should only be a single CanonicalID in each XRD (in the latest XRI
|
||||||
|
resolution spec), so I made it use the first CID found instead of the last.
|
||||||
|
|
||||||
|
2. Use case-insensitive comparison when comparing CanonicalIDs.
|
||||||
|
|
||||||
|
Wed Jun 11 15:24:12 PDT 2008 Kevin Turner <kevin@janrain.com>
|
||||||
|
* Accept response code 206 from fetcher results. Fixes #260
|
||||||
|
|
||||||
|
Wed Jun 11 11:27:25 PDT 2008 cygnus@janrain.com
|
||||||
|
* admin/fixperms: Fix stale entries
|
||||||
|
|
||||||
|
Wed Jun 11 11:08:11 PDT 2008 cygnus@janrain.com
|
||||||
|
* Add test cases for trust roots with non-ASCII characters in path or hostname
|
||||||
|
|
||||||
|
Fri Jun 6 15:50:12 PDT 2008 cygnus@janrain.com
|
||||||
|
tagged 2.1.1
|
||||||
47
vendor/gems/ruby-openid-2.1.2/INSTALL
vendored
Normal file
47
vendor/gems/ruby-openid-2.1.2/INSTALL
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
= Ruby OpenID Library Installation
|
||||||
|
|
||||||
|
== Rubygems Installation
|
||||||
|
|
||||||
|
Rubygems is a tool for installing ruby libraries and their
|
||||||
|
dependancies. If you have rubygems installed, simply:
|
||||||
|
|
||||||
|
gem install ruby-openid
|
||||||
|
|
||||||
|
== Manual Installation
|
||||||
|
|
||||||
|
Unpack the archive and run setup.rb to install:
|
||||||
|
|
||||||
|
ruby setup.rb
|
||||||
|
|
||||||
|
setup.rb installs the library into your system ruby. If don't want to
|
||||||
|
add openid to you system ruby, you may instead add the *lib* directory of
|
||||||
|
the extracted tarball to your RUBYLIB environment variable:
|
||||||
|
|
||||||
|
$ export RUBYLIB=${RUBYLIB}:/path/to/ruby-openid/lib
|
||||||
|
|
||||||
|
|
||||||
|
== Testing the Installation
|
||||||
|
|
||||||
|
Make sure everything installed ok:
|
||||||
|
$> irb
|
||||||
|
irb$> require "openid"
|
||||||
|
=> true
|
||||||
|
|
||||||
|
Or, if you installed via rubygems:
|
||||||
|
|
||||||
|
$> irb
|
||||||
|
irb$> require "rubygems"
|
||||||
|
=> true
|
||||||
|
irb$> require_gem "ruby-openid"
|
||||||
|
=> true
|
||||||
|
|
||||||
|
== Run the test suite
|
||||||
|
|
||||||
|
Go into the test directory and execute the *runtests.rb* script.
|
||||||
|
|
||||||
|
== Next steps
|
||||||
|
|
||||||
|
* Run consumer.rb in the examples directory.
|
||||||
|
* Get started writing your own consumer using OpenID::Consumer
|
||||||
|
* Write your own server with OpenID::Server
|
||||||
|
* Use the OpenIDLoginGenerator! Read example/README for more info.
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
|
The code in lib/hmac/ is Copyright 2001 by Daiki Ueno, and distributed under
|
||||||
|
the terms of the Ruby license. See http://www.ruby-lang.org/en/LICENSE.txt
|
||||||
|
|
||||||
|
lib/openid/yadis/htmltokenizer.rb is Copyright 2004 by Ben Giddings and
|
||||||
|
distributed under the terms of the Ruby license.
|
||||||
|
|
||||||
|
The remainder of this package is Copyright 2006-2008 by JanRain, Inc. and
|
||||||
|
distributed under the terms of license below:
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
2
vendor/gems/ruby-openid-2.1.2/NOTICE
vendored
Normal file
2
vendor/gems/ruby-openid-2.1.2/NOTICE
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
This product includes software developed by JanRain,
|
||||||
|
available from http://openidenabled.com/
|
||||||
82
vendor/gems/ruby-openid-2.1.2/README
vendored
Normal file
82
vendor/gems/ruby-openid-2.1.2/README
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
=Ruby OpenID
|
||||||
|
|
||||||
|
A Ruby library for verifying and serving OpenID identities.
|
||||||
|
|
||||||
|
==Features
|
||||||
|
* Easy to use API for verifying OpenID identites - OpenID::Consumer
|
||||||
|
* Support for serving OpenID identites - OpenID::Server
|
||||||
|
* Does not depend on underlying web framework
|
||||||
|
* Supports multiple storage mechanisms (Filesystem, ActiveRecord, Memory)
|
||||||
|
* Example code to help you get started, including:
|
||||||
|
* Ruby on Rails based consumer and server
|
||||||
|
* OpenIDLoginGenerator for quickly getting creating a rails app that uses
|
||||||
|
OpenID for authentication
|
||||||
|
* ActiveRecordOpenIDStore plugin
|
||||||
|
* Comprehensive test suite
|
||||||
|
* Supports both OpenID 1 and OpenID 2 transparently
|
||||||
|
|
||||||
|
==Installing
|
||||||
|
Before running the examples or writing your own code you'll need to install
|
||||||
|
the library. See the INSTALL file or use rubygems:
|
||||||
|
|
||||||
|
gem install ruby-openid
|
||||||
|
|
||||||
|
Check the installation:
|
||||||
|
|
||||||
|
$ irb
|
||||||
|
irb> require 'rubygems'
|
||||||
|
irb> require_gem 'ruby-openid'
|
||||||
|
=> true
|
||||||
|
|
||||||
|
The library is known to work with Ruby 1.8.4 on Unix, Max OSX and
|
||||||
|
Win32. Examples have been tested with Rails 1.1 and 1.2, and 2.0.
|
||||||
|
|
||||||
|
==Getting Started
|
||||||
|
The best way to start is to look at the rails_openid example.
|
||||||
|
You can run it with:
|
||||||
|
cd examples/rails_openid
|
||||||
|
script/server
|
||||||
|
|
||||||
|
If you are writing an OpenID Relying Party, a good place to start is:
|
||||||
|
examples/rails_openid/app/controllers/consumer_controller.rb
|
||||||
|
|
||||||
|
And if you are writing an OpenID provider:
|
||||||
|
examples/rails_openid/app/controllers/server_controller.rb
|
||||||
|
|
||||||
|
The library code is quite well documented, so don't be squeamish, and
|
||||||
|
look at the library itself if there's anything you don't understand in
|
||||||
|
the examples.
|
||||||
|
|
||||||
|
==Homepage
|
||||||
|
http://openidenabled.com/ruby-openid/
|
||||||
|
|
||||||
|
See also:
|
||||||
|
http://openid.net/
|
||||||
|
http://openidenabled.com/
|
||||||
|
|
||||||
|
==Community
|
||||||
|
Discussion regarding the Ruby OpenID library and other JanRain OpenID
|
||||||
|
libraries takes place on the the OpenID mailing list on
|
||||||
|
openidenabled.com.
|
||||||
|
|
||||||
|
http://lists.openidenabled.com/mailman/listinfo/dev
|
||||||
|
|
||||||
|
Please join this list to discuss, ask implementation questions, report
|
||||||
|
bugs, etc. Also check out the openid channel on the freenode IRC
|
||||||
|
network.
|
||||||
|
|
||||||
|
If you have a bugfix or feature you'd like to contribute, don't
|
||||||
|
hesitate to send it to us. For more detailed information on how to
|
||||||
|
contribute, see
|
||||||
|
|
||||||
|
http://openidenabled.com/contribute/
|
||||||
|
|
||||||
|
==Author
|
||||||
|
Copyright 2006-2008, JanRain, Inc.
|
||||||
|
|
||||||
|
Contact openid@janrain.com or visit the OpenID channel on pibb.com:
|
||||||
|
|
||||||
|
http://pibb.com/go/openid
|
||||||
|
|
||||||
|
==License
|
||||||
|
Apache Software License. For more information see the LICENSE file.
|
||||||
127
vendor/gems/ruby-openid-2.1.2/UPGRADE
vendored
Normal file
127
vendor/gems/ruby-openid-2.1.2/UPGRADE
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
= Upgrading from the OpenID 1.x series library
|
||||||
|
|
||||||
|
== Consumer Upgrade
|
||||||
|
|
||||||
|
The flow is largely the same, however there are a number of significant
|
||||||
|
changes. The consumer example is helpful to look at:
|
||||||
|
examples/rails_openid/app/controllers/consumer_controller.rb
|
||||||
|
|
||||||
|
|
||||||
|
=== Stores
|
||||||
|
|
||||||
|
You will need to require the file for the store that you are using.
|
||||||
|
For the filesystem store, this is 'openid/stores/filesystem'
|
||||||
|
They are also now in modules. The filesystem store is
|
||||||
|
OpenID::Store::Filesystem
|
||||||
|
The format has changed, and you should remove your old store directory.
|
||||||
|
|
||||||
|
The ActiveRecord store ( examples/active_record_openid_store ) still needs
|
||||||
|
to be put in a plugin directory for your rails app. There's a migration
|
||||||
|
that needs to be run; examine the README in that directory.
|
||||||
|
|
||||||
|
Also, note that the stores now can be garbage collected with the method
|
||||||
|
store.cleanup
|
||||||
|
|
||||||
|
|
||||||
|
=== Starting the OpenID transaction
|
||||||
|
|
||||||
|
The OpenIDRequest object no longer has status codes. Instead,
|
||||||
|
consumer.begin raises an OpenID::OpenIDError if there is a problem
|
||||||
|
initiating the transaction, so you'll want something along the lines of:
|
||||||
|
|
||||||
|
begin
|
||||||
|
openid_request = consumer.begin(params[:openid_identifier])
|
||||||
|
rescue OpenID::OpenIDError => e
|
||||||
|
# display error e
|
||||||
|
return
|
||||||
|
end
|
||||||
|
#success case
|
||||||
|
|
||||||
|
Data regarding the OpenID server once lived in
|
||||||
|
openid_request.service
|
||||||
|
|
||||||
|
The corresponding object in the 2.0 lib can be retrieved with
|
||||||
|
openid_request.endpoint
|
||||||
|
|
||||||
|
Getting the unverified identifier: Where you once had
|
||||||
|
openid_request.identity_url
|
||||||
|
you will now want
|
||||||
|
openid_request.endpoint.claimed_id
|
||||||
|
which might be different from what you get at the end of the transaction,
|
||||||
|
since it is now possible for users to enter their server's url directly.
|
||||||
|
|
||||||
|
Arguments on the return_to URL are now verified, so if you want to add
|
||||||
|
additional arguments to the return_to url, use
|
||||||
|
openid_request.return_to_args['param'] = value
|
||||||
|
|
||||||
|
Generating the redirect is the same as before, but add any extensions
|
||||||
|
first.
|
||||||
|
|
||||||
|
If you need to set up an SSL certificate authority list for the fetcher,
|
||||||
|
use the 'ca_file' attr_accessor on the OpenID::StandardFetcher. This has
|
||||||
|
changed from 'ca_path' in the 1.x.x series library. That is, set
|
||||||
|
OpenID.fetcher.ca_file = '/path/to/ca.list'
|
||||||
|
before calling consumer.begin.
|
||||||
|
|
||||||
|
=== Requesting Simple Registration Data
|
||||||
|
|
||||||
|
You'll need to require the code for the extension
|
||||||
|
require 'openid/extensions/sreg'
|
||||||
|
|
||||||
|
The new code for adding an SReg request now looks like:
|
||||||
|
|
||||||
|
sreg_request = OpenID::SReg::Request.new
|
||||||
|
sreg_request.request_fields(['email', 'dob'], true) # required
|
||||||
|
sreg_request.request_fields(['nickname', 'fullname'], false) # optional
|
||||||
|
sreg_request.policy_url = policy_url
|
||||||
|
openid_request.add_extension(sreg_request)
|
||||||
|
|
||||||
|
The code for adding other extensions is similar. Code for the Attribute
|
||||||
|
Exchange (AX) and Provider Authentication Policy Extension (PAPE) are
|
||||||
|
included with the library, and additional extensions can be implemented
|
||||||
|
subclassing OpenID::Extension.
|
||||||
|
|
||||||
|
|
||||||
|
=== Completing the transaction
|
||||||
|
|
||||||
|
The return_to and its arguments are verified, so you need to pass in
|
||||||
|
the base URL and the arguments. With Rails, the params method mashes
|
||||||
|
together parameters from GET, POST, and the path, so you'll need to pull
|
||||||
|
off the path "parameters" with something like
|
||||||
|
|
||||||
|
return_to = url_for(:only_path => false,
|
||||||
|
:controller => 'openid',
|
||||||
|
:action => 'complete')
|
||||||
|
parameters = params.reject{|k,v| request.path_parameters[k] }
|
||||||
|
openid_response = consumer.complete(parameters, return_to)
|
||||||
|
|
||||||
|
The response still uses the status codes, but they are now namespaced
|
||||||
|
slightly differently, for example OpenID::Consumer::SUCCESS
|
||||||
|
|
||||||
|
In the case of failure, the error message is now found in
|
||||||
|
openid_response.message
|
||||||
|
|
||||||
|
The identifier to display to the user can be found in
|
||||||
|
openid_response.endpoint.display_identifier
|
||||||
|
|
||||||
|
The Simple Registration response can be read from the OpenID response
|
||||||
|
with
|
||||||
|
sreg_response = OpenID::SReg::Response.from_success_response(openid_response)
|
||||||
|
nickname = sreg_response['nickname']
|
||||||
|
# etc.
|
||||||
|
|
||||||
|
|
||||||
|
== Server Upgrade
|
||||||
|
|
||||||
|
The server code is mostly the same as before, with the exception of
|
||||||
|
extensions. Also, you must pass in the endpoint URL to the server
|
||||||
|
constructor:
|
||||||
|
@server = OpenID::Server.new(store, server_url)
|
||||||
|
|
||||||
|
I recommend looking at
|
||||||
|
examples/rails_openid/app/controllers/server_controller.rb
|
||||||
|
for an example of the new way of doing extensions.
|
||||||
|
|
||||||
|
--
|
||||||
|
Dag Arneson, JanRain Inc.
|
||||||
|
Please direct questions to openid@janrain.com
|
||||||
36
vendor/gems/ruby-openid-2.1.2/admin/runtests.rb
vendored
Normal file
36
vendor/gems/ruby-openid-2.1.2/admin/runtests.rb
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
|
||||||
|
require "logger"
|
||||||
|
require "stringio"
|
||||||
|
require "pathname"
|
||||||
|
|
||||||
|
require 'test/unit/collector/dir'
|
||||||
|
require 'test/unit/ui/console/testrunner'
|
||||||
|
|
||||||
|
def main
|
||||||
|
old_verbose = $VERBOSE
|
||||||
|
$VERBOSE = true
|
||||||
|
|
||||||
|
tests_dir = Pathname.new(__FILE__).dirname.dirname.join('test')
|
||||||
|
|
||||||
|
# Collect tests from everything named test_*.rb.
|
||||||
|
c = Test::Unit::Collector::Dir.new
|
||||||
|
|
||||||
|
if c.respond_to?(:base=)
|
||||||
|
# In order to supress warnings from ruby 1.8.6 about accessing
|
||||||
|
# undefined member
|
||||||
|
c.base = tests_dir
|
||||||
|
suite = c.collect
|
||||||
|
else
|
||||||
|
# Because base is not defined in ruby < 1.8.6
|
||||||
|
suite = c.collect(tests_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
result = Test::Unit::UI::Console::TestRunner.run(suite)
|
||||||
|
result.passed?
|
||||||
|
ensure
|
||||||
|
$VERBOSE = old_verbose
|
||||||
|
end
|
||||||
|
|
||||||
|
exit(main)
|
||||||
32
vendor/gems/ruby-openid-2.1.2/examples/README
vendored
Normal file
32
vendor/gems/ruby-openid-2.1.2/examples/README
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
This directory contains several examples that demonstrate use of the
|
||||||
|
OpenID library. Make sure you have properly installed the library
|
||||||
|
before running the examples. These examples are a great place to
|
||||||
|
start in integrating OpenID into your application.
|
||||||
|
|
||||||
|
==Rails example
|
||||||
|
|
||||||
|
The rails_openid contains a fully functional OpenID server and relying
|
||||||
|
party, and acts as a starting point for implementing your own
|
||||||
|
production rails server. You'll need the latest version of Ruby on
|
||||||
|
Rails installed, and then:
|
||||||
|
|
||||||
|
cd rails_openid
|
||||||
|
./script/server
|
||||||
|
|
||||||
|
Open a web browser to http://localhost:3000/ and follow the instructions.
|
||||||
|
|
||||||
|
The relevant code to work from when writing your Rails OpenID Relying
|
||||||
|
Party is:
|
||||||
|
rails_openid/app/controllers/consumer_controller.rb
|
||||||
|
If you are working on an OpenID provider, check out
|
||||||
|
rails_openid/app/controllers/server_controller.rb
|
||||||
|
|
||||||
|
Since the library and examples are Apache-licensed, don't be shy about
|
||||||
|
copy-and-paste.
|
||||||
|
|
||||||
|
==Rails ActiveRecord OpenIDStore plugin
|
||||||
|
|
||||||
|
For various reasons you may want or need to deploy your ruby openid
|
||||||
|
consumer/server using an SQL based store. The active_record_openid_store
|
||||||
|
is a plugin that makes using an SQL based store simple. Follow the
|
||||||
|
README inside the plugin's dir for usage.
|
||||||
58
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/README
vendored
Normal file
58
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/README
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
=Active Record OpenID Store Plugin
|
||||||
|
|
||||||
|
A store is required by an OpenID server and optionally by the consumer
|
||||||
|
to store associations, nonces, and auth key information across
|
||||||
|
requests and processes. If rails is distributed across several
|
||||||
|
machines, they must must all have access to the same OpenID store
|
||||||
|
data, so the FilesystemStore won't do.
|
||||||
|
|
||||||
|
This directory contains a plugin for connecting your
|
||||||
|
OpenID enabled rails app to an ActiveRecord based OpenID store.
|
||||||
|
|
||||||
|
==Install
|
||||||
|
|
||||||
|
1) Copy this directory and all it's contents into your
|
||||||
|
RAILS_ROOT/vendor/plugins directory. You structure should look like
|
||||||
|
this:
|
||||||
|
|
||||||
|
RAILS_ROOT/vendor/plugins/active_record_openid_store/
|
||||||
|
|
||||||
|
2) Copy the migration, XXX_add_open_id_store_to_db.rb to your
|
||||||
|
RAILS_ROOT/db/migrate directory. Rename the XXX portion of the
|
||||||
|
file to next sequential migration number.
|
||||||
|
|
||||||
|
3) Run the migration:
|
||||||
|
|
||||||
|
rake migrate
|
||||||
|
|
||||||
|
4) Change your app to use the ActiveRecordOpenIDStore:
|
||||||
|
|
||||||
|
store = ActiveRecordOpenIDStore.new
|
||||||
|
consumer = OpenID::Consumer.new(session, store)
|
||||||
|
|
||||||
|
5) That's it! All your OpenID state will now be stored in the database.
|
||||||
|
|
||||||
|
==Upgrade
|
||||||
|
|
||||||
|
If you are upgrading from the 1.x ActiveRecord store, replace your old
|
||||||
|
RAILS_ROOT/vendor/plugins/active_record_openid_store/ directory with
|
||||||
|
the new one and run the migration XXX_upgrade_open_id_store.rb.
|
||||||
|
|
||||||
|
==What about garbage collection?
|
||||||
|
|
||||||
|
You may garbage collect unused nonces and expired associations using
|
||||||
|
the gc instance method of ActiveRecordOpenIDStore. Hook it up to a
|
||||||
|
task in your app's Rakefile like so:
|
||||||
|
|
||||||
|
desc 'GC OpenID store'
|
||||||
|
task :gc_openid_store => :environment do
|
||||||
|
ActiveRecordOpenIDStore.new.cleanup
|
||||||
|
end
|
||||||
|
|
||||||
|
Run it by typing:
|
||||||
|
|
||||||
|
rake gc_openid_store
|
||||||
|
|
||||||
|
|
||||||
|
==Questions?
|
||||||
|
Contact Dag Arneson: dag at janrain dot com
|
||||||
24
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb
vendored
Normal file
24
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Use this migration to create the tables for the ActiveRecord store
|
||||||
|
class AddOpenIdStoreToDb < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table "open_id_associations", :force => true do |t|
|
||||||
|
t.column "server_url", :binary, :null => false
|
||||||
|
t.column "handle", :string, :null => false
|
||||||
|
t.column "secret", :binary, :null => false
|
||||||
|
t.column "issued", :integer, :null => false
|
||||||
|
t.column "lifetime", :integer, :null => false
|
||||||
|
t.column "assoc_type", :string, :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "open_id_nonces", :force => true do |t|
|
||||||
|
t.column :server_url, :string, :null => false
|
||||||
|
t.column :timestamp, :integer, :null => false
|
||||||
|
t.column :salt, :string, :null => false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table "open_id_associations"
|
||||||
|
drop_table "open_id_nonces"
|
||||||
|
end
|
||||||
|
end
|
||||||
26
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb
vendored
Normal file
26
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Use this migration to upgrade the old 1.1 ActiveRecord store schema
|
||||||
|
# to the new 2.0 schema.
|
||||||
|
class UpgradeOpenIdStore < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
drop_table "open_id_settings"
|
||||||
|
drop_table "open_id_nonces"
|
||||||
|
create_table "open_id_nonces", :force => true do |t|
|
||||||
|
t.column :server_url, :string, :null => false
|
||||||
|
t.column :timestamp, :integer, :null => false
|
||||||
|
t.column :salt, :string, :null => false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table "open_id_nonces"
|
||||||
|
create_table "open_id_nonces", :force => true do |t|
|
||||||
|
t.column "nonce", :string
|
||||||
|
t.column "created", :integer
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "open_id_settings", :force => true do |t|
|
||||||
|
t.column "setting", :string
|
||||||
|
t.column "value", :binary
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
8
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/init.rb
vendored
Normal file
8
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/init.rb
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# might using the ruby-openid gem
|
||||||
|
begin
|
||||||
|
require 'rubygems'
|
||||||
|
rescue LoadError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
require 'openid'
|
||||||
|
require 'openid_ar_store'
|
||||||
10
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/lib/association.rb
vendored
Normal file
10
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/lib/association.rb
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
require 'openid/association'
|
||||||
|
require 'time'
|
||||||
|
|
||||||
|
class Association < ActiveRecord::Base
|
||||||
|
set_table_name 'open_id_associations'
|
||||||
|
def from_record
|
||||||
|
OpenID::Association.new(handle, secret, Time.at(issued), lifetime, assoc_type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/lib/nonce.rb
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/lib/nonce.rb
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
class Nonce < ActiveRecord::Base
|
||||||
|
set_table_name 'open_id_nonces'
|
||||||
|
end
|
||||||
4
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/lib/open_id_setting.rb
vendored
Normal file
4
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/lib/open_id_setting.rb
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
class OpenIdSetting < ActiveRecord::Base
|
||||||
|
|
||||||
|
validates_uniqueness_of :setting
|
||||||
|
end
|
||||||
57
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/lib/openid_ar_store.rb
vendored
Normal file
57
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/lib/openid_ar_store.rb
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
require 'association'
|
||||||
|
require 'nonce'
|
||||||
|
require 'openid/store/interface'
|
||||||
|
|
||||||
|
# not in OpenID module to avoid namespace conflict
|
||||||
|
class ActiveRecordStore < OpenID::Store::Interface
|
||||||
|
def store_association(server_url, assoc)
|
||||||
|
remove_association(server_url, assoc.handle)
|
||||||
|
Association.create!(:server_url => server_url,
|
||||||
|
:handle => assoc.handle,
|
||||||
|
:secret => assoc.secret,
|
||||||
|
:issued => assoc.issued.to_i,
|
||||||
|
:lifetime => assoc.lifetime,
|
||||||
|
:assoc_type => assoc.assoc_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_association(server_url, handle=nil)
|
||||||
|
assocs = if handle.blank?
|
||||||
|
Association.find_all_by_server_url(server_url)
|
||||||
|
else
|
||||||
|
Association.find_all_by_server_url_and_handle(server_url, handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
assocs.reverse.each do |assoc|
|
||||||
|
a = assoc.from_record
|
||||||
|
if a.expires_in == 0
|
||||||
|
assoc.destroy
|
||||||
|
else
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
end if assocs.any?
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_association(server_url, handle)
|
||||||
|
Association.delete_all(['server_url = ? AND handle = ?', server_url, handle]) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def use_nonce(server_url, timestamp, salt)
|
||||||
|
return false if Nonce.find_by_server_url_and_timestamp_and_salt(server_url, timestamp, salt)
|
||||||
|
return false if (timestamp - Time.now.to_i).abs > OpenID::Nonce.skew
|
||||||
|
Nonce.create!(:server_url => server_url, :timestamp => timestamp, :salt => salt)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def cleanup_nonces
|
||||||
|
now = Time.now.to_i
|
||||||
|
Nonce.delete_all(["timestamp > ? OR timestamp < ?", now + OpenID::Nonce.skew, now - OpenID::Nonce.skew])
|
||||||
|
end
|
||||||
|
|
||||||
|
def cleanup_associations
|
||||||
|
now = Time.now.to_i
|
||||||
|
Association.delete_all(['issued + lifetime > ?',now])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
212
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/test/store_test.rb
vendored
Normal file
212
vendor/gems/ruby-openid-2.1.2/examples/active_record_openid_store/test/store_test.rb
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
||||||
|
require 'test/unit'
|
||||||
|
RAILS_ENV = "test"
|
||||||
|
require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment.rb'))
|
||||||
|
|
||||||
|
module StoreTestCase
|
||||||
|
@@allowed_handle = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
|
||||||
|
@@allowed_nonce = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
|
def _gen_nonce
|
||||||
|
OpenID::CryptUtil.random_string(8, @@allowed_nonce)
|
||||||
|
end
|
||||||
|
|
||||||
|
def _gen_handle(n)
|
||||||
|
OpenID::CryptUtil.random_string(n, @@allowed_handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
def _gen_secret(n, chars=nil)
|
||||||
|
OpenID::CryptUtil.random_string(n, chars)
|
||||||
|
end
|
||||||
|
|
||||||
|
def _gen_assoc(issued, lifetime=600)
|
||||||
|
secret = _gen_secret(20)
|
||||||
|
handle = _gen_handle(128)
|
||||||
|
OpenID::Association.new(handle, secret, Time.now + issued, lifetime,
|
||||||
|
'HMAC-SHA1')
|
||||||
|
end
|
||||||
|
|
||||||
|
def _check_retrieve(url, handle=nil, expected=nil)
|
||||||
|
ret_assoc = @store.get_association(url, handle)
|
||||||
|
|
||||||
|
if expected.nil?
|
||||||
|
assert_nil(ret_assoc)
|
||||||
|
else
|
||||||
|
assert_equal(expected, ret_assoc)
|
||||||
|
assert_equal(expected.handle, ret_assoc.handle)
|
||||||
|
assert_equal(expected.secret, ret_assoc.secret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def _check_remove(url, handle, expected)
|
||||||
|
present = @store.remove_association(url, handle)
|
||||||
|
assert_equal(expected, present)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_store
|
||||||
|
server_url = "http://www.myopenid.com/openid"
|
||||||
|
assoc = _gen_assoc(issued=0)
|
||||||
|
|
||||||
|
# Make sure that a missing association returns no result
|
||||||
|
_check_retrieve(server_url)
|
||||||
|
|
||||||
|
# Check that after storage, getting returns the same result
|
||||||
|
@store.store_association(server_url, assoc)
|
||||||
|
_check_retrieve(server_url, nil, assoc)
|
||||||
|
|
||||||
|
# more than once
|
||||||
|
_check_retrieve(server_url, nil, assoc)
|
||||||
|
|
||||||
|
# Storing more than once has no ill effect
|
||||||
|
@store.store_association(server_url, assoc)
|
||||||
|
_check_retrieve(server_url, nil, assoc)
|
||||||
|
|
||||||
|
# Removing an association that does not exist returns not present
|
||||||
|
_check_remove(server_url, assoc.handle + 'x', false)
|
||||||
|
|
||||||
|
# Removing an association that does not exist returns not present
|
||||||
|
_check_remove(server_url + 'x', assoc.handle, false)
|
||||||
|
|
||||||
|
# Removing an association that is present returns present
|
||||||
|
_check_remove(server_url, assoc.handle, true)
|
||||||
|
|
||||||
|
# but not present on subsequent calls
|
||||||
|
_check_remove(server_url, assoc.handle, false)
|
||||||
|
|
||||||
|
# Put assoc back in the store
|
||||||
|
@store.store_association(server_url, assoc)
|
||||||
|
|
||||||
|
# More recent and expires after assoc
|
||||||
|
assoc2 = _gen_assoc(issued=1)
|
||||||
|
@store.store_association(server_url, assoc2)
|
||||||
|
|
||||||
|
# After storing an association with a different handle, but the
|
||||||
|
# same server_url, the handle with the later expiration is returned.
|
||||||
|
_check_retrieve(server_url, nil, assoc2)
|
||||||
|
|
||||||
|
# We can still retrieve the older association
|
||||||
|
_check_retrieve(server_url, assoc.handle, assoc)
|
||||||
|
|
||||||
|
# Plus we can retrieve the association with the later expiration
|
||||||
|
# explicitly
|
||||||
|
_check_retrieve(server_url, assoc2.handle, assoc2)
|
||||||
|
|
||||||
|
# More recent, and expires earlier than assoc2 or assoc. Make sure
|
||||||
|
# that we're picking the one with the latest issued date and not
|
||||||
|
# taking into account the expiration.
|
||||||
|
assoc3 = _gen_assoc(issued=2, lifetime=100)
|
||||||
|
@store.store_association(server_url, assoc3)
|
||||||
|
|
||||||
|
_check_retrieve(server_url, nil, assoc3)
|
||||||
|
_check_retrieve(server_url, assoc.handle, assoc)
|
||||||
|
_check_retrieve(server_url, assoc2.handle, assoc2)
|
||||||
|
_check_retrieve(server_url, assoc3.handle, assoc3)
|
||||||
|
|
||||||
|
_check_remove(server_url, assoc2.handle, true)
|
||||||
|
|
||||||
|
_check_retrieve(server_url, nil, assoc3)
|
||||||
|
_check_retrieve(server_url, assoc.handle, assoc)
|
||||||
|
_check_retrieve(server_url, assoc2.handle, nil)
|
||||||
|
_check_retrieve(server_url, assoc3.handle, assoc3)
|
||||||
|
|
||||||
|
_check_remove(server_url, assoc2.handle, false)
|
||||||
|
_check_remove(server_url, assoc3.handle, true)
|
||||||
|
|
||||||
|
_check_retrieve(server_url, nil, assoc)
|
||||||
|
_check_retrieve(server_url, assoc.handle, assoc)
|
||||||
|
_check_retrieve(server_url, assoc2.handle, nil)
|
||||||
|
_check_retrieve(server_url, assoc3.handle, nil)
|
||||||
|
|
||||||
|
_check_remove(server_url, assoc2.handle, false)
|
||||||
|
_check_remove(server_url, assoc.handle, true)
|
||||||
|
_check_remove(server_url, assoc3.handle, false)
|
||||||
|
|
||||||
|
_check_retrieve(server_url, nil, nil)
|
||||||
|
_check_retrieve(server_url, assoc.handle, nil)
|
||||||
|
_check_retrieve(server_url, assoc2.handle, nil)
|
||||||
|
_check_retrieve(server_url, assoc3.handle, nil)
|
||||||
|
|
||||||
|
_check_remove(server_url, assoc2.handle, false)
|
||||||
|
_check_remove(server_url, assoc.handle, false)
|
||||||
|
_check_remove(server_url, assoc3.handle, false)
|
||||||
|
|
||||||
|
assocValid1 = _gen_assoc(-3600, 7200)
|
||||||
|
assocValid2 = _gen_assoc(-5)
|
||||||
|
assocExpired1 = _gen_assoc(-7200, 3600)
|
||||||
|
assocExpired2 = _gen_assoc(-7200, 3600)
|
||||||
|
|
||||||
|
@store.cleanup_associations
|
||||||
|
@store.store_association(server_url + '1', assocValid1)
|
||||||
|
@store.store_association(server_url + '1', assocExpired1)
|
||||||
|
@store.store_association(server_url + '2', assocExpired2)
|
||||||
|
@store.store_association(server_url + '3', assocValid2)
|
||||||
|
|
||||||
|
cleaned = @store.cleanup_associations()
|
||||||
|
assert_equal(2, cleaned, "cleaned up associations")
|
||||||
|
end
|
||||||
|
|
||||||
|
def _check_use_nonce(nonce, expected, server_url, msg='')
|
||||||
|
stamp, salt = OpenID::Nonce::split_nonce(nonce)
|
||||||
|
actual = @store.use_nonce(server_url, stamp, salt)
|
||||||
|
assert_equal(expected, actual, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_nonce
|
||||||
|
server_url = "http://www.myopenid.com/openid"
|
||||||
|
[server_url, ''].each{|url|
|
||||||
|
nonce1 = OpenID::Nonce::mk_nonce
|
||||||
|
|
||||||
|
_check_use_nonce(nonce1, true, url, "#{url}: nonce allowed by default")
|
||||||
|
_check_use_nonce(nonce1, false, url, "#{url}: nonce not allowed twice")
|
||||||
|
_check_use_nonce(nonce1, false, url, "#{url}: nonce not allowed third time")
|
||||||
|
|
||||||
|
# old nonces shouldn't pass
|
||||||
|
old_nonce = OpenID::Nonce::mk_nonce(3600)
|
||||||
|
_check_use_nonce(old_nonce, false, url, "Old nonce #{old_nonce.inspect} passed")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
now = Time.now.to_i
|
||||||
|
old_nonce1 = OpenID::Nonce::mk_nonce(now - 20000)
|
||||||
|
old_nonce2 = OpenID::Nonce::mk_nonce(now - 10000)
|
||||||
|
recent_nonce = OpenID::Nonce::mk_nonce(now - 600)
|
||||||
|
|
||||||
|
orig_skew = OpenID::Nonce.skew
|
||||||
|
OpenID::Nonce.skew = 0
|
||||||
|
count = @store.cleanup_nonces
|
||||||
|
OpenID::Nonce.skew = 1000000
|
||||||
|
ts, salt = OpenID::Nonce::split_nonce(old_nonce1)
|
||||||
|
assert(@store.use_nonce(server_url, ts, salt), "oldnonce1")
|
||||||
|
ts, salt = OpenID::Nonce::split_nonce(old_nonce2)
|
||||||
|
assert(@store.use_nonce(server_url, ts, salt), "oldnonce2")
|
||||||
|
ts, salt = OpenID::Nonce::split_nonce(recent_nonce)
|
||||||
|
assert(@store.use_nonce(server_url, ts, salt), "recent_nonce")
|
||||||
|
|
||||||
|
|
||||||
|
OpenID::Nonce.skew = 1000
|
||||||
|
cleaned = @store.cleanup_nonces
|
||||||
|
assert_equal(2, cleaned, "Cleaned #{cleaned} nonces")
|
||||||
|
|
||||||
|
OpenID::Nonce.skew = 100000
|
||||||
|
ts, salt = OpenID::Nonce::split_nonce(old_nonce1)
|
||||||
|
assert(@store.use_nonce(server_url, ts, salt), "oldnonce1 after cleanup")
|
||||||
|
ts, salt = OpenID::Nonce::split_nonce(old_nonce2)
|
||||||
|
assert(@store.use_nonce(server_url, ts, salt), "oldnonce2 after cleanup")
|
||||||
|
ts, salt = OpenID::Nonce::split_nonce(recent_nonce)
|
||||||
|
assert(!@store.use_nonce(server_url, ts, salt), "recent_nonce after cleanup")
|
||||||
|
|
||||||
|
OpenID::Nonce.skew = orig_skew
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
class TestARStore < Test::Unit::TestCase
|
||||||
|
include StoreTestCase
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@store = ActiveRecordStore.new
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
49
vendor/gems/ruby-openid-2.1.2/examples/discover
vendored
Normal file
49
vendor/gems/ruby-openid-2.1.2/examples/discover
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require "openid/consumer/discovery"
|
||||||
|
require 'openid/fetchers'
|
||||||
|
|
||||||
|
OpenID::fetcher_use_env_http_proxy
|
||||||
|
|
||||||
|
$names = [[:server_url, "Server URL "],
|
||||||
|
[:local_id, "Local ID "],
|
||||||
|
[:canonical_id, "Canonical ID"],
|
||||||
|
]
|
||||||
|
|
||||||
|
def show_services(user_input, normalized, services)
|
||||||
|
puts " Claimed identifier: #{normalized}"
|
||||||
|
if services.empty?
|
||||||
|
puts " No OpenID services found"
|
||||||
|
puts
|
||||||
|
else
|
||||||
|
puts " Discovered services:"
|
||||||
|
n = 0
|
||||||
|
services.each do |service|
|
||||||
|
n += 1
|
||||||
|
puts " #{n}."
|
||||||
|
$names.each do |meth, name|
|
||||||
|
val = service.send(meth)
|
||||||
|
if val
|
||||||
|
printf(" %s: %s\n", name, val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
puts " Type URIs:"
|
||||||
|
for type_uri in service.type_uris
|
||||||
|
puts " * #{type_uri}"
|
||||||
|
end
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ARGV.each do |openid_identifier|
|
||||||
|
puts "=" * 50
|
||||||
|
puts "Running discovery on #{openid_identifier}"
|
||||||
|
begin
|
||||||
|
normalized_identifier, services = OpenID.discover(openid_identifier)
|
||||||
|
rescue OpenID::DiscoveryFailure => why
|
||||||
|
puts "Discovery failed: #{why.message}"
|
||||||
|
puts
|
||||||
|
else
|
||||||
|
show_services(openid_identifier, normalized_identifier, services)
|
||||||
|
end
|
||||||
|
end
|
||||||
153
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/README
vendored
Normal file
153
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/README
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
== Welcome to Rails
|
||||||
|
|
||||||
|
Rails is a web-application and persistence framework that includes everything
|
||||||
|
needed to create database-backed web-applications according to the
|
||||||
|
Model-View-Control pattern of separation. This pattern splits the view (also
|
||||||
|
called the presentation) into "dumb" templates that are primarily responsible
|
||||||
|
for inserting pre-built data in between HTML tags. The model contains the
|
||||||
|
"smart" domain objects (such as Account, Product, Person, Post) that holds all
|
||||||
|
the business logic and knows how to persist themselves to a database. The
|
||||||
|
controller handles the incoming requests (such as Save New Account, Update
|
||||||
|
Product, Show Post) by manipulating the model and directing data to the view.
|
||||||
|
|
||||||
|
In Rails, the model is handled by what's called an object-relational mapping
|
||||||
|
layer entitled Active Record. This layer allows you to present the data from
|
||||||
|
database rows as objects and embellish these data objects with business logic
|
||||||
|
methods. You can read more about Active Record in
|
||||||
|
link:files/vendor/rails/activerecord/README.html.
|
||||||
|
|
||||||
|
The controller and view are handled by the Action Pack, which handles both
|
||||||
|
layers by its two parts: Action View and Action Controller. These two layers
|
||||||
|
are bundled in a single package due to their heavy interdependence. This is
|
||||||
|
unlike the relationship between the Active Record and Action Pack that is much
|
||||||
|
more separate. Each of these packages can be used independently outside of
|
||||||
|
Rails. You can read more about Action Pack in
|
||||||
|
link:files/vendor/rails/actionpack/README.html.
|
||||||
|
|
||||||
|
|
||||||
|
== Getting started
|
||||||
|
|
||||||
|
1. Run the WEBrick servlet: <tt>ruby script/server</tt> (run with --help for options)
|
||||||
|
...or if you have lighttpd installed: <tt>ruby script/lighttpd</tt> (it's faster)
|
||||||
|
2. Go to http://localhost:3000/ and get "Congratulations, you've put Ruby on Rails!"
|
||||||
|
3. Follow the guidelines on the "Congratulations, you've put Ruby on Rails!" screen
|
||||||
|
|
||||||
|
|
||||||
|
== Example for Apache conf
|
||||||
|
|
||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName rails
|
||||||
|
DocumentRoot /path/application/public/
|
||||||
|
ErrorLog /path/application/log/server.log
|
||||||
|
|
||||||
|
<Directory /path/application/public/>
|
||||||
|
Options ExecCGI FollowSymLinks
|
||||||
|
AllowOverride all
|
||||||
|
Allow from all
|
||||||
|
Order allow,deny
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
||||||
|
|
||||||
|
NOTE: Be sure that CGIs can be executed in that directory as well. So ExecCGI
|
||||||
|
should be on and ".cgi" should respond. All requests from 127.0.0.1 go
|
||||||
|
through CGI, so no Apache restart is necessary for changes. All other requests
|
||||||
|
go through FCGI (or mod_ruby), which requires a restart to show changes.
|
||||||
|
|
||||||
|
|
||||||
|
== Debugging Rails
|
||||||
|
|
||||||
|
Have "tail -f" commands running on both the server.log, production.log, and
|
||||||
|
test.log files. Rails will automatically display debugging and runtime
|
||||||
|
information to these files. Debugging info will also be shown in the browser
|
||||||
|
on requests from 127.0.0.1.
|
||||||
|
|
||||||
|
|
||||||
|
== Breakpoints
|
||||||
|
|
||||||
|
Breakpoint support is available through the script/breakpointer client. This
|
||||||
|
means that you can break out of execution at any point in the code, investigate
|
||||||
|
and change the model, AND then resume execution! Example:
|
||||||
|
|
||||||
|
class WeblogController < ActionController::Base
|
||||||
|
def index
|
||||||
|
@posts = Post.find_all
|
||||||
|
breakpoint "Breaking out from the list"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
So the controller will accept the action, run the first line, then present you
|
||||||
|
with a IRB prompt in the breakpointer window. Here you can do things like:
|
||||||
|
|
||||||
|
Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
|
||||||
|
|
||||||
|
>> @posts.inspect
|
||||||
|
=> "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
|
||||||
|
#<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
|
||||||
|
>> @posts.first.title = "hello from a breakpoint"
|
||||||
|
=> "hello from a breakpoint"
|
||||||
|
|
||||||
|
...and even better is that you can examine how your runtime objects actually work:
|
||||||
|
|
||||||
|
>> f = @posts.first
|
||||||
|
=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
|
||||||
|
>> f.
|
||||||
|
Display all 152 possibilities? (y or n)
|
||||||
|
|
||||||
|
Finally, when you're ready to resume execution, you press CTRL-D
|
||||||
|
|
||||||
|
|
||||||
|
== Console
|
||||||
|
|
||||||
|
You can interact with the domain model by starting the console through script/console.
|
||||||
|
Here you'll have all parts of the application configured, just like it is when the
|
||||||
|
application is running. You can inspect domain models, change values, and save to the
|
||||||
|
database. Starting the script without arguments will launch it in the development environment.
|
||||||
|
Passing an argument will specify a different environment, like <tt>console production</tt>.
|
||||||
|
|
||||||
|
|
||||||
|
== Description of contents
|
||||||
|
|
||||||
|
app
|
||||||
|
Holds all the code that's specific to this particular application.
|
||||||
|
|
||||||
|
app/controllers
|
||||||
|
Holds controllers that should be named like weblog_controller.rb for
|
||||||
|
automated URL mapping. All controllers should descend from
|
||||||
|
ActionController::Base.
|
||||||
|
|
||||||
|
app/models
|
||||||
|
Holds models that should be named like post.rb.
|
||||||
|
Most models will descend from ActiveRecord::Base.
|
||||||
|
|
||||||
|
app/views
|
||||||
|
Holds the template files for the view that should be named like
|
||||||
|
weblog/index.rhtml for the WeblogController#index action. All views use eRuby
|
||||||
|
syntax. This directory can also be used to keep stylesheets, images, and so on
|
||||||
|
that can be symlinked to public.
|
||||||
|
|
||||||
|
app/helpers
|
||||||
|
Holds view helpers that should be named like weblog_helper.rb.
|
||||||
|
|
||||||
|
config
|
||||||
|
Configuration files for the Rails environment, the routing map, the database, and other dependencies.
|
||||||
|
|
||||||
|
components
|
||||||
|
Self-contained mini-applications that can bundle together controllers, models, and views.
|
||||||
|
|
||||||
|
lib
|
||||||
|
Application specific libraries. Basically, any kind of custom code that doesn't
|
||||||
|
belong under controllers, models, or helpers. This directory is in the load path.
|
||||||
|
|
||||||
|
public
|
||||||
|
The directory available for the web server. Contains subdirectories for images, stylesheets,
|
||||||
|
and javascripts. Also contains the dispatchers and the default HTML files.
|
||||||
|
|
||||||
|
script
|
||||||
|
Helper scripts for automation and generation.
|
||||||
|
|
||||||
|
test
|
||||||
|
Unit and functional tests along with fixtures.
|
||||||
|
|
||||||
|
vendor
|
||||||
|
External libraries that the application depends on. Also includes the plugins subdirectory.
|
||||||
|
This directory is in the load path.
|
||||||
10
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/Rakefile
vendored
Normal file
10
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/Rakefile
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
|
# for example lib/tasks/switchtower.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
|
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
||||||
|
|
||||||
|
require 'rake'
|
||||||
|
require 'rake/testtask'
|
||||||
|
require 'rake/rdoctask'
|
||||||
|
|
||||||
|
require 'tasks/rails'
|
||||||
4
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/controllers/application.rb
vendored
Normal file
4
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/controllers/application.rb
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Filters added to this controller will be run for all controllers in the application.
|
||||||
|
# Likewise, all the methods added will be available for all controllers.
|
||||||
|
class ApplicationController < ActionController::Base
|
||||||
|
end
|
||||||
122
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/controllers/consumer_controller.rb
vendored
Normal file
122
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/controllers/consumer_controller.rb
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
require 'pathname'
|
||||||
|
|
||||||
|
require "openid"
|
||||||
|
require 'openid/extensions/sreg'
|
||||||
|
require 'openid/extensions/pape'
|
||||||
|
require 'openid/store/filesystem'
|
||||||
|
|
||||||
|
class ConsumerController < ApplicationController
|
||||||
|
layout nil
|
||||||
|
|
||||||
|
def index
|
||||||
|
# render an openid form
|
||||||
|
end
|
||||||
|
|
||||||
|
def start
|
||||||
|
begin
|
||||||
|
identifier = params[:openid_identifier]
|
||||||
|
if identifier.nil?
|
||||||
|
flash[:error] = "Enter an OpenID identifier"
|
||||||
|
redirect_to :action => 'index'
|
||||||
|
return
|
||||||
|
end
|
||||||
|
oidreq = consumer.begin(identifier)
|
||||||
|
rescue OpenID::OpenIDError => e
|
||||||
|
flash[:error] = "Discovery failed for #{identifier}: #{e}"
|
||||||
|
redirect_to :action => 'index'
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if params[:use_sreg]
|
||||||
|
sregreq = OpenID::SReg::Request.new
|
||||||
|
# required fields
|
||||||
|
sregreq.request_fields(['email','nickname'], true)
|
||||||
|
# optional fields
|
||||||
|
sregreq.request_fields(['dob', 'fullname'], false)
|
||||||
|
oidreq.add_extension(sregreq)
|
||||||
|
oidreq.return_to_args['did_sreg'] = 'y'
|
||||||
|
end
|
||||||
|
if params[:use_pape]
|
||||||
|
papereq = OpenID::PAPE::Request.new
|
||||||
|
papereq.add_policy_uri(OpenID::PAPE::AUTH_PHISHING_RESISTANT)
|
||||||
|
papereq.max_auth_age = 2*60*60
|
||||||
|
oidreq.add_extension(papereq)
|
||||||
|
oidreq.return_to_args['did_pape'] = 'y'
|
||||||
|
end
|
||||||
|
if params[:force_post]
|
||||||
|
oidreq.return_to_args['force_post']='x'*2048
|
||||||
|
end
|
||||||
|
return_to = url_for :action => 'complete', :only_path => false
|
||||||
|
realm = url_for :action => 'index', :only_path => false
|
||||||
|
|
||||||
|
if oidreq.send_redirect?(realm, return_to, params[:immediate])
|
||||||
|
redirect_to oidreq.redirect_url(realm, return_to, params[:immediate])
|
||||||
|
else
|
||||||
|
render :text => oidreq.html_markup(realm, return_to, params[:immediate], {'id' => 'openid_form'})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete
|
||||||
|
# FIXME - url_for some action is not necessarily the current URL.
|
||||||
|
current_url = url_for(:action => 'complete', :only_path => false)
|
||||||
|
parameters = params.reject{|k,v|request.path_parameters[k]}
|
||||||
|
oidresp = consumer.complete(parameters, current_url)
|
||||||
|
case oidresp.status
|
||||||
|
when OpenID::Consumer::FAILURE
|
||||||
|
if oidresp.display_identifier
|
||||||
|
flash[:error] = ("Verification of #{oidresp.display_identifier}"\
|
||||||
|
" failed: #{oidresp.message}")
|
||||||
|
else
|
||||||
|
flash[:error] = "Verification failed: #{oidresp.message}"
|
||||||
|
end
|
||||||
|
when OpenID::Consumer::SUCCESS
|
||||||
|
flash[:success] = ("Verification of #{oidresp.display_identifier}"\
|
||||||
|
" succeeded.")
|
||||||
|
if params[:did_sreg]
|
||||||
|
sreg_resp = OpenID::SReg::Response.from_success_response(oidresp)
|
||||||
|
sreg_message = "Simple Registration data was requested"
|
||||||
|
if sreg_resp.empty?
|
||||||
|
sreg_message << ", but none was returned."
|
||||||
|
else
|
||||||
|
sreg_message << ". The following data were sent:"
|
||||||
|
sreg_resp.data.each {|k,v|
|
||||||
|
sreg_message << "<br/><b>#{k}</b>: #{v}"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
flash[:sreg_results] = sreg_message
|
||||||
|
end
|
||||||
|
if params[:did_pape]
|
||||||
|
pape_resp = OpenID::PAPE::Response.from_success_response(oidresp)
|
||||||
|
pape_message = "A phishing resistant authentication method was requested"
|
||||||
|
if pape_resp.auth_policies.member? OpenID::PAPE::AUTH_PHISHING_RESISTANT
|
||||||
|
pape_message << ", and the server reported one."
|
||||||
|
else
|
||||||
|
pape_message << ", but the server did not report one."
|
||||||
|
end
|
||||||
|
if pape_resp.auth_time
|
||||||
|
pape_message << "<br><b>Authentication time:</b> #{pape_resp.auth_time} seconds"
|
||||||
|
end
|
||||||
|
if pape_resp.nist_auth_level
|
||||||
|
pape_message << "<br><b>NIST Auth Level:</b> #{pape_resp.nist_auth_level}"
|
||||||
|
end
|
||||||
|
flash[:pape_results] = pape_message
|
||||||
|
end
|
||||||
|
when OpenID::Consumer::SETUP_NEEDED
|
||||||
|
flash[:alert] = "Immediate request failed - Setup Needed"
|
||||||
|
when OpenID::Consumer::CANCEL
|
||||||
|
flash[:alert] = "OpenID transaction cancelled."
|
||||||
|
else
|
||||||
|
end
|
||||||
|
redirect_to :action => 'index'
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def consumer
|
||||||
|
if @consumer.nil?
|
||||||
|
dir = Pathname.new(RAILS_ROOT).join('db').join('cstore')
|
||||||
|
store = OpenID::Store::Filesystem.new(dir)
|
||||||
|
@consumer = OpenID::Consumer.new(session, store)
|
||||||
|
end
|
||||||
|
return @consumer
|
||||||
|
end
|
||||||
|
end
|
||||||
45
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/controllers/login_controller.rb
vendored
Normal file
45
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/controllers/login_controller.rb
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Controller for handling the login, logout process for "users" of our
|
||||||
|
# little server. Users have no password. This is just an example.
|
||||||
|
|
||||||
|
require 'openid'
|
||||||
|
|
||||||
|
class LoginController < ApplicationController
|
||||||
|
|
||||||
|
layout 'server'
|
||||||
|
|
||||||
|
def base_url
|
||||||
|
url_for(:controller => 'login', :action => nil, :only_path => false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
response.headers['X-XRDS-Location'] = url_for(:controller => "server",
|
||||||
|
:action => "idp_xrds",
|
||||||
|
:only_path => false)
|
||||||
|
@base_url = base_url
|
||||||
|
# just show the login page
|
||||||
|
end
|
||||||
|
|
||||||
|
def submit
|
||||||
|
user = params[:username]
|
||||||
|
|
||||||
|
# if we get a user, log them in by putting their username in
|
||||||
|
# the session hash.
|
||||||
|
unless user.nil?
|
||||||
|
session[:username] = user unless user.nil?
|
||||||
|
session[:approvals] = []
|
||||||
|
flash[:notice] = "Your OpenID URL is <b>#{base_url}user/#{user}</b><br/><br/>Proceed to step 2 below."
|
||||||
|
else
|
||||||
|
flash[:error] = "Sorry, couldn't log you in. Try again."
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to :action => 'index'
|
||||||
|
end
|
||||||
|
|
||||||
|
def logout
|
||||||
|
# delete the username from the session hash
|
||||||
|
session[:username] = nil
|
||||||
|
session[:approvals] = nil
|
||||||
|
redirect_to :action => 'index'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
265
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/controllers/server_controller.rb
vendored
Normal file
265
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/controllers/server_controller.rb
vendored
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
require 'pathname'
|
||||||
|
|
||||||
|
# load the openid library, first trying rubygems
|
||||||
|
#begin
|
||||||
|
# require "rubygems"
|
||||||
|
# require_gem "ruby-openid", ">= 1.0"
|
||||||
|
#rescue LoadError
|
||||||
|
require "openid"
|
||||||
|
require "openid/consumer/discovery"
|
||||||
|
require 'openid/extensions/sreg'
|
||||||
|
require 'openid/extensions/pape'
|
||||||
|
require 'openid/store/filesystem'
|
||||||
|
#end
|
||||||
|
|
||||||
|
class ServerController < ApplicationController
|
||||||
|
|
||||||
|
include ServerHelper
|
||||||
|
include OpenID::Server
|
||||||
|
layout nil
|
||||||
|
|
||||||
|
def index
|
||||||
|
begin
|
||||||
|
oidreq = server.decode_request(params)
|
||||||
|
rescue ProtocolError => e
|
||||||
|
# invalid openid request, so just display a page with an error message
|
||||||
|
render :text => e.to_s, :status => 500
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# no openid.mode was given
|
||||||
|
unless oidreq
|
||||||
|
render :text => "This is an OpenID server endpoint."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
oidresp = nil
|
||||||
|
|
||||||
|
if oidreq.kind_of?(CheckIDRequest)
|
||||||
|
|
||||||
|
identity = oidreq.identity
|
||||||
|
|
||||||
|
if oidreq.id_select
|
||||||
|
if oidreq.immediate
|
||||||
|
oidresp = oidreq.answer(false)
|
||||||
|
elsif session[:username].nil?
|
||||||
|
# The user hasn't logged in.
|
||||||
|
show_decision_page(oidreq)
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# Else, set the identity to the one the user is using.
|
||||||
|
identity = url_for_user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if oidresp
|
||||||
|
nil
|
||||||
|
elsif self.is_authorized(identity, oidreq.trust_root)
|
||||||
|
oidresp = oidreq.answer(true, nil, identity)
|
||||||
|
|
||||||
|
# add the sreg response if requested
|
||||||
|
add_sreg(oidreq, oidresp)
|
||||||
|
# ditto pape
|
||||||
|
add_pape(oidreq, oidresp)
|
||||||
|
|
||||||
|
elsif oidreq.immediate
|
||||||
|
server_url = url_for :action => 'index'
|
||||||
|
oidresp = oidreq.answer(false, server_url)
|
||||||
|
|
||||||
|
else
|
||||||
|
show_decision_page(oidreq)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
oidresp = server.handle_request(oidreq)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.render_response(oidresp)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_decision_page(oidreq, message="Do you trust this site with your identity?")
|
||||||
|
session[:last_oidreq] = oidreq
|
||||||
|
@oidreq = oidreq
|
||||||
|
|
||||||
|
if message
|
||||||
|
flash[:notice] = message
|
||||||
|
end
|
||||||
|
|
||||||
|
render :template => 'server/decide', :layout => 'server'
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_page
|
||||||
|
# Yadis content-negotiation: we want to return the xrds if asked for.
|
||||||
|
accept = request.env['HTTP_ACCEPT']
|
||||||
|
|
||||||
|
# This is not technically correct, and should eventually be updated
|
||||||
|
# to do real Accept header parsing and logic. Though I expect it will work
|
||||||
|
# 99% of the time.
|
||||||
|
if accept and accept.include?('application/xrds+xml')
|
||||||
|
user_xrds
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# content negotiation failed, so just render the user page
|
||||||
|
xrds_url = url_for(:controller=>'user',:action=>params[:username])+'/xrds'
|
||||||
|
identity_page = <<EOS
|
||||||
|
<html><head>
|
||||||
|
<meta http-equiv="X-XRDS-Location" content="#{xrds_url}" />
|
||||||
|
<link rel="openid.server" href="#{url_for :action => 'index'}" />
|
||||||
|
</head><body><p>OpenID identity page for #{params[:username]}</p>
|
||||||
|
</body></html>
|
||||||
|
EOS
|
||||||
|
|
||||||
|
# Also add the Yadis location header, so that they don't have
|
||||||
|
# to parse the html unless absolutely necessary.
|
||||||
|
response.headers['X-XRDS-Location'] = xrds_url
|
||||||
|
render :text => identity_page
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_xrds
|
||||||
|
types = [
|
||||||
|
OpenID::OPENID_2_0_TYPE,
|
||||||
|
OpenID::OPENID_1_0_TYPE,
|
||||||
|
OpenID::SREG_URI,
|
||||||
|
]
|
||||||
|
|
||||||
|
render_xrds(types)
|
||||||
|
end
|
||||||
|
|
||||||
|
def idp_xrds
|
||||||
|
types = [
|
||||||
|
OpenID::OPENID_IDP_2_0_TYPE,
|
||||||
|
]
|
||||||
|
|
||||||
|
render_xrds(types)
|
||||||
|
end
|
||||||
|
|
||||||
|
def decision
|
||||||
|
oidreq = session[:last_oidreq]
|
||||||
|
session[:last_oidreq] = nil
|
||||||
|
|
||||||
|
if params[:yes].nil?
|
||||||
|
redirect_to oidreq.cancel_url
|
||||||
|
return
|
||||||
|
else
|
||||||
|
id_to_send = params[:id_to_send]
|
||||||
|
|
||||||
|
identity = oidreq.identity
|
||||||
|
if oidreq.id_select
|
||||||
|
if id_to_send and id_to_send != ""
|
||||||
|
session[:username] = id_to_send
|
||||||
|
session[:approvals] = []
|
||||||
|
identity = url_for_user
|
||||||
|
else
|
||||||
|
msg = "You must enter a username to in order to send " +
|
||||||
|
"an identifier to the Relying Party."
|
||||||
|
show_decision_page(oidreq, msg)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if session[:approvals]
|
||||||
|
session[:approvals] << oidreq.trust_root
|
||||||
|
else
|
||||||
|
session[:approvals] = [oidreq.trust_root]
|
||||||
|
end
|
||||||
|
oidresp = oidreq.answer(true, nil, identity)
|
||||||
|
add_sreg(oidreq, oidresp)
|
||||||
|
add_pape(oidreq, oidresp)
|
||||||
|
return self.render_response(oidresp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def server
|
||||||
|
if @server.nil?
|
||||||
|
server_url = url_for :action => 'index', :only_path => false
|
||||||
|
dir = Pathname.new(RAILS_ROOT).join('db').join('openid-store')
|
||||||
|
store = OpenID::Store::Filesystem.new(dir)
|
||||||
|
@server = Server.new(store, server_url)
|
||||||
|
end
|
||||||
|
return @server
|
||||||
|
end
|
||||||
|
|
||||||
|
def approved(trust_root)
|
||||||
|
return false if session[:approvals].nil?
|
||||||
|
return session[:approvals].member?(trust_root)
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_authorized(identity_url, trust_root)
|
||||||
|
return (session[:username] and (identity_url == url_for_user) and self.approved(trust_root))
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_xrds(types)
|
||||||
|
type_str = ""
|
||||||
|
|
||||||
|
types.each { |uri|
|
||||||
|
type_str += "<Type>#{uri}</Type>\n "
|
||||||
|
}
|
||||||
|
|
||||||
|
yadis = <<EOS
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xrds:XRDS
|
||||||
|
xmlns:xrds="xri://$xrds"
|
||||||
|
xmlns="xri://$xrd*($v*2.0)">
|
||||||
|
<XRD>
|
||||||
|
<Service priority="0">
|
||||||
|
#{type_str}
|
||||||
|
<URI>#{url_for(:controller => 'server', :only_path => false)}</URI>
|
||||||
|
</Service>
|
||||||
|
</XRD>
|
||||||
|
</xrds:XRDS>
|
||||||
|
EOS
|
||||||
|
|
||||||
|
response.headers['content-type'] = 'application/xrds+xml'
|
||||||
|
render :text => yadis
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_sreg(oidreq, oidresp)
|
||||||
|
# check for Simple Registration arguments and respond
|
||||||
|
sregreq = OpenID::SReg::Request.from_openid_request(oidreq)
|
||||||
|
|
||||||
|
return if sregreq.nil?
|
||||||
|
# In a real application, this data would be user-specific,
|
||||||
|
# and the user should be asked for permission to release
|
||||||
|
# it.
|
||||||
|
sreg_data = {
|
||||||
|
'nickname' => session[:username],
|
||||||
|
'fullname' => 'Mayor McCheese',
|
||||||
|
'email' => 'mayor@example.com'
|
||||||
|
}
|
||||||
|
|
||||||
|
sregresp = OpenID::SReg::Response.extract_response(sregreq, sreg_data)
|
||||||
|
oidresp.add_extension(sregresp)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_pape(oidreq, oidresp)
|
||||||
|
papereq = OpenID::PAPE::Request.from_openid_request(oidreq)
|
||||||
|
return if papereq.nil?
|
||||||
|
paperesp = OpenID::PAPE::Response.new
|
||||||
|
paperesp.nist_auth_level = 0 # we don't even do auth at all!
|
||||||
|
oidresp.add_extension(paperesp)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_response(oidresp)
|
||||||
|
if oidresp.needs_signing
|
||||||
|
signed_response = server.signatory.sign(oidresp)
|
||||||
|
end
|
||||||
|
web_response = server.encode_response(oidresp)
|
||||||
|
|
||||||
|
case web_response.code
|
||||||
|
when HTTP_OK
|
||||||
|
render :text => web_response.body, :status => 200
|
||||||
|
|
||||||
|
when HTTP_REDIRECT
|
||||||
|
redirect_to web_response.headers['location']
|
||||||
|
|
||||||
|
else
|
||||||
|
render :text => web_response.body, :status => 400
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/helpers/application_helper.rb
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/helpers/application_helper.rb
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Methods added to this helper will be available to all templates in the application.
|
||||||
|
module ApplicationHelper
|
||||||
|
end
|
||||||
2
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/helpers/login_helper.rb
vendored
Normal file
2
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/helpers/login_helper.rb
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module LoginHelper
|
||||||
|
end
|
||||||
9
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/helpers/server_helper.rb
vendored
Normal file
9
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/helpers/server_helper.rb
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
module ServerHelper
|
||||||
|
|
||||||
|
def url_for_user
|
||||||
|
url_for :controller => 'user', :action => session[:username]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
81
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/views/consumer/index.rhtml
vendored
Normal file
81
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/views/consumer/index.rhtml
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Rails OpenID Example Relying Party</title>
|
||||||
|
</head>
|
||||||
|
<style type="text/css">
|
||||||
|
* {
|
||||||
|
font-family: verdana,sans-serif;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
width: 50em;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
padding: .5em;
|
||||||
|
}
|
||||||
|
.alert {
|
||||||
|
border: 1px solid #e7dc2b;
|
||||||
|
background: #fff888;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
border: 1px solid #ff0000;
|
||||||
|
background: #ffaaaa;
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
border: 1px solid #00ff00;
|
||||||
|
background: #aaffaa;
|
||||||
|
}
|
||||||
|
#verify-form {
|
||||||
|
border: 1px solid #777777;
|
||||||
|
background: #dddddd;
|
||||||
|
margin-top: 1em;
|
||||||
|
padding-bottom: 0em;
|
||||||
|
}
|
||||||
|
input.openid {
|
||||||
|
background: url( /images/openid_login_bg.gif ) no-repeat;
|
||||||
|
background-position: 0 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
padding-left: 18px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<h1>Rails OpenID Example Relying Party</h1>
|
||||||
|
<% if flash[:alert] %>
|
||||||
|
<div class='alert'>
|
||||||
|
<%= h(flash[:alert]) %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if flash[:error] %>
|
||||||
|
<div class='error'>
|
||||||
|
<%= h(flash[:error]) %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if flash[:success] %>
|
||||||
|
<div class='success'>
|
||||||
|
<%= h(flash[:success]) %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if flash[:sreg_results] %>
|
||||||
|
<div class='alert'>
|
||||||
|
<%= flash[:sreg_results] %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if flash[:pape_results] %>
|
||||||
|
<div class='alert'>
|
||||||
|
<%= flash[:pape_results] %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<div id="verify-form">
|
||||||
|
<form method="get" accept-charset="UTF-8"
|
||||||
|
action='<%= url_for :action => 'start' %>'>
|
||||||
|
Identifier:
|
||||||
|
<input type="text" class="openid" name="openid_identifier" />
|
||||||
|
<input type="submit" value="Verify" /><br />
|
||||||
|
<input type="checkbox" name="immediate" id="immediate" /><label for="immediate">Use immediate mode</label><br/>
|
||||||
|
<input type="checkbox" name="use_sreg" id="use_sreg" /><label for="use_sreg">Request registration data</label><br/>
|
||||||
|
<input type="checkbox" name="use_pape" id="use_pape" /><label for="use_pape">Request phishing-resistent auth policy (PAPE)</label><br/>
|
||||||
|
<input type="checkbox" name="force_post" id="force_post" /><label for="force_post">Force the transaction to use POST by adding 2K of extra data</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
68
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/views/layouts/server.rhtml
vendored
Normal file
68
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/views/layouts/server.rhtml
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
<html>
|
||||||
|
<head><title>OpenID Server Example</title></head>
|
||||||
|
<style type="text/css">
|
||||||
|
* {
|
||||||
|
font-family: verdana,sans-serif;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
width: 50em;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
padding: .5em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
margin: none;
|
||||||
|
padding: none;
|
||||||
|
}
|
||||||
|
.notice {
|
||||||
|
border: 1px solid #60964f;
|
||||||
|
background: #b3dca7;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
border: 1px solid #ff0000;
|
||||||
|
background: #ffaaaa;
|
||||||
|
}
|
||||||
|
#login-form {
|
||||||
|
border: 1px solid #777777;
|
||||||
|
background: #dddddd;
|
||||||
|
margin-top: 1em;
|
||||||
|
padding-bottom: 0em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
li {margin-bottom: .5em;}
|
||||||
|
span.openid:before {
|
||||||
|
content: url(<%= @base_url %>images/openid_login_bg.gif) ;
|
||||||
|
}
|
||||||
|
span.openid {
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<% if session[:username] %>
|
||||||
|
<div style="float:right;">
|
||||||
|
Welcome, <%= session[:username] %> | <%= link_to('Log out', :controller => 'login', :action => 'logout') %><br />
|
||||||
|
<span class="openid"><%= @base_url %>user/<%= session[:username] %></span>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<h3>Ruby OpenID Server Example</h3>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<% if flash[:notice] or flash[:error] %>
|
||||||
|
<div class="<%= flash[:notice].nil? ? 'error' : 'notice' %>">
|
||||||
|
<%= flash[:error] or flash[:notice] %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= @content_for_layout %>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
56
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/views/login/index.rhtml
vendored
Normal file
56
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/views/login/index.rhtml
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
|
||||||
|
|
||||||
|
<% if session[:username].nil? %>
|
||||||
|
|
||||||
|
<div id="login-form">
|
||||||
|
<form method="get" action="<%= url_for :controller => 'login', :action => 'submit' %>">
|
||||||
|
Type a username:
|
||||||
|
<input type="text" name="username" />
|
||||||
|
<input type="submit" value="Log In" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<p> Welcome to the Ruby OpenID example. This code is a starting point
|
||||||
|
for developers wishing to implement an OpenID provider or relying
|
||||||
|
party. We've used the <a href="http://rubyonrails.org/">Rails</a>
|
||||||
|
platform to demonstrate, but the library code is not Rails specific.</p>
|
||||||
|
|
||||||
|
<h2>To use the example provider</h2>
|
||||||
|
<p>
|
||||||
|
<ol>
|
||||||
|
|
||||||
|
<li>Enter a username in the form above. You will be "Logged In"
|
||||||
|
to the server, at which point you may authenticate using an OpenID
|
||||||
|
consumer. Your OpenID URL will be displayed after you log
|
||||||
|
in.<p>The server will automatically create an identity page for
|
||||||
|
you at <%= @base_url %>user/<i>name</i></p></li>
|
||||||
|
|
||||||
|
<li><p>Because WEBrick can only handle one thing at a time, you'll need to
|
||||||
|
run another instance of the example on another port if you want to use
|
||||||
|
a relying party to use with this example provider:</p>
|
||||||
|
<blockquote>
|
||||||
|
<code>script/server --port=3001</code>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<p>(The RP needs to be able to access the provider, so unless you're
|
||||||
|
running this example on a public IP, you can't use the live example
|
||||||
|
at <a href="http://openidenabled.com/">openidenabled.com</a> on
|
||||||
|
your local provider.)</p>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>Point your browser to this new instance and follow the directions
|
||||||
|
below.</li>
|
||||||
|
<!-- Fun fact: 'url_for :port => 3001' doesn't work very well. -->
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>To use the example relying party</h2>
|
||||||
|
|
||||||
|
<p>Visit <a href="<%= url_for :controller => 'consumer' %>">/consumer</a>
|
||||||
|
and enter your OpenID.</p>
|
||||||
|
</p>
|
||||||
|
|
||||||
26
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/views/server/decide.rhtml
vendored
Normal file
26
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/app/views/server/decide.rhtml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<form method="post" action="<%= url_for :controller => 'server', :action => 'decision' %>">
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><td>Site:</td><td><%= @oidreq.trust_root %></td></tr>
|
||||||
|
|
||||||
|
<% if @oidreq.id_select %>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
You entered the server identifier at the relying party.
|
||||||
|
You'll need to send an identifier of your choosing. Enter a
|
||||||
|
username below.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Identity to send:</td>
|
||||||
|
<td><input type="text" name="id_to_send" size="25" /></td>
|
||||||
|
</tr>
|
||||||
|
<% else %>
|
||||||
|
<tr><td>Identity:</td><td><%= @oidreq.identity %></td></tr>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<input type="submit" name="yes" value="yes" />
|
||||||
|
<input type="submit" name="no" value="no" />
|
||||||
|
|
||||||
|
</form>
|
||||||
19
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/boot.rb
vendored
Normal file
19
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/boot.rb
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Don't change this file. Configuration is done in config/environment.rb and config/environments/*.rb
|
||||||
|
|
||||||
|
unless defined?(RAILS_ROOT)
|
||||||
|
root_path = File.join(File.dirname(__FILE__), '..')
|
||||||
|
unless RUBY_PLATFORM =~ /mswin32/
|
||||||
|
require 'pathname'
|
||||||
|
root_path = Pathname.new(root_path).cleanpath(true).to_s
|
||||||
|
end
|
||||||
|
RAILS_ROOT = root_path
|
||||||
|
end
|
||||||
|
|
||||||
|
if File.directory?("#{RAILS_ROOT}/vendor/rails")
|
||||||
|
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
||||||
|
else
|
||||||
|
require 'rubygems'
|
||||||
|
require 'initializer'
|
||||||
|
end
|
||||||
|
|
||||||
|
Rails::Initializer.run(:set_load_path)
|
||||||
74
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/database.yml
vendored
Normal file
74
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/database.yml
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
# MySQL (default setup). Versions 4.1 and 5.0 are recommended.
|
||||||
|
#
|
||||||
|
# Get the fast C bindings:
|
||||||
|
# gem install mysql
|
||||||
|
# (on OS X: gem install mysql -- --include=/usr/local/lib)
|
||||||
|
# And be sure to use new-style password hashing:
|
||||||
|
# http://dev.mysql.com/doc/refman/5.0/en/old-client.html
|
||||||
|
development:
|
||||||
|
adapter: sqlite3
|
||||||
|
database: db/development.sqlite3
|
||||||
|
|
||||||
|
# Warning: The database defined as 'test' will be erased and
|
||||||
|
# re-generated from your development database when you run 'rake'.
|
||||||
|
# Do not set this db to the same as development or production.
|
||||||
|
test:
|
||||||
|
adapter: sqlite3
|
||||||
|
database: ":memory:"
|
||||||
|
|
||||||
|
production:
|
||||||
|
adapter: mysql
|
||||||
|
database: rails_server_production
|
||||||
|
username: root
|
||||||
|
password:
|
||||||
|
socket: /path/to/your/mysql.sock
|
||||||
|
|
||||||
|
|
||||||
|
# PostgreSQL versions 7.4 - 8.1
|
||||||
|
#
|
||||||
|
# Get the C bindings:
|
||||||
|
# gem install postgres
|
||||||
|
# or use the pure-Ruby bindings on Windows:
|
||||||
|
# gem install postgres-pr
|
||||||
|
postgresql_example:
|
||||||
|
adapter: postgresql
|
||||||
|
database: rails_server_development
|
||||||
|
username: rails_server
|
||||||
|
password:
|
||||||
|
|
||||||
|
# Connect on a TCP socket. Omitted by default since the client uses a
|
||||||
|
# domain socket that doesn't need configuration.
|
||||||
|
#host: remote-database
|
||||||
|
#port: 5432
|
||||||
|
|
||||||
|
# Schema search path. The server defaults to $user,public
|
||||||
|
#schema_search_path: myapp,sharedapp,public
|
||||||
|
|
||||||
|
# Character set encoding. The server defaults to sql_ascii.
|
||||||
|
#encoding: UTF8
|
||||||
|
|
||||||
|
# Minimum log levels, in increasing order:
|
||||||
|
# debug5, debug4, debug3, debug2, debug1,
|
||||||
|
# info, notice, warning, error, log, fatal, or panic
|
||||||
|
# The server defaults to notice.
|
||||||
|
#min_messages: warning
|
||||||
|
|
||||||
|
|
||||||
|
# SQLite version 2.x
|
||||||
|
# gem install sqlite-ruby
|
||||||
|
sqlite_example:
|
||||||
|
adapter: sqlite
|
||||||
|
database: db/development.sqlite2
|
||||||
|
|
||||||
|
|
||||||
|
# SQLite version 3.x
|
||||||
|
# gem install sqlite3-ruby
|
||||||
|
sqlite3_example:
|
||||||
|
adapter: sqlite3
|
||||||
|
database: db/development.sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
# In-memory SQLite 3 database. Useful for tests.
|
||||||
|
sqlite3_in_memory_example:
|
||||||
|
adapter: sqlite3
|
||||||
|
database: ":memory:"
|
||||||
54
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/environment.rb
vendored
Normal file
54
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/environment.rb
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Be sure to restart your web server when you modify this file.
|
||||||
|
|
||||||
|
# Uncomment below to force Rails into production mode when
|
||||||
|
# you don't control web/app server and can't set it the proper way
|
||||||
|
# ENV['RAILS_ENV'] ||= 'production'
|
||||||
|
|
||||||
|
# Bootstrap the Rails environment, frameworks, and default configuration
|
||||||
|
require File.join(File.dirname(__FILE__), 'boot')
|
||||||
|
|
||||||
|
Rails::Initializer.run do |config|
|
||||||
|
# Settings in config/environments/* take precedence those specified here
|
||||||
|
|
||||||
|
# Skip frameworks you're not going to use
|
||||||
|
# config.frameworks -= [ :action_web_service, :action_mailer ]
|
||||||
|
|
||||||
|
# Add additional load paths for your own custom dirs
|
||||||
|
# config.load_paths += %W( #{RAILS_ROOT}/extras )
|
||||||
|
|
||||||
|
# Force all environments to use the same logger level
|
||||||
|
# (by default production uses :info, the others :debug)
|
||||||
|
# config.log_level = :debug
|
||||||
|
|
||||||
|
# Use the database for sessions instead of the file system
|
||||||
|
# (create the session table with 'rake create_sessions_table')
|
||||||
|
# config.action_controller.session_store = :active_record_store
|
||||||
|
|
||||||
|
# Enable page/fragment caching by setting a file-based store
|
||||||
|
# (remember to create the caching directory and make it readable to the application)
|
||||||
|
# config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/cache"
|
||||||
|
|
||||||
|
# Activate observers that should always be running
|
||||||
|
# config.active_record.observers = :cacher, :garbage_collector
|
||||||
|
|
||||||
|
# Make Active Record use UTC-base instead of local time
|
||||||
|
# config.active_record.default_timezone = :utc
|
||||||
|
|
||||||
|
# Use Active Record's schema dumper instead of SQL when creating the test database
|
||||||
|
# (enables use of different database adapters for development and test environments)
|
||||||
|
# config.active_record.schema_format = :ruby
|
||||||
|
|
||||||
|
# See Rails::Configuration for more options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add new inflection rules using the following format
|
||||||
|
# (all these examples are active by default):
|
||||||
|
# Inflector.inflections do |inflect|
|
||||||
|
# inflect.plural /^(ox)$/i, '\1en'
|
||||||
|
# inflect.singular /^(ox)en/i, '\1'
|
||||||
|
# inflect.irregular 'person', 'people'
|
||||||
|
# inflect.uncountable %w( fish sheep )
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Include your application configuration below
|
||||||
|
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:session_key] = '_session_id_2'
|
||||||
19
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/environments/development.rb
vendored
Normal file
19
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/environments/development.rb
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Settings specified here will take precedence over those in config/environment.rb
|
||||||
|
|
||||||
|
# In the development environment your application's code is reloaded on
|
||||||
|
# every request. This slows down response time but is perfect for development
|
||||||
|
# since you don't have to restart the webserver when you make code changes.
|
||||||
|
config.cache_classes = false
|
||||||
|
|
||||||
|
# Log error messages when you accidentally call methods on nil.
|
||||||
|
config.whiny_nils = true
|
||||||
|
|
||||||
|
# Enable the breakpoint server that script/breakpointer connects to
|
||||||
|
config.breakpoint_server = true
|
||||||
|
|
||||||
|
# Show full error reports and disable caching
|
||||||
|
config.action_controller.consider_all_requests_local = true
|
||||||
|
config.action_controller.perform_caching = false
|
||||||
|
|
||||||
|
# Don't care if the mailer can't send
|
||||||
|
config.action_mailer.raise_delivery_errors = false
|
||||||
19
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/environments/production.rb
vendored
Normal file
19
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/environments/production.rb
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Settings specified here will take precedence over those in config/environment.rb
|
||||||
|
|
||||||
|
# The production environment is meant for finished, "live" apps.
|
||||||
|
# Code is not reloaded between requests
|
||||||
|
config.cache_classes = true
|
||||||
|
|
||||||
|
# Use a different logger for distributed setups
|
||||||
|
# config.logger = SyslogLogger.new
|
||||||
|
|
||||||
|
|
||||||
|
# Full error reports are disabled and caching is turned on
|
||||||
|
config.action_controller.consider_all_requests_local = false
|
||||||
|
config.action_controller.perform_caching = true
|
||||||
|
|
||||||
|
# Enable serving of images, stylesheets, and javascripts from an asset server
|
||||||
|
# config.action_controller.asset_host = "http://assets.example.com"
|
||||||
|
|
||||||
|
# Disable delivery errors if you bad email addresses should just be ignored
|
||||||
|
# config.action_mailer.raise_delivery_errors = false
|
||||||
19
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/environments/test.rb
vendored
Normal file
19
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/environments/test.rb
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Settings specified here will take precedence over those in config/environment.rb
|
||||||
|
|
||||||
|
# The test environment is used exclusively to run your application's
|
||||||
|
# test suite. You never need to work with it otherwise. Remember that
|
||||||
|
# your test database is "scratch space" for the test suite and is wiped
|
||||||
|
# and recreated between test runs. Don't rely on the data there!
|
||||||
|
config.cache_classes = true
|
||||||
|
|
||||||
|
# Log error messages when you accidentally call methods on nil.
|
||||||
|
config.whiny_nils = true
|
||||||
|
|
||||||
|
# Show full error reports and disable caching
|
||||||
|
config.action_controller.consider_all_requests_local = true
|
||||||
|
config.action_controller.perform_caching = false
|
||||||
|
|
||||||
|
# Tell ActionMailer not to deliver emails to the real world.
|
||||||
|
# The :test delivery method accumulates sent emails in the
|
||||||
|
# ActionMailer::Base.deliveries array.
|
||||||
|
config.action_mailer.delivery_method = :test
|
||||||
24
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/routes.rb
vendored
Normal file
24
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/config/routes.rb
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
ActionController::Routing::Routes.draw do |map|
|
||||||
|
# Add your own custom routes here.
|
||||||
|
# The priority is based upon order of creation: first created -> highest priority.
|
||||||
|
|
||||||
|
# Here's a sample route:
|
||||||
|
# map.connect 'products/:id', :controller => 'catalog', :action => 'view'
|
||||||
|
# Keep in mind you can assign values other than :controller and :action
|
||||||
|
|
||||||
|
# You can have the root of your site routed by hooking up ''
|
||||||
|
# -- just remember to delete public/index.html.
|
||||||
|
# map.connect '', :controller => "welcome"
|
||||||
|
|
||||||
|
map.connect '', :controller => 'login'
|
||||||
|
map.connect 'server/xrds', :controller => 'server', :action => 'idp_xrds'
|
||||||
|
map.connect 'user/:username', :controller => 'server', :action => 'user_page'
|
||||||
|
map.connect 'user/:username/xrds', :controller => 'server', :action => 'user_xrds'
|
||||||
|
|
||||||
|
# Allow downloading Web Service WSDL as a file with an extension
|
||||||
|
# instead of a file named 'wsdl'
|
||||||
|
map.connect ':controller/service.wsdl', :action => 'wsdl'
|
||||||
|
|
||||||
|
# Install the default route as the lowest priority.
|
||||||
|
map.connect ':controller/:action/:id'
|
||||||
|
end
|
||||||
2
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/doc/README_FOR_APP
vendored
Normal file
2
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/doc/README_FOR_APP
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Use this README file to introduce your application and point to useful places in the API for learning more.
|
||||||
|
Run "rake appdoc" to generate API documentation for your models and controllers.
|
||||||
8
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/404.html
vendored
Normal file
8
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/404.html
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>File not found</h1>
|
||||||
|
<p>Change this error message for pages not found in public/404.html</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
8
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/500.html
vendored
Normal file
8
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/500.html
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||||
|
"http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Application error (Apache)</h1>
|
||||||
|
<p>Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
12
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/dispatch.cgi
vendored
Normal file
12
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/dispatch.cgi
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/ruby1.8
|
||||||
|
|
||||||
|
#!/usr/local/bin/ruby
|
||||||
|
|
||||||
|
require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
|
||||||
|
|
||||||
|
# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
|
||||||
|
# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
|
||||||
|
require "dispatcher"
|
||||||
|
|
||||||
|
ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
|
||||||
|
Dispatcher.dispatch
|
||||||
26
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/dispatch.fcgi
vendored
Normal file
26
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/dispatch.fcgi
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/usr/bin/ruby1.8
|
||||||
|
|
||||||
|
#!/usr/local/bin/ruby
|
||||||
|
#
|
||||||
|
# You may specify the path to the FastCGI crash log (a log of unhandled
|
||||||
|
# exceptions which forced the FastCGI instance to exit, great for debugging)
|
||||||
|
# and the number of requests to process before running garbage collection.
|
||||||
|
#
|
||||||
|
# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
|
||||||
|
# and the GC period is nil (turned off). A reasonable number of requests
|
||||||
|
# could range from 10-100 depending on the memory footprint of your app.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# # Default log path, normal GC behavior.
|
||||||
|
# RailsFCGIHandler.process!
|
||||||
|
#
|
||||||
|
# # Default log path, 50 requests between GC.
|
||||||
|
# RailsFCGIHandler.process! nil, 50
|
||||||
|
#
|
||||||
|
# # Custom log path, normal GC behavior.
|
||||||
|
# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
|
||||||
|
#
|
||||||
|
require File.dirname(__FILE__) + "/../config/environment"
|
||||||
|
require 'fcgi_handler'
|
||||||
|
|
||||||
|
RailsFCGIHandler.process!
|
||||||
12
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/dispatch.rb
vendored
Normal file
12
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/dispatch.rb
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/ruby1.8
|
||||||
|
|
||||||
|
#!/usr/local/bin/ruby
|
||||||
|
|
||||||
|
require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
|
||||||
|
|
||||||
|
# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
|
||||||
|
# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
|
||||||
|
require "dispatcher"
|
||||||
|
|
||||||
|
ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
|
||||||
|
Dispatcher.dispatch
|
||||||
0
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/favicon.ico
vendored
Normal file
0
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/favicon.ico
vendored
Normal file
BIN
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/images/openid_login_bg.gif
vendored
Normal file
BIN
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/images/openid_login_bg.gif
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 237 B |
750
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/javascripts/controls.js
vendored
Normal file
750
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/javascripts/controls.js
vendored
Normal file
|
|
@ -0,0 +1,750 @@
|
||||||
|
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||||
|
// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
||||||
|
// (c) 2005 Jon Tirsen (http://www.tirsen.com)
|
||||||
|
// Contributors:
|
||||||
|
// Richard Livsey
|
||||||
|
// Rahul Bhargava
|
||||||
|
// Rob Wills
|
||||||
|
//
|
||||||
|
// See scriptaculous.js for full license.
|
||||||
|
|
||||||
|
// Autocompleter.Base handles all the autocompletion functionality
|
||||||
|
// that's independent of the data source for autocompletion. This
|
||||||
|
// includes drawing the autocompletion menu, observing keyboard
|
||||||
|
// and mouse events, and similar.
|
||||||
|
//
|
||||||
|
// Specific autocompleters need to provide, at the very least,
|
||||||
|
// a getUpdatedChoices function that will be invoked every time
|
||||||
|
// the text inside the monitored textbox changes. This method
|
||||||
|
// should get the text for which to provide autocompletion by
|
||||||
|
// invoking this.getToken(), NOT by directly accessing
|
||||||
|
// this.element.value. This is to allow incremental tokenized
|
||||||
|
// autocompletion. Specific auto-completion logic (AJAX, etc)
|
||||||
|
// belongs in getUpdatedChoices.
|
||||||
|
//
|
||||||
|
// Tokenized incremental autocompletion is enabled automatically
|
||||||
|
// when an autocompleter is instantiated with the 'tokens' option
|
||||||
|
// in the options parameter, e.g.:
|
||||||
|
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
|
||||||
|
// will incrementally autocomplete with a comma as the token.
|
||||||
|
// Additionally, ',' in the above example can be replaced with
|
||||||
|
// a token array, e.g. { tokens: [',', '\n'] } which
|
||||||
|
// enables autocompletion on multiple tokens. This is most
|
||||||
|
// useful when one of the tokens is \n (a newline), as it
|
||||||
|
// allows smart autocompletion after linebreaks.
|
||||||
|
|
||||||
|
var Autocompleter = {}
|
||||||
|
Autocompleter.Base = function() {};
|
||||||
|
Autocompleter.Base.prototype = {
|
||||||
|
baseInitialize: function(element, update, options) {
|
||||||
|
this.element = $(element);
|
||||||
|
this.update = $(update);
|
||||||
|
this.hasFocus = false;
|
||||||
|
this.changed = false;
|
||||||
|
this.active = false;
|
||||||
|
this.index = 0;
|
||||||
|
this.entryCount = 0;
|
||||||
|
|
||||||
|
if (this.setOptions)
|
||||||
|
this.setOptions(options);
|
||||||
|
else
|
||||||
|
this.options = options || {};
|
||||||
|
|
||||||
|
this.options.paramName = this.options.paramName || this.element.name;
|
||||||
|
this.options.tokens = this.options.tokens || [];
|
||||||
|
this.options.frequency = this.options.frequency || 0.4;
|
||||||
|
this.options.minChars = this.options.minChars || 1;
|
||||||
|
this.options.onShow = this.options.onShow ||
|
||||||
|
function(element, update){
|
||||||
|
if(!update.style.position || update.style.position=='absolute') {
|
||||||
|
update.style.position = 'absolute';
|
||||||
|
Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
|
||||||
|
}
|
||||||
|
Effect.Appear(update,{duration:0.15});
|
||||||
|
};
|
||||||
|
this.options.onHide = this.options.onHide ||
|
||||||
|
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
|
||||||
|
|
||||||
|
if (typeof(this.options.tokens) == 'string')
|
||||||
|
this.options.tokens = new Array(this.options.tokens);
|
||||||
|
|
||||||
|
this.observer = null;
|
||||||
|
|
||||||
|
this.element.setAttribute('autocomplete','off');
|
||||||
|
|
||||||
|
Element.hide(this.update);
|
||||||
|
|
||||||
|
Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
|
||||||
|
Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
||||||
|
if(!this.iefix &&
|
||||||
|
(navigator.appVersion.indexOf('MSIE')>0) &&
|
||||||
|
(navigator.userAgent.indexOf('Opera')<0) &&
|
||||||
|
(Element.getStyle(this.update, 'position')=='absolute')) {
|
||||||
|
new Insertion.After(this.update,
|
||||||
|
'<iframe id="' + this.update.id + '_iefix" '+
|
||||||
|
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
||||||
|
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
|
||||||
|
this.iefix = $(this.update.id+'_iefix');
|
||||||
|
}
|
||||||
|
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
|
||||||
|
},
|
||||||
|
|
||||||
|
fixIEOverlapping: function() {
|
||||||
|
Position.clone(this.update, this.iefix);
|
||||||
|
this.iefix.style.zIndex = 1;
|
||||||
|
this.update.style.zIndex = 2;
|
||||||
|
Element.show(this.iefix);
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
this.stopIndicator();
|
||||||
|
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
|
||||||
|
if(this.iefix) Element.hide(this.iefix);
|
||||||
|
},
|
||||||
|
|
||||||
|
startIndicator: function() {
|
||||||
|
if(this.options.indicator) Element.show(this.options.indicator);
|
||||||
|
},
|
||||||
|
|
||||||
|
stopIndicator: function() {
|
||||||
|
if(this.options.indicator) Element.hide(this.options.indicator);
|
||||||
|
},
|
||||||
|
|
||||||
|
onKeyPress: function(event) {
|
||||||
|
if(this.active)
|
||||||
|
switch(event.keyCode) {
|
||||||
|
case Event.KEY_TAB:
|
||||||
|
case Event.KEY_RETURN:
|
||||||
|
this.selectEntry();
|
||||||
|
Event.stop(event);
|
||||||
|
case Event.KEY_ESC:
|
||||||
|
this.hide();
|
||||||
|
this.active = false;
|
||||||
|
Event.stop(event);
|
||||||
|
return;
|
||||||
|
case Event.KEY_LEFT:
|
||||||
|
case Event.KEY_RIGHT:
|
||||||
|
return;
|
||||||
|
case Event.KEY_UP:
|
||||||
|
this.markPrevious();
|
||||||
|
this.render();
|
||||||
|
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
|
||||||
|
return;
|
||||||
|
case Event.KEY_DOWN:
|
||||||
|
this.markNext();
|
||||||
|
this.render();
|
||||||
|
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.changed = true;
|
||||||
|
this.hasFocus = true;
|
||||||
|
|
||||||
|
if(this.observer) clearTimeout(this.observer);
|
||||||
|
this.observer =
|
||||||
|
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
|
||||||
|
},
|
||||||
|
|
||||||
|
onHover: function(event) {
|
||||||
|
var element = Event.findElement(event, 'LI');
|
||||||
|
if(this.index != element.autocompleteIndex)
|
||||||
|
{
|
||||||
|
this.index = element.autocompleteIndex;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
Event.stop(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
onClick: function(event) {
|
||||||
|
var element = Event.findElement(event, 'LI');
|
||||||
|
this.index = element.autocompleteIndex;
|
||||||
|
this.selectEntry();
|
||||||
|
this.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
onBlur: function(event) {
|
||||||
|
// needed to make click events working
|
||||||
|
setTimeout(this.hide.bind(this), 250);
|
||||||
|
this.hasFocus = false;
|
||||||
|
this.active = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
if(this.entryCount > 0) {
|
||||||
|
for (var i = 0; i < this.entryCount; i++)
|
||||||
|
this.index==i ?
|
||||||
|
Element.addClassName(this.getEntry(i),"selected") :
|
||||||
|
Element.removeClassName(this.getEntry(i),"selected");
|
||||||
|
|
||||||
|
if(this.hasFocus) {
|
||||||
|
this.show();
|
||||||
|
this.active = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.active = false;
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
markPrevious: function() {
|
||||||
|
if(this.index > 0) this.index--
|
||||||
|
else this.index = this.entryCount-1;
|
||||||
|
},
|
||||||
|
|
||||||
|
markNext: function() {
|
||||||
|
if(this.index < this.entryCount-1) this.index++
|
||||||
|
else this.index = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEntry: function(index) {
|
||||||
|
return this.update.firstChild.childNodes[index];
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurrentEntry: function() {
|
||||||
|
return this.getEntry(this.index);
|
||||||
|
},
|
||||||
|
|
||||||
|
selectEntry: function() {
|
||||||
|
this.active = false;
|
||||||
|
this.updateElement(this.getCurrentEntry());
|
||||||
|
},
|
||||||
|
|
||||||
|
updateElement: function(selectedElement) {
|
||||||
|
if (this.options.updateElement) {
|
||||||
|
this.options.updateElement(selectedElement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
|
||||||
|
var lastTokenPos = this.findLastToken();
|
||||||
|
if (lastTokenPos != -1) {
|
||||||
|
var newValue = this.element.value.substr(0, lastTokenPos + 1);
|
||||||
|
var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
|
||||||
|
if (whitespace)
|
||||||
|
newValue += whitespace[0];
|
||||||
|
this.element.value = newValue + value;
|
||||||
|
} else {
|
||||||
|
this.element.value = value;
|
||||||
|
}
|
||||||
|
this.element.focus();
|
||||||
|
|
||||||
|
if (this.options.afterUpdateElement)
|
||||||
|
this.options.afterUpdateElement(this.element, selectedElement);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateChoices: function(choices) {
|
||||||
|
if(!this.changed && this.hasFocus) {
|
||||||
|
this.update.innerHTML = choices;
|
||||||
|
Element.cleanWhitespace(this.update);
|
||||||
|
Element.cleanWhitespace(this.update.firstChild);
|
||||||
|
|
||||||
|
if(this.update.firstChild && this.update.firstChild.childNodes) {
|
||||||
|
this.entryCount =
|
||||||
|
this.update.firstChild.childNodes.length;
|
||||||
|
for (var i = 0; i < this.entryCount; i++) {
|
||||||
|
var entry = this.getEntry(i);
|
||||||
|
entry.autocompleteIndex = i;
|
||||||
|
this.addObservers(entry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.entryCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stopIndicator();
|
||||||
|
|
||||||
|
this.index = 0;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addObservers: function(element) {
|
||||||
|
Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
|
||||||
|
Event.observe(element, "click", this.onClick.bindAsEventListener(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
onObserverEvent: function() {
|
||||||
|
this.changed = false;
|
||||||
|
if(this.getToken().length>=this.options.minChars) {
|
||||||
|
this.startIndicator();
|
||||||
|
this.getUpdatedChoices();
|
||||||
|
} else {
|
||||||
|
this.active = false;
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getToken: function() {
|
||||||
|
var tokenPos = this.findLastToken();
|
||||||
|
if (tokenPos != -1)
|
||||||
|
var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
|
||||||
|
else
|
||||||
|
var ret = this.element.value;
|
||||||
|
|
||||||
|
return /\n/.test(ret) ? '' : ret;
|
||||||
|
},
|
||||||
|
|
||||||
|
findLastToken: function() {
|
||||||
|
var lastTokenPos = -1;
|
||||||
|
|
||||||
|
for (var i=0; i<this.options.tokens.length; i++) {
|
||||||
|
var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
|
||||||
|
if (thisTokenPos > lastTokenPos)
|
||||||
|
lastTokenPos = thisTokenPos;
|
||||||
|
}
|
||||||
|
return lastTokenPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ajax.Autocompleter = Class.create();
|
||||||
|
Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
|
||||||
|
initialize: function(element, update, url, options) {
|
||||||
|
this.baseInitialize(element, update, options);
|
||||||
|
this.options.asynchronous = true;
|
||||||
|
this.options.onComplete = this.onComplete.bind(this);
|
||||||
|
this.options.defaultParams = this.options.parameters || null;
|
||||||
|
this.url = url;
|
||||||
|
},
|
||||||
|
|
||||||
|
getUpdatedChoices: function() {
|
||||||
|
entry = encodeURIComponent(this.options.paramName) + '=' +
|
||||||
|
encodeURIComponent(this.getToken());
|
||||||
|
|
||||||
|
this.options.parameters = this.options.callback ?
|
||||||
|
this.options.callback(this.element, entry) : entry;
|
||||||
|
|
||||||
|
if(this.options.defaultParams)
|
||||||
|
this.options.parameters += '&' + this.options.defaultParams;
|
||||||
|
|
||||||
|
new Ajax.Request(this.url, this.options);
|
||||||
|
},
|
||||||
|
|
||||||
|
onComplete: function(request) {
|
||||||
|
this.updateChoices(request.responseText);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// The local array autocompleter. Used when you'd prefer to
|
||||||
|
// inject an array of autocompletion options into the page, rather
|
||||||
|
// than sending out Ajax queries, which can be quite slow sometimes.
|
||||||
|
//
|
||||||
|
// The constructor takes four parameters. The first two are, as usual,
|
||||||
|
// the id of the monitored textbox, and id of the autocompletion menu.
|
||||||
|
// The third is the array you want to autocomplete from, and the fourth
|
||||||
|
// is the options block.
|
||||||
|
//
|
||||||
|
// Extra local autocompletion options:
|
||||||
|
// - choices - How many autocompletion choices to offer
|
||||||
|
//
|
||||||
|
// - partialSearch - If false, the autocompleter will match entered
|
||||||
|
// text only at the beginning of strings in the
|
||||||
|
// autocomplete array. Defaults to true, which will
|
||||||
|
// match text at the beginning of any *word* in the
|
||||||
|
// strings in the autocomplete array. If you want to
|
||||||
|
// search anywhere in the string, additionally set
|
||||||
|
// the option fullSearch to true (default: off).
|
||||||
|
//
|
||||||
|
// - fullSsearch - Search anywhere in autocomplete array strings.
|
||||||
|
//
|
||||||
|
// - partialChars - How many characters to enter before triggering
|
||||||
|
// a partial match (unlike minChars, which defines
|
||||||
|
// how many characters are required to do any match
|
||||||
|
// at all). Defaults to 2.
|
||||||
|
//
|
||||||
|
// - ignoreCase - Whether to ignore case when autocompleting.
|
||||||
|
// Defaults to true.
|
||||||
|
//
|
||||||
|
// It's possible to pass in a custom function as the 'selector'
|
||||||
|
// option, if you prefer to write your own autocompletion logic.
|
||||||
|
// In that case, the other options above will not apply unless
|
||||||
|
// you support them.
|
||||||
|
|
||||||
|
Autocompleter.Local = Class.create();
|
||||||
|
Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
|
||||||
|
initialize: function(element, update, array, options) {
|
||||||
|
this.baseInitialize(element, update, options);
|
||||||
|
this.options.array = array;
|
||||||
|
},
|
||||||
|
|
||||||
|
getUpdatedChoices: function() {
|
||||||
|
this.updateChoices(this.options.selector(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
setOptions: function(options) {
|
||||||
|
this.options = Object.extend({
|
||||||
|
choices: 10,
|
||||||
|
partialSearch: true,
|
||||||
|
partialChars: 2,
|
||||||
|
ignoreCase: true,
|
||||||
|
fullSearch: false,
|
||||||
|
selector: function(instance) {
|
||||||
|
var ret = []; // Beginning matches
|
||||||
|
var partial = []; // Inside matches
|
||||||
|
var entry = instance.getToken();
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < instance.options.array.length &&
|
||||||
|
ret.length < instance.options.choices ; i++) {
|
||||||
|
|
||||||
|
var elem = instance.options.array[i];
|
||||||
|
var foundPos = instance.options.ignoreCase ?
|
||||||
|
elem.toLowerCase().indexOf(entry.toLowerCase()) :
|
||||||
|
elem.indexOf(entry);
|
||||||
|
|
||||||
|
while (foundPos != -1) {
|
||||||
|
if (foundPos == 0 && elem.length != entry.length) {
|
||||||
|
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
|
||||||
|
elem.substr(entry.length) + "</li>");
|
||||||
|
break;
|
||||||
|
} else if (entry.length >= instance.options.partialChars &&
|
||||||
|
instance.options.partialSearch && foundPos != -1) {
|
||||||
|
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
|
||||||
|
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
|
||||||
|
elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
|
||||||
|
foundPos + entry.length) + "</li>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foundPos = instance.options.ignoreCase ?
|
||||||
|
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
|
||||||
|
elem.indexOf(entry, foundPos + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (partial.length)
|
||||||
|
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
|
||||||
|
return "<ul>" + ret.join('') + "</ul>";
|
||||||
|
}
|
||||||
|
}, options || {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// AJAX in-place editor
|
||||||
|
//
|
||||||
|
// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
|
||||||
|
|
||||||
|
// Use this if you notice weird scrolling problems on some browsers,
|
||||||
|
// the DOM might be a bit confused when this gets called so do this
|
||||||
|
// waits 1 ms (with setTimeout) until it does the activation
|
||||||
|
Field.scrollFreeActivate = function(field) {
|
||||||
|
setTimeout(function() {
|
||||||
|
Field.activate(field);
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ajax.InPlaceEditor = Class.create();
|
||||||
|
Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
|
||||||
|
Ajax.InPlaceEditor.prototype = {
|
||||||
|
initialize: function(element, url, options) {
|
||||||
|
this.url = url;
|
||||||
|
this.element = $(element);
|
||||||
|
|
||||||
|
this.options = Object.extend({
|
||||||
|
okText: "ok",
|
||||||
|
cancelText: "cancel",
|
||||||
|
savingText: "Saving...",
|
||||||
|
clickToEditText: "Click to edit",
|
||||||
|
okText: "ok",
|
||||||
|
rows: 1,
|
||||||
|
onComplete: function(transport, element) {
|
||||||
|
new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
|
||||||
|
},
|
||||||
|
onFailure: function(transport) {
|
||||||
|
alert("Error communicating with the server: " + transport.responseText.stripTags());
|
||||||
|
},
|
||||||
|
callback: function(form) {
|
||||||
|
return Form.serialize(form);
|
||||||
|
},
|
||||||
|
handleLineBreaks: true,
|
||||||
|
loadingText: 'Loading...',
|
||||||
|
savingClassName: 'inplaceeditor-saving',
|
||||||
|
loadingClassName: 'inplaceeditor-loading',
|
||||||
|
formClassName: 'inplaceeditor-form',
|
||||||
|
highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
|
||||||
|
highlightendcolor: "#FFFFFF",
|
||||||
|
externalControl: null,
|
||||||
|
ajaxOptions: {}
|
||||||
|
}, options || {});
|
||||||
|
|
||||||
|
if(!this.options.formId && this.element.id) {
|
||||||
|
this.options.formId = this.element.id + "-inplaceeditor";
|
||||||
|
if ($(this.options.formId)) {
|
||||||
|
// there's already a form with that name, don't specify an id
|
||||||
|
this.options.formId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.externalControl) {
|
||||||
|
this.options.externalControl = $(this.options.externalControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.originalBackground = Element.getStyle(this.element, 'background-color');
|
||||||
|
if (!this.originalBackground) {
|
||||||
|
this.originalBackground = "transparent";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.title = this.options.clickToEditText;
|
||||||
|
|
||||||
|
this.onclickListener = this.enterEditMode.bindAsEventListener(this);
|
||||||
|
this.mouseoverListener = this.enterHover.bindAsEventListener(this);
|
||||||
|
this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
|
||||||
|
Event.observe(this.element, 'click', this.onclickListener);
|
||||||
|
Event.observe(this.element, 'mouseover', this.mouseoverListener);
|
||||||
|
Event.observe(this.element, 'mouseout', this.mouseoutListener);
|
||||||
|
if (this.options.externalControl) {
|
||||||
|
Event.observe(this.options.externalControl, 'click', this.onclickListener);
|
||||||
|
Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
|
||||||
|
Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enterEditMode: function(evt) {
|
||||||
|
if (this.saving) return;
|
||||||
|
if (this.editing) return;
|
||||||
|
this.editing = true;
|
||||||
|
this.onEnterEditMode();
|
||||||
|
if (this.options.externalControl) {
|
||||||
|
Element.hide(this.options.externalControl);
|
||||||
|
}
|
||||||
|
Element.hide(this.element);
|
||||||
|
this.createForm();
|
||||||
|
this.element.parentNode.insertBefore(this.form, this.element);
|
||||||
|
Field.scrollFreeActivate(this.editField);
|
||||||
|
// stop the event to avoid a page refresh in Safari
|
||||||
|
if (evt) {
|
||||||
|
Event.stop(evt);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
createForm: function() {
|
||||||
|
this.form = document.createElement("form");
|
||||||
|
this.form.id = this.options.formId;
|
||||||
|
Element.addClassName(this.form, this.options.formClassName)
|
||||||
|
this.form.onsubmit = this.onSubmit.bind(this);
|
||||||
|
|
||||||
|
this.createEditField();
|
||||||
|
|
||||||
|
if (this.options.textarea) {
|
||||||
|
var br = document.createElement("br");
|
||||||
|
this.form.appendChild(br);
|
||||||
|
}
|
||||||
|
|
||||||
|
okButton = document.createElement("input");
|
||||||
|
okButton.type = "submit";
|
||||||
|
okButton.value = this.options.okText;
|
||||||
|
this.form.appendChild(okButton);
|
||||||
|
|
||||||
|
cancelLink = document.createElement("a");
|
||||||
|
cancelLink.href = "#";
|
||||||
|
cancelLink.appendChild(document.createTextNode(this.options.cancelText));
|
||||||
|
cancelLink.onclick = this.onclickCancel.bind(this);
|
||||||
|
this.form.appendChild(cancelLink);
|
||||||
|
},
|
||||||
|
hasHTMLLineBreaks: function(string) {
|
||||||
|
if (!this.options.handleLineBreaks) return false;
|
||||||
|
return string.match(/<br/i) || string.match(/<p>/i);
|
||||||
|
},
|
||||||
|
convertHTMLLineBreaks: function(string) {
|
||||||
|
return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
|
||||||
|
},
|
||||||
|
createEditField: function() {
|
||||||
|
var text;
|
||||||
|
if(this.options.loadTextURL) {
|
||||||
|
text = this.options.loadingText;
|
||||||
|
} else {
|
||||||
|
text = this.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
|
||||||
|
this.options.textarea = false;
|
||||||
|
var textField = document.createElement("input");
|
||||||
|
textField.type = "text";
|
||||||
|
textField.name = "value";
|
||||||
|
textField.value = text;
|
||||||
|
textField.style.backgroundColor = this.options.highlightcolor;
|
||||||
|
var size = this.options.size || this.options.cols || 0;
|
||||||
|
if (size != 0) textField.size = size;
|
||||||
|
this.editField = textField;
|
||||||
|
} else {
|
||||||
|
this.options.textarea = true;
|
||||||
|
var textArea = document.createElement("textarea");
|
||||||
|
textArea.name = "value";
|
||||||
|
textArea.value = this.convertHTMLLineBreaks(text);
|
||||||
|
textArea.rows = this.options.rows;
|
||||||
|
textArea.cols = this.options.cols || 40;
|
||||||
|
this.editField = textArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.options.loadTextURL) {
|
||||||
|
this.loadExternalText();
|
||||||
|
}
|
||||||
|
this.form.appendChild(this.editField);
|
||||||
|
},
|
||||||
|
getText: function() {
|
||||||
|
return this.element.innerHTML;
|
||||||
|
},
|
||||||
|
loadExternalText: function() {
|
||||||
|
Element.addClassName(this.form, this.options.loadingClassName);
|
||||||
|
this.editField.disabled = true;
|
||||||
|
new Ajax.Request(
|
||||||
|
this.options.loadTextURL,
|
||||||
|
Object.extend({
|
||||||
|
asynchronous: true,
|
||||||
|
onComplete: this.onLoadedExternalText.bind(this)
|
||||||
|
}, this.options.ajaxOptions)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onLoadedExternalText: function(transport) {
|
||||||
|
Element.removeClassName(this.form, this.options.loadingClassName);
|
||||||
|
this.editField.disabled = false;
|
||||||
|
this.editField.value = transport.responseText.stripTags();
|
||||||
|
},
|
||||||
|
onclickCancel: function() {
|
||||||
|
this.onComplete();
|
||||||
|
this.leaveEditMode();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onFailure: function(transport) {
|
||||||
|
this.options.onFailure(transport);
|
||||||
|
if (this.oldInnerHTML) {
|
||||||
|
this.element.innerHTML = this.oldInnerHTML;
|
||||||
|
this.oldInnerHTML = null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onSubmit: function() {
|
||||||
|
// onLoading resets these so we need to save them away for the Ajax call
|
||||||
|
var form = this.form;
|
||||||
|
var value = this.editField.value;
|
||||||
|
|
||||||
|
// do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
|
||||||
|
// which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
|
||||||
|
// to be displayed indefinitely
|
||||||
|
this.onLoading();
|
||||||
|
|
||||||
|
new Ajax.Updater(
|
||||||
|
{
|
||||||
|
success: this.element,
|
||||||
|
// don't update on failure (this could be an option)
|
||||||
|
failure: null
|
||||||
|
},
|
||||||
|
this.url,
|
||||||
|
Object.extend({
|
||||||
|
parameters: this.options.callback(form, value),
|
||||||
|
onComplete: this.onComplete.bind(this),
|
||||||
|
onFailure: this.onFailure.bind(this)
|
||||||
|
}, this.options.ajaxOptions)
|
||||||
|
);
|
||||||
|
// stop the event to avoid a page refresh in Safari
|
||||||
|
if (arguments.length > 1) {
|
||||||
|
Event.stop(arguments[0]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onLoading: function() {
|
||||||
|
this.saving = true;
|
||||||
|
this.removeForm();
|
||||||
|
this.leaveHover();
|
||||||
|
this.showSaving();
|
||||||
|
},
|
||||||
|
showSaving: function() {
|
||||||
|
this.oldInnerHTML = this.element.innerHTML;
|
||||||
|
this.element.innerHTML = this.options.savingText;
|
||||||
|
Element.addClassName(this.element, this.options.savingClassName);
|
||||||
|
this.element.style.backgroundColor = this.originalBackground;
|
||||||
|
Element.show(this.element);
|
||||||
|
},
|
||||||
|
removeForm: function() {
|
||||||
|
if(this.form) {
|
||||||
|
if (this.form.parentNode) Element.remove(this.form);
|
||||||
|
this.form = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enterHover: function() {
|
||||||
|
if (this.saving) return;
|
||||||
|
this.element.style.backgroundColor = this.options.highlightcolor;
|
||||||
|
if (this.effect) {
|
||||||
|
this.effect.cancel();
|
||||||
|
}
|
||||||
|
Element.addClassName(this.element, this.options.hoverClassName)
|
||||||
|
},
|
||||||
|
leaveHover: function() {
|
||||||
|
if (this.options.backgroundColor) {
|
||||||
|
this.element.style.backgroundColor = this.oldBackground;
|
||||||
|
}
|
||||||
|
Element.removeClassName(this.element, this.options.hoverClassName)
|
||||||
|
if (this.saving) return;
|
||||||
|
this.effect = new Effect.Highlight(this.element, {
|
||||||
|
startcolor: this.options.highlightcolor,
|
||||||
|
endcolor: this.options.highlightendcolor,
|
||||||
|
restorecolor: this.originalBackground
|
||||||
|
});
|
||||||
|
},
|
||||||
|
leaveEditMode: function() {
|
||||||
|
Element.removeClassName(this.element, this.options.savingClassName);
|
||||||
|
this.removeForm();
|
||||||
|
this.leaveHover();
|
||||||
|
this.element.style.backgroundColor = this.originalBackground;
|
||||||
|
Element.show(this.element);
|
||||||
|
if (this.options.externalControl) {
|
||||||
|
Element.show(this.options.externalControl);
|
||||||
|
}
|
||||||
|
this.editing = false;
|
||||||
|
this.saving = false;
|
||||||
|
this.oldInnerHTML = null;
|
||||||
|
this.onLeaveEditMode();
|
||||||
|
},
|
||||||
|
onComplete: function(transport) {
|
||||||
|
this.leaveEditMode();
|
||||||
|
this.options.onComplete.bind(this)(transport, this.element);
|
||||||
|
},
|
||||||
|
onEnterEditMode: function() {},
|
||||||
|
onLeaveEditMode: function() {},
|
||||||
|
dispose: function() {
|
||||||
|
if (this.oldInnerHTML) {
|
||||||
|
this.element.innerHTML = this.oldInnerHTML;
|
||||||
|
}
|
||||||
|
this.leaveEditMode();
|
||||||
|
Event.stopObserving(this.element, 'click', this.onclickListener);
|
||||||
|
Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
|
||||||
|
Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
|
||||||
|
if (this.options.externalControl) {
|
||||||
|
Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
|
||||||
|
Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
|
||||||
|
Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delayed observer, like Form.Element.Observer,
|
||||||
|
// but waits for delay after last key input
|
||||||
|
// Ideal for live-search fields
|
||||||
|
|
||||||
|
Form.Element.DelayedObserver = Class.create();
|
||||||
|
Form.Element.DelayedObserver.prototype = {
|
||||||
|
initialize: function(element, delay, callback) {
|
||||||
|
this.delay = delay || 0.5;
|
||||||
|
this.element = $(element);
|
||||||
|
this.callback = callback;
|
||||||
|
this.timer = null;
|
||||||
|
this.lastValue = $F(this.element);
|
||||||
|
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
||||||
|
},
|
||||||
|
delayedListener: function(event) {
|
||||||
|
if(this.lastValue == $F(this.element)) return;
|
||||||
|
if(this.timer) clearTimeout(this.timer);
|
||||||
|
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
|
||||||
|
this.lastValue = $F(this.element);
|
||||||
|
},
|
||||||
|
onTimerEvent: function() {
|
||||||
|
this.timer = null;
|
||||||
|
this.callback(this.element, $F(this.element));
|
||||||
|
}
|
||||||
|
};
|
||||||
584
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/javascripts/dragdrop.js
vendored
Normal file
584
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/javascripts/dragdrop.js
vendored
Normal file
|
|
@ -0,0 +1,584 @@
|
||||||
|
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||||
|
//
|
||||||
|
// See scriptaculous.js for full license.
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
var Droppables = {
|
||||||
|
drops: [],
|
||||||
|
|
||||||
|
remove: function(element) {
|
||||||
|
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
|
||||||
|
},
|
||||||
|
|
||||||
|
add: function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var options = Object.extend({
|
||||||
|
greedy: true,
|
||||||
|
hoverclass: null
|
||||||
|
}, arguments[1] || {});
|
||||||
|
|
||||||
|
// cache containers
|
||||||
|
if(options.containment) {
|
||||||
|
options._containers = [];
|
||||||
|
var containment = options.containment;
|
||||||
|
if((typeof containment == 'object') &&
|
||||||
|
(containment.constructor == Array)) {
|
||||||
|
containment.each( function(c) { options._containers.push($(c)) });
|
||||||
|
} else {
|
||||||
|
options._containers.push($(containment));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.accept) options.accept = [options.accept].flatten();
|
||||||
|
|
||||||
|
Element.makePositioned(element); // fix IE
|
||||||
|
options.element = element;
|
||||||
|
|
||||||
|
this.drops.push(options);
|
||||||
|
},
|
||||||
|
|
||||||
|
isContained: function(element, drop) {
|
||||||
|
var parentNode = element.parentNode;
|
||||||
|
return drop._containers.detect(function(c) { return parentNode == c });
|
||||||
|
},
|
||||||
|
|
||||||
|
isAffected: function(point, element, drop) {
|
||||||
|
return (
|
||||||
|
(drop.element!=element) &&
|
||||||
|
((!drop._containers) ||
|
||||||
|
this.isContained(element, drop)) &&
|
||||||
|
((!drop.accept) ||
|
||||||
|
(Element.classNames(element).detect(
|
||||||
|
function(v) { return drop.accept.include(v) } ) )) &&
|
||||||
|
Position.within(drop.element, point[0], point[1]) );
|
||||||
|
},
|
||||||
|
|
||||||
|
deactivate: function(drop) {
|
||||||
|
if(drop.hoverclass)
|
||||||
|
Element.removeClassName(drop.element, drop.hoverclass);
|
||||||
|
this.last_active = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
activate: function(drop) {
|
||||||
|
if(drop.hoverclass)
|
||||||
|
Element.addClassName(drop.element, drop.hoverclass);
|
||||||
|
this.last_active = drop;
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function(point, element) {
|
||||||
|
if(!this.drops.length) return;
|
||||||
|
|
||||||
|
if(this.last_active) this.deactivate(this.last_active);
|
||||||
|
this.drops.each( function(drop) {
|
||||||
|
if(Droppables.isAffected(point, element, drop)) {
|
||||||
|
if(drop.onHover)
|
||||||
|
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
||||||
|
if(drop.greedy) {
|
||||||
|
Droppables.activate(drop);
|
||||||
|
throw $break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
fire: function(event, element) {
|
||||||
|
if(!this.last_active) return;
|
||||||
|
Position.prepare();
|
||||||
|
|
||||||
|
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
|
||||||
|
if (this.last_active.onDrop)
|
||||||
|
this.last_active.onDrop(element, this.last_active.element, event);
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
if(this.last_active)
|
||||||
|
this.deactivate(this.last_active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var Draggables = {
|
||||||
|
drags: [],
|
||||||
|
observers: [],
|
||||||
|
|
||||||
|
register: function(draggable) {
|
||||||
|
if(this.drags.length == 0) {
|
||||||
|
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
|
||||||
|
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
|
||||||
|
this.eventKeypress = this.keyPress.bindAsEventListener(this);
|
||||||
|
|
||||||
|
Event.observe(document, "mouseup", this.eventMouseUp);
|
||||||
|
Event.observe(document, "mousemove", this.eventMouseMove);
|
||||||
|
Event.observe(document, "keypress", this.eventKeypress);
|
||||||
|
}
|
||||||
|
this.drags.push(draggable);
|
||||||
|
},
|
||||||
|
|
||||||
|
unregister: function(draggable) {
|
||||||
|
this.drags = this.drags.reject(function(d) { return d==draggable });
|
||||||
|
if(this.drags.length == 0) {
|
||||||
|
Event.stopObserving(document, "mouseup", this.eventMouseUp);
|
||||||
|
Event.stopObserving(document, "mousemove", this.eventMouseMove);
|
||||||
|
Event.stopObserving(document, "keypress", this.eventKeypress);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
activate: function(draggable) {
|
||||||
|
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
|
||||||
|
this.activeDraggable = draggable;
|
||||||
|
},
|
||||||
|
|
||||||
|
deactivate: function(draggbale) {
|
||||||
|
this.activeDraggable = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateDrag: function(event) {
|
||||||
|
if(!this.activeDraggable) return;
|
||||||
|
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||||
|
// Mozilla-based browsers fire successive mousemove events with
|
||||||
|
// the same coordinates, prevent needless redrawing (moz bug?)
|
||||||
|
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
|
||||||
|
this._lastPointer = pointer;
|
||||||
|
this.activeDraggable.updateDrag(event, pointer);
|
||||||
|
},
|
||||||
|
|
||||||
|
endDrag: function(event) {
|
||||||
|
if(!this.activeDraggable) return;
|
||||||
|
this._lastPointer = null;
|
||||||
|
this.activeDraggable.endDrag(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
keyPress: function(event) {
|
||||||
|
if(this.activeDraggable)
|
||||||
|
this.activeDraggable.keyPress(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
addObserver: function(observer) {
|
||||||
|
this.observers.push(observer);
|
||||||
|
this._cacheObserverCallbacks();
|
||||||
|
},
|
||||||
|
|
||||||
|
removeObserver: function(element) { // element instead of observer fixes mem leaks
|
||||||
|
this.observers = this.observers.reject( function(o) { return o.element==element });
|
||||||
|
this._cacheObserverCallbacks();
|
||||||
|
},
|
||||||
|
|
||||||
|
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
|
||||||
|
if(this[eventName+'Count'] > 0)
|
||||||
|
this.observers.each( function(o) {
|
||||||
|
if(o[eventName]) o[eventName](eventName, draggable, event);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_cacheObserverCallbacks: function() {
|
||||||
|
['onStart','onEnd','onDrag'].each( function(eventName) {
|
||||||
|
Draggables[eventName+'Count'] = Draggables.observers.select(
|
||||||
|
function(o) { return o[eventName]; }
|
||||||
|
).length;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
var Draggable = Class.create();
|
||||||
|
Draggable.prototype = {
|
||||||
|
initialize: function(element) {
|
||||||
|
var options = Object.extend({
|
||||||
|
handle: false,
|
||||||
|
starteffect: function(element) {
|
||||||
|
new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
|
||||||
|
},
|
||||||
|
reverteffect: function(element, top_offset, left_offset) {
|
||||||
|
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
|
||||||
|
element._revert = new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
|
||||||
|
},
|
||||||
|
endeffect: function(element) {
|
||||||
|
new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
|
||||||
|
},
|
||||||
|
zindex: 1000,
|
||||||
|
revert: false,
|
||||||
|
snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
|
||||||
|
}, arguments[1] || {});
|
||||||
|
|
||||||
|
this.element = $(element);
|
||||||
|
|
||||||
|
if(options.handle && (typeof options.handle == 'string'))
|
||||||
|
this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
|
||||||
|
if(!this.handle) this.handle = $(options.handle);
|
||||||
|
if(!this.handle) this.handle = this.element;
|
||||||
|
|
||||||
|
Element.makePositioned(this.element); // fix IE
|
||||||
|
|
||||||
|
this.delta = this.currentDelta();
|
||||||
|
this.options = options;
|
||||||
|
this.dragging = false;
|
||||||
|
|
||||||
|
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
|
||||||
|
Event.observe(this.handle, "mousedown", this.eventMouseDown);
|
||||||
|
|
||||||
|
Draggables.register(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
|
||||||
|
Draggables.unregister(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
currentDelta: function() {
|
||||||
|
return([
|
||||||
|
parseInt(this.element.style.left || '0'),
|
||||||
|
parseInt(this.element.style.top || '0')]);
|
||||||
|
},
|
||||||
|
|
||||||
|
initDrag: function(event) {
|
||||||
|
if(Event.isLeftClick(event)) {
|
||||||
|
// abort on form elements, fixes a Firefox issue
|
||||||
|
var src = Event.element(event);
|
||||||
|
if(src.tagName && (
|
||||||
|
src.tagName=='INPUT' ||
|
||||||
|
src.tagName=='SELECT' ||
|
||||||
|
src.tagName=='BUTTON' ||
|
||||||
|
src.tagName=='TEXTAREA')) return;
|
||||||
|
|
||||||
|
if(this.element._revert) {
|
||||||
|
this.element._revert.cancel();
|
||||||
|
this.element._revert = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||||
|
var pos = Position.cumulativeOffset(this.element);
|
||||||
|
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
|
||||||
|
|
||||||
|
Draggables.activate(this);
|
||||||
|
Event.stop(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
startDrag: function(event) {
|
||||||
|
this.dragging = true;
|
||||||
|
|
||||||
|
if(this.options.zindex) {
|
||||||
|
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
|
||||||
|
this.element.style.zIndex = this.options.zindex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.options.ghosting) {
|
||||||
|
this._clone = this.element.cloneNode(true);
|
||||||
|
Position.absolutize(this.element);
|
||||||
|
this.element.parentNode.insertBefore(this._clone, this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
Draggables.notify('onStart', this, event);
|
||||||
|
if(this.options.starteffect) this.options.starteffect(this.element);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateDrag: function(event, pointer) {
|
||||||
|
if(!this.dragging) this.startDrag(event);
|
||||||
|
Position.prepare();
|
||||||
|
Droppables.show(pointer, this.element);
|
||||||
|
Draggables.notify('onDrag', this, event);
|
||||||
|
this.draw(pointer);
|
||||||
|
if(this.options.change) this.options.change(this);
|
||||||
|
|
||||||
|
// fix AppleWebKit rendering
|
||||||
|
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
|
||||||
|
Event.stop(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
finishDrag: function(event, success) {
|
||||||
|
this.dragging = false;
|
||||||
|
|
||||||
|
if(this.options.ghosting) {
|
||||||
|
Position.relativize(this.element);
|
||||||
|
Element.remove(this._clone);
|
||||||
|
this._clone = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(success) Droppables.fire(event, this.element);
|
||||||
|
Draggables.notify('onEnd', this, event);
|
||||||
|
|
||||||
|
var revert = this.options.revert;
|
||||||
|
if(revert && typeof revert == 'function') revert = revert(this.element);
|
||||||
|
|
||||||
|
var d = this.currentDelta();
|
||||||
|
if(revert && this.options.reverteffect) {
|
||||||
|
this.options.reverteffect(this.element,
|
||||||
|
d[1]-this.delta[1], d[0]-this.delta[0]);
|
||||||
|
} else {
|
||||||
|
this.delta = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.options.zindex)
|
||||||
|
this.element.style.zIndex = this.originalZ;
|
||||||
|
|
||||||
|
if(this.options.endeffect)
|
||||||
|
this.options.endeffect(this.element);
|
||||||
|
|
||||||
|
Draggables.deactivate(this);
|
||||||
|
Droppables.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
keyPress: function(event) {
|
||||||
|
if(!event.keyCode==Event.KEY_ESC) return;
|
||||||
|
this.finishDrag(event, false);
|
||||||
|
Event.stop(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
endDrag: function(event) {
|
||||||
|
if(!this.dragging) return;
|
||||||
|
this.finishDrag(event, true);
|
||||||
|
Event.stop(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
draw: function(point) {
|
||||||
|
var pos = Position.cumulativeOffset(this.element);
|
||||||
|
var d = this.currentDelta();
|
||||||
|
pos[0] -= d[0]; pos[1] -= d[1];
|
||||||
|
|
||||||
|
var p = [0,1].map(function(i){ return (point[i]-pos[i]-this.offset[i]) }.bind(this));
|
||||||
|
|
||||||
|
if(this.options.snap) {
|
||||||
|
if(typeof this.options.snap == 'function') {
|
||||||
|
p = this.options.snap(p[0],p[1]);
|
||||||
|
} else {
|
||||||
|
if(this.options.snap instanceof Array) {
|
||||||
|
p = p.map( function(v, i) {
|
||||||
|
return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
|
||||||
|
} else {
|
||||||
|
p = p.map( function(v) {
|
||||||
|
return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
var style = this.element.style;
|
||||||
|
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
|
||||||
|
style.left = p[0] + "px";
|
||||||
|
if((!this.options.constraint) || (this.options.constraint=='vertical'))
|
||||||
|
style.top = p[1] + "px";
|
||||||
|
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
var SortableObserver = Class.create();
|
||||||
|
SortableObserver.prototype = {
|
||||||
|
initialize: function(element, observer) {
|
||||||
|
this.element = $(element);
|
||||||
|
this.observer = observer;
|
||||||
|
this.lastValue = Sortable.serialize(this.element);
|
||||||
|
},
|
||||||
|
|
||||||
|
onStart: function() {
|
||||||
|
this.lastValue = Sortable.serialize(this.element);
|
||||||
|
},
|
||||||
|
|
||||||
|
onEnd: function() {
|
||||||
|
Sortable.unmark();
|
||||||
|
if(this.lastValue != Sortable.serialize(this.element))
|
||||||
|
this.observer(this.element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var Sortable = {
|
||||||
|
sortables: new Array(),
|
||||||
|
|
||||||
|
options: function(element){
|
||||||
|
element = $(element);
|
||||||
|
return this.sortables.detect(function(s) { return s.element == element });
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function(element){
|
||||||
|
element = $(element);
|
||||||
|
this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
|
||||||
|
Draggables.removeObserver(s.element);
|
||||||
|
s.droppables.each(function(d){ Droppables.remove(d) });
|
||||||
|
s.draggables.invoke('destroy');
|
||||||
|
});
|
||||||
|
this.sortables = this.sortables.reject(function(s) { return s.element == element });
|
||||||
|
},
|
||||||
|
|
||||||
|
create: function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var options = Object.extend({
|
||||||
|
element: element,
|
||||||
|
tag: 'li', // assumes li children, override with tag: 'tagname'
|
||||||
|
dropOnEmpty: false,
|
||||||
|
tree: false, // fixme: unimplemented
|
||||||
|
overlap: 'vertical', // one of 'vertical', 'horizontal'
|
||||||
|
constraint: 'vertical', // one of 'vertical', 'horizontal', false
|
||||||
|
containment: element, // also takes array of elements (or id's); or false
|
||||||
|
handle: false, // or a CSS class
|
||||||
|
only: false,
|
||||||
|
hoverclass: null,
|
||||||
|
ghosting: false,
|
||||||
|
format: null,
|
||||||
|
onChange: Prototype.emptyFunction,
|
||||||
|
onUpdate: Prototype.emptyFunction
|
||||||
|
}, arguments[1] || {});
|
||||||
|
|
||||||
|
// clear any old sortable with same element
|
||||||
|
this.destroy(element);
|
||||||
|
|
||||||
|
// build options for the draggables
|
||||||
|
var options_for_draggable = {
|
||||||
|
revert: true,
|
||||||
|
ghosting: options.ghosting,
|
||||||
|
constraint: options.constraint,
|
||||||
|
handle: options.handle };
|
||||||
|
|
||||||
|
if(options.starteffect)
|
||||||
|
options_for_draggable.starteffect = options.starteffect;
|
||||||
|
|
||||||
|
if(options.reverteffect)
|
||||||
|
options_for_draggable.reverteffect = options.reverteffect;
|
||||||
|
else
|
||||||
|
if(options.ghosting) options_for_draggable.reverteffect = function(element) {
|
||||||
|
element.style.top = 0;
|
||||||
|
element.style.left = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(options.endeffect)
|
||||||
|
options_for_draggable.endeffect = options.endeffect;
|
||||||
|
|
||||||
|
if(options.zindex)
|
||||||
|
options_for_draggable.zindex = options.zindex;
|
||||||
|
|
||||||
|
// build options for the droppables
|
||||||
|
var options_for_droppable = {
|
||||||
|
overlap: options.overlap,
|
||||||
|
containment: options.containment,
|
||||||
|
hoverclass: options.hoverclass,
|
||||||
|
onHover: Sortable.onHover,
|
||||||
|
greedy: !options.dropOnEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix for gecko engine
|
||||||
|
Element.cleanWhitespace(element);
|
||||||
|
|
||||||
|
options.draggables = [];
|
||||||
|
options.droppables = [];
|
||||||
|
|
||||||
|
// make it so
|
||||||
|
|
||||||
|
// drop on empty handling
|
||||||
|
if(options.dropOnEmpty) {
|
||||||
|
Droppables.add(element,
|
||||||
|
{containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
|
||||||
|
options.droppables.push(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
(this.findElements(element, options) || []).each( function(e) {
|
||||||
|
// handles are per-draggable
|
||||||
|
var handle = options.handle ?
|
||||||
|
Element.childrenWithClassName(e, options.handle)[0] : e;
|
||||||
|
options.draggables.push(
|
||||||
|
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
||||||
|
Droppables.add(e, options_for_droppable);
|
||||||
|
options.droppables.push(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
// keep reference
|
||||||
|
this.sortables.push(options);
|
||||||
|
|
||||||
|
// for onupdate
|
||||||
|
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// return all suitable-for-sortable elements in a guaranteed order
|
||||||
|
findElements: function(element, options) {
|
||||||
|
if(!element.hasChildNodes()) return null;
|
||||||
|
var elements = [];
|
||||||
|
$A(element.childNodes).each( function(e) {
|
||||||
|
if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
|
||||||
|
(!options.only || (Element.hasClassName(e, options.only))))
|
||||||
|
elements.push(e);
|
||||||
|
if(options.tree) {
|
||||||
|
var grandchildren = this.findElements(e, options);
|
||||||
|
if(grandchildren) elements.push(grandchildren);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (elements.length>0 ? elements.flatten() : null);
|
||||||
|
},
|
||||||
|
|
||||||
|
onHover: function(element, dropon, overlap) {
|
||||||
|
if(overlap>0.5) {
|
||||||
|
Sortable.mark(dropon, 'before');
|
||||||
|
if(dropon.previousSibling != element) {
|
||||||
|
var oldParentNode = element.parentNode;
|
||||||
|
element.style.visibility = "hidden"; // fix gecko rendering
|
||||||
|
dropon.parentNode.insertBefore(element, dropon);
|
||||||
|
if(dropon.parentNode!=oldParentNode)
|
||||||
|
Sortable.options(oldParentNode).onChange(element);
|
||||||
|
Sortable.options(dropon.parentNode).onChange(element);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Sortable.mark(dropon, 'after');
|
||||||
|
var nextElement = dropon.nextSibling || null;
|
||||||
|
if(nextElement != element) {
|
||||||
|
var oldParentNode = element.parentNode;
|
||||||
|
element.style.visibility = "hidden"; // fix gecko rendering
|
||||||
|
dropon.parentNode.insertBefore(element, nextElement);
|
||||||
|
if(dropon.parentNode!=oldParentNode)
|
||||||
|
Sortable.options(oldParentNode).onChange(element);
|
||||||
|
Sortable.options(dropon.parentNode).onChange(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onEmptyHover: function(element, dropon) {
|
||||||
|
if(element.parentNode!=dropon) {
|
||||||
|
var oldParentNode = element.parentNode;
|
||||||
|
dropon.appendChild(element);
|
||||||
|
Sortable.options(oldParentNode).onChange(element);
|
||||||
|
Sortable.options(dropon).onChange(element);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
unmark: function() {
|
||||||
|
if(Sortable._marker) Element.hide(Sortable._marker);
|
||||||
|
},
|
||||||
|
|
||||||
|
mark: function(dropon, position) {
|
||||||
|
// mark on ghosting only
|
||||||
|
var sortable = Sortable.options(dropon.parentNode);
|
||||||
|
if(sortable && !sortable.ghosting) return;
|
||||||
|
|
||||||
|
if(!Sortable._marker) {
|
||||||
|
Sortable._marker = $('dropmarker') || document.createElement('DIV');
|
||||||
|
Element.hide(Sortable._marker);
|
||||||
|
Element.addClassName(Sortable._marker, 'dropmarker');
|
||||||
|
Sortable._marker.style.position = 'absolute';
|
||||||
|
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
|
||||||
|
}
|
||||||
|
var offsets = Position.cumulativeOffset(dropon);
|
||||||
|
Sortable._marker.style.left = offsets[0] + 'px';
|
||||||
|
Sortable._marker.style.top = offsets[1] + 'px';
|
||||||
|
|
||||||
|
if(position=='after')
|
||||||
|
if(sortable.overlap == 'horizontal')
|
||||||
|
Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
|
||||||
|
else
|
||||||
|
Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
|
||||||
|
|
||||||
|
Element.show(Sortable._marker);
|
||||||
|
},
|
||||||
|
|
||||||
|
serialize: function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var sortableOptions = this.options(element);
|
||||||
|
var options = Object.extend({
|
||||||
|
tag: sortableOptions.tag,
|
||||||
|
only: sortableOptions.only,
|
||||||
|
name: element.id,
|
||||||
|
format: sortableOptions.format || /^[^_]*_(.*)$/
|
||||||
|
}, arguments[1] || {});
|
||||||
|
return $(this.findElements(element, options) || []).map( function(item) {
|
||||||
|
return (encodeURIComponent(options.name) + "[]=" +
|
||||||
|
encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : ''));
|
||||||
|
}).join("&");
|
||||||
|
}
|
||||||
|
}
|
||||||
854
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/javascripts/effects.js
vendored
Normal file
854
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/javascripts/effects.js
vendored
Normal file
|
|
@ -0,0 +1,854 @@
|
||||||
|
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||||
|
// Contributors:
|
||||||
|
// Justin Palmer (http://encytemedia.com/)
|
||||||
|
// Mark Pilgrim (http://diveintomark.org/)
|
||||||
|
// Martin Bialasinki
|
||||||
|
//
|
||||||
|
// See scriptaculous.js for full license.
|
||||||
|
|
||||||
|
/* ------------- element ext -------------- */
|
||||||
|
|
||||||
|
// converts rgb() and #xxx to #xxxxxx format,
|
||||||
|
// returns self (or first argument) if not convertable
|
||||||
|
String.prototype.parseColor = function() {
|
||||||
|
var color = '#';
|
||||||
|
if(this.slice(0,4) == 'rgb(') {
|
||||||
|
var cols = this.slice(4,this.length-1).split(',');
|
||||||
|
var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
|
||||||
|
} else {
|
||||||
|
if(this.slice(0,1) == '#') {
|
||||||
|
if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
|
||||||
|
if(this.length==7) color = this.toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(color.length==7 ? color : (arguments[0] || this));
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
|
||||||
|
var children = $(element).childNodes;
|
||||||
|
var text = '';
|
||||||
|
var classtest = new RegExp('^([^ ]+ )*' + ignoreclass+ '( [^ ]+)*$','i');
|
||||||
|
|
||||||
|
for (var i = 0; i < children.length; i++) {
|
||||||
|
if(children[i].nodeType==3) {
|
||||||
|
text+=children[i].nodeValue;
|
||||||
|
} else {
|
||||||
|
if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
|
||||||
|
text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.setStyle = function(element, style) {
|
||||||
|
element = $(element);
|
||||||
|
for(k in style) element.style[k.camelize()] = style[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.setContentZoom = function(element, percent) {
|
||||||
|
Element.setStyle(element, {fontSize: (percent/100) + 'em'});
|
||||||
|
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.getOpacity = function(element){
|
||||||
|
var opacity;
|
||||||
|
if (opacity = Element.getStyle(element, 'opacity'))
|
||||||
|
return parseFloat(opacity);
|
||||||
|
if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
|
||||||
|
if(opacity[1]) return parseFloat(opacity[1]) / 100;
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.setOpacity = function(element, value){
|
||||||
|
element= $(element);
|
||||||
|
if (value == 1){
|
||||||
|
Element.setStyle(element, { opacity:
|
||||||
|
(/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
|
||||||
|
0.999999 : null });
|
||||||
|
if(/MSIE/.test(navigator.userAgent))
|
||||||
|
Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
|
||||||
|
} else {
|
||||||
|
if(value < 0.00001) value = 0;
|
||||||
|
Element.setStyle(element, {opacity: value});
|
||||||
|
if(/MSIE/.test(navigator.userAgent))
|
||||||
|
Element.setStyle(element,
|
||||||
|
{ filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
|
||||||
|
'alpha(opacity='+value*100+')' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.getInlineOpacity = function(element){
|
||||||
|
return $(element).style.opacity || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.childrenWithClassName = function(element, className) {
|
||||||
|
return $A($(element).getElementsByTagName('*')).select(
|
||||||
|
function(c) { return Element.hasClassName(c, className) });
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.call = function() {
|
||||||
|
var args = arguments;
|
||||||
|
this.each(function(f){ f.apply(this, args) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
var Effect = {
|
||||||
|
tagifyText: function(element) {
|
||||||
|
var tagifyStyle = 'position:relative';
|
||||||
|
if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
|
||||||
|
element = $(element);
|
||||||
|
$A(element.childNodes).each( function(child) {
|
||||||
|
if(child.nodeType==3) {
|
||||||
|
child.nodeValue.toArray().each( function(character) {
|
||||||
|
element.insertBefore(
|
||||||
|
Builder.node('span',{style: tagifyStyle},
|
||||||
|
character == ' ' ? String.fromCharCode(160) : character),
|
||||||
|
child);
|
||||||
|
});
|
||||||
|
Element.remove(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
multiple: function(element, effect) {
|
||||||
|
var elements;
|
||||||
|
if(((typeof element == 'object') ||
|
||||||
|
(typeof element == 'function')) &&
|
||||||
|
(element.length))
|
||||||
|
elements = element;
|
||||||
|
else
|
||||||
|
elements = $(element).childNodes;
|
||||||
|
|
||||||
|
var options = Object.extend({
|
||||||
|
speed: 0.1,
|
||||||
|
delay: 0.0
|
||||||
|
}, arguments[2] || {});
|
||||||
|
var masterDelay = options.delay;
|
||||||
|
|
||||||
|
$A(elements).each( function(element, index) {
|
||||||
|
new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var Effect2 = Effect; // deprecated
|
||||||
|
|
||||||
|
/* ------------- transitions ------------- */
|
||||||
|
|
||||||
|
Effect.Transitions = {}
|
||||||
|
|
||||||
|
Effect.Transitions.linear = function(pos) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
Effect.Transitions.sinoidal = function(pos) {
|
||||||
|
return (-Math.cos(pos*Math.PI)/2) + 0.5;
|
||||||
|
}
|
||||||
|
Effect.Transitions.reverse = function(pos) {
|
||||||
|
return 1-pos;
|
||||||
|
}
|
||||||
|
Effect.Transitions.flicker = function(pos) {
|
||||||
|
return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
|
||||||
|
}
|
||||||
|
Effect.Transitions.wobble = function(pos) {
|
||||||
|
return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
|
||||||
|
}
|
||||||
|
Effect.Transitions.pulse = function(pos) {
|
||||||
|
return (Math.floor(pos*10) % 2 == 0 ?
|
||||||
|
(pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
|
||||||
|
}
|
||||||
|
Effect.Transitions.none = function(pos) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Effect.Transitions.full = function(pos) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------- core effects ------------- */
|
||||||
|
|
||||||
|
Effect.Queue = {
|
||||||
|
effects: [],
|
||||||
|
_each: function(iterator) {
|
||||||
|
this.effects._each(iterator);
|
||||||
|
},
|
||||||
|
interval: null,
|
||||||
|
add: function(effect) {
|
||||||
|
var timestamp = new Date().getTime();
|
||||||
|
|
||||||
|
switch(effect.options.queue) {
|
||||||
|
case 'front':
|
||||||
|
// move unstarted effects after this effect
|
||||||
|
this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
|
||||||
|
e.startOn += effect.finishOn;
|
||||||
|
e.finishOn += effect.finishOn;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'end':
|
||||||
|
// start effect after last queued effect has finished
|
||||||
|
timestamp = this.effects.pluck('finishOn').max() || timestamp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
effect.startOn += timestamp;
|
||||||
|
effect.finishOn += timestamp;
|
||||||
|
this.effects.push(effect);
|
||||||
|
if(!this.interval)
|
||||||
|
this.interval = setInterval(this.loop.bind(this), 40);
|
||||||
|
},
|
||||||
|
remove: function(effect) {
|
||||||
|
this.effects = this.effects.reject(function(e) { return e==effect });
|
||||||
|
if(this.effects.length == 0) {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.interval = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loop: function() {
|
||||||
|
var timePos = new Date().getTime();
|
||||||
|
this.effects.invoke('loop', timePos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object.extend(Effect.Queue, Enumerable);
|
||||||
|
|
||||||
|
Effect.Base = function() {};
|
||||||
|
Effect.Base.prototype = {
|
||||||
|
position: null,
|
||||||
|
setOptions: function(options) {
|
||||||
|
this.options = Object.extend({
|
||||||
|
transition: Effect.Transitions.sinoidal,
|
||||||
|
duration: 1.0, // seconds
|
||||||
|
fps: 25.0, // max. 25fps due to Effect.Queue implementation
|
||||||
|
sync: false, // true for combining
|
||||||
|
from: 0.0,
|
||||||
|
to: 1.0,
|
||||||
|
delay: 0.0,
|
||||||
|
queue: 'parallel'
|
||||||
|
}, options || {});
|
||||||
|
},
|
||||||
|
start: function(options) {
|
||||||
|
this.setOptions(options || {});
|
||||||
|
this.currentFrame = 0;
|
||||||
|
this.state = 'idle';
|
||||||
|
this.startOn = this.options.delay*1000;
|
||||||
|
this.finishOn = this.startOn + (this.options.duration*1000);
|
||||||
|
this.event('beforeStart');
|
||||||
|
if(!this.options.sync) Effect.Queue.add(this);
|
||||||
|
},
|
||||||
|
loop: function(timePos) {
|
||||||
|
if(timePos >= this.startOn) {
|
||||||
|
if(timePos >= this.finishOn) {
|
||||||
|
this.render(1.0);
|
||||||
|
this.cancel();
|
||||||
|
this.event('beforeFinish');
|
||||||
|
if(this.finish) this.finish();
|
||||||
|
this.event('afterFinish');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
|
||||||
|
var frame = Math.round(pos * this.options.fps * this.options.duration);
|
||||||
|
if(frame > this.currentFrame) {
|
||||||
|
this.render(pos);
|
||||||
|
this.currentFrame = frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: function(pos) {
|
||||||
|
if(this.state == 'idle') {
|
||||||
|
this.state = 'running';
|
||||||
|
this.event('beforeSetup');
|
||||||
|
if(this.setup) this.setup();
|
||||||
|
this.event('afterSetup');
|
||||||
|
}
|
||||||
|
if(this.state == 'running') {
|
||||||
|
if(this.options.transition) pos = this.options.transition(pos);
|
||||||
|
pos *= (this.options.to-this.options.from);
|
||||||
|
pos += this.options.from;
|
||||||
|
this.position = pos;
|
||||||
|
this.event('beforeUpdate');
|
||||||
|
if(this.update) this.update(pos);
|
||||||
|
this.event('afterUpdate');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel: function() {
|
||||||
|
if(!this.options.sync) Effect.Queue.remove(this);
|
||||||
|
this.state = 'finished';
|
||||||
|
},
|
||||||
|
event: function(eventName) {
|
||||||
|
if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
|
||||||
|
if(this.options[eventName]) this.options[eventName](this);
|
||||||
|
},
|
||||||
|
inspect: function() {
|
||||||
|
return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Parallel = Class.create();
|
||||||
|
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
|
||||||
|
initialize: function(effects) {
|
||||||
|
this.effects = effects || [];
|
||||||
|
this.start(arguments[1]);
|
||||||
|
},
|
||||||
|
update: function(position) {
|
||||||
|
this.effects.invoke('render', position);
|
||||||
|
},
|
||||||
|
finish: function(position) {
|
||||||
|
this.effects.each( function(effect) {
|
||||||
|
effect.render(1.0);
|
||||||
|
effect.cancel();
|
||||||
|
effect.event('beforeFinish');
|
||||||
|
if(effect.finish) effect.finish(position);
|
||||||
|
effect.event('afterFinish');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Effect.Opacity = Class.create();
|
||||||
|
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
|
||||||
|
initialize: function(element) {
|
||||||
|
this.element = $(element);
|
||||||
|
// make this work on IE on elements without 'layout'
|
||||||
|
if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
|
||||||
|
Element.setStyle(this.element, {zoom: 1});
|
||||||
|
var options = Object.extend({
|
||||||
|
from: Element.getOpacity(this.element) || 0.0,
|
||||||
|
to: 1.0
|
||||||
|
}, arguments[1] || {});
|
||||||
|
this.start(options);
|
||||||
|
},
|
||||||
|
update: function(position) {
|
||||||
|
Element.setOpacity(this.element, position);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Effect.MoveBy = Class.create();
|
||||||
|
Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), {
|
||||||
|
initialize: function(element, toTop, toLeft) {
|
||||||
|
this.element = $(element);
|
||||||
|
this.toTop = toTop;
|
||||||
|
this.toLeft = toLeft;
|
||||||
|
this.start(arguments[3]);
|
||||||
|
},
|
||||||
|
setup: function() {
|
||||||
|
// Bug in Opera: Opera returns the "real" position of a static element or
|
||||||
|
// relative element that does not have top/left explicitly set.
|
||||||
|
// ==> Always set top and left for position relative elements in your stylesheets
|
||||||
|
// (to 0 if you do not need them)
|
||||||
|
Element.makePositioned(this.element);
|
||||||
|
this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
|
||||||
|
this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
|
||||||
|
},
|
||||||
|
update: function(position) {
|
||||||
|
Element.setStyle(this.element, {
|
||||||
|
top: this.toTop * position + this.originalTop + 'px',
|
||||||
|
left: this.toLeft * position + this.originalLeft + 'px'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Effect.Scale = Class.create();
|
||||||
|
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
|
||||||
|
initialize: function(element, percent) {
|
||||||
|
this.element = $(element)
|
||||||
|
var options = Object.extend({
|
||||||
|
scaleX: true,
|
||||||
|
scaleY: true,
|
||||||
|
scaleContent: true,
|
||||||
|
scaleFromCenter: false,
|
||||||
|
scaleMode: 'box', // 'box' or 'contents' or {} with provided values
|
||||||
|
scaleFrom: 100.0,
|
||||||
|
scaleTo: percent
|
||||||
|
}, arguments[2] || {});
|
||||||
|
this.start(options);
|
||||||
|
},
|
||||||
|
setup: function() {
|
||||||
|
this.restoreAfterFinish = this.options.restoreAfterFinish || false;
|
||||||
|
this.elementPositioning = Element.getStyle(this.element,'position');
|
||||||
|
|
||||||
|
this.originalStyle = {};
|
||||||
|
['top','left','width','height','fontSize'].each( function(k) {
|
||||||
|
this.originalStyle[k] = this.element.style[k];
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.originalTop = this.element.offsetTop;
|
||||||
|
this.originalLeft = this.element.offsetLeft;
|
||||||
|
|
||||||
|
var fontSize = Element.getStyle(this.element,'font-size') || '100%';
|
||||||
|
['em','px','%'].each( function(fontSizeType) {
|
||||||
|
if(fontSize.indexOf(fontSizeType)>0) {
|
||||||
|
this.fontSize = parseFloat(fontSize);
|
||||||
|
this.fontSizeType = fontSizeType;
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
|
||||||
|
|
||||||
|
this.dims = null;
|
||||||
|
if(this.options.scaleMode=='box')
|
||||||
|
this.dims = [this.element.offsetHeight, this.element.offsetWidth];
|
||||||
|
if(/^content/.test(this.options.scaleMode))
|
||||||
|
this.dims = [this.element.scrollHeight, this.element.scrollWidth];
|
||||||
|
if(!this.dims)
|
||||||
|
this.dims = [this.options.scaleMode.originalHeight,
|
||||||
|
this.options.scaleMode.originalWidth];
|
||||||
|
},
|
||||||
|
update: function(position) {
|
||||||
|
var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
|
||||||
|
if(this.options.scaleContent && this.fontSize)
|
||||||
|
Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType });
|
||||||
|
this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
|
||||||
|
},
|
||||||
|
finish: function(position) {
|
||||||
|
if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle);
|
||||||
|
},
|
||||||
|
setDimensions: function(height, width) {
|
||||||
|
var d = {};
|
||||||
|
if(this.options.scaleX) d.width = width + 'px';
|
||||||
|
if(this.options.scaleY) d.height = height + 'px';
|
||||||
|
if(this.options.scaleFromCenter) {
|
||||||
|
var topd = (height - this.dims[0])/2;
|
||||||
|
var leftd = (width - this.dims[1])/2;
|
||||||
|
if(this.elementPositioning == 'absolute') {
|
||||||
|
if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
|
||||||
|
if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
|
||||||
|
} else {
|
||||||
|
if(this.options.scaleY) d.top = -topd + 'px';
|
||||||
|
if(this.options.scaleX) d.left = -leftd + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Element.setStyle(this.element, d);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Effect.Highlight = Class.create();
|
||||||
|
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
|
||||||
|
initialize: function(element) {
|
||||||
|
this.element = $(element);
|
||||||
|
var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
|
||||||
|
this.start(options);
|
||||||
|
},
|
||||||
|
setup: function() {
|
||||||
|
// Prevent executing on elements not in the layout flow
|
||||||
|
if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; }
|
||||||
|
// Disable background image during the effect
|
||||||
|
this.oldStyle = {
|
||||||
|
backgroundImage: Element.getStyle(this.element, 'background-image') };
|
||||||
|
Element.setStyle(this.element, {backgroundImage: 'none'});
|
||||||
|
if(!this.options.endcolor)
|
||||||
|
this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff');
|
||||||
|
if(!this.options.restorecolor)
|
||||||
|
this.options.restorecolor = Element.getStyle(this.element, 'background-color');
|
||||||
|
// init color calculations
|
||||||
|
this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
|
||||||
|
this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
|
||||||
|
},
|
||||||
|
update: function(position) {
|
||||||
|
Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){
|
||||||
|
return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
|
||||||
|
},
|
||||||
|
finish: function() {
|
||||||
|
Element.setStyle(this.element, Object.extend(this.oldStyle, {
|
||||||
|
backgroundColor: this.options.restorecolor
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Effect.ScrollTo = Class.create();
|
||||||
|
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
|
||||||
|
initialize: function(element) {
|
||||||
|
this.element = $(element);
|
||||||
|
this.start(arguments[1] || {});
|
||||||
|
},
|
||||||
|
setup: function() {
|
||||||
|
Position.prepare();
|
||||||
|
var offsets = Position.cumulativeOffset(this.element);
|
||||||
|
if(this.options.offset) offsets[1] += this.options.offset;
|
||||||
|
var max = window.innerHeight ?
|
||||||
|
window.height - window.innerHeight :
|
||||||
|
document.body.scrollHeight -
|
||||||
|
(document.documentElement.clientHeight ?
|
||||||
|
document.documentElement.clientHeight : document.body.clientHeight);
|
||||||
|
this.scrollStart = Position.deltaY;
|
||||||
|
this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
|
||||||
|
},
|
||||||
|
update: function(position) {
|
||||||
|
Position.prepare();
|
||||||
|
window.scrollTo(Position.deltaX,
|
||||||
|
this.scrollStart + (position*this.delta));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ------------- combination effects ------------- */
|
||||||
|
|
||||||
|
Effect.Fade = function(element) {
|
||||||
|
var oldOpacity = Element.getInlineOpacity(element);
|
||||||
|
var options = Object.extend({
|
||||||
|
from: Element.getOpacity(element) || 1.0,
|
||||||
|
to: 0.0,
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
if(effect.options.to!=0) return;
|
||||||
|
hide(effect.element);
|
||||||
|
setStyle(effect.element, {opacity: oldOpacity}); }}
|
||||||
|
}, arguments[1] || {});
|
||||||
|
return new Effect.Opacity(element,options);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Appear = function(element) {
|
||||||
|
var options = Object.extend({
|
||||||
|
from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0),
|
||||||
|
to: 1.0,
|
||||||
|
beforeSetup: function(effect) { with(Element) {
|
||||||
|
setOpacity(effect.element, effect.options.from);
|
||||||
|
show(effect.element); }}
|
||||||
|
}, arguments[1] || {});
|
||||||
|
return new Effect.Opacity(element,options);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Puff = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') };
|
||||||
|
return new Effect.Parallel(
|
||||||
|
[ new Effect.Scale(element, 200,
|
||||||
|
{ sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
|
||||||
|
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
|
||||||
|
Object.extend({ duration: 1.0,
|
||||||
|
beforeSetupInternal: function(effect) { with(Element) {
|
||||||
|
setStyle(effect.effects[0].element, {position: 'absolute'}); }},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
hide(effect.effects[0].element);
|
||||||
|
setStyle(effect.effects[0].element, oldStyle); }}
|
||||||
|
}, arguments[1] || {})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.BlindUp = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
Element.makeClipping(element);
|
||||||
|
return new Effect.Scale(element, 0,
|
||||||
|
Object.extend({ scaleContent: false,
|
||||||
|
scaleX: false,
|
||||||
|
restoreAfterFinish: true,
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
[hide, undoClipping].call(effect.element); }}
|
||||||
|
}, arguments[1] || {})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.BlindDown = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var oldHeight = Element.getStyle(element, 'height');
|
||||||
|
var elementDimensions = Element.getDimensions(element);
|
||||||
|
return new Effect.Scale(element, 100,
|
||||||
|
Object.extend({ scaleContent: false,
|
||||||
|
scaleX: false,
|
||||||
|
scaleFrom: 0,
|
||||||
|
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
|
||||||
|
restoreAfterFinish: true,
|
||||||
|
afterSetup: function(effect) { with(Element) {
|
||||||
|
makeClipping(effect.element);
|
||||||
|
setStyle(effect.element, {height: '0px'});
|
||||||
|
show(effect.element);
|
||||||
|
}},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
undoClipping(effect.element);
|
||||||
|
setStyle(effect.element, {height: oldHeight});
|
||||||
|
}}
|
||||||
|
}, arguments[1] || {})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.SwitchOff = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var oldOpacity = Element.getInlineOpacity(element);
|
||||||
|
return new Effect.Appear(element, {
|
||||||
|
duration: 0.4,
|
||||||
|
from: 0,
|
||||||
|
transition: Effect.Transitions.flicker,
|
||||||
|
afterFinishInternal: function(effect) {
|
||||||
|
new Effect.Scale(effect.element, 1, {
|
||||||
|
duration: 0.3, scaleFromCenter: true,
|
||||||
|
scaleX: false, scaleContent: false, restoreAfterFinish: true,
|
||||||
|
beforeSetup: function(effect) { with(Element) {
|
||||||
|
[makePositioned,makeClipping].call(effect.element);
|
||||||
|
}},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
[hide,undoClipping,undoPositioned].call(effect.element);
|
||||||
|
setStyle(effect.element, {opacity: oldOpacity});
|
||||||
|
}}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.DropOut = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var oldStyle = {
|
||||||
|
top: Element.getStyle(element, 'top'),
|
||||||
|
left: Element.getStyle(element, 'left'),
|
||||||
|
opacity: Element.getInlineOpacity(element) };
|
||||||
|
return new Effect.Parallel(
|
||||||
|
[ new Effect.MoveBy(element, 100, 0, { sync: true }),
|
||||||
|
new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
|
||||||
|
Object.extend(
|
||||||
|
{ duration: 0.5,
|
||||||
|
beforeSetup: function(effect) { with(Element) {
|
||||||
|
makePositioned(effect.effects[0].element); }},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
[hide, undoPositioned].call(effect.effects[0].element);
|
||||||
|
setStyle(effect.effects[0].element, oldStyle); }}
|
||||||
|
}, arguments[1] || {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Shake = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var oldStyle = {
|
||||||
|
top: Element.getStyle(element, 'top'),
|
||||||
|
left: Element.getStyle(element, 'left') };
|
||||||
|
return new Effect.MoveBy(element, 0, 20,
|
||||||
|
{ duration: 0.05, afterFinishInternal: function(effect) {
|
||||||
|
new Effect.MoveBy(effect.element, 0, -40,
|
||||||
|
{ duration: 0.1, afterFinishInternal: function(effect) {
|
||||||
|
new Effect.MoveBy(effect.element, 0, 40,
|
||||||
|
{ duration: 0.1, afterFinishInternal: function(effect) {
|
||||||
|
new Effect.MoveBy(effect.element, 0, -40,
|
||||||
|
{ duration: 0.1, afterFinishInternal: function(effect) {
|
||||||
|
new Effect.MoveBy(effect.element, 0, 40,
|
||||||
|
{ duration: 0.1, afterFinishInternal: function(effect) {
|
||||||
|
new Effect.MoveBy(effect.element, 0, -20,
|
||||||
|
{ duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
undoPositioned(effect.element);
|
||||||
|
setStyle(effect.element, oldStyle);
|
||||||
|
}}}) }}) }}) }}) }}) }});
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.SlideDown = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
Element.cleanWhitespace(element);
|
||||||
|
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
|
||||||
|
var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
|
||||||
|
var elementDimensions = Element.getDimensions(element);
|
||||||
|
return new Effect.Scale(element, 100, Object.extend({
|
||||||
|
scaleContent: false,
|
||||||
|
scaleX: false,
|
||||||
|
scaleFrom: 0,
|
||||||
|
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
|
||||||
|
restoreAfterFinish: true,
|
||||||
|
afterSetup: function(effect) { with(Element) {
|
||||||
|
makePositioned(effect.element);
|
||||||
|
makePositioned(effect.element.firstChild);
|
||||||
|
if(window.opera) setStyle(effect.element, {top: ''});
|
||||||
|
makeClipping(effect.element);
|
||||||
|
setStyle(effect.element, {height: '0px'});
|
||||||
|
show(element); }},
|
||||||
|
afterUpdateInternal: function(effect) { with(Element) {
|
||||||
|
setStyle(effect.element.firstChild, {bottom:
|
||||||
|
(effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
undoClipping(effect.element);
|
||||||
|
undoPositioned(effect.element.firstChild);
|
||||||
|
undoPositioned(effect.element);
|
||||||
|
setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
|
||||||
|
}, arguments[1] || {})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.SlideUp = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
Element.cleanWhitespace(element);
|
||||||
|
var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
|
||||||
|
return new Effect.Scale(element, 0,
|
||||||
|
Object.extend({ scaleContent: false,
|
||||||
|
scaleX: false,
|
||||||
|
scaleMode: 'box',
|
||||||
|
scaleFrom: 100,
|
||||||
|
restoreAfterFinish: true,
|
||||||
|
beforeStartInternal: function(effect) { with(Element) {
|
||||||
|
makePositioned(effect.element);
|
||||||
|
makePositioned(effect.element.firstChild);
|
||||||
|
if(window.opera) setStyle(effect.element, {top: ''});
|
||||||
|
makeClipping(effect.element);
|
||||||
|
show(element); }},
|
||||||
|
afterUpdateInternal: function(effect) { with(Element) {
|
||||||
|
setStyle(effect.element.firstChild, {bottom:
|
||||||
|
(effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
[hide, undoClipping].call(effect.element);
|
||||||
|
undoPositioned(effect.element.firstChild);
|
||||||
|
undoPositioned(effect.element);
|
||||||
|
setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
|
||||||
|
}, arguments[1] || {})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bug in opera makes the TD containing this element expand for a instance after finish
|
||||||
|
Effect.Squish = function(element) {
|
||||||
|
return new Effect.Scale(element, window.opera ? 1 : 0,
|
||||||
|
{ restoreAfterFinish: true,
|
||||||
|
beforeSetup: function(effect) { with(Element) {
|
||||||
|
makeClipping(effect.element); }},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
hide(effect.element);
|
||||||
|
undoClipping(effect.element); }}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Grow = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var options = Object.extend({
|
||||||
|
direction: 'center',
|
||||||
|
moveTransistion: Effect.Transitions.sinoidal,
|
||||||
|
scaleTransition: Effect.Transitions.sinoidal,
|
||||||
|
opacityTransition: Effect.Transitions.full
|
||||||
|
}, arguments[1] || {});
|
||||||
|
var oldStyle = {
|
||||||
|
top: element.style.top,
|
||||||
|
left: element.style.left,
|
||||||
|
height: element.style.height,
|
||||||
|
width: element.style.width,
|
||||||
|
opacity: Element.getInlineOpacity(element) };
|
||||||
|
|
||||||
|
var dims = Element.getDimensions(element);
|
||||||
|
var initialMoveX, initialMoveY;
|
||||||
|
var moveX, moveY;
|
||||||
|
|
||||||
|
switch (options.direction) {
|
||||||
|
case 'top-left':
|
||||||
|
initialMoveX = initialMoveY = moveX = moveY = 0;
|
||||||
|
break;
|
||||||
|
case 'top-right':
|
||||||
|
initialMoveX = dims.width;
|
||||||
|
initialMoveY = moveY = 0;
|
||||||
|
moveX = -dims.width;
|
||||||
|
break;
|
||||||
|
case 'bottom-left':
|
||||||
|
initialMoveX = moveX = 0;
|
||||||
|
initialMoveY = dims.height;
|
||||||
|
moveY = -dims.height;
|
||||||
|
break;
|
||||||
|
case 'bottom-right':
|
||||||
|
initialMoveX = dims.width;
|
||||||
|
initialMoveY = dims.height;
|
||||||
|
moveX = -dims.width;
|
||||||
|
moveY = -dims.height;
|
||||||
|
break;
|
||||||
|
case 'center':
|
||||||
|
initialMoveX = dims.width / 2;
|
||||||
|
initialMoveY = dims.height / 2;
|
||||||
|
moveX = -dims.width / 2;
|
||||||
|
moveY = -dims.height / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Effect.MoveBy(element, initialMoveY, initialMoveX, {
|
||||||
|
duration: 0.01,
|
||||||
|
beforeSetup: function(effect) { with(Element) {
|
||||||
|
hide(effect.element);
|
||||||
|
makeClipping(effect.element);
|
||||||
|
makePositioned(effect.element);
|
||||||
|
}},
|
||||||
|
afterFinishInternal: function(effect) {
|
||||||
|
new Effect.Parallel(
|
||||||
|
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
|
||||||
|
new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: options.moveTransition }),
|
||||||
|
new Effect.Scale(effect.element, 100, {
|
||||||
|
scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
|
||||||
|
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
|
||||||
|
], Object.extend({
|
||||||
|
beforeSetup: function(effect) { with(Element) {
|
||||||
|
setStyle(effect.effects[0].element, {height: '0px'});
|
||||||
|
show(effect.effects[0].element); }},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
[undoClipping, undoPositioned].call(effect.effects[0].element);
|
||||||
|
setStyle(effect.effects[0].element, oldStyle); }}
|
||||||
|
}, options)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Shrink = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var options = Object.extend({
|
||||||
|
direction: 'center',
|
||||||
|
moveTransistion: Effect.Transitions.sinoidal,
|
||||||
|
scaleTransition: Effect.Transitions.sinoidal,
|
||||||
|
opacityTransition: Effect.Transitions.none
|
||||||
|
}, arguments[1] || {});
|
||||||
|
var oldStyle = {
|
||||||
|
top: element.style.top,
|
||||||
|
left: element.style.left,
|
||||||
|
height: element.style.height,
|
||||||
|
width: element.style.width,
|
||||||
|
opacity: Element.getInlineOpacity(element) };
|
||||||
|
|
||||||
|
var dims = Element.getDimensions(element);
|
||||||
|
var moveX, moveY;
|
||||||
|
|
||||||
|
switch (options.direction) {
|
||||||
|
case 'top-left':
|
||||||
|
moveX = moveY = 0;
|
||||||
|
break;
|
||||||
|
case 'top-right':
|
||||||
|
moveX = dims.width;
|
||||||
|
moveY = 0;
|
||||||
|
break;
|
||||||
|
case 'bottom-left':
|
||||||
|
moveX = 0;
|
||||||
|
moveY = dims.height;
|
||||||
|
break;
|
||||||
|
case 'bottom-right':
|
||||||
|
moveX = dims.width;
|
||||||
|
moveY = dims.height;
|
||||||
|
break;
|
||||||
|
case 'center':
|
||||||
|
moveX = dims.width / 2;
|
||||||
|
moveY = dims.height / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Effect.Parallel(
|
||||||
|
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
|
||||||
|
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
|
||||||
|
new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: options.moveTransition })
|
||||||
|
], Object.extend({
|
||||||
|
beforeStartInternal: function(effect) { with(Element) {
|
||||||
|
[makePositioned, makeClipping].call(effect.effects[0].element) }},
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
[hide, undoClipping, undoPositioned].call(effect.effects[0].element);
|
||||||
|
setStyle(effect.effects[0].element, oldStyle); }}
|
||||||
|
}, options)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Pulsate = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var options = arguments[1] || {};
|
||||||
|
var oldOpacity = Element.getInlineOpacity(element);
|
||||||
|
var transition = options.transition || Effect.Transitions.sinoidal;
|
||||||
|
var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
|
||||||
|
reverser.bind(transition);
|
||||||
|
return new Effect.Opacity(element,
|
||||||
|
Object.extend(Object.extend({ duration: 3.0, from: 0,
|
||||||
|
afterFinishInternal: function(effect) { Element.setStyle(effect.element, {opacity: oldOpacity}); }
|
||||||
|
}, options), {transition: reverser}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Fold = function(element) {
|
||||||
|
element = $(element);
|
||||||
|
var oldStyle = {
|
||||||
|
top: element.style.top,
|
||||||
|
left: element.style.left,
|
||||||
|
width: element.style.width,
|
||||||
|
height: element.style.height };
|
||||||
|
Element.makeClipping(element);
|
||||||
|
return new Effect.Scale(element, 5, Object.extend({
|
||||||
|
scaleContent: false,
|
||||||
|
scaleX: false,
|
||||||
|
afterFinishInternal: function(effect) {
|
||||||
|
new Effect.Scale(element, 1, {
|
||||||
|
scaleContent: false,
|
||||||
|
scaleY: false,
|
||||||
|
afterFinishInternal: function(effect) { with(Element) {
|
||||||
|
[hide, undoClipping].call(effect.element);
|
||||||
|
setStyle(effect.element, oldStyle);
|
||||||
|
}} });
|
||||||
|
}}, arguments[1] || {}));
|
||||||
|
}
|
||||||
1785
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/javascripts/prototype.js
vendored
Normal file
1785
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/javascripts/prototype.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/robots.txt
vendored
Normal file
1
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/public/robots.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/about
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/about
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../config/boot'
|
||||||
|
require 'commands/about'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/breakpointer
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/breakpointer
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../config/boot'
|
||||||
|
require 'commands/breakpointer'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/console
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/console
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../config/boot'
|
||||||
|
require 'commands/console'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/destroy
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/destroy
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../config/boot'
|
||||||
|
require 'commands/destroy'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/generate
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/generate
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../config/boot'
|
||||||
|
require 'commands/generate'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/performance/benchmarker
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/performance/benchmarker
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../../config/boot'
|
||||||
|
require 'commands/performance/benchmarker'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/performance/profiler
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/performance/profiler
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../../config/boot'
|
||||||
|
require 'commands/performance/profiler'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/plugin
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/plugin
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../config/boot'
|
||||||
|
require 'commands/plugin'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/process/reaper
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/process/reaper
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../../config/boot'
|
||||||
|
require 'commands/process/reaper'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/process/spawner
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/process/spawner
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../../config/boot'
|
||||||
|
require 'commands/process/spawner'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/process/spinner
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/process/spinner
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../../config/boot'
|
||||||
|
require 'commands/process/spinner'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/runner
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/runner
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../config/boot'
|
||||||
|
require 'commands/runner'
|
||||||
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/server
vendored
Normal file
3
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/script/server
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
require File.dirname(__FILE__) + '/../config/boot'
|
||||||
|
require 'commands/server'
|
||||||
18
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/test/functional/login_controller_test.rb
vendored
Normal file
18
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/test/functional/login_controller_test.rb
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
require File.dirname(__FILE__) + '/../test_helper'
|
||||||
|
require 'login_controller'
|
||||||
|
|
||||||
|
# Re-raise errors caught by the controller.
|
||||||
|
class LoginController; def rescue_action(e) raise e end; end
|
||||||
|
|
||||||
|
class LoginControllerTest < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
@controller = LoginController.new
|
||||||
|
@request = ActionController::TestRequest.new
|
||||||
|
@response = ActionController::TestResponse.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# Replace this with your real tests.
|
||||||
|
def test_truth
|
||||||
|
assert true
|
||||||
|
end
|
||||||
|
end
|
||||||
18
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/test/functional/server_controller_test.rb
vendored
Normal file
18
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/test/functional/server_controller_test.rb
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
require File.dirname(__FILE__) + '/../test_helper'
|
||||||
|
require 'server_controller'
|
||||||
|
|
||||||
|
# Re-raise errors caught by the controller.
|
||||||
|
class ServerController; def rescue_action(e) raise e end; end
|
||||||
|
|
||||||
|
class ServerControllerTest < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
@controller = ServerController.new
|
||||||
|
@request = ActionController::TestRequest.new
|
||||||
|
@response = ActionController::TestResponse.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# Replace this with your real tests.
|
||||||
|
def test_truth
|
||||||
|
assert true
|
||||||
|
end
|
||||||
|
end
|
||||||
28
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/test/test_helper.rb
vendored
Normal file
28
vendor/gems/ruby-openid-2.1.2/examples/rails_openid/test/test_helper.rb
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
ENV["RAILS_ENV"] = "test"
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
||||||
|
require 'test_help'
|
||||||
|
|
||||||
|
class Test::Unit::TestCase
|
||||||
|
# Transactional fixtures accelerate your tests by wrapping each test method
|
||||||
|
# in a transaction that's rolled back on completion. This ensures that the
|
||||||
|
# test database remains unchanged so your fixtures don't have to be reloaded
|
||||||
|
# between every test method. Fewer database queries means faster tests.
|
||||||
|
#
|
||||||
|
# Read Mike Clark's excellent walkthrough at
|
||||||
|
# http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
|
||||||
|
#
|
||||||
|
# Every Active Record database supports transactions except MyISAM tables
|
||||||
|
# in MySQL. Turn off transactional fixtures in this case; however, if you
|
||||||
|
# don't care one way or the other, switching from MyISAM to InnoDB tables
|
||||||
|
# is recommended.
|
||||||
|
self.use_transactional_fixtures = true
|
||||||
|
|
||||||
|
# Instantiated fixtures are slow, but give you @david where otherwise you
|
||||||
|
# would need people(:david). If you don't want to migrate your existing
|
||||||
|
# test cases which use the @david style and don't mind the speed hit (each
|
||||||
|
# instantiated fixtures translates to a database query per test method),
|
||||||
|
# then set this back to true.
|
||||||
|
self.use_instantiated_fixtures = false
|
||||||
|
|
||||||
|
# Add more helper methods to be used by all tests here...
|
||||||
|
end
|
||||||
112
vendor/gems/ruby-openid-2.1.2/lib/hmac/hmac.rb
vendored
Normal file
112
vendor/gems/ruby-openid-2.1.2/lib/hmac/hmac.rb
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
# Copyright (C) 2001 Daiki Ueno <ueno@unixuser.org>
|
||||||
|
# This library is distributed under the terms of the Ruby license.
|
||||||
|
|
||||||
|
# This module provides common interface to HMAC engines.
|
||||||
|
# HMAC standard is documented in RFC 2104:
|
||||||
|
#
|
||||||
|
# H. Krawczyk et al., "HMAC: Keyed-Hashing for Message Authentication",
|
||||||
|
# RFC 2104, February 1997
|
||||||
|
#
|
||||||
|
# These APIs are inspired by JCE 1.2's javax.crypto.Mac interface.
|
||||||
|
#
|
||||||
|
# <URL:http://java.sun.com/security/JCE1.2/spec/apidoc/javax/crypto/Mac.html>
|
||||||
|
|
||||||
|
module HMAC
|
||||||
|
class Base
|
||||||
|
def initialize(algorithm, block_size, output_length, key)
|
||||||
|
@algorithm = algorithm
|
||||||
|
@block_size = block_size
|
||||||
|
@output_length = output_length
|
||||||
|
@status = STATUS_UNDEFINED
|
||||||
|
@key_xor_ipad = ''
|
||||||
|
@key_xor_opad = ''
|
||||||
|
set_key(key) unless key.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def check_status
|
||||||
|
unless @status == STATUS_INITIALIZED
|
||||||
|
raise RuntimeError,
|
||||||
|
"The underlying hash algorithm has not yet been initialized."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
public
|
||||||
|
def set_key(key)
|
||||||
|
# If key is longer than the block size, apply hash function
|
||||||
|
# to key and use the result as a real key.
|
||||||
|
key = @algorithm.digest(key) if key.size > @block_size
|
||||||
|
key_xor_ipad = "\x36" * @block_size
|
||||||
|
key_xor_opad = "\x5C" * @block_size
|
||||||
|
for i in 0 .. key.size - 1
|
||||||
|
key_xor_ipad[i] ^= key[i]
|
||||||
|
key_xor_opad[i] ^= key[i]
|
||||||
|
end
|
||||||
|
@key_xor_ipad = key_xor_ipad
|
||||||
|
@key_xor_opad = key_xor_opad
|
||||||
|
@md = @algorithm.new
|
||||||
|
@status = STATUS_INITIALIZED
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_key
|
||||||
|
@key_xor_ipad.gsub!(/./, '?')
|
||||||
|
@key_xor_opad.gsub!(/./, '?')
|
||||||
|
@key_xor_ipad[0..-1] = ''
|
||||||
|
@key_xor_opad[0..-1] = ''
|
||||||
|
@status = STATUS_UNDEFINED
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(text)
|
||||||
|
check_status
|
||||||
|
# perform inner H
|
||||||
|
md = @algorithm.new
|
||||||
|
md.update(@key_xor_ipad)
|
||||||
|
md.update(text)
|
||||||
|
str = md.digest
|
||||||
|
# perform outer H
|
||||||
|
md = @algorithm.new
|
||||||
|
md.update(@key_xor_opad)
|
||||||
|
md.update(str)
|
||||||
|
@md = md
|
||||||
|
end
|
||||||
|
alias << update
|
||||||
|
|
||||||
|
def digest
|
||||||
|
check_status
|
||||||
|
@md.digest
|
||||||
|
end
|
||||||
|
|
||||||
|
def hexdigest
|
||||||
|
check_status
|
||||||
|
@md.hexdigest
|
||||||
|
end
|
||||||
|
alias to_s hexdigest
|
||||||
|
|
||||||
|
# These two class methods below are safer than using above
|
||||||
|
# instance methods combinatorially because an instance will have
|
||||||
|
# held a key even if it's no longer in use.
|
||||||
|
def Base.digest(key, text)
|
||||||
|
begin
|
||||||
|
hmac = self.new(key)
|
||||||
|
hmac.update(text)
|
||||||
|
hmac.digest
|
||||||
|
ensure
|
||||||
|
hmac.reset_key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def Base.hexdigest(key, text)
|
||||||
|
begin
|
||||||
|
hmac = self.new(key)
|
||||||
|
hmac.update(text)
|
||||||
|
hmac.hexdigest
|
||||||
|
ensure
|
||||||
|
hmac.reset_key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private_class_method :new, :digest, :hexdigest
|
||||||
|
end
|
||||||
|
|
||||||
|
STATUS_UNDEFINED, STATUS_INITIALIZED = 0, 1
|
||||||
|
end
|
||||||
11
vendor/gems/ruby-openid-2.1.2/lib/hmac/sha1.rb
vendored
Normal file
11
vendor/gems/ruby-openid-2.1.2/lib/hmac/sha1.rb
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
require 'hmac/hmac'
|
||||||
|
require 'digest/sha1'
|
||||||
|
|
||||||
|
module HMAC
|
||||||
|
class SHA1 < Base
|
||||||
|
def initialize(key = nil)
|
||||||
|
super(Digest::SHA1, 64, 20, key)
|
||||||
|
end
|
||||||
|
public_class_method :new, :digest, :hexdigest
|
||||||
|
end
|
||||||
|
end
|
||||||
25
vendor/gems/ruby-openid-2.1.2/lib/hmac/sha2.rb
vendored
Normal file
25
vendor/gems/ruby-openid-2.1.2/lib/hmac/sha2.rb
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
require 'hmac/hmac'
|
||||||
|
require 'digest/sha2'
|
||||||
|
|
||||||
|
module HMAC
|
||||||
|
class SHA256 < Base
|
||||||
|
def initialize(key = nil)
|
||||||
|
super(Digest::SHA256, 64, 32, key)
|
||||||
|
end
|
||||||
|
public_class_method :new, :digest, :hexdigest
|
||||||
|
end
|
||||||
|
|
||||||
|
class SHA384 < Base
|
||||||
|
def initialize(key = nil)
|
||||||
|
super(Digest::SHA384, 128, 48, key)
|
||||||
|
end
|
||||||
|
public_class_method :new, :digest, :hexdigest
|
||||||
|
end
|
||||||
|
|
||||||
|
class SHA512 < Base
|
||||||
|
def initialize(key = nil)
|
||||||
|
super(Digest::SHA512, 128, 64, key)
|
||||||
|
end
|
||||||
|
public_class_method :new, :digest, :hexdigest
|
||||||
|
end
|
||||||
|
end
|
||||||
20
vendor/gems/ruby-openid-2.1.2/lib/openid.rb
vendored
Normal file
20
vendor/gems/ruby-openid-2.1.2/lib/openid.rb
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Copyright 2006-2007 JanRain, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
||||||
|
# may not use this file except in compliance with the License. You may
|
||||||
|
# obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied. See the License for the specific language governing
|
||||||
|
# permissions and limitations under the License.
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
VERSION = "2.1.2"
|
||||||
|
end
|
||||||
|
|
||||||
|
require "openid/consumer"
|
||||||
|
require 'openid/server'
|
||||||
249
vendor/gems/ruby-openid-2.1.2/lib/openid/association.rb
vendored
Normal file
249
vendor/gems/ruby-openid-2.1.2/lib/openid/association.rb
vendored
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
require "openid/kvform"
|
||||||
|
require "openid/util"
|
||||||
|
require "openid/cryptutil"
|
||||||
|
require "openid/message"
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
|
||||||
|
def self.get_secret_size(assoc_type)
|
||||||
|
if assoc_type == 'HMAC-SHA1'
|
||||||
|
return 20
|
||||||
|
elsif assoc_type == 'HMAC-SHA256'
|
||||||
|
return 32
|
||||||
|
else
|
||||||
|
raise ArgumentError("Unsupported association type: #{assoc_type}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# An Association holds the shared secret between a relying party and
|
||||||
|
# an OpenID provider.
|
||||||
|
class Association
|
||||||
|
attr_reader :handle, :secret, :issued, :lifetime, :assoc_type
|
||||||
|
|
||||||
|
FIELD_ORDER =
|
||||||
|
[:version, :handle, :secret, :issued, :lifetime, :assoc_type,]
|
||||||
|
|
||||||
|
# Load a serialized Association
|
||||||
|
def self.deserialize(serialized)
|
||||||
|
parsed = Util.kv_to_seq(serialized)
|
||||||
|
parsed_fields = parsed.map{|k, v| k.to_sym}
|
||||||
|
if parsed_fields != FIELD_ORDER
|
||||||
|
raise ProtocolError, 'Unexpected fields in serialized association'\
|
||||||
|
" (Expected #{FIELD_ORDER.inspect}, got #{parsed_fields.inspect})"
|
||||||
|
end
|
||||||
|
version, handle, secret64, issued_s, lifetime_s, assoc_type =
|
||||||
|
parsed.map {|field, value| value}
|
||||||
|
if version != '2'
|
||||||
|
raise ProtocolError, "Attempted to deserialize unsupported version "\
|
||||||
|
"(#{parsed[0][1].inspect})"
|
||||||
|
end
|
||||||
|
|
||||||
|
self.new(handle,
|
||||||
|
Util.from_base64(secret64),
|
||||||
|
Time.at(issued_s.to_i),
|
||||||
|
lifetime_s.to_i,
|
||||||
|
assoc_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create an Association with an issued time of now
|
||||||
|
def self.from_expires_in(expires_in, handle, secret, assoc_type)
|
||||||
|
issued = Time.now
|
||||||
|
self.new(handle, secret, issued, expires_in, assoc_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(handle, secret, issued, lifetime, assoc_type)
|
||||||
|
@handle = handle
|
||||||
|
@secret = secret
|
||||||
|
@issued = issued
|
||||||
|
@lifetime = lifetime
|
||||||
|
@assoc_type = assoc_type
|
||||||
|
end
|
||||||
|
|
||||||
|
# Serialize the association to a form that's consistent across
|
||||||
|
# JanRain OpenID libraries.
|
||||||
|
def serialize
|
||||||
|
data = {
|
||||||
|
:version => '2',
|
||||||
|
:handle => handle,
|
||||||
|
:secret => Util.to_base64(secret),
|
||||||
|
:issued => issued.to_i.to_s,
|
||||||
|
:lifetime => lifetime.to_i.to_s,
|
||||||
|
:assoc_type => assoc_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.assert(data.length == FIELD_ORDER.length)
|
||||||
|
|
||||||
|
pairs = FIELD_ORDER.map{|field| [field.to_s, data[field]]}
|
||||||
|
return Util.seq_to_kv(pairs, strict=true)
|
||||||
|
end
|
||||||
|
|
||||||
|
# The number of seconds until this association expires
|
||||||
|
def expires_in(now=nil)
|
||||||
|
if now.nil?
|
||||||
|
now = Time.now.to_i
|
||||||
|
else
|
||||||
|
now = now.to_i
|
||||||
|
end
|
||||||
|
time_diff = (issued.to_i + lifetime) - now
|
||||||
|
if time_diff < 0
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return time_diff
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a signature for a sequence of [key, value] pairs
|
||||||
|
def sign(pairs)
|
||||||
|
kv = Util.seq_to_kv(pairs)
|
||||||
|
case assoc_type
|
||||||
|
when 'HMAC-SHA1'
|
||||||
|
CryptUtil.hmac_sha1(@secret, kv)
|
||||||
|
when 'HMAC-SHA256'
|
||||||
|
CryptUtil.hmac_sha256(@secret, kv)
|
||||||
|
else
|
||||||
|
raise ProtocolError, "Association has unknown type: "\
|
||||||
|
"#{assoc_type.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the list of pairs that form the signed elements of the
|
||||||
|
# given message
|
||||||
|
def make_pairs(message)
|
||||||
|
signed = message.get_arg(OPENID_NS, 'signed')
|
||||||
|
if signed.nil?
|
||||||
|
raise ProtocolError, 'Missing signed list'
|
||||||
|
end
|
||||||
|
signed_fields = signed.split(',', -1)
|
||||||
|
data = message.to_post_args
|
||||||
|
signed_fields.map {|field| [field, data.fetch('openid.'+field,'')] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return whether the message's signature passes
|
||||||
|
def check_message_signature(message)
|
||||||
|
message_sig = message.get_arg(OPENID_NS, 'sig')
|
||||||
|
if message_sig.nil?
|
||||||
|
raise ProtocolError, "#{message} has no sig."
|
||||||
|
end
|
||||||
|
calculated_sig = get_message_signature(message)
|
||||||
|
return calculated_sig == message_sig
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the signature for this message
|
||||||
|
def get_message_signature(message)
|
||||||
|
Util.to_base64(sign(make_pairs(message)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
(other.class == self.class and
|
||||||
|
other.handle == self.handle and
|
||||||
|
other.secret == self.secret and
|
||||||
|
|
||||||
|
# The internals of the time objects seemed to differ
|
||||||
|
# in an opaque way when serializing/unserializing.
|
||||||
|
# I don't think this will be a problem.
|
||||||
|
other.issued.to_i == self.issued.to_i and
|
||||||
|
|
||||||
|
other.lifetime == self.lifetime and
|
||||||
|
other.assoc_type == self.assoc_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add a signature (and a signed list) to a message.
|
||||||
|
def sign_message(message)
|
||||||
|
if (message.has_key?(OPENID_NS, 'sig') or
|
||||||
|
message.has_key?(OPENID_NS, 'signed'))
|
||||||
|
raise ArgumentError, 'Message already has signed list or signature'
|
||||||
|
end
|
||||||
|
|
||||||
|
extant_handle = message.get_arg(OPENID_NS, 'assoc_handle')
|
||||||
|
if extant_handle and extant_handle != self.handle
|
||||||
|
raise ArgumentError, "Message has a different association handle"
|
||||||
|
end
|
||||||
|
|
||||||
|
signed_message = message.copy()
|
||||||
|
signed_message.set_arg(OPENID_NS, 'assoc_handle', self.handle)
|
||||||
|
message_keys = signed_message.to_post_args.keys()
|
||||||
|
|
||||||
|
signed_list = []
|
||||||
|
message_keys.each { |k|
|
||||||
|
if k.starts_with?('openid.')
|
||||||
|
signed_list << k[7..-1]
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
signed_list << 'signed'
|
||||||
|
signed_list.sort!
|
||||||
|
|
||||||
|
signed_message.set_arg(OPENID_NS, 'signed', signed_list.join(','))
|
||||||
|
sig = get_message_signature(signed_message)
|
||||||
|
signed_message.set_arg(OPENID_NS, 'sig', sig)
|
||||||
|
return signed_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AssociationNegotiator
|
||||||
|
attr_reader :allowed_types
|
||||||
|
|
||||||
|
def self.get_session_types(assoc_type)
|
||||||
|
case assoc_type
|
||||||
|
when 'HMAC-SHA1'
|
||||||
|
['DH-SHA1', 'no-encryption']
|
||||||
|
when 'HMAC-SHA256'
|
||||||
|
['DH-SHA256', 'no-encryption']
|
||||||
|
else
|
||||||
|
raise ProtocolError, "Unknown association type #{assoc_type.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.check_session_type(assoc_type, session_type)
|
||||||
|
if !get_session_types(assoc_type).include?(session_type)
|
||||||
|
raise ProtocolError, "Session type #{session_type.inspect} not "\
|
||||||
|
"valid for association type #{assoc_type.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(allowed_types)
|
||||||
|
self.allowed_types=(allowed_types)
|
||||||
|
end
|
||||||
|
|
||||||
|
def copy
|
||||||
|
Marshal.load(Marshal.dump(self))
|
||||||
|
end
|
||||||
|
|
||||||
|
def allowed_types=(allowed_types)
|
||||||
|
allowed_types.each do |assoc_type, session_type|
|
||||||
|
self.class.check_session_type(assoc_type, session_type)
|
||||||
|
end
|
||||||
|
@allowed_types = allowed_types
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_allowed_type(assoc_type, session_type=nil)
|
||||||
|
if session_type.nil?
|
||||||
|
session_types = self.class.get_session_types(assoc_type)
|
||||||
|
else
|
||||||
|
self.class.check_session_type(assoc_type, session_type)
|
||||||
|
session_types = [session_type]
|
||||||
|
end
|
||||||
|
for session_type in session_types do
|
||||||
|
@allowed_types << [assoc_type, session_type]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def allowed?(assoc_type, session_type)
|
||||||
|
@allowed_types.include?([assoc_type, session_type])
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_allowed_type
|
||||||
|
@allowed_types.empty? ? nil : @allowed_types[0]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
DefaultNegotiator =
|
||||||
|
AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1'],
|
||||||
|
['HMAC-SHA1', 'no-encryption'],
|
||||||
|
['HMAC-SHA256', 'DH-SHA256'],
|
||||||
|
['HMAC-SHA256', 'no-encryption']])
|
||||||
|
|
||||||
|
EncryptedNegotiator =
|
||||||
|
AssociationNegotiator.new([['HMAC-SHA1', 'DH-SHA1'],
|
||||||
|
['HMAC-SHA256', 'DH-SHA256']])
|
||||||
|
end
|
||||||
394
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer.rb
vendored
Normal file
394
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer.rb
vendored
Normal file
|
|
@ -0,0 +1,394 @@
|
||||||
|
require "openid/consumer/idres.rb"
|
||||||
|
require "openid/consumer/checkid_request.rb"
|
||||||
|
require "openid/consumer/associationmanager.rb"
|
||||||
|
require "openid/consumer/responses.rb"
|
||||||
|
require "openid/consumer/discovery_manager"
|
||||||
|
require "openid/consumer/discovery"
|
||||||
|
require "openid/message"
|
||||||
|
require "openid/yadis/discovery"
|
||||||
|
require "openid/store/nonce"
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
# OpenID support for Relying Parties (aka Consumers).
|
||||||
|
#
|
||||||
|
# This module documents the main interface with the OpenID consumer
|
||||||
|
# library. The only part of the library which has to be used and
|
||||||
|
# isn't documented in full here is the store required to create an
|
||||||
|
# Consumer instance.
|
||||||
|
#
|
||||||
|
# = OVERVIEW
|
||||||
|
#
|
||||||
|
# The OpenID identity verification process most commonly uses the
|
||||||
|
# following steps, as visible to the user of this library:
|
||||||
|
#
|
||||||
|
# 1. The user enters their OpenID into a field on the consumer's
|
||||||
|
# site, and hits a login button.
|
||||||
|
#
|
||||||
|
# 2. The consumer site discovers the user's OpenID provider using
|
||||||
|
# the Yadis protocol.
|
||||||
|
#
|
||||||
|
# 3. The consumer site sends the browser a redirect to the OpenID
|
||||||
|
# provider. This is the authentication request as described in
|
||||||
|
# the OpenID specification.
|
||||||
|
#
|
||||||
|
# 4. The OpenID provider's site sends the browser a redirect back to
|
||||||
|
# the consumer site. This redirect contains the provider's
|
||||||
|
# response to the authentication request.
|
||||||
|
#
|
||||||
|
# The most important part of the flow to note is the consumer's site
|
||||||
|
# must handle two separate HTTP requests in order to perform the
|
||||||
|
# full identity check.
|
||||||
|
#
|
||||||
|
# = LIBRARY DESIGN
|
||||||
|
#
|
||||||
|
# This consumer library is designed with that flow in mind. The
|
||||||
|
# goal is to make it as easy as possible to perform the above steps
|
||||||
|
# securely.
|
||||||
|
#
|
||||||
|
# At a high level, there are two important parts in the consumer
|
||||||
|
# library. The first important part is this module, which contains
|
||||||
|
# the interface to actually use this library. The second is
|
||||||
|
# openid/store/interface.rb, which describes the interface to use if
|
||||||
|
# you need to create a custom method for storing the state this
|
||||||
|
# library needs to maintain between requests.
|
||||||
|
#
|
||||||
|
# In general, the second part is less important for users of the
|
||||||
|
# library to know about, as several implementations are provided
|
||||||
|
# which cover a wide variety of situations in which consumers may
|
||||||
|
# use the library.
|
||||||
|
#
|
||||||
|
# The Consumer class has methods corresponding to the actions
|
||||||
|
# necessary in each of steps 2, 3, and 4 described in the overview.
|
||||||
|
# Use of this library should be as easy as creating an Consumer
|
||||||
|
# instance and calling the methods appropriate for the action the
|
||||||
|
# site wants to take.
|
||||||
|
#
|
||||||
|
# This library automatically detects which version of the OpenID
|
||||||
|
# protocol should be used for a transaction and constructs the
|
||||||
|
# proper requests and responses. Users of this library do not need
|
||||||
|
# to worry about supporting multiple protocol versions; the library
|
||||||
|
# supports them implicitly. Depending on the version of the
|
||||||
|
# protocol in use, the OpenID transaction may be more secure. See
|
||||||
|
# the OpenID specifications for more information.
|
||||||
|
#
|
||||||
|
# = SESSIONS, STORES, AND STATELESS MODE
|
||||||
|
#
|
||||||
|
# The Consumer object keeps track of two types of state:
|
||||||
|
#
|
||||||
|
# 1. State of the user's current authentication attempt. Things
|
||||||
|
# like the identity URL, the list of endpoints discovered for
|
||||||
|
# that URL, and in case where some endpoints are unreachable, the
|
||||||
|
# list of endpoints already tried. This state needs to be held
|
||||||
|
# from Consumer.begin() to Consumer.complete(), but it is only
|
||||||
|
# applicable to a single session with a single user agent, and at
|
||||||
|
# the end of the authentication process (i.e. when an OP replies
|
||||||
|
# with either <tt>id_res</tt>. or <tt>cancel</tt> it may be
|
||||||
|
# discarded.
|
||||||
|
#
|
||||||
|
# 2. State of relationships with servers, i.e. shared secrets
|
||||||
|
# (associations) with servers and nonces seen on signed messages.
|
||||||
|
# This information should persist from one session to the next
|
||||||
|
# and should not be bound to a particular user-agent.
|
||||||
|
#
|
||||||
|
# These two types of storage are reflected in the first two
|
||||||
|
# arguments of Consumer's constructor, <tt>session</tt> and
|
||||||
|
# <tt>store</tt>. <tt>session</tt> is a dict-like object and we
|
||||||
|
# hope your web framework provides you with one of these bound to
|
||||||
|
# the user agent. <tt>store</tt> is an instance of Store.
|
||||||
|
#
|
||||||
|
# Since the store does hold secrets shared between your application
|
||||||
|
# and the OpenID provider, you should be careful about how you use
|
||||||
|
# it in a shared hosting environment. If the filesystem or database
|
||||||
|
# permissions of your web host allow strangers to read from them, do
|
||||||
|
# not store your data there! If you have no safe place to store
|
||||||
|
# your data, construct your consumer with nil for the store, and it
|
||||||
|
# will operate only in stateless mode. Stateless mode may be
|
||||||
|
# slower, put more load on the OpenID provider, and trusts the
|
||||||
|
# provider to keep you safe from replay attacks.
|
||||||
|
#
|
||||||
|
# Several store implementation are provided, and the interface is
|
||||||
|
# fully documented so that custom stores can be used as well. See
|
||||||
|
# the documentation for the Consumer class for more information on
|
||||||
|
# the interface for stores. The implementations that are provided
|
||||||
|
# allow the consumer site to store the necessary data in several
|
||||||
|
# different ways, including several SQL databases and normal files
|
||||||
|
# on disk.
|
||||||
|
#
|
||||||
|
# = IMMEDIATE MODE
|
||||||
|
#
|
||||||
|
# In the flow described above, the user may need to confirm to the
|
||||||
|
# OpenID provider that it's ok to disclose his or her identity. The
|
||||||
|
# provider may draw pages asking for information from the user
|
||||||
|
# before it redirects the browser back to the consumer's site. This
|
||||||
|
# is generally transparent to the consumer site, so it is typically
|
||||||
|
# ignored as an implementation detail.
|
||||||
|
#
|
||||||
|
# There can be times, however, where the consumer site wants to get
|
||||||
|
# a response immediately. When this is the case, the consumer can
|
||||||
|
# put the library in immediate mode. In immediate mode, there is an
|
||||||
|
# extra response possible from the server, which is essentially the
|
||||||
|
# server reporting that it doesn't have enough information to answer
|
||||||
|
# the question yet.
|
||||||
|
#
|
||||||
|
# = USING THIS LIBRARY
|
||||||
|
#
|
||||||
|
# Integrating this library into an application is usually a
|
||||||
|
# relatively straightforward process. The process should basically
|
||||||
|
# follow this plan:
|
||||||
|
#
|
||||||
|
# Add an OpenID login field somewhere on your site. When an OpenID
|
||||||
|
# is entered in that field and the form is submitted, it should make
|
||||||
|
# a request to the your site which includes that OpenID URL.
|
||||||
|
#
|
||||||
|
# First, the application should instantiate a Consumer with a
|
||||||
|
# session for per-user state and store for shared state using the
|
||||||
|
# store of choice.
|
||||||
|
#
|
||||||
|
# Next, the application should call the <tt>begin</tt> method of
|
||||||
|
# Consumer instance. This method takes the OpenID URL as entered by
|
||||||
|
# the user. The <tt>begin</tt> method returns a CheckIDRequest
|
||||||
|
# object.
|
||||||
|
#
|
||||||
|
# Next, the application should call the redirect_url method on the
|
||||||
|
# CheckIDRequest object. The parameter <tt>return_to</tt> is the
|
||||||
|
# URL that the OpenID server will send the user back to after
|
||||||
|
# attempting to verify his or her identity. The <tt>realm</tt>
|
||||||
|
# parameter is the URL (or URL pattern) that identifies your web
|
||||||
|
# site to the user when he or she is authorizing it. Send a
|
||||||
|
# redirect to the resulting URL to the user's browser.
|
||||||
|
#
|
||||||
|
# That's the first half of the authentication process. The second
|
||||||
|
# half of the process is done after the user's OpenID Provider sends
|
||||||
|
# the user's browser a redirect back to your site to complete their
|
||||||
|
# login.
|
||||||
|
#
|
||||||
|
# When that happens, the user will contact your site at the URL
|
||||||
|
# given as the <tt>return_to</tt> URL to the redirect_url call made
|
||||||
|
# above. The request will have several query parameters added to
|
||||||
|
# the URL by the OpenID provider as the information necessary to
|
||||||
|
# finish the request.
|
||||||
|
#
|
||||||
|
# Get a Consumer instance with the same session and store as before
|
||||||
|
# and call its complete() method, passing in all the received query
|
||||||
|
# arguments and URL currently being handled.
|
||||||
|
#
|
||||||
|
# There are multiple possible return types possible from that
|
||||||
|
# method. These indicate the whether or not the login was
|
||||||
|
# successful, and include any additional information appropriate for
|
||||||
|
# their type.
|
||||||
|
class Consumer
|
||||||
|
attr_accessor :session_key_prefix
|
||||||
|
|
||||||
|
# Initialize a Consumer instance.
|
||||||
|
#
|
||||||
|
# You should create a new instance of the Consumer object with
|
||||||
|
# every HTTP request that handles OpenID transactions.
|
||||||
|
#
|
||||||
|
# session: the session object to use to store request information.
|
||||||
|
# The session should behave like a hash.
|
||||||
|
#
|
||||||
|
# store: an object that implements the interface in Store.
|
||||||
|
def initialize(session, store)
|
||||||
|
@session = session
|
||||||
|
@store = store
|
||||||
|
@session_key_prefix = 'OpenID::Consumer::'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start the OpenID authentication process. See steps 1-2 in the
|
||||||
|
# overview for the Consumer class.
|
||||||
|
#
|
||||||
|
# user_url: Identity URL given by the user. This method performs a
|
||||||
|
# textual transformation of the URL to try and make sure it is
|
||||||
|
# normalized. For example, a user_url of example.com will be
|
||||||
|
# normalized to http://example.com/ normalizing and resolving any
|
||||||
|
# redirects the server might issue.
|
||||||
|
#
|
||||||
|
# anonymous: A boolean value. Whether to make an anonymous
|
||||||
|
# request of the OpenID provider. Such a request does not ask for
|
||||||
|
# an authorization assertion for an OpenID identifier, but may be
|
||||||
|
# used with extensions to pass other data. e.g. "I don't care who
|
||||||
|
# you are, but I'd like to know your time zone."
|
||||||
|
#
|
||||||
|
# Returns a CheckIDRequest object containing the discovered
|
||||||
|
# information, with a method for building a redirect URL to the
|
||||||
|
# server, as described in step 3 of the overview. This object may
|
||||||
|
# also be used to add extension arguments to the request, using
|
||||||
|
# its add_extension_arg method.
|
||||||
|
#
|
||||||
|
# Raises DiscoveryFailure when no OpenID server can be found for
|
||||||
|
# this URL.
|
||||||
|
def begin(openid_identifier, anonymous=false)
|
||||||
|
manager = discovery_manager(openid_identifier)
|
||||||
|
service = manager.get_next_service(&method(:discover))
|
||||||
|
|
||||||
|
if service.nil?
|
||||||
|
raise DiscoveryFailure.new("No usable OpenID services were found "\
|
||||||
|
"for #{openid_identifier.inspect}", nil)
|
||||||
|
else
|
||||||
|
begin_without_discovery(service, anonymous)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start OpenID verification without doing OpenID server
|
||||||
|
# discovery. This method is used internally by Consumer.begin()
|
||||||
|
# after discovery is performed, and exists to provide an interface
|
||||||
|
# for library users needing to perform their own discovery.
|
||||||
|
#
|
||||||
|
# service: an OpenID service endpoint descriptor. This object and
|
||||||
|
# factories for it are found in the openid/consumer/discovery.rb
|
||||||
|
# module.
|
||||||
|
#
|
||||||
|
# Returns an OpenID authentication request object.
|
||||||
|
def begin_without_discovery(service, anonymous)
|
||||||
|
assoc = association_manager(service).get_association
|
||||||
|
checkid_request = CheckIDRequest.new(assoc, service)
|
||||||
|
checkid_request.anonymous = anonymous
|
||||||
|
|
||||||
|
if service.compatibility_mode
|
||||||
|
rt_args = checkid_request.return_to_args
|
||||||
|
rt_args[Consumer.openid1_return_to_nonce_name] = Nonce.mk_nonce
|
||||||
|
rt_args[Consumer.openid1_return_to_claimed_id_name] =
|
||||||
|
service.claimed_id
|
||||||
|
end
|
||||||
|
|
||||||
|
self.last_requested_endpoint = service
|
||||||
|
return checkid_request
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called to interpret the server's response to an OpenID
|
||||||
|
# request. It is called in step 4 of the flow described in the
|
||||||
|
# Consumer overview.
|
||||||
|
#
|
||||||
|
# query: A hash of the query parameters for this HTTP request.
|
||||||
|
# Note that in rails, this is <b>not</b> <tt>params</tt> but
|
||||||
|
# <tt>params.reject{|k,v|request.path_parameters[k]}</tt>
|
||||||
|
# because <tt>controller</tt> and <tt>action</tt> and other
|
||||||
|
# "path parameters" are included in params.
|
||||||
|
#
|
||||||
|
# current_url: Extract the URL of the current request from your
|
||||||
|
# application's web request framework and specify it here to have it
|
||||||
|
# checked against the openid.return_to value in the response. Do not
|
||||||
|
# just pass <tt>args['openid.return_to']</tt> here; that will defeat the
|
||||||
|
# purpose of this check. (See OpenID Authentication 2.0 section 11.1.)
|
||||||
|
#
|
||||||
|
# If the return_to URL check fails, the status of the completion will be
|
||||||
|
# FAILURE.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns a subclass of Response. The type of response is
|
||||||
|
# indicated by the status attribute, which will be one of
|
||||||
|
# SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED.
|
||||||
|
def complete(query, current_url)
|
||||||
|
message = Message.from_post_args(query)
|
||||||
|
mode = message.get_arg(OPENID_NS, 'mode', 'invalid')
|
||||||
|
begin
|
||||||
|
meth = method('complete_' + mode)
|
||||||
|
rescue NameError
|
||||||
|
meth = method(:complete_invalid)
|
||||||
|
end
|
||||||
|
response = meth.call(message, current_url)
|
||||||
|
cleanup_last_requested_endpoint
|
||||||
|
if [SUCCESS, CANCEL].member?(response.status)
|
||||||
|
cleanup_session
|
||||||
|
end
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def session_get(name)
|
||||||
|
@session[session_key(name)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def session_set(name, val)
|
||||||
|
@session[session_key(name)] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
def session_key(suffix)
|
||||||
|
@session_key_prefix + suffix
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_requested_endpoint
|
||||||
|
session_get('last_requested_endpoint')
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_requested_endpoint=(endpoint)
|
||||||
|
session_set('last_requested_endpoint', endpoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cleanup_last_requested_endpoint
|
||||||
|
@session[session_key('last_requested_endpoint')] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def discovery_manager(openid_identifier)
|
||||||
|
DiscoveryManager.new(@session, openid_identifier, @session_key_prefix)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cleanup_session
|
||||||
|
discovery_manager(nil).cleanup(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def discover(identifier)
|
||||||
|
OpenID.discover(identifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
def negotiator
|
||||||
|
DefaultNegotiator
|
||||||
|
end
|
||||||
|
|
||||||
|
def association_manager(service)
|
||||||
|
AssociationManager.new(@store, service.server_url,
|
||||||
|
service.compatibility_mode, negotiator)
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_idres(message, current_url)
|
||||||
|
IdResHandler.new(message, current_url, @store, last_requested_endpoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_invalid(message, unused_return_to)
|
||||||
|
mode = message.get_arg(OPENID_NS, 'mode', '<No mode set>')
|
||||||
|
return FailureResponse.new(last_requested_endpoint,
|
||||||
|
"Invalid openid.mode: #{mode}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_cancel(unused_message, unused_return_to)
|
||||||
|
return CancelResponse.new(last_requested_endpoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_error(message, unused_return_to)
|
||||||
|
error = message.get_arg(OPENID_NS, 'error')
|
||||||
|
contact = message.get_arg(OPENID_NS, 'contact')
|
||||||
|
reference = message.get_arg(OPENID_NS, 'reference')
|
||||||
|
|
||||||
|
return FailureResponse.new(last_requested_endpoint,
|
||||||
|
error, contact, reference)
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_setup_needed(message, unused_return_to)
|
||||||
|
if message.is_openid1
|
||||||
|
return complete_invalid(message, nil)
|
||||||
|
else
|
||||||
|
return SetupNeededResponse.new(last_requested_endpoint, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_id_res(message, current_url)
|
||||||
|
if message.is_openid1
|
||||||
|
setup_url = message.get_arg(OPENID1_NS, 'user_setup_url')
|
||||||
|
if !setup_url.nil?
|
||||||
|
return SetupNeededResponse.new(last_requested_endpoint, setup_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
idres = handle_idres(message, current_url)
|
||||||
|
rescue OpenIDError => why
|
||||||
|
return FailureResponse.new(last_requested_endpoint, why.message)
|
||||||
|
else
|
||||||
|
return SuccessResponse.new(idres.endpoint, message,
|
||||||
|
idres.signed_fields)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
340
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/associationmanager.rb
vendored
Normal file
340
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/associationmanager.rb
vendored
Normal file
|
|
@ -0,0 +1,340 @@
|
||||||
|
require "openid/dh"
|
||||||
|
require "openid/util"
|
||||||
|
require "openid/kvpost"
|
||||||
|
require "openid/cryptutil"
|
||||||
|
require "openid/protocolerror"
|
||||||
|
require "openid/association"
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
class Consumer
|
||||||
|
|
||||||
|
# A superclass for implementing Diffie-Hellman association sessions.
|
||||||
|
class DiffieHellmanSession
|
||||||
|
class << self
|
||||||
|
attr_reader :session_type, :secret_size, :allowed_assoc_types,
|
||||||
|
:hashfunc
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(dh=nil)
|
||||||
|
if dh.nil?
|
||||||
|
dh = DiffieHellman.from_defaults
|
||||||
|
end
|
||||||
|
@dh = dh
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the query parameters for requesting an association
|
||||||
|
# using this Diffie-Hellman association session
|
||||||
|
def get_request
|
||||||
|
args = {'dh_consumer_public' => CryptUtil.num_to_base64(@dh.public)}
|
||||||
|
if (!@dh.using_default_values?)
|
||||||
|
args['dh_modulus'] = CryptUtil.num_to_base64(@dh.modulus)
|
||||||
|
args['dh_gen'] = CryptUtil.num_to_base64(@dh.generator)
|
||||||
|
end
|
||||||
|
|
||||||
|
return args
|
||||||
|
end
|
||||||
|
|
||||||
|
# Process the response from a successful association request and
|
||||||
|
# return the shared secret for this association
|
||||||
|
def extract_secret(response)
|
||||||
|
dh_server_public64 = response.get_arg(OPENID_NS, 'dh_server_public',
|
||||||
|
NO_DEFAULT)
|
||||||
|
enc_mac_key64 = response.get_arg(OPENID_NS, 'enc_mac_key', NO_DEFAULT)
|
||||||
|
dh_server_public = CryptUtil.base64_to_num(dh_server_public64)
|
||||||
|
enc_mac_key = Util.from_base64(enc_mac_key64)
|
||||||
|
return @dh.xor_secret(self.class.hashfunc,
|
||||||
|
dh_server_public, enc_mac_key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# A Diffie-Hellman association session that uses SHA1 as its hash
|
||||||
|
# function
|
||||||
|
class DiffieHellmanSHA1Session < DiffieHellmanSession
|
||||||
|
@session_type = 'DH-SHA1'
|
||||||
|
@secret_size = 20
|
||||||
|
@allowed_assoc_types = ['HMAC-SHA1']
|
||||||
|
@hashfunc = CryptUtil.method(:sha1)
|
||||||
|
end
|
||||||
|
|
||||||
|
# A Diffie-Hellman association session that uses SHA256 as its hash
|
||||||
|
# function
|
||||||
|
class DiffieHellmanSHA256Session < DiffieHellmanSession
|
||||||
|
@session_type = 'DH-SHA256'
|
||||||
|
@secret_size = 32
|
||||||
|
@allowed_assoc_types = ['HMAC-SHA256']
|
||||||
|
@hashfunc = CryptUtil.method(:sha256)
|
||||||
|
end
|
||||||
|
|
||||||
|
# An association session that does not use encryption
|
||||||
|
class NoEncryptionSession
|
||||||
|
class << self
|
||||||
|
attr_reader :session_type, :allowed_assoc_types
|
||||||
|
end
|
||||||
|
@session_type = 'no-encryption'
|
||||||
|
@allowed_assoc_types = ['HMAC-SHA1', 'HMAC-SHA256']
|
||||||
|
|
||||||
|
def get_request
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_secret(response)
|
||||||
|
mac_key64 = response.get_arg(OPENID_NS, 'mac_key', NO_DEFAULT)
|
||||||
|
return Util.from_base64(mac_key64)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# An object that manages creating and storing associations for an
|
||||||
|
# OpenID provider endpoint
|
||||||
|
class AssociationManager
|
||||||
|
def self.create_session(session_type)
|
||||||
|
case session_type
|
||||||
|
when 'no-encryption'
|
||||||
|
NoEncryptionSession.new
|
||||||
|
when 'DH-SHA1'
|
||||||
|
DiffieHellmanSHA1Session.new
|
||||||
|
when 'DH-SHA256'
|
||||||
|
DiffieHellmanSHA256Session.new
|
||||||
|
else
|
||||||
|
raise ArgumentError, "Unknown association session type: "\
|
||||||
|
"#{session_type.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(store, server_url, compatibility_mode=false,
|
||||||
|
negotiator=nil)
|
||||||
|
@store = store
|
||||||
|
@server_url = server_url
|
||||||
|
@compatibility_mode = compatibility_mode
|
||||||
|
@negotiator = negotiator || DefaultNegotiator
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_association
|
||||||
|
if @store.nil?
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
assoc = @store.get_association(@server_url)
|
||||||
|
if assoc.nil? || assoc.expires_in <= 0
|
||||||
|
assoc = negotiate_association
|
||||||
|
if !assoc.nil?
|
||||||
|
@store.store_association(@server_url, assoc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return assoc
|
||||||
|
end
|
||||||
|
|
||||||
|
def negotiate_association
|
||||||
|
assoc_type, session_type = @negotiator.get_allowed_type
|
||||||
|
begin
|
||||||
|
return request_association(assoc_type, session_type)
|
||||||
|
rescue ServerError => why
|
||||||
|
supported_types = extract_supported_association_type(why, assoc_type)
|
||||||
|
if !supported_types.nil?
|
||||||
|
# Attempt to create an association from the assoc_type and
|
||||||
|
# session_type that the server told us it supported.
|
||||||
|
assoc_type, session_type = supported_types
|
||||||
|
begin
|
||||||
|
return request_association(assoc_type, session_type)
|
||||||
|
rescue ServerError => why
|
||||||
|
Util.log("Server #{@server_url} refused its suggested " \
|
||||||
|
"association type: session_type=#{session_type}, " \
|
||||||
|
"assoc_type=#{assoc_type}")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
def extract_supported_association_type(server_error, assoc_type)
|
||||||
|
# Any error message whose code is not 'unsupported-type' should
|
||||||
|
# be considered a total failure.
|
||||||
|
if (server_error.error_code != 'unsupported-type' or
|
||||||
|
server_error.message.is_openid1)
|
||||||
|
Util.log("Server error when requesting an association from "\
|
||||||
|
"#{@server_url}: #{server_error.error_text}")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# The server didn't like the association/session type that we
|
||||||
|
# sent, and it sent us back a message that might tell us how to
|
||||||
|
# handle it.
|
||||||
|
Util.log("Unsupported association type #{assoc_type}: "\
|
||||||
|
"#{server_error.error_text}")
|
||||||
|
|
||||||
|
# Extract the session_type and assoc_type from the error message
|
||||||
|
assoc_type = server_error.message.get_arg(OPENID_NS, 'assoc_type')
|
||||||
|
session_type = server_error.message.get_arg(OPENID_NS, 'session_type')
|
||||||
|
|
||||||
|
if assoc_type.nil? or session_type.nil?
|
||||||
|
Util.log("Server #{@server_url} responded with unsupported "\
|
||||||
|
"association session but did not supply a fallback.")
|
||||||
|
return nil
|
||||||
|
elsif !@negotiator.allowed?(assoc_type, session_type)
|
||||||
|
Util.log("Server sent unsupported session/association type: "\
|
||||||
|
"session_type=#{session_type}, assoc_type=#{assoc_type}")
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
return [assoc_type, session_type]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make and process one association request to this endpoint's OP
|
||||||
|
# endpoint URL. Returns an association object or nil if the
|
||||||
|
# association processing failed. Raises ServerError when the
|
||||||
|
# remote OpenID server returns an error.
|
||||||
|
def request_association(assoc_type, session_type)
|
||||||
|
assoc_session, args = create_associate_request(assoc_type, session_type)
|
||||||
|
|
||||||
|
begin
|
||||||
|
response = OpenID.make_kv_post(args, @server_url)
|
||||||
|
return extract_association(response, assoc_session)
|
||||||
|
rescue HTTPStatusError => why
|
||||||
|
Util.log("Got HTTP status error when requesting association: #{why}")
|
||||||
|
return nil
|
||||||
|
rescue Message::KeyNotFound => why
|
||||||
|
Util.log("Missing required parameter in response from "\
|
||||||
|
"#{@server_url}: #{why}")
|
||||||
|
return nil
|
||||||
|
|
||||||
|
rescue ProtocolError => why
|
||||||
|
Util.log("Protocol error processing response from #{@server_url}: "\
|
||||||
|
"#{why}")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create an association request for the given assoc_type and
|
||||||
|
# session_type. Returns a pair of the association session object
|
||||||
|
# and the request message that will be sent to the server.
|
||||||
|
def create_associate_request(assoc_type, session_type)
|
||||||
|
assoc_session = self.class.create_session(session_type)
|
||||||
|
args = {
|
||||||
|
'mode' => 'associate',
|
||||||
|
'assoc_type' => assoc_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !@compatibility_mode
|
||||||
|
args['ns'] = OPENID2_NS
|
||||||
|
end
|
||||||
|
|
||||||
|
# Leave out the session type if we're in compatibility mode
|
||||||
|
# *and* it's no-encryption.
|
||||||
|
if !@compatibility_mode ||
|
||||||
|
assoc_session.class.session_type != 'no-encryption'
|
||||||
|
args['session_type'] = assoc_session.class.session_type
|
||||||
|
end
|
||||||
|
|
||||||
|
args.merge!(assoc_session.get_request)
|
||||||
|
message = Message.from_openid_args(args)
|
||||||
|
return assoc_session, message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given an association response message, extract the OpenID 1.X
|
||||||
|
# session type. Returns the association type for this message
|
||||||
|
#
|
||||||
|
# This function mostly takes care of the 'no-encryption' default
|
||||||
|
# behavior in OpenID 1.
|
||||||
|
#
|
||||||
|
# If the association type is plain-text, this function will
|
||||||
|
# return 'no-encryption'
|
||||||
|
def get_openid1_session_type(assoc_response)
|
||||||
|
# If it's an OpenID 1 message, allow session_type to default
|
||||||
|
# to nil (which signifies "no-encryption")
|
||||||
|
session_type = assoc_response.get_arg(OPENID1_NS, 'session_type')
|
||||||
|
|
||||||
|
# Handle the differences between no-encryption association
|
||||||
|
# respones in OpenID 1 and 2:
|
||||||
|
|
||||||
|
# no-encryption is not really a valid session type for
|
||||||
|
# OpenID 1, but we'll accept it anyway, while issuing a
|
||||||
|
# warning.
|
||||||
|
if session_type == 'no-encryption'
|
||||||
|
Util.log("WARNING: #{@server_url} sent 'no-encryption'"\
|
||||||
|
"for OpenID 1.X")
|
||||||
|
|
||||||
|
# Missing or empty session type is the way to flag a
|
||||||
|
# 'no-encryption' response. Change the session type to
|
||||||
|
# 'no-encryption' so that it can be handled in the same
|
||||||
|
# way as OpenID 2 'no-encryption' respones.
|
||||||
|
elsif session_type == '' || session_type.nil?
|
||||||
|
session_type = 'no-encryption'
|
||||||
|
end
|
||||||
|
|
||||||
|
return session_type
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.extract_expires_in(message)
|
||||||
|
# expires_in should be a base-10 string.
|
||||||
|
expires_in_str = message.get_arg(OPENID_NS, 'expires_in', NO_DEFAULT)
|
||||||
|
if !(/\A\d+\Z/ =~ expires_in_str)
|
||||||
|
raise ProtocolError, "Invalid expires_in field: #{expires_in_str}"
|
||||||
|
end
|
||||||
|
expires_in_str.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
# Attempt to extract an association from the response, given the
|
||||||
|
# association response message and the established association
|
||||||
|
# session.
|
||||||
|
def extract_association(assoc_response, assoc_session)
|
||||||
|
# Extract the common fields from the response, raising an
|
||||||
|
# exception if they are not found
|
||||||
|
assoc_type = assoc_response.get_arg(OPENID_NS, 'assoc_type',
|
||||||
|
NO_DEFAULT)
|
||||||
|
assoc_handle = assoc_response.get_arg(OPENID_NS, 'assoc_handle',
|
||||||
|
NO_DEFAULT)
|
||||||
|
expires_in = self.class.extract_expires_in(assoc_response)
|
||||||
|
|
||||||
|
# OpenID 1 has funny association session behaviour.
|
||||||
|
if assoc_response.is_openid1
|
||||||
|
session_type = get_openid1_session_type(assoc_response)
|
||||||
|
else
|
||||||
|
session_type = assoc_response.get_arg(OPENID2_NS, 'session_type',
|
||||||
|
NO_DEFAULT)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Session type mismatch
|
||||||
|
if assoc_session.class.session_type != session_type
|
||||||
|
if (assoc_response.is_openid1 and session_type == 'no-encryption')
|
||||||
|
# In OpenID 1, any association request can result in a
|
||||||
|
# 'no-encryption' association response. Setting
|
||||||
|
# assoc_session to a new no-encryption session should
|
||||||
|
# make the rest of this function work properly for
|
||||||
|
# that case.
|
||||||
|
assoc_session = NoEncryptionSession.new
|
||||||
|
else
|
||||||
|
# Any other mismatch, regardless of protocol version
|
||||||
|
# results in the failure of the association session
|
||||||
|
# altogether.
|
||||||
|
raise ProtocolError, "Session type mismatch. Expected "\
|
||||||
|
"#{assoc_session.class.session_type}, got "\
|
||||||
|
"#{session_type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make sure assoc_type is valid for session_type
|
||||||
|
if !assoc_session.class.allowed_assoc_types.member?(assoc_type)
|
||||||
|
raise ProtocolError, "Unsupported assoc_type for session "\
|
||||||
|
"#{assoc_session.class.session_type} "\
|
||||||
|
"returned: #{assoc_type}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delegate to the association session to extract the secret
|
||||||
|
# from the response, however is appropriate for that session
|
||||||
|
# type.
|
||||||
|
begin
|
||||||
|
secret = assoc_session.extract_secret(assoc_response)
|
||||||
|
rescue Message::KeyNotFound, ArgumentError => why
|
||||||
|
raise ProtocolError, "Malformed response for "\
|
||||||
|
"#{assoc_session.class.session_type} "\
|
||||||
|
"session: #{why.message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return Association.from_expires_in(expires_in, assoc_handle, secret,
|
||||||
|
assoc_type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
186
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/checkid_request.rb
vendored
Normal file
186
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/checkid_request.rb
vendored
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
require "openid/message"
|
||||||
|
require "openid/util"
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
class Consumer
|
||||||
|
# An object that holds the state necessary for generating an
|
||||||
|
# OpenID authentication request. This object holds the association
|
||||||
|
# with the server and the discovered information with which the
|
||||||
|
# request will be made.
|
||||||
|
#
|
||||||
|
# It is separate from the consumer because you may wish to add
|
||||||
|
# things to the request before sending it on its way to the
|
||||||
|
# server. It also has serialization options that let you encode
|
||||||
|
# the authentication request as a URL or as a form POST.
|
||||||
|
class CheckIDRequest
|
||||||
|
attr_accessor :return_to_args, :message
|
||||||
|
attr_reader :endpoint
|
||||||
|
|
||||||
|
# Users of this library should not create instances of this
|
||||||
|
# class. Instances of this class are created by the library
|
||||||
|
# when needed.
|
||||||
|
def initialize(assoc, endpoint)
|
||||||
|
@assoc = assoc
|
||||||
|
@endpoint = endpoint
|
||||||
|
@return_to_args = {}
|
||||||
|
@message = Message.new(endpoint.preferred_namespace)
|
||||||
|
@anonymous = false
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :anonymous
|
||||||
|
|
||||||
|
# Set whether this request should be made anonymously. If a
|
||||||
|
# request is anonymous, the identifier will not be sent in the
|
||||||
|
# request. This is only useful if you are making another kind of
|
||||||
|
# request with an extension in this request.
|
||||||
|
#
|
||||||
|
# Anonymous requests are not allowed when the request is made
|
||||||
|
# with OpenID 1.
|
||||||
|
def anonymous=(is_anonymous)
|
||||||
|
if is_anonymous && @message.is_openid1
|
||||||
|
raise ArgumentError, ("OpenID1 requests MUST include the "\
|
||||||
|
"identifier in the request")
|
||||||
|
end
|
||||||
|
@anonymous = is_anonymous
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add an object that implements the extension interface for
|
||||||
|
# adding arguments to an OpenID message to this checkid request.
|
||||||
|
#
|
||||||
|
# extension_request: an OpenID::Extension object.
|
||||||
|
def add_extension(extension_request)
|
||||||
|
extension_request.to_message(@message)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add an extension argument to this OpenID authentication
|
||||||
|
# request. You probably want to use add_extension and the
|
||||||
|
# OpenID::Extension interface.
|
||||||
|
#
|
||||||
|
# Use caution when adding arguments, because they will be
|
||||||
|
# URL-escaped and appended to the redirect URL, which can easily
|
||||||
|
# get quite long.
|
||||||
|
def add_extension_arg(namespace, key, value)
|
||||||
|
@message.set_arg(namespace, key, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Produce a OpenID::Message representing this request.
|
||||||
|
#
|
||||||
|
# Not specifying a return_to URL means that the user will not be
|
||||||
|
# returned to the site issuing the request upon its completion.
|
||||||
|
#
|
||||||
|
# If immediate mode is requested, the OpenID provider is to send
|
||||||
|
# back a response immediately, useful for behind-the-scenes
|
||||||
|
# authentication attempts. Otherwise the OpenID provider may
|
||||||
|
# engage the user before providing a response. This is the
|
||||||
|
# default case, as the user may need to provide credentials or
|
||||||
|
# approve the request before a positive response can be sent.
|
||||||
|
def get_message(realm, return_to=nil, immediate=false)
|
||||||
|
if !return_to.nil?
|
||||||
|
return_to = Util.append_args(return_to, @return_to_args)
|
||||||
|
elsif immediate
|
||||||
|
raise ArgumentError, ('"return_to" is mandatory when using '\
|
||||||
|
'"checkid_immediate"')
|
||||||
|
elsif @message.is_openid1
|
||||||
|
raise ArgumentError, ('"return_to" is mandatory for OpenID 1 '\
|
||||||
|
'requests')
|
||||||
|
elsif @return_to_args.empty?
|
||||||
|
raise ArgumentError, ('extra "return_to" arguments were specified, '\
|
||||||
|
'but no return_to was specified')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
message = @message.copy
|
||||||
|
|
||||||
|
mode = immediate ? 'checkid_immediate' : 'checkid_setup'
|
||||||
|
message.set_arg(OPENID_NS, 'mode', mode)
|
||||||
|
|
||||||
|
realm_key = message.is_openid1 ? 'trust_root' : 'realm'
|
||||||
|
message.set_arg(OPENID_NS, realm_key, realm)
|
||||||
|
|
||||||
|
if !return_to.nil?
|
||||||
|
message.set_arg(OPENID_NS, 'return_to', return_to)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not @anonymous
|
||||||
|
if @endpoint.is_op_identifier
|
||||||
|
# This will never happen when we're in OpenID 1
|
||||||
|
# compatibility mode, as long as is_op_identifier()
|
||||||
|
# returns false whenever preferred_namespace returns
|
||||||
|
# OPENID1_NS.
|
||||||
|
claimed_id = request_identity = IDENTIFIER_SELECT
|
||||||
|
else
|
||||||
|
request_identity = @endpoint.get_local_id
|
||||||
|
claimed_id = @endpoint.claimed_id
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is true for both OpenID 1 and 2
|
||||||
|
message.set_arg(OPENID_NS, 'identity', request_identity)
|
||||||
|
|
||||||
|
if message.is_openid2
|
||||||
|
message.set_arg(OPENID2_NS, 'claimed_id', claimed_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @assoc
|
||||||
|
message.set_arg(OPENID_NS, 'assoc_handle', @assoc.handle)
|
||||||
|
assoc_log_msg = "with assocication #{@assoc.handle}"
|
||||||
|
else
|
||||||
|
assoc_log_msg = 'using stateless mode.'
|
||||||
|
end
|
||||||
|
|
||||||
|
Util.log("Generated #{mode} request to #{@endpoint.server_url} "\
|
||||||
|
"#{assoc_log_msg}")
|
||||||
|
return message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a URL with an encoded OpenID request.
|
||||||
|
#
|
||||||
|
# The resulting URL is the OpenID provider's endpoint URL with
|
||||||
|
# parameters appended as query arguments. You should redirect
|
||||||
|
# the user agent to this URL.
|
||||||
|
#
|
||||||
|
# OpenID 2.0 endpoints also accept POST requests, see
|
||||||
|
# 'send_redirect?' and 'form_markup'.
|
||||||
|
def redirect_url(realm, return_to=nil, immediate=false)
|
||||||
|
message = get_message(realm, return_to, immediate)
|
||||||
|
return message.to_url(@endpoint.server_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get html for a form to submit this request to the IDP.
|
||||||
|
#
|
||||||
|
# form_tag_attrs is a hash of attributes to be added to the form
|
||||||
|
# tag. 'accept-charset' and 'enctype' have defaults that can be
|
||||||
|
# overridden. If a value is supplied for 'action' or 'method',
|
||||||
|
# it will be replaced.
|
||||||
|
def form_markup(realm, return_to=nil, immediate=false,
|
||||||
|
form_tag_attrs=nil)
|
||||||
|
message = get_message(realm, return_to, immediate)
|
||||||
|
return message.to_form_markup(@endpoint.server_url, form_tag_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get a complete HTML document that autosubmits the request to the IDP
|
||||||
|
# with javascript. This method wraps form_markup - see that method's
|
||||||
|
# documentation for help with the parameters.
|
||||||
|
def html_markup(realm, return_to=nil, immediate=false,
|
||||||
|
form_tag_attrs=nil)
|
||||||
|
Util.auto_submit_html(form_markup(realm,
|
||||||
|
return_to,
|
||||||
|
immediate,
|
||||||
|
form_tag_attrs))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Should this OpenID authentication request be sent as a HTTP
|
||||||
|
# redirect or as a POST (form submission)?
|
||||||
|
#
|
||||||
|
# This takes the same parameters as redirect_url or form_markup
|
||||||
|
def send_redirect?(realm, return_to=nil, immediate=false)
|
||||||
|
if @endpoint.compatibility_mode
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
url = redirect_url(realm, return_to, immediate)
|
||||||
|
return url.length <= OPENID1_URL_LIMIT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
490
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/discovery.rb
vendored
Normal file
490
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/discovery.rb
vendored
Normal file
|
|
@ -0,0 +1,490 @@
|
||||||
|
# Functions to discover OpenID endpoints from identifiers.
|
||||||
|
|
||||||
|
require 'uri'
|
||||||
|
require 'openid/util'
|
||||||
|
require 'openid/fetchers'
|
||||||
|
require 'openid/urinorm'
|
||||||
|
require 'openid/message'
|
||||||
|
require 'openid/yadis/discovery'
|
||||||
|
require 'openid/yadis/xrds'
|
||||||
|
require 'openid/yadis/xri'
|
||||||
|
require 'openid/yadis/services'
|
||||||
|
require 'openid/yadis/filters'
|
||||||
|
require 'openid/consumer/html_parse'
|
||||||
|
require 'openid/yadis/xrires'
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
|
||||||
|
OPENID_1_0_NS = 'http://openid.net/xmlns/1.0'
|
||||||
|
OPENID_IDP_2_0_TYPE = 'http://specs.openid.net/auth/2.0/server'
|
||||||
|
OPENID_2_0_TYPE = 'http://specs.openid.net/auth/2.0/signon'
|
||||||
|
OPENID_1_1_TYPE = 'http://openid.net/signon/1.1'
|
||||||
|
OPENID_1_0_TYPE = 'http://openid.net/signon/1.0'
|
||||||
|
|
||||||
|
OPENID_1_0_MESSAGE_NS = OPENID1_NS
|
||||||
|
OPENID_2_0_MESSAGE_NS = OPENID2_NS
|
||||||
|
|
||||||
|
# Object representing an OpenID service endpoint.
|
||||||
|
class OpenIDServiceEndpoint
|
||||||
|
|
||||||
|
# OpenID service type URIs, listed in order of preference. The
|
||||||
|
# ordering of this list affects yadis and XRI service discovery.
|
||||||
|
OPENID_TYPE_URIS = [
|
||||||
|
OPENID_IDP_2_0_TYPE,
|
||||||
|
|
||||||
|
OPENID_2_0_TYPE,
|
||||||
|
OPENID_1_1_TYPE,
|
||||||
|
OPENID_1_0_TYPE,
|
||||||
|
]
|
||||||
|
|
||||||
|
# the verified identifier.
|
||||||
|
attr_accessor :claimed_id
|
||||||
|
|
||||||
|
# For XRI, the persistent identifier.
|
||||||
|
attr_accessor :canonical_id
|
||||||
|
|
||||||
|
attr_accessor :server_url, :type_uris, :local_id, :used_yadis
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@claimed_id = nil
|
||||||
|
@server_url = nil
|
||||||
|
@type_uris = []
|
||||||
|
@local_id = nil
|
||||||
|
@canonical_id = nil
|
||||||
|
@used_yadis = false # whether this came from an XRDS
|
||||||
|
@display_identifier = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def display_identifier
|
||||||
|
return @display_identifier if @display_identifier
|
||||||
|
|
||||||
|
return @claimed_id if @claimed_id.nil?
|
||||||
|
|
||||||
|
begin
|
||||||
|
parsed_identifier = URI.parse(@claimed_id)
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
raise ProtocolError, "Claimed identifier #{claimed_id} is not a valid URI"
|
||||||
|
end
|
||||||
|
|
||||||
|
return @claimed_id if not parsed_identifier.fragment
|
||||||
|
|
||||||
|
disp = parsed_identifier
|
||||||
|
disp.fragment = nil
|
||||||
|
|
||||||
|
return disp.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def display_identifier=(display_identifier)
|
||||||
|
@display_identifier = display_identifier
|
||||||
|
end
|
||||||
|
|
||||||
|
def uses_extension(extension_uri)
|
||||||
|
return @type_uris.member?(extension_uri)
|
||||||
|
end
|
||||||
|
|
||||||
|
def preferred_namespace
|
||||||
|
if (@type_uris.member?(OPENID_IDP_2_0_TYPE) or
|
||||||
|
@type_uris.member?(OPENID_2_0_TYPE))
|
||||||
|
return OPENID_2_0_MESSAGE_NS
|
||||||
|
else
|
||||||
|
return OPENID_1_0_MESSAGE_NS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def supports_type(type_uri)
|
||||||
|
# Does this endpoint support this type?
|
||||||
|
#
|
||||||
|
# I consider C{/server} endpoints to implicitly support C{/signon}.
|
||||||
|
(
|
||||||
|
@type_uris.member?(type_uri) or
|
||||||
|
(type_uri == OPENID_2_0_TYPE and is_op_identifier())
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def compatibility_mode
|
||||||
|
return preferred_namespace() != OPENID_2_0_MESSAGE_NS
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_op_identifier
|
||||||
|
return @type_uris.member?(OPENID_IDP_2_0_TYPE)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_service(yadis_url, uri, type_uris, service_element)
|
||||||
|
# Set the state of this object based on the contents of the
|
||||||
|
# service element.
|
||||||
|
@type_uris = type_uris
|
||||||
|
@server_url = uri
|
||||||
|
@used_yadis = true
|
||||||
|
|
||||||
|
if !is_op_identifier()
|
||||||
|
# XXX: This has crappy implications for Service elements that
|
||||||
|
# contain both 'server' and 'signon' Types. But that's a
|
||||||
|
# pathological configuration anyway, so I don't think I care.
|
||||||
|
@local_id = OpenID.find_op_local_identifier(service_element,
|
||||||
|
@type_uris)
|
||||||
|
@claimed_id = yadis_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_local_id
|
||||||
|
# Return the identifier that should be sent as the
|
||||||
|
# openid.identity parameter to the server.
|
||||||
|
if @local_id.nil? and @canonical_id.nil?
|
||||||
|
return @claimed_id
|
||||||
|
else
|
||||||
|
return (@local_id or @canonical_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_basic_service_endpoint(endpoint)
|
||||||
|
# Create a new instance of this class from the endpoint object
|
||||||
|
# passed in.
|
||||||
|
#
|
||||||
|
# @return: nil or OpenIDServiceEndpoint for this endpoint object"""
|
||||||
|
|
||||||
|
type_uris = endpoint.match_types(OPENID_TYPE_URIS)
|
||||||
|
|
||||||
|
# If any Type URIs match and there is an endpoint URI specified,
|
||||||
|
# then this is an OpenID endpoint
|
||||||
|
if (!type_uris.nil? and !type_uris.empty?) and !endpoint.uri.nil?
|
||||||
|
openid_endpoint = self.new
|
||||||
|
openid_endpoint.parse_service(
|
||||||
|
endpoint.yadis_url,
|
||||||
|
endpoint.uri,
|
||||||
|
endpoint.type_uris,
|
||||||
|
endpoint.service_element)
|
||||||
|
else
|
||||||
|
openid_endpoint = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return openid_endpoint
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_html(uri, html)
|
||||||
|
# Parse the given document as HTML looking for an OpenID <link
|
||||||
|
# rel=...>
|
||||||
|
#
|
||||||
|
# @rtype: [OpenIDServiceEndpoint]
|
||||||
|
|
||||||
|
discovery_types = [
|
||||||
|
[OPENID_2_0_TYPE, 'openid2.provider', 'openid2.local_id'],
|
||||||
|
[OPENID_1_1_TYPE, 'openid.server', 'openid.delegate'],
|
||||||
|
]
|
||||||
|
|
||||||
|
link_attrs = OpenID.parse_link_attrs(html)
|
||||||
|
services = []
|
||||||
|
discovery_types.each { |type_uri, op_endpoint_rel, local_id_rel|
|
||||||
|
|
||||||
|
op_endpoint_url = OpenID.find_first_href(link_attrs, op_endpoint_rel)
|
||||||
|
|
||||||
|
if !op_endpoint_url
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
service = self.new
|
||||||
|
service.claimed_id = uri
|
||||||
|
service.local_id = OpenID.find_first_href(link_attrs, local_id_rel)
|
||||||
|
service.server_url = op_endpoint_url
|
||||||
|
service.type_uris = [type_uri]
|
||||||
|
|
||||||
|
services << service
|
||||||
|
}
|
||||||
|
|
||||||
|
return services
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_xrds(uri, xrds)
|
||||||
|
# Parse the given document as XRDS looking for OpenID services.
|
||||||
|
#
|
||||||
|
# @rtype: [OpenIDServiceEndpoint]
|
||||||
|
#
|
||||||
|
# @raises L{XRDSError}: When the XRDS does not parse.
|
||||||
|
return Yadis::apply_filter(uri, xrds, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_discovery_result(discoveryResult)
|
||||||
|
# Create endpoints from a DiscoveryResult.
|
||||||
|
#
|
||||||
|
# @type discoveryResult: L{DiscoveryResult}
|
||||||
|
#
|
||||||
|
# @rtype: list of L{OpenIDServiceEndpoint}
|
||||||
|
#
|
||||||
|
# @raises L{XRDSError}: When the XRDS does not parse.
|
||||||
|
if discoveryResult.is_xrds()
|
||||||
|
meth = self.method('from_xrds')
|
||||||
|
else
|
||||||
|
meth = self.method('from_html')
|
||||||
|
end
|
||||||
|
|
||||||
|
return meth.call(discoveryResult.normalized_uri,
|
||||||
|
discoveryResult.response_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_op_endpoint_url(op_endpoint_url)
|
||||||
|
# Construct an OP-Identifier OpenIDServiceEndpoint object for
|
||||||
|
# a given OP Endpoint URL
|
||||||
|
#
|
||||||
|
# @param op_endpoint_url: The URL of the endpoint
|
||||||
|
# @rtype: OpenIDServiceEndpoint
|
||||||
|
service = self.new
|
||||||
|
service.server_url = op_endpoint_url
|
||||||
|
service.type_uris = [OPENID_IDP_2_0_TYPE]
|
||||||
|
return service
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
return sprintf("<%s server_url=%s claimed_id=%s " +
|
||||||
|
"local_id=%s canonical_id=%s used_yadis=%s>",
|
||||||
|
self.class, @server_url, @claimed_id,
|
||||||
|
@local_id, @canonical_id, @used_yadis)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_op_local_identifier(service_element, type_uris)
|
||||||
|
# Find the OP-Local Identifier for this xrd:Service element.
|
||||||
|
#
|
||||||
|
# This considers openid:Delegate to be a synonym for xrd:LocalID
|
||||||
|
# if both OpenID 1.X and OpenID 2.0 types are present. If only
|
||||||
|
# OpenID 1.X is present, it returns the value of
|
||||||
|
# openid:Delegate. If only OpenID 2.0 is present, it returns the
|
||||||
|
# value of xrd:LocalID. If there is more than one LocalID tag and
|
||||||
|
# the values are different, it raises a DiscoveryFailure. This is
|
||||||
|
# also triggered when the xrd:LocalID and openid:Delegate tags are
|
||||||
|
# different.
|
||||||
|
|
||||||
|
# XXX: Test this function on its own!
|
||||||
|
|
||||||
|
# Build the list of tags that could contain the OP-Local
|
||||||
|
# Identifier
|
||||||
|
local_id_tags = []
|
||||||
|
if type_uris.member?(OPENID_1_1_TYPE) or
|
||||||
|
type_uris.member?(OPENID_1_0_TYPE)
|
||||||
|
# local_id_tags << Yadis::nsTag(OPENID_1_0_NS, 'openid', 'Delegate')
|
||||||
|
service_element.add_namespace('openid', OPENID_1_0_NS)
|
||||||
|
local_id_tags << "openid:Delegate"
|
||||||
|
end
|
||||||
|
|
||||||
|
if type_uris.member?(OPENID_2_0_TYPE)
|
||||||
|
# local_id_tags.append(Yadis::nsTag(XRD_NS_2_0, 'xrd', 'LocalID'))
|
||||||
|
service_element.add_namespace('xrd', Yadis::XRD_NS_2_0)
|
||||||
|
local_id_tags << "xrd:LocalID"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Walk through all the matching tags and make sure that they all
|
||||||
|
# have the same value
|
||||||
|
local_id = nil
|
||||||
|
local_id_tags.each { |local_id_tag|
|
||||||
|
service_element.each_element(local_id_tag) { |local_id_element|
|
||||||
|
if local_id.nil?
|
||||||
|
local_id = local_id_element.text
|
||||||
|
elsif local_id != local_id_element.text
|
||||||
|
format = 'More than one %s tag found in one service element'
|
||||||
|
message = sprintf(format, local_id_tag)
|
||||||
|
raise DiscoveryFailure.new(message, nil)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return local_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.normalize_url(url)
|
||||||
|
# Normalize a URL, converting normalization failures to
|
||||||
|
# DiscoveryFailure
|
||||||
|
begin
|
||||||
|
normalized = URINorm.urinorm(url)
|
||||||
|
rescue URI::Error => why
|
||||||
|
raise DiscoveryFailure.new("Error normalizing #{url}: #{why.message}", nil)
|
||||||
|
else
|
||||||
|
defragged = URI::parse(normalized)
|
||||||
|
defragged.fragment = nil
|
||||||
|
return defragged.normalize.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.best_matching_service(service, preferred_types)
|
||||||
|
# Return the index of the first matching type, or something higher
|
||||||
|
# if no type matches.
|
||||||
|
#
|
||||||
|
# This provides an ordering in which service elements that contain
|
||||||
|
# a type that comes earlier in the preferred types list come
|
||||||
|
# before service elements that come later. If a service element
|
||||||
|
# has more than one type, the most preferred one wins.
|
||||||
|
preferred_types.each_with_index { |value, index|
|
||||||
|
if service.type_uris.member?(value)
|
||||||
|
return index
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferred_types.length
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.arrange_by_type(service_list, preferred_types)
|
||||||
|
# Rearrange service_list in a new list so services are ordered by
|
||||||
|
# types listed in preferred_types. Return the new list.
|
||||||
|
|
||||||
|
# Build a list with the service elements in tuples whose
|
||||||
|
# comparison will prefer the one with the best matching service
|
||||||
|
prio_services = []
|
||||||
|
|
||||||
|
service_list.each_with_index { |s, index|
|
||||||
|
prio_services << [best_matching_service(s, preferred_types), index, s]
|
||||||
|
}
|
||||||
|
|
||||||
|
prio_services.sort!
|
||||||
|
|
||||||
|
# Now that the services are sorted by priority, remove the sort
|
||||||
|
# keys from the list.
|
||||||
|
(0...prio_services.length).each { |i|
|
||||||
|
prio_services[i] = prio_services[i][2]
|
||||||
|
}
|
||||||
|
|
||||||
|
return prio_services
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_op_or_user_services(openid_services)
|
||||||
|
# Extract OP Identifier services. If none found, return the rest,
|
||||||
|
# sorted with most preferred first according to
|
||||||
|
# OpenIDServiceEndpoint.openid_type_uris.
|
||||||
|
#
|
||||||
|
# openid_services is a list of OpenIDServiceEndpoint objects.
|
||||||
|
#
|
||||||
|
# Returns a list of OpenIDServiceEndpoint objects.
|
||||||
|
|
||||||
|
op_services = arrange_by_type(openid_services, [OPENID_IDP_2_0_TYPE])
|
||||||
|
|
||||||
|
openid_services = arrange_by_type(openid_services,
|
||||||
|
OpenIDServiceEndpoint::OPENID_TYPE_URIS)
|
||||||
|
|
||||||
|
if !op_services.empty?
|
||||||
|
return op_services
|
||||||
|
else
|
||||||
|
return openid_services
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.discover_yadis(uri)
|
||||||
|
# Discover OpenID services for a URI. Tries Yadis and falls back
|
||||||
|
# on old-style <link rel='...'> discovery if Yadis fails.
|
||||||
|
#
|
||||||
|
# @param uri: normalized identity URL
|
||||||
|
# @type uri: str
|
||||||
|
#
|
||||||
|
# @return: (claimed_id, services)
|
||||||
|
# @rtype: (str, list(OpenIDServiceEndpoint))
|
||||||
|
#
|
||||||
|
# @raises DiscoveryFailure: when discovery fails.
|
||||||
|
|
||||||
|
# Might raise a yadis.discover.DiscoveryFailure if no document
|
||||||
|
# came back for that URI at all. I don't think falling back to
|
||||||
|
# OpenID 1.0 discovery on the same URL will help, so don't bother
|
||||||
|
# to catch it.
|
||||||
|
response = Yadis.discover(uri)
|
||||||
|
|
||||||
|
yadis_url = response.normalized_uri
|
||||||
|
body = response.response_text
|
||||||
|
|
||||||
|
begin
|
||||||
|
openid_services = OpenIDServiceEndpoint.from_xrds(yadis_url, body)
|
||||||
|
rescue Yadis::XRDSError
|
||||||
|
# Does not parse as a Yadis XRDS file
|
||||||
|
openid_services = []
|
||||||
|
end
|
||||||
|
|
||||||
|
if openid_services.empty?
|
||||||
|
# Either not an XRDS or there are no OpenID services.
|
||||||
|
|
||||||
|
if response.is_xrds
|
||||||
|
# if we got the Yadis content-type or followed the Yadis
|
||||||
|
# header, re-fetch the document without following the Yadis
|
||||||
|
# header, with no Accept header.
|
||||||
|
return self.discover_no_yadis(uri)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Try to parse the response as HTML.
|
||||||
|
# <link rel="...">
|
||||||
|
openid_services = OpenIDServiceEndpoint.from_html(yadis_url, body)
|
||||||
|
end
|
||||||
|
|
||||||
|
return [yadis_url, self.get_op_or_user_services(openid_services)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.discover_xri(iname)
|
||||||
|
endpoints = []
|
||||||
|
|
||||||
|
begin
|
||||||
|
canonical_id, services = Yadis::XRI::ProxyResolver.new().query(
|
||||||
|
iname, OpenIDServiceEndpoint::OPENID_TYPE_URIS)
|
||||||
|
|
||||||
|
if canonical_id.nil?
|
||||||
|
raise Yadis::XRDSError.new(sprintf('No CanonicalID found for XRI %s', iname))
|
||||||
|
end
|
||||||
|
|
||||||
|
flt = Yadis.make_filter(OpenIDServiceEndpoint)
|
||||||
|
|
||||||
|
services.each { |service_element|
|
||||||
|
endpoints += flt.get_service_endpoints(iname, service_element)
|
||||||
|
}
|
||||||
|
rescue Yadis::XRDSError => why
|
||||||
|
Util.log('xrds error on ' + iname + ': ' + why.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
endpoints.each { |endpoint|
|
||||||
|
# Is there a way to pass this through the filter to the endpoint
|
||||||
|
# constructor instead of tacking it on after?
|
||||||
|
endpoint.canonical_id = canonical_id
|
||||||
|
endpoint.claimed_id = canonical_id
|
||||||
|
endpoint.display_identifier = iname
|
||||||
|
}
|
||||||
|
|
||||||
|
# FIXME: returned xri should probably be in some normal form
|
||||||
|
return [iname, self.get_op_or_user_services(endpoints)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.discover_no_yadis(uri)
|
||||||
|
http_resp = OpenID.fetch(uri)
|
||||||
|
if http_resp.code != "200" and http_resp.code != "206"
|
||||||
|
raise DiscoveryFailure.new(
|
||||||
|
"HTTP Response status from identity URL host is not \"200\". "\
|
||||||
|
"Got status #{http_resp.code.inspect}", http_resp)
|
||||||
|
end
|
||||||
|
|
||||||
|
claimed_id = http_resp.final_url
|
||||||
|
openid_services = OpenIDServiceEndpoint.from_html(
|
||||||
|
claimed_id, http_resp.body)
|
||||||
|
return [claimed_id, openid_services]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.discover_uri(uri)
|
||||||
|
# Hack to work around URI parsing for URls with *no* scheme.
|
||||||
|
if uri.index("://").nil?
|
||||||
|
uri = 'http://' + uri
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
parsed = URI::parse(uri)
|
||||||
|
rescue URI::InvalidURIError => why
|
||||||
|
raise DiscoveryFailure.new("URI is not valid: #{why.message}", nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
if !parsed.scheme.nil? and !parsed.scheme.empty?
|
||||||
|
if !['http', 'https'].member?(parsed.scheme)
|
||||||
|
raise DiscoveryFailure.new(
|
||||||
|
"URI scheme #{parsed.scheme} is not HTTP or HTTPS", nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
uri = self.normalize_url(uri)
|
||||||
|
claimed_id, openid_services = self.discover_yadis(uri)
|
||||||
|
claimed_id = self.normalize_url(claimed_id)
|
||||||
|
return [claimed_id, openid_services]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.discover(identifier)
|
||||||
|
if Yadis::XRI::identifier_scheme(identifier) == :xri
|
||||||
|
normalized_identifier, services = discover_xri(identifier)
|
||||||
|
else
|
||||||
|
return discover_uri(identifier)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
123
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/discovery_manager.rb
vendored
Normal file
123
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/discovery_manager.rb
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
module OpenID
|
||||||
|
class Consumer
|
||||||
|
|
||||||
|
# A set of discovered services, for tracking which providers have
|
||||||
|
# been attempted for an OpenID identifier
|
||||||
|
class DiscoveredServices
|
||||||
|
attr_reader :current
|
||||||
|
|
||||||
|
def initialize(starting_url, yadis_url, services)
|
||||||
|
@starting_url = starting_url
|
||||||
|
@yadis_url = yadis_url
|
||||||
|
@services = services.dup
|
||||||
|
@current = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def next
|
||||||
|
@current = @services.shift
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_url?(url)
|
||||||
|
[@starting_url, @yadis_url].member?(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def started?
|
||||||
|
!@current.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def empty?
|
||||||
|
@services.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Manages calling discovery and tracking which endpoints have
|
||||||
|
# already been attempted.
|
||||||
|
class DiscoveryManager
|
||||||
|
def initialize(session, url, session_key_suffix=nil)
|
||||||
|
@url = url
|
||||||
|
|
||||||
|
@session = session
|
||||||
|
@session_key_suffix = session_key_suffix || 'auth'
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_next_service
|
||||||
|
manager = get_manager
|
||||||
|
if !manager.nil? && manager.empty?
|
||||||
|
destroy_manager
|
||||||
|
manager = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if manager.nil?
|
||||||
|
yadis_url, services = yield @url
|
||||||
|
manager = create_manager(yadis_url, services)
|
||||||
|
end
|
||||||
|
|
||||||
|
if !manager.nil?
|
||||||
|
service = manager.next
|
||||||
|
store(manager)
|
||||||
|
else
|
||||||
|
service = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return service
|
||||||
|
end
|
||||||
|
|
||||||
|
def cleanup(force=false)
|
||||||
|
manager = get_manager(force)
|
||||||
|
if !manager.nil?
|
||||||
|
service = manager.current
|
||||||
|
destroy_manager(force)
|
||||||
|
else
|
||||||
|
service = nil
|
||||||
|
end
|
||||||
|
return service
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def get_manager(force=false)
|
||||||
|
manager = load
|
||||||
|
if force || manager.nil? || manager.for_url?(@url)
|
||||||
|
return manager
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_manager(yadis_url, services)
|
||||||
|
manager = get_manager
|
||||||
|
if !manager.nil?
|
||||||
|
raise StandardError, "There is already a manager for #{yadis_url}"
|
||||||
|
end
|
||||||
|
if services.empty?
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
manager = DiscoveredServices.new(@url, yadis_url, services)
|
||||||
|
store(manager)
|
||||||
|
return manager
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy_manager(force=false)
|
||||||
|
if !get_manager(force).nil?
|
||||||
|
destroy!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def session_key
|
||||||
|
'OpenID::Consumer::DiscoveredServices::' + @session_key_suffix
|
||||||
|
end
|
||||||
|
|
||||||
|
def store(manager)
|
||||||
|
@session[session_key] = manager
|
||||||
|
end
|
||||||
|
|
||||||
|
def load
|
||||||
|
@session[session_key]
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy!
|
||||||
|
@session[session_key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
134
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/html_parse.rb
vendored
Normal file
134
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/html_parse.rb
vendored
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
require "openid/yadis/htmltokenizer"
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
|
||||||
|
# Stuff to remove before we start looking for tags
|
||||||
|
REMOVED_RE = /
|
||||||
|
# Comments
|
||||||
|
<!--.*?-->
|
||||||
|
|
||||||
|
# CDATA blocks
|
||||||
|
| <!\[CDATA\[.*?\]\]>
|
||||||
|
|
||||||
|
# script blocks
|
||||||
|
| <script\b
|
||||||
|
|
||||||
|
# make sure script is not an XML namespace
|
||||||
|
(?!:)
|
||||||
|
|
||||||
|
[^>]*>.*?<\/script>
|
||||||
|
|
||||||
|
/mixu
|
||||||
|
|
||||||
|
def OpenID.openid_unescape(s)
|
||||||
|
s.gsub('&','&').gsub('<','<').gsub('>','>').gsub('"','"')
|
||||||
|
end
|
||||||
|
|
||||||
|
def OpenID.unescape_hash(h)
|
||||||
|
newh = {}
|
||||||
|
h.map{|k,v|
|
||||||
|
newh[k]=openid_unescape(v)
|
||||||
|
}
|
||||||
|
newh
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def OpenID.parse_link_attrs(html)
|
||||||
|
stripped = html.gsub(REMOVED_RE,'')
|
||||||
|
parser = HTMLTokenizer.new(stripped)
|
||||||
|
|
||||||
|
links = []
|
||||||
|
# to keep track of whether or not we are in the head element
|
||||||
|
in_head = false
|
||||||
|
in_html = false
|
||||||
|
saw_head = false
|
||||||
|
|
||||||
|
begin
|
||||||
|
while el = parser.getTag('head', '/head', 'link', 'body', '/body',
|
||||||
|
'html', '/html')
|
||||||
|
|
||||||
|
# we are leaving head or have reached body, so we bail
|
||||||
|
return links if ['/head', 'body', '/body', '/html'].member?(el.tag_name)
|
||||||
|
|
||||||
|
# enforce html > head > link
|
||||||
|
if el.tag_name == 'html'
|
||||||
|
in_html = true
|
||||||
|
end
|
||||||
|
next unless in_html
|
||||||
|
if el.tag_name == 'head'
|
||||||
|
if saw_head
|
||||||
|
return links #only allow one head
|
||||||
|
end
|
||||||
|
saw_head = true
|
||||||
|
unless el.to_s[-2] == 47 # tag ends with a /: a short tag
|
||||||
|
in_head = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next unless in_head
|
||||||
|
|
||||||
|
return links if el.tag_name == 'html'
|
||||||
|
|
||||||
|
if el.tag_name == 'link'
|
||||||
|
links << unescape_hash(el.attr_hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
rescue Exception # just stop parsing if there's an error
|
||||||
|
end
|
||||||
|
return links
|
||||||
|
end
|
||||||
|
|
||||||
|
def OpenID.rel_matches(rel_attr, target_rel)
|
||||||
|
# Does this target_rel appear in the rel_str?
|
||||||
|
# XXX: TESTME
|
||||||
|
rels = rel_attr.strip().split()
|
||||||
|
rels.each { |rel|
|
||||||
|
rel = rel.downcase
|
||||||
|
if rel == target_rel
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def OpenID.link_has_rel(link_attrs, target_rel)
|
||||||
|
# Does this link have target_rel as a relationship?
|
||||||
|
|
||||||
|
# XXX: TESTME
|
||||||
|
rel_attr = link_attrs['rel']
|
||||||
|
return (rel_attr and rel_matches(rel_attr, target_rel))
|
||||||
|
end
|
||||||
|
|
||||||
|
def OpenID.find_links_rel(link_attrs_list, target_rel)
|
||||||
|
# Filter the list of link attributes on whether it has target_rel
|
||||||
|
# as a relationship.
|
||||||
|
|
||||||
|
# XXX: TESTME
|
||||||
|
matchesTarget = lambda { |attrs| link_has_rel(attrs, target_rel) }
|
||||||
|
result = []
|
||||||
|
|
||||||
|
link_attrs_list.each { |item|
|
||||||
|
if matchesTarget.call(item)
|
||||||
|
result << item
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
def OpenID.find_first_href(link_attrs_list, target_rel)
|
||||||
|
# Return the value of the href attribute for the first link tag in
|
||||||
|
# the list that has target_rel as a relationship.
|
||||||
|
|
||||||
|
# XXX: TESTME
|
||||||
|
matches = find_links_rel(link_attrs_list, target_rel)
|
||||||
|
if !matches or matches.empty?
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
first = matches[0]
|
||||||
|
return first['href']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
523
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/idres.rb
vendored
Normal file
523
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/idres.rb
vendored
Normal file
|
|
@ -0,0 +1,523 @@
|
||||||
|
require "openid/message"
|
||||||
|
require "openid/protocolerror"
|
||||||
|
require "openid/kvpost"
|
||||||
|
require "openid/consumer/discovery"
|
||||||
|
require "openid/urinorm"
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
class TypeURIMismatch < ProtocolError
|
||||||
|
attr_reader :type_uri, :endpoint
|
||||||
|
|
||||||
|
def initialize(type_uri, endpoint)
|
||||||
|
@type_uri = type_uri
|
||||||
|
@endpoint = endpoint
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Consumer
|
||||||
|
@openid1_return_to_nonce_name = 'rp_nonce'
|
||||||
|
@openid1_return_to_claimed_id_name = 'openid1_claimed_id'
|
||||||
|
|
||||||
|
# Set the name of the query parameter that this library will use
|
||||||
|
# to thread a nonce through an OpenID 1 transaction. It will be
|
||||||
|
# appended to the return_to URL.
|
||||||
|
def self.openid1_return_to_nonce_name=(query_arg_name)
|
||||||
|
@openid1_return_to_nonce_name = query_arg_name
|
||||||
|
end
|
||||||
|
|
||||||
|
# See openid1_return_to_nonce_name= documentation
|
||||||
|
def self.openid1_return_to_nonce_name
|
||||||
|
@openid1_return_to_nonce_name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set the name of the query parameter that this library will use
|
||||||
|
# to thread the requested URL through an OpenID 1 transaction (for
|
||||||
|
# use when verifying discovered information). It will be appended
|
||||||
|
# to the return_to URL.
|
||||||
|
def self.openid1_return_to_claimed_id_name=(query_arg_name)
|
||||||
|
@openid1_return_to_claimed_id_name = query_arg_name
|
||||||
|
end
|
||||||
|
|
||||||
|
# See openid1_return_to_claimed_id_name=
|
||||||
|
def self.openid1_return_to_claimed_id_name
|
||||||
|
@openid1_return_to_claimed_id_name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Handles an openid.mode=id_res response. This object is
|
||||||
|
# instantiated and used by the Consumer.
|
||||||
|
class IdResHandler
|
||||||
|
attr_reader :endpoint, :message
|
||||||
|
|
||||||
|
def initialize(message, current_url, store=nil, endpoint=nil)
|
||||||
|
@store = store # Fer the nonce and invalidate_handle
|
||||||
|
@message = message
|
||||||
|
@endpoint = endpoint
|
||||||
|
@current_url = current_url
|
||||||
|
@signed_list = nil
|
||||||
|
|
||||||
|
# Start the verification process
|
||||||
|
id_res
|
||||||
|
end
|
||||||
|
|
||||||
|
def signed_fields
|
||||||
|
signed_list.map {|x| 'openid.' + x}
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
# This method will raise ProtocolError unless the request is a
|
||||||
|
# valid id_res response. Once it has been verified, the methods
|
||||||
|
# 'endpoint', 'message', and 'signed_fields' contain the
|
||||||
|
# verified information.
|
||||||
|
def id_res
|
||||||
|
check_for_fields
|
||||||
|
verify_return_to
|
||||||
|
verify_discovery_results
|
||||||
|
check_signature
|
||||||
|
check_nonce
|
||||||
|
end
|
||||||
|
|
||||||
|
def server_url
|
||||||
|
@endpoint.nil? ? nil : @endpoint.server_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def openid_namespace
|
||||||
|
@message.get_openid_namespace
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch(field, default=NO_DEFAULT)
|
||||||
|
@message.get_arg(OPENID_NS, field, default)
|
||||||
|
end
|
||||||
|
|
||||||
|
def signed_list
|
||||||
|
if @signed_list.nil?
|
||||||
|
signed_list_str = fetch('signed', nil)
|
||||||
|
if signed_list_str.nil?
|
||||||
|
raise ProtocolError, 'Response missing signed list'
|
||||||
|
end
|
||||||
|
|
||||||
|
@signed_list = signed_list_str.split(',', -1)
|
||||||
|
end
|
||||||
|
@signed_list
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_for_fields
|
||||||
|
# XXX: if a field is missing, we should not have to explicitly
|
||||||
|
# check that it's present, just make sure that the fields are
|
||||||
|
# actually being used by the rest of the code in
|
||||||
|
# tests. Although, which fields are signed does need to be
|
||||||
|
# checked somewhere.
|
||||||
|
basic_fields = ['return_to', 'assoc_handle', 'sig', 'signed']
|
||||||
|
basic_sig_fields = ['return_to', 'identity']
|
||||||
|
|
||||||
|
case openid_namespace
|
||||||
|
when OPENID2_NS
|
||||||
|
require_fields = basic_fields + ['op_endpoint']
|
||||||
|
require_sigs = basic_sig_fields +
|
||||||
|
['response_nonce', 'claimed_id', 'assoc_handle',]
|
||||||
|
when OPENID1_NS
|
||||||
|
require_fields = basic_fields + ['identity']
|
||||||
|
require_sigs = basic_sig_fields
|
||||||
|
else
|
||||||
|
raise RuntimeError, "check_for_fields doesn't know about "\
|
||||||
|
"namespace #{openid_namespace.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
require_fields.each do |field|
|
||||||
|
if !@message.has_key?(OPENID_NS, field)
|
||||||
|
raise ProtocolError, "Missing required field #{field}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require_sigs.each do |field|
|
||||||
|
# Field is present and not in signed list
|
||||||
|
if @message.has_key?(OPENID_NS, field) && !signed_list.member?(field)
|
||||||
|
raise ProtocolError, "#{field.inspect} not signed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_return_to
|
||||||
|
begin
|
||||||
|
msg_return_to = URI.parse(URINorm::urinorm(fetch('return_to')))
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
raise ProtocolError, ("return_to is not a valid URI")
|
||||||
|
end
|
||||||
|
|
||||||
|
verify_return_to_args(msg_return_to)
|
||||||
|
if !@current_url.nil?
|
||||||
|
verify_return_to_base(msg_return_to)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_return_to_args(msg_return_to)
|
||||||
|
return_to_parsed_query = {}
|
||||||
|
if !msg_return_to.query.nil?
|
||||||
|
CGI.parse(msg_return_to.query).each_pair do |k, vs|
|
||||||
|
return_to_parsed_query[k] = vs[0]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
query = @message.to_post_args
|
||||||
|
return_to_parsed_query.each_pair do |rt_key, rt_val|
|
||||||
|
msg_val = query[rt_key]
|
||||||
|
if msg_val.nil?
|
||||||
|
raise ProtocolError, "Message missing return_to argument '#{rt_key}'"
|
||||||
|
elsif msg_val != rt_val
|
||||||
|
raise ProtocolError, ("Parameter '#{rt_key}' value "\
|
||||||
|
"#{msg_val.inspect} does not match "\
|
||||||
|
"return_to's value #{rt_val.inspect}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@message.get_args(BARE_NS).each_pair do |bare_key, bare_val|
|
||||||
|
rt_val = return_to_parsed_query[bare_key]
|
||||||
|
if not return_to_parsed_query.has_key? bare_key
|
||||||
|
# This may be caused by your web framework throwing extra
|
||||||
|
# entries in to your parameters hash that were not GET or
|
||||||
|
# POST parameters. For example, Rails has been known to
|
||||||
|
# add "controller" and "action" keys; another server adds
|
||||||
|
# at least a "format" key.
|
||||||
|
raise ProtocolError, ("Unexpected parameter (not on return_to): "\
|
||||||
|
"'#{bare_key}'=#{rt_val.inspect})")
|
||||||
|
end
|
||||||
|
if rt_val != bare_val
|
||||||
|
raise ProtocolError, ("Parameter '#{bare_key}' value "\
|
||||||
|
"#{bare_val.inspect} does not match "\
|
||||||
|
"return_to's value #{rt_val.inspect}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_return_to_base(msg_return_to)
|
||||||
|
begin
|
||||||
|
app_parsed = URI.parse(URINorm::urinorm(@current_url))
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
raise ProtocolError, "current_url is not a valid URI: #{@current_url}"
|
||||||
|
end
|
||||||
|
|
||||||
|
[:scheme, :host, :port, :path].each do |meth|
|
||||||
|
if msg_return_to.send(meth) != app_parsed.send(meth)
|
||||||
|
raise ProtocolError, "return_to #{meth.to_s} does not match"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Raises ProtocolError if the signature is bad
|
||||||
|
def check_signature
|
||||||
|
if @store.nil?
|
||||||
|
assoc = nil
|
||||||
|
else
|
||||||
|
assoc = @store.get_association(server_url, fetch('assoc_handle'))
|
||||||
|
end
|
||||||
|
|
||||||
|
if assoc.nil?
|
||||||
|
check_auth
|
||||||
|
else
|
||||||
|
if assoc.expires_in <= 0
|
||||||
|
# XXX: It might be a good idea sometimes to re-start the
|
||||||
|
# authentication with a new association. Doing it
|
||||||
|
# automatically opens the possibility for
|
||||||
|
# denial-of-service by a server that just returns expired
|
||||||
|
# associations (or really short-lived associations)
|
||||||
|
raise ProtocolError, "Association with #{server_url} expired"
|
||||||
|
elsif !assoc.check_message_signature(@message)
|
||||||
|
raise ProtocolError, "Bad signature in response from #{server_url}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_auth
|
||||||
|
Util.log("Using 'check_authentication' with #{server_url}")
|
||||||
|
begin
|
||||||
|
request = create_check_auth_request
|
||||||
|
rescue Message::KeyNotFound => why
|
||||||
|
raise ProtocolError, "Could not generate 'check_authentication' "\
|
||||||
|
"request: #{why.message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
response = OpenID.make_kv_post(request, server_url)
|
||||||
|
|
||||||
|
process_check_auth_response(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_check_auth_request
|
||||||
|
signed_list = @message.get_arg(OPENID_NS, 'signed', NO_DEFAULT).split(',')
|
||||||
|
|
||||||
|
# check that we got all the signed arguments
|
||||||
|
signed_list.each {|k|
|
||||||
|
@message.get_aliased_arg(k, NO_DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
ca_message = @message.copy
|
||||||
|
ca_message.set_arg(OPENID_NS, 'mode', 'check_authentication')
|
||||||
|
|
||||||
|
return ca_message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Process the response message from a check_authentication
|
||||||
|
# request, invalidating associations if requested.
|
||||||
|
def process_check_auth_response(response)
|
||||||
|
is_valid = response.get_arg(OPENID_NS, 'is_valid', 'false')
|
||||||
|
|
||||||
|
invalidate_handle = response.get_arg(OPENID_NS, 'invalidate_handle')
|
||||||
|
if !invalidate_handle.nil?
|
||||||
|
Util.log("Received 'invalidate_handle' from server #{server_url}")
|
||||||
|
if @store.nil?
|
||||||
|
Util.log('Unexpectedly got "invalidate_handle" without a store!')
|
||||||
|
else
|
||||||
|
@store.remove_association(server_url, invalidate_handle)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_valid != 'true'
|
||||||
|
raise ProtocolError, ("Server #{server_url} responds that the "\
|
||||||
|
"'check_authentication' call is not valid")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_nonce
|
||||||
|
case openid_namespace
|
||||||
|
when OPENID1_NS
|
||||||
|
nonce =
|
||||||
|
@message.get_arg(BARE_NS, Consumer.openid1_return_to_nonce_name)
|
||||||
|
|
||||||
|
# We generated the nonce, so it uses the empty string as the
|
||||||
|
# server URL
|
||||||
|
server_url = ''
|
||||||
|
when OPENID2_NS
|
||||||
|
nonce = @message.get_arg(OPENID2_NS, 'response_nonce')
|
||||||
|
server_url = self.server_url
|
||||||
|
else
|
||||||
|
raise StandardError, 'Not reached'
|
||||||
|
end
|
||||||
|
|
||||||
|
if nonce.nil?
|
||||||
|
raise ProtocolError, 'Nonce missing from response'
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
time, extra = Nonce.split_nonce(nonce)
|
||||||
|
rescue ArgumentError => why
|
||||||
|
raise ProtocolError, "Malformed nonce: #{nonce.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if !@store.nil? && !@store.use_nonce(server_url, time, extra)
|
||||||
|
raise ProtocolError, ("Nonce already used or out of range: "\
|
||||||
|
"#{nonce.inspect}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_discovery_results
|
||||||
|
begin
|
||||||
|
case openid_namespace
|
||||||
|
when OPENID1_NS
|
||||||
|
verify_discovery_results_openid1
|
||||||
|
when OPENID2_NS
|
||||||
|
verify_discovery_results_openid2
|
||||||
|
else
|
||||||
|
raise StandardError, "Not reached: #{openid_namespace}"
|
||||||
|
end
|
||||||
|
rescue Message::KeyNotFound => why
|
||||||
|
raise ProtocolError, "Missing required field: #{why.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_discovery_results_openid2
|
||||||
|
to_match = OpenIDServiceEndpoint.new
|
||||||
|
to_match.type_uris = [OPENID_2_0_TYPE]
|
||||||
|
to_match.claimed_id = fetch('claimed_id', nil)
|
||||||
|
to_match.local_id = fetch('identity', nil)
|
||||||
|
to_match.server_url = fetch('op_endpoint')
|
||||||
|
|
||||||
|
if to_match.claimed_id.nil? && !to_match.local_id.nil?
|
||||||
|
raise ProtocolError, ('openid.identity is present without '\
|
||||||
|
'openid.claimed_id')
|
||||||
|
elsif !to_match.claimed_id.nil? && to_match.local_id.nil?
|
||||||
|
raise ProtocolError, ('openid.claimed_id is present without '\
|
||||||
|
'openid.identity')
|
||||||
|
|
||||||
|
# This is a response without identifiers, so there's really no
|
||||||
|
# checking that we can do, so return an endpoint that's for
|
||||||
|
# the specified `openid.op_endpoint'
|
||||||
|
elsif to_match.claimed_id.nil?
|
||||||
|
@endpoint =
|
||||||
|
OpenIDServiceEndpoint.from_op_endpoint_url(to_match.server_url)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if @endpoint.nil?
|
||||||
|
Util.log('No pre-discovered information supplied')
|
||||||
|
discover_and_verify(to_match.claimed_id, [to_match])
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
verify_discovery_single(@endpoint, to_match)
|
||||||
|
rescue ProtocolError => why
|
||||||
|
Util.log("Error attempting to use stored discovery "\
|
||||||
|
"information: #{why.message}")
|
||||||
|
Util.log("Attempting discovery to verify endpoint")
|
||||||
|
discover_and_verify(to_match.claimed_id, [to_match])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @endpoint.claimed_id != to_match.claimed_id
|
||||||
|
@endpoint = @endpoint.dup
|
||||||
|
@endpoint.claimed_id = to_match.claimed_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_discovery_results_openid1
|
||||||
|
claimed_id =
|
||||||
|
@message.get_arg(BARE_NS, Consumer.openid1_return_to_claimed_id_name)
|
||||||
|
|
||||||
|
if claimed_id.nil?
|
||||||
|
if @endpoint.nil?
|
||||||
|
raise ProtocolError, ("When using OpenID 1, the claimed ID must "\
|
||||||
|
"be supplied, either by passing it through "\
|
||||||
|
"as a return_to parameter or by using a "\
|
||||||
|
"session, and supplied to the IdResHandler "\
|
||||||
|
"when it is constructed.")
|
||||||
|
else
|
||||||
|
claimed_id = @endpoint.claimed_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
to_match = OpenIDServiceEndpoint.new
|
||||||
|
to_match.type_uris = [OPENID_1_1_TYPE]
|
||||||
|
to_match.local_id = fetch('identity')
|
||||||
|
# Restore delegate information from the initiation phase
|
||||||
|
to_match.claimed_id = claimed_id
|
||||||
|
|
||||||
|
to_match_1_0 = to_match.dup
|
||||||
|
to_match_1_0.type_uris = [OPENID_1_0_TYPE]
|
||||||
|
|
||||||
|
if !@endpoint.nil?
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
verify_discovery_single(@endpoint, to_match)
|
||||||
|
rescue TypeURIMismatch
|
||||||
|
verify_discovery_single(@endpoint, to_match_1_0)
|
||||||
|
end
|
||||||
|
rescue ProtocolError => why
|
||||||
|
Util.log('Error attempting to use stored discovery information: ' +
|
||||||
|
why.message)
|
||||||
|
Util.log('Attempting discovery to verify endpoint')
|
||||||
|
else
|
||||||
|
return @endpoint
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Either no endpoint was supplied or OpenID 1.x verification
|
||||||
|
# of the information that's in the message failed on that
|
||||||
|
# endpoint.
|
||||||
|
discover_and_verify(to_match.claimed_id, [to_match, to_match_1_0])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given an endpoint object created from the information in an
|
||||||
|
# OpenID response, perform discovery and verify the discovery
|
||||||
|
# results, returning the matching endpoint that is the result of
|
||||||
|
# doing that discovery.
|
||||||
|
def discover_and_verify(claimed_id, to_match_endpoints)
|
||||||
|
Util.log("Performing discovery on #{claimed_id}")
|
||||||
|
_, services = OpenID.discover(claimed_id)
|
||||||
|
if services.length == 0
|
||||||
|
# XXX: this might want to be something other than
|
||||||
|
# ProtocolError. In Python, it's DiscoveryFailure
|
||||||
|
raise ProtocolError, ("No OpenID information found at "\
|
||||||
|
"#{claimed_id}")
|
||||||
|
end
|
||||||
|
verify_discovered_services(claimed_id, services, to_match_endpoints)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def verify_discovered_services(claimed_id, services, to_match_endpoints)
|
||||||
|
# Search the services resulting from discovery to find one
|
||||||
|
# that matches the information from the assertion
|
||||||
|
failure_messages = []
|
||||||
|
for endpoint in services
|
||||||
|
for to_match_endpoint in to_match_endpoints
|
||||||
|
begin
|
||||||
|
verify_discovery_single(endpoint, to_match_endpoint)
|
||||||
|
rescue ProtocolError => why
|
||||||
|
failure_messages << why.message
|
||||||
|
else
|
||||||
|
# It matches, so discover verification has
|
||||||
|
# succeeded. Return this endpoint.
|
||||||
|
@endpoint = endpoint
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Util.log("Discovery verification failure for #{claimed_id}")
|
||||||
|
failure_messages.each do |failure_message|
|
||||||
|
Util.log(" * Endpoint mismatch: " + failure_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
# XXX: is DiscoveryFailure in Python OpenID
|
||||||
|
raise ProtocolError, ("No matching endpoint found after "\
|
||||||
|
"discovering #{claimed_id}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_discovery_single(endpoint, to_match)
|
||||||
|
# Every type URI that's in the to_match endpoint has to be
|
||||||
|
# present in the discovered endpoint.
|
||||||
|
for type_uri in to_match.type_uris
|
||||||
|
if !endpoint.uses_extension(type_uri)
|
||||||
|
raise TypeURIMismatch.new(type_uri, endpoint)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fragments do not influence discovery, so we can't compare a
|
||||||
|
# claimed identifier with a fragment to discovered information.
|
||||||
|
defragged_claimed_id =
|
||||||
|
case Yadis::XRI.identifier_scheme(endpoint.claimed_id)
|
||||||
|
when :xri
|
||||||
|
endpoint.claimed_id
|
||||||
|
when :uri
|
||||||
|
begin
|
||||||
|
parsed = URI.parse(endpoint.claimed_id)
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
endpoint.claimed_id
|
||||||
|
else
|
||||||
|
parsed.fragment = nil
|
||||||
|
parsed.to_s
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise StandardError, 'Not reached'
|
||||||
|
end
|
||||||
|
|
||||||
|
if defragged_claimed_id != endpoint.claimed_id
|
||||||
|
raise ProtocolError, ("Claimed ID does not match (different "\
|
||||||
|
"subjects!), Expected "\
|
||||||
|
"#{defragged_claimed_id}, got "\
|
||||||
|
"#{endpoint.claimed_id}")
|
||||||
|
end
|
||||||
|
|
||||||
|
if to_match.get_local_id != endpoint.get_local_id
|
||||||
|
raise ProtocolError, ("local_id mismatch. Expected "\
|
||||||
|
"#{to_match.get_local_id}, got "\
|
||||||
|
"#{endpoint.get_local_id}")
|
||||||
|
end
|
||||||
|
|
||||||
|
# If the server URL is nil, this must be an OpenID 1
|
||||||
|
# response, because op_endpoint is a required parameter in
|
||||||
|
# OpenID 2. In that case, we don't actually care what the
|
||||||
|
# discovered server_url is, because signature checking or
|
||||||
|
# check_auth should take care of that check for us.
|
||||||
|
if to_match.server_url.nil?
|
||||||
|
if to_match.preferred_namespace != OPENID1_NS
|
||||||
|
raise StandardError,
|
||||||
|
"The code calling this must ensure that OpenID 2 "\
|
||||||
|
"responses have a non-none `openid.op_endpoint' and "\
|
||||||
|
"that it is set as the `server_url' attribute of the "\
|
||||||
|
"`to_match' endpoint."
|
||||||
|
end
|
||||||
|
elsif to_match.server_url != endpoint.server_url
|
||||||
|
raise ProtocolError, ("OP Endpoint mismatch. Expected"\
|
||||||
|
"#{to_match.server_url}, got "\
|
||||||
|
"#{endpoint.server_url}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
148
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/responses.rb
vendored
Normal file
148
vendor/gems/ruby-openid-2.1.2/lib/openid/consumer/responses.rb
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
module OpenID
|
||||||
|
class Consumer
|
||||||
|
# Code returned when either the of the
|
||||||
|
# OpenID::OpenIDConsumer.begin_auth or OpenID::OpenIDConsumer.complete_auth
|
||||||
|
# methods return successfully.
|
||||||
|
SUCCESS = :success
|
||||||
|
|
||||||
|
# Code OpenID::OpenIDConsumer.complete_auth
|
||||||
|
# returns when the value it received indicated an invalid login.
|
||||||
|
FAILURE = :failure
|
||||||
|
|
||||||
|
# Code returned by OpenIDConsumer.complete_auth when the user
|
||||||
|
# cancels the operation from the server.
|
||||||
|
CANCEL = :cancel
|
||||||
|
|
||||||
|
# Code returned by OpenID::OpenIDConsumer.complete_auth when the
|
||||||
|
# OpenIDConsumer instance is in immediate mode and ther server sends back a
|
||||||
|
# URL for the user to login with.
|
||||||
|
SETUP_NEEDED = :setup_needed
|
||||||
|
|
||||||
|
|
||||||
|
module Response
|
||||||
|
attr_reader :endpoint
|
||||||
|
|
||||||
|
def status
|
||||||
|
self.class::STATUS
|
||||||
|
end
|
||||||
|
|
||||||
|
# The identity URL that has been authenticated; the Claimed Identifier.
|
||||||
|
# See also display_identifier.
|
||||||
|
def identity_url
|
||||||
|
@endpoint ? @endpoint.claimed_id : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# The display identifier is related to the Claimed Identifier, but the
|
||||||
|
# two are not always identical. The display identifier is something the
|
||||||
|
# user should recognize as what they entered, whereas the response's
|
||||||
|
# claimed identifier (in the identity_url attribute) may have extra
|
||||||
|
# information for better persistence.
|
||||||
|
#
|
||||||
|
# URLs will be stripped of their fragments for display. XRIs will
|
||||||
|
# display the human-readable identifier (i-name) instead of the
|
||||||
|
# persistent identifier (i-number).
|
||||||
|
#
|
||||||
|
# Use the display identifier in your user interface. Use identity_url
|
||||||
|
# for querying your database or authorization server, or other
|
||||||
|
# identifier equality comparisons.
|
||||||
|
def display_identifier
|
||||||
|
@endpoint ? @endpoint.display_identifier : nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# A successful acknowledgement from the OpenID server that the
|
||||||
|
# supplied URL is, indeed controlled by the requesting agent.
|
||||||
|
class SuccessResponse
|
||||||
|
include Response
|
||||||
|
|
||||||
|
STATUS = SUCCESS
|
||||||
|
|
||||||
|
attr_reader :message, :signed_fields
|
||||||
|
|
||||||
|
def initialize(endpoint, message, signed_fields)
|
||||||
|
# Don't use :endpoint=, because endpoint should never be nil
|
||||||
|
# for a successfull transaction.
|
||||||
|
@endpoint = endpoint
|
||||||
|
@identity_url = endpoint.claimed_id
|
||||||
|
@message = message
|
||||||
|
@signed_fields = signed_fields
|
||||||
|
end
|
||||||
|
|
||||||
|
# Was this authentication response an OpenID 1 authentication
|
||||||
|
# response?
|
||||||
|
def is_openid1
|
||||||
|
@message.is_openid1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return whether a particular key is signed, regardless of its
|
||||||
|
# namespace alias
|
||||||
|
def signed?(ns_uri, ns_key)
|
||||||
|
@signed_fields.member?(@message.get_key(ns_uri, ns_key))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the specified signed field if available, otherwise
|
||||||
|
# return default
|
||||||
|
def get_signed(ns_uri, ns_key, default=nil)
|
||||||
|
if singed?(ns_uri, ns_key)
|
||||||
|
return @message.get_arg(ns_uri, ns_key, default)
|
||||||
|
else
|
||||||
|
return default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get signed arguments from the response message. Return a dict
|
||||||
|
# of all arguments in the specified namespace. If any of the
|
||||||
|
# arguments are not signed, return nil.
|
||||||
|
def get_signed_ns(ns_uri)
|
||||||
|
msg_args = @message.get_args(ns_uri)
|
||||||
|
msg_args.each_key do |key|
|
||||||
|
if !signed?(ns_uri, key)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return msg_args
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return response arguments in the specified namespace.
|
||||||
|
# If require_signed is true and the arguments are not signed,
|
||||||
|
# return nil.
|
||||||
|
def extension_response(namespace_uri, require_signed)
|
||||||
|
if require_signed
|
||||||
|
get_signed_ns(namespace_uri)
|
||||||
|
else
|
||||||
|
@message.get_args(namespace_uri)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class FailureResponse
|
||||||
|
include Response
|
||||||
|
STATUS = FAILURE
|
||||||
|
|
||||||
|
attr_reader :message, :contact, :reference
|
||||||
|
def initialize(endpoint, message, contact=nil, reference=nil)
|
||||||
|
@endpoint = endpoint
|
||||||
|
@message = message
|
||||||
|
@contact = contact
|
||||||
|
@reference = reference
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class CancelResponse
|
||||||
|
include Response
|
||||||
|
STATUS = CANCEL
|
||||||
|
def initialize(endpoint)
|
||||||
|
@endpoint = endpoint
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class SetupNeededResponse
|
||||||
|
include Response
|
||||||
|
STATUS = SETUP_NEEDED
|
||||||
|
def initialize(endpoint, setup_url)
|
||||||
|
@endpoint = endpoint
|
||||||
|
@setup_url = setup_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
97
vendor/gems/ruby-openid-2.1.2/lib/openid/cryptutil.rb
vendored
Normal file
97
vendor/gems/ruby-openid-2.1.2/lib/openid/cryptutil.rb
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
require "openid/util"
|
||||||
|
require "digest/sha1"
|
||||||
|
require "digest/sha2"
|
||||||
|
begin
|
||||||
|
require "digest/hmac"
|
||||||
|
rescue LoadError
|
||||||
|
require "hmac/sha1"
|
||||||
|
require "hmac/sha2"
|
||||||
|
end
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
# This module contains everything needed to perform low-level
|
||||||
|
# cryptograph and data manipulation tasks.
|
||||||
|
module CryptUtil
|
||||||
|
|
||||||
|
# Generate a random number, doing a little extra work to make it
|
||||||
|
# more likely that it's suitable for cryptography. If your system
|
||||||
|
# doesn't have /dev/urandom then this number is not
|
||||||
|
# cryptographically safe. See
|
||||||
|
# <http://www.cosine.org/2007/08/07/security-ruby-kernel-rand/>
|
||||||
|
# for more information. max is the largest possible value of such
|
||||||
|
# a random number, where the result will be less than max.
|
||||||
|
def CryptUtil.rand(max)
|
||||||
|
Kernel.srand()
|
||||||
|
return Kernel.rand(max)
|
||||||
|
end
|
||||||
|
|
||||||
|
def CryptUtil.sha1(text)
|
||||||
|
return Digest::SHA1.digest(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
def CryptUtil.hmac_sha1(key, text)
|
||||||
|
if Digest.const_defined? :HMAC
|
||||||
|
Digest::HMAC.new(key,Digest::SHA1).update(text).digest
|
||||||
|
else
|
||||||
|
return HMAC::SHA1.digest(key, text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def CryptUtil.sha256(text)
|
||||||
|
return Digest::SHA256.digest(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
def CryptUtil.hmac_sha256(key, text)
|
||||||
|
if Digest.const_defined? :HMAC
|
||||||
|
Digest::HMAC.new(key,Digest::SHA256).update(text).digest
|
||||||
|
else
|
||||||
|
return HMAC::SHA256.digest(key, text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a random string of the given length, composed of the
|
||||||
|
# specified characters. If chars is nil, generate a string
|
||||||
|
# composed of characters in the range 0..255.
|
||||||
|
def CryptUtil.random_string(length, chars=nil)
|
||||||
|
s = ""
|
||||||
|
|
||||||
|
unless chars.nil?
|
||||||
|
length.times { s << chars[rand(chars.length)] }
|
||||||
|
else
|
||||||
|
length.times { s << rand(256).chr }
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert a number to its binary representation; return a string
|
||||||
|
# of bytes.
|
||||||
|
def CryptUtil.num_to_binary(n)
|
||||||
|
bits = n.to_s(2)
|
||||||
|
prepend = (8 - bits.length % 8)
|
||||||
|
bits = ('0' * prepend) + bits
|
||||||
|
return [bits].pack('B*')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert a string of bytes into a number.
|
||||||
|
def CryptUtil.binary_to_num(s)
|
||||||
|
# taken from openid-ruby 0.0.1
|
||||||
|
s = "\000" * (4 - (s.length % 4)) + s
|
||||||
|
num = 0
|
||||||
|
s.unpack('N*').each do |x|
|
||||||
|
num <<= 32
|
||||||
|
num |= x
|
||||||
|
end
|
||||||
|
return num
|
||||||
|
end
|
||||||
|
|
||||||
|
# Encode a number as a base64-encoded byte string.
|
||||||
|
def CryptUtil.num_to_base64(l)
|
||||||
|
return OpenID::Util.to_base64(num_to_binary(l))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Decode a base64 byte string to a number.
|
||||||
|
def CryptUtil.base64_to_num(s)
|
||||||
|
return binary_to_num(OpenID::Util.from_base64(s))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
89
vendor/gems/ruby-openid-2.1.2/lib/openid/dh.rb
vendored
Normal file
89
vendor/gems/ruby-openid-2.1.2/lib/openid/dh.rb
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
require "openid/util"
|
||||||
|
require "openid/cryptutil"
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
|
||||||
|
# Encapsulates a Diffie-Hellman key exchange. This class is used
|
||||||
|
# internally by both the consumer and server objects.
|
||||||
|
#
|
||||||
|
# Read more about Diffie-Hellman on wikipedia:
|
||||||
|
# http://en.wikipedia.org/wiki/Diffie-Hellman
|
||||||
|
|
||||||
|
class DiffieHellman
|
||||||
|
|
||||||
|
# From the OpenID specification
|
||||||
|
@@default_mod = 155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443
|
||||||
|
@@default_gen = 2
|
||||||
|
|
||||||
|
attr_reader :modulus, :generator, :public
|
||||||
|
|
||||||
|
# A new DiffieHellman object, using the modulus and generator from
|
||||||
|
# the OpenID specification
|
||||||
|
def DiffieHellman.from_defaults
|
||||||
|
DiffieHellman.new(@@default_mod, @@default_gen)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(modulus=nil, generator=nil, priv=nil)
|
||||||
|
@modulus = modulus.nil? ? @@default_mod : modulus
|
||||||
|
@generator = generator.nil? ? @@default_gen : generator
|
||||||
|
set_private(priv.nil? ? OpenID::CryptUtil.rand(@modulus-2) + 1 : priv)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_shared_secret(composite)
|
||||||
|
DiffieHellman.powermod(composite, @private, @modulus)
|
||||||
|
end
|
||||||
|
|
||||||
|
def xor_secret(algorithm, composite, secret)
|
||||||
|
dh_shared = get_shared_secret(composite)
|
||||||
|
packed_dh_shared = OpenID::CryptUtil.num_to_binary(dh_shared)
|
||||||
|
hashed_dh_shared = algorithm.call(packed_dh_shared)
|
||||||
|
return DiffieHellman.strxor(secret, hashed_dh_shared)
|
||||||
|
end
|
||||||
|
|
||||||
|
def using_default_values?
|
||||||
|
@generator == @@default_gen && @modulus == @@default_mod
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def set_private(priv)
|
||||||
|
@private = priv
|
||||||
|
@public = DiffieHellman.powermod(@generator, @private, @modulus)
|
||||||
|
end
|
||||||
|
|
||||||
|
def DiffieHellman.strxor(s, t)
|
||||||
|
if s.length != t.length
|
||||||
|
raise ArgumentError, "strxor: lengths don't match. " +
|
||||||
|
"Inputs were #{s.inspect} and #{t.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if String.method_defined? :bytes
|
||||||
|
s.bytes.zip(t.bytes).map{|sb,tb| sb^tb}.pack('C*')
|
||||||
|
else
|
||||||
|
indices = 0...(s.length)
|
||||||
|
chrs = indices.collect {|i| (s[i]^t[i]).chr}
|
||||||
|
chrs.join("")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This code is taken from this post:
|
||||||
|
# <http://blade.nagaokaut.ac.jp/cgi-bin/scat.\rb/ruby/ruby-talk/19098>
|
||||||
|
# by Eric Lee Green.
|
||||||
|
def DiffieHellman.powermod(x, n, q)
|
||||||
|
counter=0
|
||||||
|
n_p=n
|
||||||
|
y_p=1
|
||||||
|
z_p=x
|
||||||
|
while n_p != 0
|
||||||
|
if n_p[0]==1
|
||||||
|
y_p=(y_p*z_p) % q
|
||||||
|
end
|
||||||
|
n_p = n_p >> 1
|
||||||
|
z_p = (z_p * z_p) % q
|
||||||
|
counter += 1
|
||||||
|
end
|
||||||
|
return y_p
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
39
vendor/gems/ruby-openid-2.1.2/lib/openid/extension.rb
vendored
Normal file
39
vendor/gems/ruby-openid-2.1.2/lib/openid/extension.rb
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
require 'openid/message'
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
# An interface for OpenID extensions.
|
||||||
|
class Extension < Object
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@ns_uri = nil
|
||||||
|
@ns_alias = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the string arguments that should be added to an OpenID
|
||||||
|
# message for this extension.
|
||||||
|
def get_extension_args
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add the arguments from this extension to the provided
|
||||||
|
# message, or create a new message containing only those
|
||||||
|
# arguments. Returns the message with added extension args.
|
||||||
|
def to_message(message = nil)
|
||||||
|
if message.nil?
|
||||||
|
# warnings.warn('Passing None to Extension.toMessage is deprecated. '
|
||||||
|
# 'Creating a message assuming you want OpenID 2.',
|
||||||
|
# DeprecationWarning, stacklevel=2)
|
||||||
|
Message.new(OPENID2_NS)
|
||||||
|
end
|
||||||
|
message = Message.new if message.nil?
|
||||||
|
|
||||||
|
implicit = message.is_openid1()
|
||||||
|
|
||||||
|
message.namespaces.add_alias(@ns_uri, @ns_alias, implicit)
|
||||||
|
# XXX python ignores keyerror if m.ns.getAlias(uri) == alias
|
||||||
|
|
||||||
|
message.update_args(@ns_uri, get_extension_args)
|
||||||
|
return message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
516
vendor/gems/ruby-openid-2.1.2/lib/openid/extensions/ax.rb
vendored
Normal file
516
vendor/gems/ruby-openid-2.1.2/lib/openid/extensions/ax.rb
vendored
Normal file
|
|
@ -0,0 +1,516 @@
|
||||||
|
# Implements the OpenID attribute exchange specification, version 1.0
|
||||||
|
|
||||||
|
require 'openid/extension'
|
||||||
|
require 'openid/trustroot'
|
||||||
|
require 'openid/message'
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
module AX
|
||||||
|
|
||||||
|
UNLIMITED_VALUES = "unlimited"
|
||||||
|
MINIMUM_SUPPORTED_ALIAS_LENGTH = 32
|
||||||
|
|
||||||
|
# check alias for invalid characters, raise AXError if found
|
||||||
|
def self.check_alias(name)
|
||||||
|
if name.match(/(,|\.)/)
|
||||||
|
raise Error, ("Alias #{name.inspect} must not contain a "\
|
||||||
|
"comma or period.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Raised when data does not comply with AX 1.0 specification
|
||||||
|
class Error < ArgumentError
|
||||||
|
end
|
||||||
|
|
||||||
|
# Abstract class containing common code for attribute exchange messages
|
||||||
|
class AXMessage < Extension
|
||||||
|
attr_accessor :ns_alias, :mode, :ns_uri
|
||||||
|
|
||||||
|
NS_URI = 'http://openid.net/srv/ax/1.0'
|
||||||
|
def initialize
|
||||||
|
@ns_alias = 'ax'
|
||||||
|
@ns_uri = NS_URI
|
||||||
|
@mode = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
# Raise an exception if the mode in the attribute exchange
|
||||||
|
# arguments does not match what is expected for this class.
|
||||||
|
def check_mode(ax_args)
|
||||||
|
actual_mode = ax_args['mode']
|
||||||
|
if actual_mode != @mode
|
||||||
|
raise Error, "Expected mode #{mode.inspect}, got #{actual_mode.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_args
|
||||||
|
{'mode' => @mode}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Represents a single attribute in an attribute exchange
|
||||||
|
# request. This should be added to an Request object in order to
|
||||||
|
# request the attribute.
|
||||||
|
#
|
||||||
|
# @ivar required: Whether the attribute will be marked as required
|
||||||
|
# when presented to the subject of the attribute exchange
|
||||||
|
# request.
|
||||||
|
# @type required: bool
|
||||||
|
#
|
||||||
|
# @ivar count: How many values of this type to request from the
|
||||||
|
# subject. Defaults to one.
|
||||||
|
# @type count: int
|
||||||
|
#
|
||||||
|
# @ivar type_uri: The identifier that determines what the attribute
|
||||||
|
# represents and how it is serialized. For example, one type URI
|
||||||
|
# representing dates could represent a Unix timestamp in base 10
|
||||||
|
# and another could represent a human-readable string.
|
||||||
|
# @type type_uri: str
|
||||||
|
#
|
||||||
|
# @ivar ns_alias: The name that should be given to this alias in the
|
||||||
|
# request. If it is not supplied, a generic name will be
|
||||||
|
# assigned. For example, if you want to call a Unix timestamp
|
||||||
|
# value 'tstamp', set its alias to that value. If two attributes
|
||||||
|
# in the same message request to use the same alias, the request
|
||||||
|
# will fail to be generated.
|
||||||
|
# @type alias: str or NoneType
|
||||||
|
class AttrInfo < Object
|
||||||
|
attr_reader :type_uri, :count, :ns_alias
|
||||||
|
attr_accessor :required
|
||||||
|
def initialize(type_uri, ns_alias=nil, required=false, count=1)
|
||||||
|
@type_uri = type_uri
|
||||||
|
@count = count
|
||||||
|
@required = required
|
||||||
|
@ns_alias = ns_alias
|
||||||
|
end
|
||||||
|
|
||||||
|
def wants_unlimited_values?
|
||||||
|
@count == UNLIMITED_VALUES
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given a namespace mapping and a string containing a
|
||||||
|
# comma-separated list of namespace aliases, return a list of type
|
||||||
|
# URIs that correspond to those aliases.
|
||||||
|
# namespace_map: OpenID::NamespaceMap
|
||||||
|
def self.to_type_uris(namespace_map, alias_list_s)
|
||||||
|
return [] if alias_list_s.nil?
|
||||||
|
alias_list_s.split(',').inject([]) {|uris, name|
|
||||||
|
type_uri = namespace_map.get_namespace_uri(name)
|
||||||
|
raise IndexError, "No type defined for attribute name #{name.inspect}" if type_uri.nil?
|
||||||
|
uris << type_uri
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# An attribute exchange 'fetch_request' message. This message is
|
||||||
|
# sent by a relying party when it wishes to obtain attributes about
|
||||||
|
# the subject of an OpenID authentication request.
|
||||||
|
class FetchRequest < AXMessage
|
||||||
|
attr_reader :requested_attributes
|
||||||
|
attr_accessor :update_url
|
||||||
|
|
||||||
|
def initialize(update_url = nil)
|
||||||
|
super()
|
||||||
|
@mode = 'fetch_request'
|
||||||
|
@requested_attributes = {}
|
||||||
|
@update_url = update_url
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add an attribute to this attribute exchange request.
|
||||||
|
# attribute: AttrInfo, the attribute being requested
|
||||||
|
# Raises IndexError if the requested attribute is already present
|
||||||
|
# in this request.
|
||||||
|
def add(attribute)
|
||||||
|
if @requested_attributes[attribute.type_uri]
|
||||||
|
raise IndexError, "The attribute #{attribute.type_uri} has already been requested"
|
||||||
|
end
|
||||||
|
@requested_attributes[attribute.type_uri] = attribute
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the serialized form of this attribute fetch request.
|
||||||
|
# returns a hash of the arguments
|
||||||
|
def get_extension_args
|
||||||
|
aliases = NamespaceMap.new
|
||||||
|
required = []
|
||||||
|
if_available = []
|
||||||
|
ax_args = new_args
|
||||||
|
@requested_attributes.each{|type_uri, attribute|
|
||||||
|
if attribute.ns_alias
|
||||||
|
name = aliases.add_alias(type_uri, attribute.ns_alias)
|
||||||
|
else
|
||||||
|
name = aliases.add(type_uri)
|
||||||
|
end
|
||||||
|
if attribute.required
|
||||||
|
required << name
|
||||||
|
else
|
||||||
|
if_available << name
|
||||||
|
end
|
||||||
|
if attribute.count != 1
|
||||||
|
ax_args["count.#{name}"] = attribute.count.to_s
|
||||||
|
end
|
||||||
|
ax_args["type.#{name}"] = type_uri
|
||||||
|
}
|
||||||
|
|
||||||
|
unless required.empty?
|
||||||
|
ax_args['required'] = required.join(',')
|
||||||
|
end
|
||||||
|
unless if_available.empty?
|
||||||
|
ax_args['if_available'] = if_available.join(',')
|
||||||
|
end
|
||||||
|
return ax_args
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the type URIs for all attributes that have been marked
|
||||||
|
# as required.
|
||||||
|
def get_required_attrs
|
||||||
|
@requested_attributes.inject([]) {|required, (type_uri, attribute)|
|
||||||
|
if attribute.required
|
||||||
|
required << type_uri
|
||||||
|
else
|
||||||
|
required
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extract a FetchRequest from an OpenID message
|
||||||
|
# message: OpenID::Message
|
||||||
|
# return a FetchRequest or nil if AX arguments are not present
|
||||||
|
def self.from_openid_request(oidreq)
|
||||||
|
message = oidreq.message
|
||||||
|
ax_args = message.get_args(NS_URI)
|
||||||
|
return nil if ax_args == {}
|
||||||
|
req = new
|
||||||
|
req.parse_extension_args(ax_args)
|
||||||
|
|
||||||
|
if req.update_url
|
||||||
|
realm = message.get_arg(OPENID_NS, 'realm',
|
||||||
|
message.get_arg(OPENID_NS, 'return_to'))
|
||||||
|
if realm.nil? or realm.empty?
|
||||||
|
raise Error, "Cannot validate update_url #{req.update_url.inspect} against absent realm"
|
||||||
|
end
|
||||||
|
tr = TrustRoot::TrustRoot.parse(realm)
|
||||||
|
unless tr.validate_url(req.update_url)
|
||||||
|
raise Error, "Update URL #{req.update_url.inspect} failed validation against realm #{realm.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return req
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_extension_args(ax_args)
|
||||||
|
check_mode(ax_args)
|
||||||
|
|
||||||
|
aliases = NamespaceMap.new
|
||||||
|
|
||||||
|
ax_args.each{|k,v|
|
||||||
|
if k.index('type.') == 0
|
||||||
|
name = k[5..-1]
|
||||||
|
type_uri = v
|
||||||
|
aliases.add_alias(type_uri, name)
|
||||||
|
|
||||||
|
count_key = 'count.'+name
|
||||||
|
count_s = ax_args[count_key]
|
||||||
|
count = 1
|
||||||
|
if count_s
|
||||||
|
if count_s == UNLIMITED_VALUES
|
||||||
|
count = count_s
|
||||||
|
else
|
||||||
|
count = count_s.to_i
|
||||||
|
if count <= 0
|
||||||
|
raise Error, "Invalid value for count #{count_key.inspect}: #{count_s.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
add(AttrInfo.new(type_uri, name, false, count))
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
required = AX.to_type_uris(aliases, ax_args['required'])
|
||||||
|
required.each{|type_uri|
|
||||||
|
@requested_attributes[type_uri].required = true
|
||||||
|
}
|
||||||
|
if_available = AX.to_type_uris(aliases, ax_args['if_available'])
|
||||||
|
all_type_uris = required + if_available
|
||||||
|
|
||||||
|
aliases.namespace_uris.each{|type_uri|
|
||||||
|
unless all_type_uris.member? type_uri
|
||||||
|
raise Error, "Type URI #{type_uri.inspect} was in the request but not present in 'required' or 'if_available'"
|
||||||
|
end
|
||||||
|
}
|
||||||
|
@update_url = ax_args['update_url']
|
||||||
|
end
|
||||||
|
|
||||||
|
# return the list of AttrInfo objects contained in the FetchRequest
|
||||||
|
def attributes
|
||||||
|
@requested_attributes.values
|
||||||
|
end
|
||||||
|
|
||||||
|
# return the list of requested attribute type URIs
|
||||||
|
def requested_types
|
||||||
|
@requested_attributes.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def member?(type_uri)
|
||||||
|
! @requested_attributes[type_uri].nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# Abstract class that implements a message that has attribute
|
||||||
|
# keys and values. It contains the common code between
|
||||||
|
# fetch_response and store_request.
|
||||||
|
class KeyValueMessage < AXMessage
|
||||||
|
attr_reader :data
|
||||||
|
def initialize
|
||||||
|
super()
|
||||||
|
@mode = nil
|
||||||
|
@data = {}
|
||||||
|
@data.default = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add a single value for the given attribute type to the
|
||||||
|
# message. If there are already values specified for this type,
|
||||||
|
# this value will be sent in addition to the values already
|
||||||
|
# specified.
|
||||||
|
def add_value(type_uri, value)
|
||||||
|
@data[type_uri] = @data[type_uri] << value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set the values for the given attribute type. This replaces
|
||||||
|
# any values that have already been set for this attribute.
|
||||||
|
def set_values(type_uri, values)
|
||||||
|
@data[type_uri] = values
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the extension arguments for the key/value pairs
|
||||||
|
# contained in this message.
|
||||||
|
def _get_extension_kv_args(aliases = nil)
|
||||||
|
aliases = NamespaceMap.new if aliases.nil?
|
||||||
|
|
||||||
|
ax_args = new_args
|
||||||
|
|
||||||
|
@data.each{|type_uri, values|
|
||||||
|
name = aliases.add(type_uri)
|
||||||
|
ax_args['type.'+name] = type_uri
|
||||||
|
ax_args['count.'+name] = values.size.to_s
|
||||||
|
|
||||||
|
values.each_with_index{|value, i|
|
||||||
|
key = "value.#{name}.#{i+1}"
|
||||||
|
ax_args[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ax_args
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse attribute exchange key/value arguments into this object.
|
||||||
|
|
||||||
|
def parse_extension_args(ax_args)
|
||||||
|
check_mode(ax_args)
|
||||||
|
aliases = NamespaceMap.new
|
||||||
|
|
||||||
|
ax_args.each{|k, v|
|
||||||
|
if k.index('type.') == 0
|
||||||
|
type_uri = v
|
||||||
|
name = k[5..-1]
|
||||||
|
|
||||||
|
AX.check_alias(name)
|
||||||
|
aliases.add_alias(type_uri,name)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
aliases.each{|type_uri, name|
|
||||||
|
count_s = ax_args['count.'+name]
|
||||||
|
count = count_s.to_i
|
||||||
|
if count_s.nil?
|
||||||
|
value = ax_args['value.'+name]
|
||||||
|
if value.nil?
|
||||||
|
raise IndexError, "Missing #{'value.'+name} in FetchResponse"
|
||||||
|
elsif value.empty?
|
||||||
|
values = []
|
||||||
|
else
|
||||||
|
values = [value]
|
||||||
|
end
|
||||||
|
elsif count_s.to_i == 0
|
||||||
|
values = []
|
||||||
|
else
|
||||||
|
values = (1..count).inject([]){|l,i|
|
||||||
|
key = "value.#{name}.#{i}"
|
||||||
|
v = ax_args[key]
|
||||||
|
raise IndexError, "Missing #{key} in FetchResponse" if v.nil?
|
||||||
|
l << v
|
||||||
|
}
|
||||||
|
end
|
||||||
|
@data[type_uri] = values
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get a single value for an attribute. If no value was sent
|
||||||
|
# for this attribute, use the supplied default. If there is more
|
||||||
|
# than one value for this attribute, this method will fail.
|
||||||
|
def get_single(type_uri, default = nil)
|
||||||
|
values = @data[type_uri]
|
||||||
|
return default if values.empty?
|
||||||
|
if values.size != 1
|
||||||
|
raise Error, "More than one value present for #{type_uri.inspect}"
|
||||||
|
else
|
||||||
|
return values[0]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# retrieve the list of values for this attribute
|
||||||
|
def get(type_uri)
|
||||||
|
@data[type_uri]
|
||||||
|
end
|
||||||
|
|
||||||
|
# retrieve the list of values for this attribute
|
||||||
|
def [](type_uri)
|
||||||
|
@data[type_uri]
|
||||||
|
end
|
||||||
|
|
||||||
|
# get the number of responses for this attribute
|
||||||
|
def count(type_uri)
|
||||||
|
@data[type_uri].size
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# A fetch_response attribute exchange message
|
||||||
|
class FetchResponse < KeyValueMessage
|
||||||
|
attr_reader :update_url
|
||||||
|
|
||||||
|
def initialize(update_url = nil)
|
||||||
|
super()
|
||||||
|
@mode = 'fetch_response'
|
||||||
|
@update_url = update_url
|
||||||
|
end
|
||||||
|
|
||||||
|
# Serialize this object into arguments in the attribute
|
||||||
|
# exchange namespace
|
||||||
|
# Takes an optional FetchRequest. If specified, the response will be
|
||||||
|
# validated against this request, and empty responses for requested
|
||||||
|
# fields with no data will be sent.
|
||||||
|
def get_extension_args(request = nil)
|
||||||
|
aliases = NamespaceMap.new
|
||||||
|
zero_value_types = []
|
||||||
|
|
||||||
|
if request
|
||||||
|
# Validate the data in the context of the request (the
|
||||||
|
# same attributes should be present in each, and the
|
||||||
|
# counts in the response must be no more than the counts
|
||||||
|
# in the request)
|
||||||
|
@data.keys.each{|type_uri|
|
||||||
|
unless request.member? type_uri
|
||||||
|
raise IndexError, "Response attribute not present in request: #{type_uri.inspect}"
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
request.attributes.each{|attr_info|
|
||||||
|
# Copy the aliases from the request so that reading
|
||||||
|
# the response in light of the request is easier
|
||||||
|
if attr_info.ns_alias.nil?
|
||||||
|
aliases.add(attr_info.type_uri)
|
||||||
|
else
|
||||||
|
aliases.add_alias(attr_info.type_uri, attr_info.ns_alias)
|
||||||
|
end
|
||||||
|
values = @data[attr_info.type_uri]
|
||||||
|
if values.empty? # @data defaults to []
|
||||||
|
zero_value_types << attr_info
|
||||||
|
end
|
||||||
|
if attr_info.count != UNLIMITED_VALUES and attr_info.count < values.size
|
||||||
|
raise Error, "More than the number of requested values were specified for #{attr_info.type_uri.inspect}"
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
kv_args = _get_extension_kv_args(aliases)
|
||||||
|
|
||||||
|
# Add the KV args into the response with the args that are
|
||||||
|
# unique to the fetch_response
|
||||||
|
ax_args = new_args
|
||||||
|
|
||||||
|
zero_value_types.each{|attr_info|
|
||||||
|
name = aliases.get_alias(attr_info.type_uri)
|
||||||
|
kv_args['type.' + name] = attr_info.type_uri
|
||||||
|
kv_args['count.' + name] = '0'
|
||||||
|
}
|
||||||
|
update_url = (request and request.update_url or @update_url)
|
||||||
|
ax_args['update_url'] = update_url unless update_url.nil?
|
||||||
|
ax_args.update(kv_args)
|
||||||
|
return ax_args
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_extension_args(ax_args)
|
||||||
|
super
|
||||||
|
@update_url = ax_args['update_url']
|
||||||
|
end
|
||||||
|
|
||||||
|
# Construct a FetchResponse object from an OpenID library
|
||||||
|
# SuccessResponse object.
|
||||||
|
def self.from_success_response(success_response, signed=true)
|
||||||
|
obj = self.new
|
||||||
|
if signed
|
||||||
|
ax_args = success_response.get_signed_ns(obj.ns_uri)
|
||||||
|
else
|
||||||
|
ax_args = success_response.message.get_args(obj.ns_uri)
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
obj.parse_extension_args(ax_args)
|
||||||
|
return obj
|
||||||
|
rescue Error => e
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# A store request attribute exchange message representation
|
||||||
|
class StoreRequest < KeyValueMessage
|
||||||
|
def initialize
|
||||||
|
super
|
||||||
|
@mode = 'store_request'
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_extension_args(aliases=nil)
|
||||||
|
ax_args = new_args
|
||||||
|
kv_args = _get_extension_kv_args(aliases)
|
||||||
|
ax_args.update(kv_args)
|
||||||
|
return ax_args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# An indication that the store request was processed along with
|
||||||
|
# this OpenID transaction.
|
||||||
|
class StoreResponse < AXMessage
|
||||||
|
SUCCESS_MODE = 'store_response_success'
|
||||||
|
FAILURE_MODE = 'store_response_failure'
|
||||||
|
attr_reader :error_message
|
||||||
|
|
||||||
|
def initialize(succeeded = true, error_message = nil)
|
||||||
|
super()
|
||||||
|
if succeeded and error_message
|
||||||
|
raise Error, "Error message included in a success response"
|
||||||
|
end
|
||||||
|
if succeeded
|
||||||
|
@mode = SUCCESS_MODE
|
||||||
|
else
|
||||||
|
@mode = FAILURE_MODE
|
||||||
|
end
|
||||||
|
@error_message = error_message
|
||||||
|
end
|
||||||
|
|
||||||
|
def succeeded?
|
||||||
|
@mode == SUCCESS_MODE
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_extension_args
|
||||||
|
ax_args = new_args
|
||||||
|
if !succeeded? and error_message
|
||||||
|
ax_args['error'] = @error_message
|
||||||
|
end
|
||||||
|
return ax_args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
179
vendor/gems/ruby-openid-2.1.2/lib/openid/extensions/pape.rb
vendored
Normal file
179
vendor/gems/ruby-openid-2.1.2/lib/openid/extensions/pape.rb
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
# An implementation of the OpenID Provider Authentication Policy
|
||||||
|
# Extension 1.0
|
||||||
|
# see: http://openid.net/specs/
|
||||||
|
|
||||||
|
require 'openid/extension'
|
||||||
|
|
||||||
|
module OpenID
|
||||||
|
|
||||||
|
module PAPE
|
||||||
|
NS_URI = "http://specs.openid.net/extensions/pape/1.0"
|
||||||
|
AUTH_MULTI_FACTOR_PHYSICAL =
|
||||||
|
'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical'
|
||||||
|
AUTH_MULTI_FACTOR =
|
||||||
|
'http://schemas.openid.net/pape/policies/2007/06/multi-factor'
|
||||||
|
AUTH_PHISHING_RESISTANT =
|
||||||
|
'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant'
|
||||||
|
TIME_VALIDATOR = /\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ/
|
||||||
|
# A Provider Authentication Policy request, sent from a relying
|
||||||
|
# party to a provider
|
||||||
|
class Request < Extension
|
||||||
|
attr_accessor :preferred_auth_policies, :max_auth_age, :ns_alias, :ns_uri
|
||||||
|
def initialize(preferred_auth_policies=[], max_auth_age=nil)
|
||||||
|
@ns_alias = 'pape'
|
||||||
|
@ns_uri = NS_URI
|
||||||
|
@preferred_auth_policies = preferred_auth_policies
|
||||||
|
@max_auth_age = max_auth_age
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add an acceptable authentication policy URI to this request
|
||||||
|
# This method is intended to be used by the relying party to add
|
||||||
|
# acceptable authentication types to the request.
|
||||||
|
def add_policy_uri(policy_uri)
|
||||||
|
unless @preferred_auth_policies.member? policy_uri
|
||||||
|
@preferred_auth_policies << policy_uri
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_extension_args
|
||||||
|
ns_args = {
|
||||||
|
'preferred_auth_policies' => @preferred_auth_policies.join(' ')
|
||||||
|
}
|
||||||
|
ns_args['max_auth_age'] = @max_auth_age.to_s if @max_auth_age
|
||||||
|
return ns_args
|
||||||
|
end
|
||||||
|
|
||||||
|
# Instantiate a Request object from the arguments in a
|
||||||
|
# checkid_* OpenID message
|
||||||
|
# return nil if the extension was not requested.
|
||||||
|
def self.from_openid_request(oid_req)
|
||||||
|
pape_req = new
|
||||||
|
args = oid_req.message.get_args(NS_URI)
|
||||||
|
if args == {}
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
pape_req.parse_extension_args(args)
|
||||||
|
return pape_req
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set the state of this request to be that expressed in these
|
||||||
|
# PAPE arguments
|
||||||
|
def parse_extension_args(args)
|
||||||
|
@preferred_auth_policies = []
|
||||||
|
policies_str = args['preferred_auth_policies']
|
||||||
|
if policies_str
|
||||||
|
policies_str.split(' ').each{|uri|
|
||||||
|
add_policy_uri(uri)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
max_auth_age_str = args['max_auth_age']
|
||||||
|
if max_auth_age_str
|
||||||
|
@max_auth_age = max_auth_age_str.to_i
|
||||||
|
else
|
||||||
|
@max_auth_age = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given a list of authentication policy URIs that a provider
|
||||||
|
# supports, this method returns the subset of those types
|
||||||
|
# that are preferred by the relying party.
|
||||||
|
def preferred_types(supported_types)
|
||||||
|
@preferred_auth_policies.select{|uri| supported_types.member? uri}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# A Provider Authentication Policy response, sent from a provider
|
||||||
|
# to a relying party
|
||||||
|
class Response < Extension
|
||||||
|
attr_accessor :ns_alias, :auth_policies, :auth_time, :nist_auth_level
|
||||||
|
def initialize(auth_policies=[], auth_time=nil, nist_auth_level=nil)
|
||||||
|
@ns_alias = 'pape'
|
||||||
|
@ns_uri = NS_URI
|
||||||
|
@auth_policies = auth_policies
|
||||||
|
@auth_time = auth_time
|
||||||
|
@nist_auth_level = nist_auth_level
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add a policy URI to the response
|
||||||
|
# see http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#auth_policies
|
||||||
|
def add_policy_uri(policy_uri)
|
||||||
|
@auth_policies << policy_uri unless @auth_policies.member?(policy_uri)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a Response object from an OpenID::Consumer::SuccessResponse
|
||||||
|
def self.from_success_response(success_response)
|
||||||
|
args = success_response.get_signed_ns(NS_URI)
|
||||||
|
return nil if args.nil?
|
||||||
|
pape_resp = new
|
||||||
|
pape_resp.parse_extension_args(args)
|
||||||
|
return pape_resp
|
||||||
|
end
|
||||||
|
|
||||||
|
# parse the provider authentication policy arguments into the
|
||||||
|
# internal state of this object
|
||||||
|
# if strict is specified, raise an exception when bad data is
|
||||||
|
# encountered
|
||||||
|
def parse_extension_args(args, strict=false)
|
||||||
|
policies_str = args['auth_policies']
|
||||||
|
if policies_str and policies_str != 'none'
|
||||||
|
@auth_policies = policies_str.split(' ')
|
||||||
|
end
|
||||||
|
|
||||||
|
nist_level_str = args['nist_auth_level']
|
||||||
|
if nist_level_str
|
||||||
|
# special handling of zero to handle to_i behavior
|
||||||
|
if nist_level_str.strip == '0'
|
||||||
|
nist_level = 0
|
||||||
|
else
|
||||||
|
nist_level = nist_level_str.to_i
|
||||||
|
# if it's zero here we have a bad value
|
||||||
|
if nist_level == 0
|
||||||
|
nist_level = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if nist_level and nist_level >= 0 and nist_level < 5
|
||||||
|
@nist_auth_level = nist_level
|
||||||
|
elsif strict
|
||||||
|
raise ArgumentError, "nist_auth_level must be an integer 0 through 4, not #{nist_level_str.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
auth_time_str = args['auth_time']
|
||||||
|
if auth_time_str
|
||||||
|
# validate time string
|
||||||
|
if auth_time_str =~ TIME_VALIDATOR
|
||||||
|
@auth_time = auth_time_str
|
||||||
|
elsif strict
|
||||||
|
raise ArgumentError, "auth_time must be in RFC3339 format"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_extension_args
|
||||||
|
ns_args = {}
|
||||||
|
if @auth_policies.empty?
|
||||||
|
ns_args['auth_policies'] = 'none'
|
||||||
|
else
|
||||||
|
ns_args['auth_policies'] = @auth_policies.join(' ')
|
||||||
|
end
|
||||||
|
if @nist_auth_level
|
||||||
|
unless (0..4).member? @nist_auth_level
|
||||||
|
raise ArgumentError, "nist_auth_level must be an integer 0 through 4, not #{@nist_auth_level.inspect}"
|
||||||
|
end
|
||||||
|
ns_args['nist_auth_level'] = @nist_auth_level.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
if @auth_time
|
||||||
|
unless @auth_time =~ TIME_VALIDATOR
|
||||||
|
raise ArgumentError, "auth_time must be in RFC3339 format"
|
||||||
|
end
|
||||||
|
ns_args['auth_time'] = @auth_time
|
||||||
|
end
|
||||||
|
return ns_args
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue