ruby-openid-2.1.8debian.orig/0000755000175000017500000000000011753312574014555 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/0000755000175000017500000000000011753312574016373 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/active_record_openid_store/0000755000175000017500000000000011753312574023756 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb0000644000175000017500000000140411753312574031411 0ustar paulpaul# 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 ruby-openid-2.1.8debian.orig/examples/active_record_openid_store/init.rb0000644000175000017500000000020311753312574025241 0ustar paulpaul# might using the ruby-openid gem begin require 'rubygems' rescue LoadError nil end require 'openid' require 'openid_ar_store' ruby-openid-2.1.8debian.orig/examples/active_record_openid_store/README0000644000175000017500000000330311753312574024635 0ustar paulpaul=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 ruby-openid-2.1.8debian.orig/examples/active_record_openid_store/test/0000755000175000017500000000000011753312574024735 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/active_record_openid_store/test/store_test.rb0000644000175000017500000001622611753312574027464 0ustar paulpaul$:.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 ruby-openid-2.1.8debian.orig/examples/active_record_openid_store/lib/0000755000175000017500000000000011753312574024524 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/active_record_openid_store/lib/association.rb0000644000175000017500000000035411753312574027367 0ustar paulpaulrequire '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 ruby-openid-2.1.8debian.orig/examples/active_record_openid_store/lib/nonce.rb0000644000175000017500000000010711753312574026151 0ustar paulpaulclass Nonce < ActiveRecord::Base set_table_name 'open_id_nonces' end ruby-openid-2.1.8debian.orig/examples/active_record_openid_store/lib/open_id_setting.rb0000644000175000017500000000012311753312574030217 0ustar paulpaulclass OpenIdSetting < ActiveRecord::Base validates_uniqueness_of :setting end ruby-openid-2.1.8debian.orig/examples/active_record_openid_store/lib/openid_ar_store.rb0000644000175000017500000000336211753312574030231 0ustar paulpaulrequire '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 ruby-openid-2.1.8debian.orig/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb0000644000175000017500000000147611753312574031672 0ustar paulpaul# 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 ruby-openid-2.1.8debian.orig/examples/discover0000755000175000017500000000225211753312574020140 0ustar paulpaul#!/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 ruby-openid-2.1.8debian.orig/examples/rails_openid/0000755000175000017500000000000011753312574021043 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/doc/0000755000175000017500000000000011753312574021610 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/doc/README_FOR_APP0000644000175000017500000000027411753312574023701 0ustar paulpaulUse 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.ruby-openid-2.1.8debian.orig/examples/rails_openid/README0000644000175000017500000001366011753312574021731 0ustar paulpaul== 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: ruby script/server (run with --help for options) ...or if you have lighttpd installed: ruby script/lighttpd (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 ServerName rails DocumentRoot /path/application/public/ ErrorLog /path/application/log/server.log Options ExecCGI FollowSymLinks AllowOverride all Allow from all Order allow,deny 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 => "[#nil, \"body\"=>nil, \"id\"=>\"1\"}>, #\"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 => #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 console production. == 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. ruby-openid-2.1.8debian.orig/examples/rails_openid/config/0000755000175000017500000000000011753312574022310 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/config/boot.rb0000644000175000017500000000104411753312574023577 0ustar paulpaul# 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) ruby-openid-2.1.8debian.orig/examples/rails_openid/config/environments/0000755000175000017500000000000011753312574025037 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/config/environments/test.rb0000644000175000017500000000153011753312574026342 0ustar paulpaul# 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 = :testruby-openid-2.1.8debian.orig/examples/rails_openid/config/environments/production.rb0000644000175000017500000000142011753312574027547 0ustar paulpaul# 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 ruby-openid-2.1.8debian.orig/examples/rails_openid/config/environments/development.rb0000644000175000017500000000144011753312574027705 0ustar paulpaul# 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 ruby-openid-2.1.8debian.orig/examples/rails_openid/config/database.yml0000644000175000017500000000360111753312574024577 0ustar paulpaul# 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:"ruby-openid-2.1.8debian.orig/examples/rails_openid/config/environment.rb0000644000175000017500000000422411753312574025203 0ustar paulpaul# 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' ruby-openid-2.1.8debian.orig/examples/rails_openid/config/routes.rb0000644000175000017500000000203711753312574024160 0ustar paulpaulActionController::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 ruby-openid-2.1.8debian.orig/examples/rails_openid/app/0000755000175000017500000000000011753312574021623 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/app/helpers/0000755000175000017500000000000011753312574023265 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/app/helpers/login_helper.rb0000644000175000017500000000002711753312574026260 0ustar paulpaulmodule LoginHelper end ruby-openid-2.1.8debian.orig/examples/rails_openid/app/helpers/server_helper.rb0000644000175000017500000000016611753312574026462 0ustar paulpaul module ServerHelper def url_for_user url_for :controller => 'user', :action => session[:username] end end ruby-openid-2.1.8debian.orig/examples/rails_openid/app/helpers/application_helper.rb0000644000175000017500000000016311753312574027454 0ustar paulpaul# Methods added to this helper will be available to all templates in the application. module ApplicationHelper end ruby-openid-2.1.8debian.orig/examples/rails_openid/app/controllers/0000755000175000017500000000000011753312574024171 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/app/controllers/login_controller.rb0000644000175000017500000000234011753312574030070 0ustar paulpaul# 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 #{base_url}user/#{user}

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 ruby-openid-2.1.8debian.orig/examples/rails_openid/app/controllers/application.rb0000644000175000017500000000033011753312574027015 0ustar paulpaul# 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 endruby-openid-2.1.8debian.orig/examples/rails_openid/app/controllers/consumer_controller.rb0000644000175000017500000001015511753312574030616 0ustar paulpaulrequire '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', :id => nil, :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 << "
#{k}: #{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 << "
Authentication time: #{pape_resp.auth_time} seconds" end if pape_resp.nist_auth_level pape_message << "
NIST Auth Level: #{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 ruby-openid-2.1.8debian.orig/examples/rails_openid/app/controllers/server_controller.rb0000644000175000017500000001513311753312574030272 0ustar paulpaulrequire '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 = <

OpenID identity page for #{params[:username]}

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 += "#{uri}\n " } yadis = < #{type_str} #{url_for(:controller => 'server', :only_path => false)} 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 ruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/0000755000175000017500000000000011753312574022760 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/login/0000755000175000017500000000000011753312574024070 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/login/index.rhtml0000644000175000017500000000341011753312574026245 0ustar paulpaul <% if session[:username].nil? %>
Type a username:
<% end %>

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 Rails platform to demonstrate, but the library code is not Rails specific.

To use the example provider

  1. 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.

    The server will automatically create an identity page for you at <%= @base_url %>user/name

  2. 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:

    script/server --port=3001

    (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 openidenabled.com on your local provider.)

  3. Point your browser to this new instance and follow the directions below.

To use the example relying party

Visit /consumer and enter your OpenID.

ruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/layouts/0000755000175000017500000000000011753312574024460 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/layouts/server.rhtml0000644000175000017500000000270211753312574027037 0ustar paulpaul OpenID Server Example <% if session[:username] %>
Welcome, <%= session[:username] %> | <%= link_to('Log out', :controller => 'login', :action => 'logout') %>
<%= @base_url %>user/<%= session[:username] %>
<% end %>

Ruby OpenID Server Example


<% if flash[:notice] or flash[:error] %>
<%= flash[:error] or flash[:notice] %>
<% end %> <%= @content_for_layout %> ruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/server/0000755000175000017500000000000011753312574024266 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/server/decide.rhtml0000644000175000017500000000131711753312574026555 0ustar paulpaul
<% if @oidreq.id_select %> <% else %> <% end %>
Site:<%= @oidreq.trust_root %>
You entered the server identifier at the relying party. You'll need to send an identifier of your choosing. Enter a username below.
Identity to send:
Identity:<%= @oidreq.identity %>
ruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/consumer/0000755000175000017500000000000011753312574024613 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/app/views/consumer/index.rhtml0000644000175000017500000000451311753312574026775 0ustar paulpaul Rails OpenID Example Relying Party

Rails OpenID Example Relying Party

<% if flash[:alert] %>
<%= h(flash[:alert]) %>
<% end %> <% if flash[:error] %>
<%= h(flash[:error]) %>
<% end %> <% if flash[:success] %>
<%= h(flash[:success]) %>
<% end %> <% if flash[:sreg_results] %>
<%= flash[:sreg_results] %>
<% end %> <% if flash[:pape_results] %>
<%= flash[:pape_results] %>
<% end %>
'> Identifier:



ruby-openid-2.1.8debian.orig/examples/rails_openid/test/0000755000175000017500000000000011753312574022022 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/test/test_helper.rb0000644000175000017500000000244511753312574024672 0ustar paulpaulENV["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 ruby-openid-2.1.8debian.orig/examples/rails_openid/test/functional/0000755000175000017500000000000011753312574024164 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/test/functional/login_controller_test.rb0000644000175000017500000000073611753312574031131 0ustar paulpaulrequire 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 ruby-openid-2.1.8debian.orig/examples/rails_openid/test/functional/server_controller_test.rb0000644000175000017500000000074211753312574031324 0ustar paulpaulrequire 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 ruby-openid-2.1.8debian.orig/examples/rails_openid/script/0000755000175000017500000000000011753312574022347 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/script/destroy0000755000175000017500000000014111753312574023762 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' require 'commands/destroy'ruby-openid-2.1.8debian.orig/examples/rails_openid/script/breakpointer0000755000175000017500000000014611753312574024763 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' require 'commands/breakpointer'ruby-openid-2.1.8debian.orig/examples/rails_openid/script/generate0000755000175000017500000000014211753312574024064 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' require 'commands/generate'ruby-openid-2.1.8debian.orig/examples/rails_openid/script/about0000755000175000017500000000013711753312574023410 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' require 'commands/about'ruby-openid-2.1.8debian.orig/examples/rails_openid/script/performance/0000755000175000017500000000000011753312574024650 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/script/performance/benchmarker0000755000175000017500000000016511753312574027061 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../config/boot' require 'commands/performance/benchmarker' ruby-openid-2.1.8debian.orig/examples/rails_openid/script/performance/profiler0000755000175000017500000000016211753312574026417 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../config/boot' require 'commands/performance/profiler' ruby-openid-2.1.8debian.orig/examples/rails_openid/script/server0000755000175000017500000000014011753312574023576 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' require 'commands/server'ruby-openid-2.1.8debian.orig/examples/rails_openid/script/process/0000755000175000017500000000000011753312574024025 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/script/process/reaper0000755000175000017500000000015411753312574025231 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../config/boot' require 'commands/process/reaper' ruby-openid-2.1.8debian.orig/examples/rails_openid/script/process/spawner0000755000175000017500000000015511753312574025433 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../config/boot' require 'commands/process/spawner' ruby-openid-2.1.8debian.orig/examples/rails_openid/script/process/spinner0000755000175000017500000000015511753312574025432 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../config/boot' require 'commands/process/spinner' ruby-openid-2.1.8debian.orig/examples/rails_openid/script/console0000755000175000017500000000014111753312574023733 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' require 'commands/console'ruby-openid-2.1.8debian.orig/examples/rails_openid/script/runner0000755000175000017500000000014011753312574023601 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' require 'commands/runner'ruby-openid-2.1.8debian.orig/examples/rails_openid/script/plugin0000755000175000017500000000014011753312574023566 0ustar paulpaul#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../config/boot' require 'commands/plugin'ruby-openid-2.1.8debian.orig/examples/rails_openid/public/0000755000175000017500000000000011753312574022321 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/public/favicon.ico0000644000175000017500000000000011753312574024430 0ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/public/500.html0000644000175000017500000000047611753312574023522 0ustar paulpaul

Application error (Apache)

Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html

ruby-openid-2.1.8debian.orig/examples/rails_openid/public/javascripts/0000755000175000017500000000000011753312574024652 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/public/javascripts/prototype.js0000644000175000017500000013476311753312574027273 0ustar paulpaul/* Prototype JavaScript framework, version 1.4.0 * (c) 2005 Sam Stephenson * * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff * against the source tree, available from the Prototype darcs repository. * * Prototype is freely distributable under the terms of an MIT-style license. * * For details, see the Prototype web site: http://prototype.conio.net/ * /*--------------------------------------------------------------------------*/ var Prototype = { Version: '1.4.0', ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', emptyFunction: function() {}, K: function(x) {return x} } var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } var Abstract = new Object(); Object.extend = function(destination, source) { for (property in source) { destination[property] = source[property]; } return destination; } Object.inspect = function(object) { try { if (object == undefined) return 'undefined'; if (object == null) return 'null'; return object.inspect ? object.inspect() : object.toString(); } catch (e) { if (e instanceof RangeError) return '...'; throw e; } } Function.prototype.bind = function() { var __method = this, args = $A(arguments), object = args.shift(); return function() { return __method.apply(object, args.concat($A(arguments))); } } Function.prototype.bindAsEventListener = function(object) { var __method = this; return function(event) { return __method.call(object, event || window.event); } } Object.extend(Number.prototype, { toColorPart: function() { var digits = this.toString(16); if (this < 16) return '0' + digits; return digits; }, succ: function() { return this + 1; }, times: function(iterator) { $R(0, this, true).each(iterator); return this; } }); var Try = { these: function() { var returnValue; for (var i = 0; i < arguments.length; i++) { var lambda = arguments[i]; try { returnValue = lambda(); break; } catch (e) {} } return returnValue; } } /*--------------------------------------------------------------------------*/ var PeriodicalExecuter = Class.create(); PeriodicalExecuter.prototype = { initialize: function(callback, frequency) { this.callback = callback; this.frequency = frequency; this.currentlyExecuting = false; this.registerCallback(); }, registerCallback: function() { setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); }, onTimerEvent: function() { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; this.callback(); } finally { this.currentlyExecuting = false; } } } } /*--------------------------------------------------------------------------*/ function $() { var elements = new Array(); for (var i = 0; i < arguments.length; i++) { var element = arguments[i]; if (typeof element == 'string') element = document.getElementById(element); if (arguments.length == 1) return element; elements.push(element); } return elements; } Object.extend(String.prototype, { stripTags: function() { return this.replace(/<\/?[^>]+>/gi, ''); }, stripScripts: function() { return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); }, extractScripts: function() { var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); return (this.match(matchAll) || []).map(function(scriptTag) { return (scriptTag.match(matchOne) || ['', ''])[1]; }); }, evalScripts: function() { return this.extractScripts().map(eval); }, escapeHTML: function() { var div = document.createElement('div'); var text = document.createTextNode(this); div.appendChild(text); return div.innerHTML; }, unescapeHTML: function() { var div = document.createElement('div'); div.innerHTML = this.stripTags(); return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; }, toQueryParams: function() { var pairs = this.match(/^\??(.*)$/)[1].split('&'); return pairs.inject({}, function(params, pairString) { var pair = pairString.split('='); params[pair[0]] = pair[1]; return params; }); }, toArray: function() { return this.split(''); }, camelize: function() { var oStringList = this.split('-'); if (oStringList.length == 1) return oStringList[0]; var camelizedString = this.indexOf('-') == 0 ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0]; for (var i = 1, len = oStringList.length; i < len; i++) { var s = oStringList[i]; camelizedString += s.charAt(0).toUpperCase() + s.substring(1); } return camelizedString; }, inspect: function() { return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'"; } }); String.prototype.parseQuery = String.prototype.toQueryParams; var $break = new Object(); var $continue = new Object(); var Enumerable = { each: function(iterator) { var index = 0; try { this._each(function(value) { try { iterator(value, index++); } catch (e) { if (e != $continue) throw e; } }); } catch (e) { if (e != $break) throw e; } }, all: function(iterator) { var result = true; this.each(function(value, index) { result = result && !!(iterator || Prototype.K)(value, index); if (!result) throw $break; }); return result; }, any: function(iterator) { var result = true; this.each(function(value, index) { if (result = !!(iterator || Prototype.K)(value, index)) throw $break; }); return result; }, collect: function(iterator) { var results = []; this.each(function(value, index) { results.push(iterator(value, index)); }); return results; }, detect: function (iterator) { var result; this.each(function(value, index) { if (iterator(value, index)) { result = value; throw $break; } }); return result; }, findAll: function(iterator) { var results = []; this.each(function(value, index) { if (iterator(value, index)) results.push(value); }); return results; }, grep: function(pattern, iterator) { var results = []; this.each(function(value, index) { var stringValue = value.toString(); if (stringValue.match(pattern)) results.push((iterator || Prototype.K)(value, index)); }) return results; }, include: function(object) { var found = false; this.each(function(value) { if (value == object) { found = true; throw $break; } }); return found; }, inject: function(memo, iterator) { this.each(function(value, index) { memo = iterator(memo, value, index); }); return memo; }, invoke: function(method) { var args = $A(arguments).slice(1); return this.collect(function(value) { return value[method].apply(value, args); }); }, max: function(iterator) { var result; this.each(function(value, index) { value = (iterator || Prototype.K)(value, index); if (value >= (result || value)) result = value; }); return result; }, min: function(iterator) { var result; this.each(function(value, index) { value = (iterator || Prototype.K)(value, index); if (value <= (result || value)) result = value; }); return result; }, partition: function(iterator) { var trues = [], falses = []; this.each(function(value, index) { ((iterator || Prototype.K)(value, index) ? trues : falses).push(value); }); return [trues, falses]; }, pluck: function(property) { var results = []; this.each(function(value, index) { results.push(value[property]); }); return results; }, reject: function(iterator) { var results = []; this.each(function(value, index) { if (!iterator(value, index)) results.push(value); }); return results; }, sortBy: function(iterator) { return this.collect(function(value, index) { return {value: value, criteria: iterator(value, index)}; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; return a < b ? -1 : a > b ? 1 : 0; }).pluck('value'); }, toArray: function() { return this.collect(Prototype.K); }, zip: function() { var iterator = Prototype.K, args = $A(arguments); if (typeof args.last() == 'function') iterator = args.pop(); var collections = [this].concat(args).map($A); return this.map(function(value, index) { iterator(value = collections.pluck(index)); return value; }); }, inspect: function() { return '#'; } } Object.extend(Enumerable, { map: Enumerable.collect, find: Enumerable.detect, select: Enumerable.findAll, member: Enumerable.include, entries: Enumerable.toArray }); var $A = Array.from = function(iterable) { if (!iterable) return []; if (iterable.toArray) { return iterable.toArray(); } else { var results = []; for (var i = 0; i < iterable.length; i++) results.push(iterable[i]); return results; } } Object.extend(Array.prototype, Enumerable); Array.prototype._reverse = Array.prototype.reverse; Object.extend(Array.prototype, { _each: function(iterator) { for (var i = 0; i < this.length; i++) iterator(this[i]); }, clear: function() { this.length = 0; return this; }, first: function() { return this[0]; }, last: function() { return this[this.length - 1]; }, compact: function() { return this.select(function(value) { return value != undefined || value != null; }); }, flatten: function() { return this.inject([], function(array, value) { return array.concat(value.constructor == Array ? value.flatten() : [value]); }); }, without: function() { var values = $A(arguments); return this.select(function(value) { return !values.include(value); }); }, indexOf: function(object) { for (var i = 0; i < this.length; i++) if (this[i] == object) return i; return -1; }, reverse: function(inline) { return (inline !== false ? this : this.toArray())._reverse(); }, shift: function() { var result = this[0]; for (var i = 0; i < this.length - 1; i++) this[i] = this[i + 1]; this.length--; return result; }, inspect: function() { return '[' + this.map(Object.inspect).join(', ') + ']'; } }); var Hash = { _each: function(iterator) { for (key in this) { var value = this[key]; if (typeof value == 'function') continue; var pair = [key, value]; pair.key = key; pair.value = value; iterator(pair); } }, keys: function() { return this.pluck('key'); }, values: function() { return this.pluck('value'); }, merge: function(hash) { return $H(hash).inject($H(this), function(mergedHash, pair) { mergedHash[pair.key] = pair.value; return mergedHash; }); }, toQueryString: function() { return this.map(function(pair) { return pair.map(encodeURIComponent).join('='); }).join('&'); }, inspect: function() { return '#'; } } function $H(object) { var hash = Object.extend({}, object || {}); Object.extend(hash, Enumerable); Object.extend(hash, Hash); return hash; } ObjectRange = Class.create(); Object.extend(ObjectRange.prototype, Enumerable); Object.extend(ObjectRange.prototype, { initialize: function(start, end, exclusive) { this.start = start; this.end = end; this.exclusive = exclusive; }, _each: function(iterator) { var value = this.start; do { iterator(value); value = value.succ(); } while (this.include(value)); }, include: function(value) { if (value < this.start) return false; if (this.exclusive) return value < this.end; return value <= this.end; } }); var $R = function(start, end, exclusive) { return new ObjectRange(start, end, exclusive); } var Ajax = { getTransport: function() { return Try.these( function() {return new ActiveXObject('Msxml2.XMLHTTP')}, function() {return new ActiveXObject('Microsoft.XMLHTTP')}, function() {return new XMLHttpRequest()} ) || false; }, activeRequestCount: 0 } Ajax.Responders = { responders: [], _each: function(iterator) { this.responders._each(iterator); }, register: function(responderToAdd) { if (!this.include(responderToAdd)) this.responders.push(responderToAdd); }, unregister: function(responderToRemove) { this.responders = this.responders.without(responderToRemove); }, dispatch: function(callback, request, transport, json) { this.each(function(responder) { if (responder[callback] && typeof responder[callback] == 'function') { try { responder[callback].apply(responder, [request, transport, json]); } catch (e) {} } }); } }; Object.extend(Ajax.Responders, Enumerable); Ajax.Responders.register({ onCreate: function() { Ajax.activeRequestCount++; }, onComplete: function() { Ajax.activeRequestCount--; } }); Ajax.Base = function() {}; Ajax.Base.prototype = { setOptions: function(options) { this.options = { method: 'post', asynchronous: true, parameters: '' } Object.extend(this.options, options || {}); }, responseIsSuccess: function() { return this.transport.status == undefined || this.transport.status == 0 || (this.transport.status >= 200 && this.transport.status < 300); }, responseIsFailure: function() { return !this.responseIsSuccess(); } } Ajax.Request = Class.create(); Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Request.prototype = Object.extend(new Ajax.Base(), { initialize: function(url, options) { this.transport = Ajax.getTransport(); this.setOptions(options); this.request(url); }, request: function(url) { var parameters = this.options.parameters || ''; if (parameters.length > 0) parameters += '&_='; try { this.url = url; if (this.options.method == 'get' && parameters.length > 0) this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; Ajax.Responders.dispatch('onCreate', this, this.transport); this.transport.open(this.options.method, this.url, this.options.asynchronous); if (this.options.asynchronous) { this.transport.onreadystatechange = this.onStateChange.bind(this); setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); } this.setRequestHeaders(); var body = this.options.postBody ? this.options.postBody : parameters; this.transport.send(this.options.method == 'post' ? body : null); } catch (e) { this.dispatchException(e); } }, setRequestHeaders: function() { var requestHeaders = ['X-Requested-With', 'XMLHttpRequest', 'X-Prototype-Version', Prototype.Version]; if (this.options.method == 'post') { requestHeaders.push('Content-type', 'application/x-www-form-urlencoded'); /* Force "Connection: close" for Mozilla browsers to work around * a bug where XMLHttpReqeuest sends an incorrect Content-length * header. See Mozilla Bugzilla #246651. */ if (this.transport.overrideMimeType) requestHeaders.push('Connection', 'close'); } if (this.options.requestHeaders) requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); for (var i = 0; i < requestHeaders.length; i += 2) this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); }, onStateChange: function() { var readyState = this.transport.readyState; if (readyState != 1) this.respondToReadyState(this.transport.readyState); }, header: function(name) { try { return this.transport.getResponseHeader(name); } catch (e) {} }, evalJSON: function() { try { return eval(this.header('X-JSON')); } catch (e) {} }, evalResponse: function() { try { return eval(this.transport.responseText); } catch (e) { this.dispatchException(e); } }, respondToReadyState: function(readyState) { var event = Ajax.Request.Events[readyState]; var transport = this.transport, json = this.evalJSON(); if (event == 'Complete') { try { (this.options['on' + this.transport.status] || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] || Prototype.emptyFunction)(transport, json); } catch (e) { this.dispatchException(e); } if ((this.header('Content-type') || '').match(/^text\/javascript/i)) this.evalResponse(); } try { (this.options['on' + event] || Prototype.emptyFunction)(transport, json); Ajax.Responders.dispatch('on' + event, this, transport, json); } catch (e) { this.dispatchException(e); } /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ if (event == 'Complete') this.transport.onreadystatechange = Prototype.emptyFunction; }, dispatchException: function(exception) { (this.options.onException || Prototype.emptyFunction)(this, exception); Ajax.Responders.dispatch('onException', this, exception); } }); Ajax.Updater = Class.create(); Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { initialize: function(container, url, options) { this.containers = { success: container.success ? $(container.success) : $(container), failure: container.failure ? $(container.failure) : (container.success ? null : $(container)) } this.transport = Ajax.getTransport(); this.setOptions(options); var onComplete = this.options.onComplete || Prototype.emptyFunction; this.options.onComplete = (function(transport, object) { this.updateContent(); onComplete(transport, object); }).bind(this); this.request(url); }, updateContent: function() { var receiver = this.responseIsSuccess() ? this.containers.success : this.containers.failure; var response = this.transport.responseText; if (!this.options.evalScripts) response = response.stripScripts(); if (receiver) { if (this.options.insertion) { new this.options.insertion(receiver, response); } else { Element.update(receiver, response); } } if (this.responseIsSuccess()) { if (this.onComplete) setTimeout(this.onComplete.bind(this), 10); } } }); Ajax.PeriodicalUpdater = Class.create(); Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { initialize: function(container, url, options) { this.setOptions(options); this.onComplete = this.options.onComplete; this.frequency = (this.options.frequency || 2); this.decay = (this.options.decay || 1); this.updater = {}; this.container = container; this.url = url; this.start(); }, start: function() { this.options.onComplete = this.updateComplete.bind(this); this.onTimerEvent(); }, stop: function() { this.updater.onComplete = undefined; clearTimeout(this.timer); (this.onComplete || Prototype.emptyFunction).apply(this, arguments); }, updateComplete: function(request) { if (this.options.decay) { this.decay = (request.responseText == this.lastText ? this.decay * this.options.decay : 1); this.lastText = request.responseText; } this.timer = setTimeout(this.onTimerEvent.bind(this), this.decay * this.frequency * 1000); }, onTimerEvent: function() { this.updater = new Ajax.Updater(this.container, this.url, this.options); } }); document.getElementsByClassName = function(className, parentElement) { var children = ($(parentElement) || document.body).getElementsByTagName('*'); return $A(children).inject([], function(elements, child) { if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) elements.push(child); return elements; }); } /*--------------------------------------------------------------------------*/ if (!window.Element) { var Element = new Object(); } Object.extend(Element, { visible: function(element) { return $(element).style.display != 'none'; }, toggle: function() { for (var i = 0; i < arguments.length; i++) { var element = $(arguments[i]); Element[Element.visible(element) ? 'hide' : 'show'](element); } }, hide: function() { for (var i = 0; i < arguments.length; i++) { var element = $(arguments[i]); element.style.display = 'none'; } }, show: function() { for (var i = 0; i < arguments.length; i++) { var element = $(arguments[i]); element.style.display = ''; } }, remove: function(element) { element = $(element); element.parentNode.removeChild(element); }, update: function(element, html) { $(element).innerHTML = html.stripScripts(); setTimeout(function() {html.evalScripts()}, 10); }, getHeight: function(element) { element = $(element); return element.offsetHeight; }, classNames: function(element) { return new Element.ClassNames(element); }, hasClassName: function(element, className) { if (!(element = $(element))) return; return Element.classNames(element).include(className); }, addClassName: function(element, className) { if (!(element = $(element))) return; return Element.classNames(element).add(className); }, removeClassName: function(element, className) { if (!(element = $(element))) return; return Element.classNames(element).remove(className); }, // removes whitespace-only text node children cleanWhitespace: function(element) { element = $(element); for (var i = 0; i < element.childNodes.length; i++) { var node = element.childNodes[i]; if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) Element.remove(node); } }, empty: function(element) { return $(element).innerHTML.match(/^\s*$/); }, scrollTo: function(element) { element = $(element); var x = element.x ? element.x : element.offsetLeft, y = element.y ? element.y : element.offsetTop; window.scrollTo(x, y); }, getStyle: function(element, style) { element = $(element); var value = element.style[style.camelize()]; if (!value) { if (document.defaultView && document.defaultView.getComputedStyle) { var css = document.defaultView.getComputedStyle(element, null); value = css ? css.getPropertyValue(style) : null; } else if (element.currentStyle) { value = element.currentStyle[style.camelize()]; } } if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) if (Element.getStyle(element, 'position') == 'static') value = 'auto'; return value == 'auto' ? null : value; }, setStyle: function(element, style) { element = $(element); for (name in style) element.style[name.camelize()] = style[name]; }, getDimensions: function(element) { element = $(element); if (Element.getStyle(element, 'display') != 'none') return {width: element.offsetWidth, height: element.offsetHeight}; // All *Width and *Height properties give 0 on elements with display none, // so enable the element temporarily var els = element.style; var originalVisibility = els.visibility; var originalPosition = els.position; els.visibility = 'hidden'; els.position = 'absolute'; els.display = ''; var originalWidth = element.clientWidth; var originalHeight = element.clientHeight; els.display = 'none'; els.position = originalPosition; els.visibility = originalVisibility; return {width: originalWidth, height: originalHeight}; }, makePositioned: function(element) { element = $(element); var pos = Element.getStyle(element, 'position'); if (pos == 'static' || !pos) { element._madePositioned = true; element.style.position = 'relative'; // Opera returns the offset relative to the positioning context, when an // element is position relative but top and left have not been defined if (window.opera) { element.style.top = 0; element.style.left = 0; } } }, undoPositioned: function(element) { element = $(element); if (element._madePositioned) { element._madePositioned = undefined; element.style.position = element.style.top = element.style.left = element.style.bottom = element.style.right = ''; } }, makeClipping: function(element) { element = $(element); if (element._overflow) return; element._overflow = element.style.overflow; if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') element.style.overflow = 'hidden'; }, undoClipping: function(element) { element = $(element); if (element._overflow) return; element.style.overflow = element._overflow; element._overflow = undefined; } }); var Toggle = new Object(); Toggle.display = Element.toggle; /*--------------------------------------------------------------------------*/ Abstract.Insertion = function(adjacency) { this.adjacency = adjacency; } Abstract.Insertion.prototype = { initialize: function(element, content) { this.element = $(element); this.content = content.stripScripts(); if (this.adjacency && this.element.insertAdjacentHTML) { try { this.element.insertAdjacentHTML(this.adjacency, this.content); } catch (e) { if (this.element.tagName.toLowerCase() == 'tbody') { this.insertContent(this.contentFromAnonymousTable()); } else { throw e; } } } else { this.range = this.element.ownerDocument.createRange(); if (this.initializeRange) this.initializeRange(); this.insertContent([this.range.createContextualFragment(this.content)]); } setTimeout(function() {content.evalScripts()}, 10); }, contentFromAnonymousTable: function() { var div = document.createElement('div'); div.innerHTML = '' + this.content + '
'; return $A(div.childNodes[0].childNodes[0].childNodes); } } var Insertion = new Object(); Insertion.Before = Class.create(); Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { initializeRange: function() { this.range.setStartBefore(this.element); }, insertContent: function(fragments) { fragments.each((function(fragment) { this.element.parentNode.insertBefore(fragment, this.element); }).bind(this)); } }); Insertion.Top = Class.create(); Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { initializeRange: function() { this.range.selectNodeContents(this.element); this.range.collapse(true); }, insertContent: function(fragments) { fragments.reverse(false).each((function(fragment) { this.element.insertBefore(fragment, this.element.firstChild); }).bind(this)); } }); Insertion.Bottom = Class.create(); Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { initializeRange: function() { this.range.selectNodeContents(this.element); this.range.collapse(this.element); }, insertContent: function(fragments) { fragments.each((function(fragment) { this.element.appendChild(fragment); }).bind(this)); } }); Insertion.After = Class.create(); Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { initializeRange: function() { this.range.setStartAfter(this.element); }, insertContent: function(fragments) { fragments.each((function(fragment) { this.element.parentNode.insertBefore(fragment, this.element.nextSibling); }).bind(this)); } }); /*--------------------------------------------------------------------------*/ Element.ClassNames = Class.create(); Element.ClassNames.prototype = { initialize: function(element) { this.element = $(element); }, _each: function(iterator) { this.element.className.split(/\s+/).select(function(name) { return name.length > 0; })._each(iterator); }, set: function(className) { this.element.className = className; }, add: function(classNameToAdd) { if (this.include(classNameToAdd)) return; this.set(this.toArray().concat(classNameToAdd).join(' ')); }, remove: function(classNameToRemove) { if (!this.include(classNameToRemove)) return; this.set(this.select(function(className) { return className != classNameToRemove; }).join(' ')); }, toString: function() { return this.toArray().join(' '); } } Object.extend(Element.ClassNames.prototype, Enumerable); var Field = { clear: function() { for (var i = 0; i < arguments.length; i++) $(arguments[i]).value = ''; }, focus: function(element) { $(element).focus(); }, present: function() { for (var i = 0; i < arguments.length; i++) if ($(arguments[i]).value == '') return false; return true; }, select: function(element) { $(element).select(); }, activate: function(element) { element = $(element); element.focus(); if (element.select) element.select(); } } /*--------------------------------------------------------------------------*/ var Form = { serialize: function(form) { var elements = Form.getElements($(form)); var queryComponents = new Array(); for (var i = 0; i < elements.length; i++) { var queryComponent = Form.Element.serialize(elements[i]); if (queryComponent) queryComponents.push(queryComponent); } return queryComponents.join('&'); }, getElements: function(form) { form = $(form); var elements = new Array(); for (tagName in Form.Element.Serializers) { var tagElements = form.getElementsByTagName(tagName); for (var j = 0; j < tagElements.length; j++) elements.push(tagElements[j]); } return elements; }, getInputs: function(form, typeName, name) { form = $(form); var inputs = form.getElementsByTagName('input'); if (!typeName && !name) return inputs; var matchingInputs = new Array(); for (var i = 0; i < inputs.length; i++) { var input = inputs[i]; if ((typeName && input.type != typeName) || (name && input.name != name)) continue; matchingInputs.push(input); } return matchingInputs; }, disable: function(form) { var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; element.blur(); element.disabled = 'true'; } }, enable: function(form) { var elements = Form.getElements(form); for (var i = 0; i < elements.length; i++) { var element = elements[i]; element.disabled = ''; } }, findFirstElement: function(form) { return Form.getElements(form).find(function(element) { return element.type != 'hidden' && !element.disabled && ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); }); }, focusFirstElement: function(form) { Field.activate(Form.findFirstElement(form)); }, reset: function(form) { $(form).reset(); } } Form.Element = { serialize: function(element) { element = $(element); var method = element.tagName.toLowerCase(); var parameter = Form.Element.Serializers[method](element); if (parameter) { var key = encodeURIComponent(parameter[0]); if (key.length == 0) return; if (parameter[1].constructor != Array) parameter[1] = [parameter[1]]; return parameter[1].map(function(value) { return key + '=' + encodeURIComponent(value); }).join('&'); } }, getValue: function(element) { element = $(element); var method = element.tagName.toLowerCase(); var parameter = Form.Element.Serializers[method](element); if (parameter) return parameter[1]; } } Form.Element.Serializers = { input: function(element) { switch (element.type.toLowerCase()) { case 'submit': case 'hidden': case 'password': case 'text': return Form.Element.Serializers.textarea(element); case 'checkbox': case 'radio': return Form.Element.Serializers.inputSelector(element); } return false; }, inputSelector: function(element) { if (element.checked) return [element.name, element.value]; }, textarea: function(element) { return [element.name, element.value]; }, select: function(element) { return Form.Element.Serializers[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element); }, selectOne: function(element) { var value = '', opt, index = element.selectedIndex; if (index >= 0) { opt = element.options[index]; value = opt.value; if (!value && !('value' in opt)) value = opt.text; } return [element.name, value]; }, selectMany: function(element) { var value = new Array(); for (var i = 0; i < element.length; i++) { var opt = element.options[i]; if (opt.selected) { var optValue = opt.value; if (!optValue && !('value' in opt)) optValue = opt.text; value.push(optValue); } } return [element.name, value]; } } /*--------------------------------------------------------------------------*/ var $F = Form.Element.getValue; /*--------------------------------------------------------------------------*/ Abstract.TimedObserver = function() {} Abstract.TimedObserver.prototype = { initialize: function(element, frequency, callback) { this.frequency = frequency; this.element = $(element); this.callback = callback; this.lastValue = this.getValue(); this.registerCallback(); }, registerCallback: function() { setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); }, onTimerEvent: function() { var value = this.getValue(); if (this.lastValue != value) { this.callback(this.element, value); this.lastValue = value; } } } Form.Element.Observer = Class.create(); Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { getValue: function() { return Form.Element.getValue(this.element); } }); Form.Observer = Class.create(); Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { getValue: function() { return Form.serialize(this.element); } }); /*--------------------------------------------------------------------------*/ Abstract.EventObserver = function() {} Abstract.EventObserver.prototype = { initialize: function(element, callback) { this.element = $(element); this.callback = callback; this.lastValue = this.getValue(); if (this.element.tagName.toLowerCase() == 'form') this.registerFormCallbacks(); else this.registerCallback(this.element); }, onElementEvent: function() { var value = this.getValue(); if (this.lastValue != value) { this.callback(this.element, value); this.lastValue = value; } }, registerFormCallbacks: function() { var elements = Form.getElements(this.element); for (var i = 0; i < elements.length; i++) this.registerCallback(elements[i]); }, registerCallback: function(element) { if (element.type) { switch (element.type.toLowerCase()) { case 'checkbox': case 'radio': Event.observe(element, 'click', this.onElementEvent.bind(this)); break; case 'password': case 'text': case 'textarea': case 'select-one': case 'select-multiple': Event.observe(element, 'change', this.onElementEvent.bind(this)); break; } } } } Form.Element.EventObserver = Class.create(); Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { getValue: function() { return Form.Element.getValue(this.element); } }); Form.EventObserver = Class.create(); Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { getValue: function() { return Form.serialize(this.element); } }); if (!window.Event) { var Event = new Object(); } Object.extend(Event, { KEY_BACKSPACE: 8, KEY_TAB: 9, KEY_RETURN: 13, KEY_ESC: 27, KEY_LEFT: 37, KEY_UP: 38, KEY_RIGHT: 39, KEY_DOWN: 40, KEY_DELETE: 46, element: function(event) { return event.target || event.srcElement; }, isLeftClick: function(event) { return (((event.which) && (event.which == 1)) || ((event.button) && (event.button == 1))); }, pointerX: function(event) { return event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)); }, pointerY: function(event) { return event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop)); }, stop: function(event) { if (event.preventDefault) { event.preventDefault(); event.stopPropagation(); } else { event.returnValue = false; event.cancelBubble = true; } }, // find the first node with the given tagName, starting from the // node the event was triggered on; traverses the DOM upwards findElement: function(event, tagName) { var element = Event.element(event); while (element.parentNode && (!element.tagName || (element.tagName.toUpperCase() != tagName.toUpperCase()))) element = element.parentNode; return element; }, observers: false, _observeAndCache: function(element, name, observer, useCapture) { if (!this.observers) this.observers = []; if (element.addEventListener) { this.observers.push([element, name, observer, useCapture]); element.addEventListener(name, observer, useCapture); } else if (element.attachEvent) { this.observers.push([element, name, observer, useCapture]); element.attachEvent('on' + name, observer); } }, unloadCache: function() { if (!Event.observers) return; for (var i = 0; i < Event.observers.length; i++) { Event.stopObserving.apply(this, Event.observers[i]); Event.observers[i][0] = null; } Event.observers = false; }, observe: function(element, name, observer, useCapture) { var element = $(element); useCapture = useCapture || false; if (name == 'keypress' && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.attachEvent)) name = 'keydown'; this._observeAndCache(element, name, observer, useCapture); }, stopObserving: function(element, name, observer, useCapture) { var element = $(element); useCapture = useCapture || false; if (name == 'keypress' && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.detachEvent)) name = 'keydown'; if (element.removeEventListener) { element.removeEventListener(name, observer, useCapture); } else if (element.detachEvent) { element.detachEvent('on' + name, observer); } } }); /* prevent memory leaks in IE */ Event.observe(window, 'unload', Event.unloadCache, false); var Position = { // set to true if needed, warning: firefox performance problems // NOT neeeded for page scrolling, only if draggable contained in // scrollable elements includeScrollOffsets: false, // must be called before calling withinIncludingScrolloffset, every time the // page is scrolled prepare: function() { this.deltaX = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0; this.deltaY = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; }, realOffset: function(element) { var valueT = 0, valueL = 0; do { valueT += element.scrollTop || 0; valueL += element.scrollLeft || 0; element = element.parentNode; } while (element); return [valueL, valueT]; }, cumulativeOffset: function(element) { var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; element = element.offsetParent; } while (element); return [valueL, valueT]; }, positionedOffset: function(element) { var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; element = element.offsetParent; if (element) { p = Element.getStyle(element, 'position'); if (p == 'relative' || p == 'absolute') break; } } while (element); return [valueL, valueT]; }, offsetParent: function(element) { if (element.offsetParent) return element.offsetParent; if (element == document.body) return element; while ((element = element.parentNode) && element != document.body) if (Element.getStyle(element, 'position') != 'static') return element; return document.body; }, // caches x/y coordinate pair to use with overlap within: function(element, x, y) { if (this.includeScrollOffsets) return this.withinIncludingScrolloffsets(element, x, y); this.xcomp = x; this.ycomp = y; this.offset = this.cumulativeOffset(element); return (y >= this.offset[1] && y < this.offset[1] + element.offsetHeight && x >= this.offset[0] && x < this.offset[0] + element.offsetWidth); }, withinIncludingScrolloffsets: function(element, x, y) { var offsetcache = this.realOffset(element); this.xcomp = x + offsetcache[0] - this.deltaX; this.ycomp = y + offsetcache[1] - this.deltaY; this.offset = this.cumulativeOffset(element); return (this.ycomp >= this.offset[1] && this.ycomp < this.offset[1] + element.offsetHeight && this.xcomp >= this.offset[0] && this.xcomp < this.offset[0] + element.offsetWidth); }, // within must be called directly before overlap: function(mode, element) { if (!mode) return 0; if (mode == 'vertical') return ((this.offset[1] + element.offsetHeight) - this.ycomp) / element.offsetHeight; if (mode == 'horizontal') return ((this.offset[0] + element.offsetWidth) - this.xcomp) / element.offsetWidth; }, clone: function(source, target) { source = $(source); target = $(target); target.style.position = 'absolute'; var offsets = this.cumulativeOffset(source); target.style.top = offsets[1] + 'px'; target.style.left = offsets[0] + 'px'; target.style.width = source.offsetWidth + 'px'; target.style.height = source.offsetHeight + 'px'; }, page: function(forElement) { var valueT = 0, valueL = 0; var element = forElement; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; // Safari fix if (element.offsetParent==document.body) if (Element.getStyle(element,'position')=='absolute') break; } while (element = element.offsetParent); element = forElement; do { valueT -= element.scrollTop || 0; valueL -= element.scrollLeft || 0; } while (element = element.parentNode); return [valueL, valueT]; }, clone: function(source, target) { var options = Object.extend({ setLeft: true, setTop: true, setWidth: true, setHeight: true, offsetTop: 0, offsetLeft: 0 }, arguments[2] || {}) // find page position of source source = $(source); var p = Position.page(source); // find coordinate system to use target = $(target); var delta = [0, 0]; var parent = null; // delta [0,0] will do fine with position: fixed elements, // position:absolute needs offsetParent deltas if (Element.getStyle(target,'position') == 'absolute') { parent = Position.offsetParent(target); delta = Position.page(parent); } // correct by body offsets (fixes Safari) if (parent == document.body) { delta[0] -= document.body.offsetLeft; delta[1] -= document.body.offsetTop; } // set position if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; if(options.setWidth) target.style.width = source.offsetWidth + 'px'; if(options.setHeight) target.style.height = source.offsetHeight + 'px'; }, absolutize: function(element) { element = $(element); if (element.style.position == 'absolute') return; Position.prepare(); var offsets = Position.positionedOffset(element); var top = offsets[1]; var left = offsets[0]; var width = element.clientWidth; var height = element.clientHeight; element._originalLeft = left - parseFloat(element.style.left || 0); element._originalTop = top - parseFloat(element.style.top || 0); element._originalWidth = element.style.width; element._originalHeight = element.style.height; element.style.position = 'absolute'; element.style.top = top + 'px';; element.style.left = left + 'px';; element.style.width = width + 'px';; element.style.height = height + 'px';; }, relativize: function(element) { element = $(element); if (element.style.position == 'relative') return; Position.prepare(); element.style.position = 'relative'; var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); element.style.top = top + 'px'; element.style.left = left + 'px'; element.style.height = element._originalHeight; element.style.width = element._originalWidth; } } // Safari returns margins on body which is incorrect if the child is absolutely // positioned. For performance reasons, redefine Position.cumulativeOffset for // KHTML/WebKit only. if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { Position.cumulativeOffset = function(element) { var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; if (element.offsetParent == document.body) if (Element.getStyle(element, 'position') == 'absolute') break; element = element.offsetParent; } while (element); return [valueL, valueT]; } }ruby-openid-2.1.8debian.orig/examples/rails_openid/public/javascripts/effects.js0000644000175000017500000007306111753312574026636 0ustar paulpaul// 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.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] || {})); } ruby-openid-2.1.8debian.orig/examples/rails_openid/public/javascripts/controls.js0000644000175000017500000006165711753312574027072 0ustar paulpaul// 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, ''); 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 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("
  • " + elem.substr(0, entry.length) + "" + elem.substr(entry.length) + "
  • "); 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("
  • " + elem.substr(0, foundPos) + "" + elem.substr(foundPos, entry.length) + "" + elem.substr( foundPos + entry.length) + "
  • "); 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 "
      " + ret.join('') + "
    "; } }, 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(/
    /i); }, convertHTMLLineBreaks: function(string) { return string.replace(/
    /gi, "\n").replace(//gi, "\n").replace(/<\/p>/gi, "\n").replace(/

    /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)); } };ruby-openid-2.1.8debian.orig/examples/rails_openid/public/javascripts/dragdrop.js0000644000175000017500000004442511753312574027023 0ustar paulpaul// 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("&"); } }ruby-openid-2.1.8debian.orig/examples/rails_openid/public/404.html0000644000175000017500000000035311753312574023517 0ustar paulpaul

    File not found

    Change this error message for pages not found in public/404.html

    ruby-openid-2.1.8debian.orig/examples/rails_openid/public/images/0000755000175000017500000000000011753312574023566 5ustar paulpaulruby-openid-2.1.8debian.orig/examples/rails_openid/public/images/openid_login_bg.gif0000644000175000017500000000035511753312574027376 0ustar paulpaulGIF89aÄíííááâÊÊÊüüüþi ÿkÒÒÒÿöðÿõîÕÕÕÑļðððÍÍÍùùùÝÝÝþcÞÒÊùïèÇËÍÇÇÇýfüx)ÏÒÔ÷õóо´ÿ±‚ÿäÔÝÎÅñòôÒÕ×ýýýÿÿÿ!ù,jà'Ždižè¨eH:ÅÓºBGê-ÁFÀÂS&ÅO!™$€Á 8‘[„`AÁpŸK Œ†Q(´Üâ'àžg%2) z)94–—#!;ruby-openid-2.1.8debian.orig/examples/rails_openid/public/dispatch.rb0000644000175000017500000000076311753312574024453 0ustar paulpaul#!/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.dispatchruby-openid-2.1.8debian.orig/examples/rails_openid/public/robots.txt0000644000175000017500000000014311753312574024370 0ustar paulpaul# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt fileruby-openid-2.1.8debian.orig/examples/rails_openid/public/dispatch.cgi0000644000175000017500000000076311753312574024612 0ustar paulpaul#!/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.dispatchruby-openid-2.1.8debian.orig/examples/rails_openid/public/dispatch.fcgi0000644000175000017500000000156111753312574024755 0ustar paulpaul#!/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! ruby-openid-2.1.8debian.orig/examples/rails_openid/Rakefile0000644000175000017500000000046311753312574022513 0ustar paulpaul# 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'ruby-openid-2.1.8debian.orig/examples/README0000644000175000017500000000232111753312574017251 0ustar paulpaulThis 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. ruby-openid-2.1.8debian.orig/CHANGELOG0000644000175000017500000001355411753312574015777 0ustar paulpaulMon Jan 23 12:48:00 PST 2006 brian@janrain.com * fixed bug in expiresIn. added expired? method M ./lib/openid/filestore.rb -1 +1 M ./lib/openid/stores.rb +4 Mon Jan 23 12:46:37 PST 2006 brian@janrain.com * removed deps section from INSTALL file. deps are now included in lib because they are so small and to lower to bar of installing the library. M ./INSTALL -9 Tue Jan 17 14:45:57 PST 2006 brian@janrain.com * added better handling of non-URL input M ./lib/openid/consumer.rb -1 +5 Sat Jan 14 19:39:57 PST 2006 brian@janrain.com * added html and hmac deps into lib since they are so small A ./lib/hmac-md5.rb A ./lib/hmac-rmd160.rb A ./lib/hmac-sha1.rb A ./lib/hmac-sha2.rb A ./lib/hmac.rb A ./lib/html/ A ./lib/html/htmltokenizer.rb Mon Jan 16 15:04:05 PST 2006 Josh Hoyt * Add script that will prepare the repository for release A ./admin/fixperms A ./admin/prepare-release Mon Jan 16 14:35:27 PST 2006 Josh Hoyt * Add custom boring file A ./admin/darcs-ignore Mon Jan 16 14:07:13 PST 2006 Josh Hoyt * Put the build-docs script into the admin directory ./build-docs -> ./admin/build-docs A ./admin/ Mon Jan 16 14:05:47 PST 2006 Josh Hoyt * Add script to build documentation A ./build-docs Wed Jan 4 16:06:41 PST 2006 brian@janrain.com tagged ruby-openid-0.9.2 Wed Jan 4 16:02:32 PST 2006 brian@janrain.com * added openid_login_generator rails generator to examples A ./examples/openid_login_generator/ A ./examples/openid_login_generator/USAGE A ./examples/openid_login_generator/openid_login_generator.rb A ./examples/openid_login_generator/templates/ A ./examples/openid_login_generator/templates/README A ./examples/openid_login_generator/templates/controller.rb A ./examples/openid_login_generator/templates/helper.rb A ./examples/openid_login_generator/templates/login_system.rb A ./examples/openid_login_generator/templates/user.rb A ./examples/openid_login_generator/templates/view_login.rhtml A ./examples/openid_login_generator/templates/view_logout.rhtml A ./examples/openid_login_generator/templates/view_signup.rhtml A ./examples/openid_login_generator/templates/view_welcome.rhtml Wed Jan 4 16:01:12 PST 2006 brian@janrain.com * updated examples README to include openid_login_generator M ./examples/README +11 Wed Jan 4 14:58:24 PST 2006 brian@janrain.com * added link to ruby library from consumer.rb example M ./examples/consumer.rb -1 +1 Wed Jan 4 10:56:45 PST 2006 brian@janrain.com * ensure Content-type header is present for POSTs M ./lib/openid/fetchers.rb -1 +2 Fri Dec 30 17:05:25 PST 2005 brian@janrain.com tagged ruby-openid-0.9.1 Fri Dec 30 17:03:54 PST 2005 brian@janrain.com * added Ruby on Rails example consumer M ./examples/README -1 +14 A ./examples/openid_rails.tar.gz Thu Dec 29 16:00:20 PST 2005 brian@janrain.com tagged ruby-openid-0.9.0 Thu Dec 29 15:43:07 PST 2005 brian@janrain.com * removed docs directory. generated rdoc html will be added manually to tarballs, and not be kept in repository R ./docs/ R ./docs/README Thu Dec 29 15:21:21 PST 2005 brian@janrain.com * added more docs for stores M ./TODO -2 +4 M ./lib/openid/filestore.rb -16 +3 M ./lib/openid/stores.rb -9 +1 Thu Dec 29 14:58:52 PST 2005 brian@janrain.com * Huge documentation patch M ./INSTALL -12 +22 M ./README -1 +1 M ./lib/openid/consumer.rb -24 +370 M ./lib/openid/fetchers.rb -2 +1 M ./lib/openid/filestore.rb -6 +4 M ./lib/openid/stores.rb -2 +1 Thu Dec 29 10:59:54 PST 2005 brian@janrain.com * added more info and rdoc formatting to README M ./README -10 +26 Thu Dec 29 09:45:51 PST 2005 brian@janrain.com * fixed bad comment M ./examples/consumer.rb -1 +1 Wed Dec 28 17:59:48 PST 2005 brian@janrain.com * added platform agnositc temp dir discovery M ./examples/consumer.rb -1 +5 Wed Dec 28 17:13:21 PST 2005 brian@janrain.com * moved getOpenIDParamerters to util M ./lib/openid/consumer.rb -10 +2 M ./lib/openid/util.rb +8 Wed Dec 28 15:47:51 PST 2005 brian@janrain.com * code cleanup M ./lib/openid/consumer.rb -5 Wed Dec 28 15:29:31 PST 2005 brian@janrain.com * added linkparse to test suite script M ./test/runtests -1 +1 Wed Dec 28 15:29:07 PST 2005 brian@janrain.com * added link parsing tests, lots of em A ./test/linkparse.rb Wed Dec 28 15:28:07 PST 2005 brian@janrain.com * link parsing more robust: handle non-html data, and make sure link tag is in head M ./lib/openid/parse.rb -5 +13 Tue Dec 27 16:11:09 PST 2005 brian@janrain.com * added more tests for openid/util M ./test/dh.rb -2 +1 M ./test/runtests +1 A ./test/util.rb Tue Dec 27 16:10:28 PST 2005 brian@janrain.com * change util methods to use all use /dev/urandom if available M ./lib/openid/util.rb -15 +35 Tue Dec 27 16:09:53 PST 2005 brian@janrain.com * changed tmp pathname to something more useful M ./examples/consumer.rb -1 +1 Fri Dec 16 09:04:59 PST 2005 Josh Hoyt * Removed (now obsolete) interface.rb This has been subsumed by consumer.rb R ./lib/openid/interface.rb Thu Dec 15 18:25:04 PST 2005 brian@janrain.com * initial checkin A ./COPYING A ./INSTALL A ./README A ./TODO A ./docs/ A ./docs/README A ./examples/ A ./examples/README A ./examples/consumer.rb A ./lib/ A ./lib/openid/ A ./lib/openid/consumer.rb A ./lib/openid/dh.rb A ./lib/openid/fetchers.rb A ./lib/openid/filestore.rb A ./lib/openid/interface.rb A ./lib/openid/parse.rb A ./lib/openid/stores.rb A ./lib/openid/util.rb A ./setup.rb A ./test/ A ./test/assoc.rb A ./test/dh.rb A ./test/runtests A ./test/teststore.rb ruby-openid-2.1.8debian.orig/INSTALL0000644000175000017500000000212611753312574015607 0ustar paulpaul= 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. ruby-openid-2.1.8debian.orig/NOTICE0000644000175000017500000000015111753312574015456 0ustar paulpaulThis product includes software developed by JanRain, available from http://github.com/openid/ruby-openid ruby-openid-2.1.8debian.orig/UPGRADE0000644000175000017500000001053611753312574015574 0ustar paulpaul= 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 ruby-openid-2.1.8debian.orig/README0000644000175000017500000000457111753312574015444 0ustar paulpaul=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://github.com/openid/ruby-openid See also: http://openid.net/ ==Community Discussion regarding the Ruby OpenID library and other JanRain OpenID libraries takes place on the the OpenID mailing list on openid.net. http://openid.net/developers/dev-mailing-lists/ 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. ruby-openid-2.1.8debian.orig/admin/0000755000175000017500000000000011753312574015645 5ustar paulpaulruby-openid-2.1.8debian.orig/admin/runtests.rb0000644000175000017500000000162011753312574020060 0ustar paulpaul#!/usr/bin/ruby require "logger" require "stringio" require "pathname" require 'test/unit/collector/dir' require 'test/unit/ui/console/testrunner' begin require 'rubygems' require 'memcache' rescue LoadError else if ENV['TESTING_MEMCACHE'] TESTING_MEMCACHE = MemCache.new(ENV['TESTING_MEMCACHE']) end end 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) ruby-openid-2.1.8debian.orig/LICENSE0000644000175000017500000002675211753312574015576 0ustar paulpaulThe 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 Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ruby-openid-2.1.8debian.orig/test/0000755000175000017500000000000011753312574015534 5ustar paulpaulruby-openid-2.1.8debian.orig/test/test_extension.rb0000644000175000017500000000254111753312574021136 0ustar paulpaulrequire 'openid/extension' require 'openid/message' require 'test/unit' module OpenID class DummyExtension < OpenID::Extension TEST_URI = 'http://an.extension' TEST_ALIAS = 'dummy' def initialize @ns_uri = TEST_URI @ns_alias = TEST_ALIAS end def get_extension_args return {} end end class ToMessageTest < Test::Unit::TestCase def test_OpenID1 oid1_msg = Message.new(OPENID1_NS) ext = DummyExtension.new ext.to_message(oid1_msg) namespaces = oid1_msg.namespaces assert(namespaces.implicit?(DummyExtension::TEST_URI)) assert_equal( DummyExtension::TEST_URI, namespaces.get_namespace_uri(DummyExtension::TEST_ALIAS)) assert_equal(DummyExtension::TEST_ALIAS, namespaces.get_alias(DummyExtension::TEST_URI)) end def test_OpenID2 oid2_msg = Message.new(OPENID2_NS) ext = DummyExtension.new ext.to_message(oid2_msg) namespaces = oid2_msg.namespaces assert(!namespaces.implicit?(DummyExtension::TEST_URI)) assert_equal( DummyExtension::TEST_URI, namespaces.get_namespace_uri(DummyExtension::TEST_ALIAS)) assert_equal(DummyExtension::TEST_ALIAS, namespaces.get_alias(DummyExtension::TEST_URI)) end end end ruby-openid-2.1.8debian.orig/test/test_idres.rb0000644000175000017500000010146011753312574020230 0ustar paulpaulrequire "testutil" require "util" require "test/unit" require "openid/consumer/idres" require "openid/protocolerror" require "openid/store/memory" require "openid/store/nonce" module OpenID class Consumer class IdResHandler # Subclass of IdResHandler that doesn't do verification upon # construction. All of the tests call this, except for the ones # explicitly for id_res. class IdResHandler < OpenID::Consumer::IdResHandler def id_res end end class CheckForFieldsTest < Test::Unit::TestCase include ProtocolErrorMixin BASE_FIELDS = ['return_to', 'assoc_handle', 'sig', 'signed'] OPENID2_FIELDS = BASE_FIELDS + ['op_endpoint'] OPENID1_FIELDS = BASE_FIELDS + ['identity'] OPENID1_SIGNED = ['return_to', 'identity'] OPENID2_SIGNED = OPENID1_SIGNED + ['response_nonce', 'claimed_id', 'assoc_handle', 'op_endpoint'] def mkMsg(ns, fields, signed_fields) msg = Message.new(ns) fields.each do |field| msg.set_arg(OPENID_NS, field, "don't care") end if fields.member?('signed') msg.set_arg(OPENID_NS, 'signed', signed_fields.join(',')) end msg end 1.times do # so as not to bleed into the outer namespace n = 0 [[], ['foo'], ['bar', 'baz'], ].each do |signed_fields| test = lambda do msg = mkMsg(OPENID2_NS, OPENID2_FIELDS, signed_fields) idres = IdResHandler.new(msg, nil) assert_equal(signed_fields, idres.send(:signed_list)) # Do it again to make sure logic for caching is correct assert_equal(signed_fields, idres.send(:signed_list)) end define_method("test_signed_list_#{n += 1}", test) end end # test all missing fields for OpenID 1 and 2 1.times do [["openid1", OPENID1_NS, OPENID1_FIELDS], ["openid1", OPENID11_NS, OPENID1_FIELDS], ["openid2", OPENID2_NS, OPENID2_FIELDS], ].each do |ver, ns, all_fields| all_fields.each do |field| test = lambda do fields = all_fields.dup fields.delete(field) msg = mkMsg(ns, fields, []) idres = IdResHandler.new(msg, nil) assert_protocol_error("Missing required field #{field}") { idres.send(:check_for_fields) } end define_method("test_#{ver}_check_missing_#{field}", test) end end end # Test all missing signed for OpenID 1 and 2 1.times do [["openid1", OPENID1_NS, OPENID1_FIELDS, OPENID1_SIGNED], ["openid1", OPENID11_NS, OPENID1_FIELDS, OPENID1_SIGNED], ["openid2", OPENID2_NS, OPENID2_FIELDS, OPENID2_SIGNED], ].each do |ver, ns, all_fields, signed_fields| signed_fields.each do |signed_field| test = lambda do fields = signed_fields.dup fields.delete(signed_field) msg = mkMsg(ns, all_fields, fields) # Make sure the signed field is actually in the request msg.set_arg(OPENID_NS, signed_field, "don't care") idres = IdResHandler.new(msg, nil) assert_protocol_error("#{signed_field.inspect} not signed") { idres.send(:check_for_fields) } end define_method("test_#{ver}_check_missing_signed_#{signed_field}", test) end end end def test_112 args = {'openid.assoc_handle' => 'fa1f5ff0-cde4-11dc-a183-3714bfd55ca8', 'openid.claimed_id' => 'http://binkley.lan/user/test01', 'openid.identity' => 'http://test01.binkley.lan/', 'openid.mode' => 'id_res', 'openid.ns' => 'http://specs.openid.net/auth/2.0', 'openid.ns.pape' => 'http://specs.openid.net/extensions/pape/1.0', 'openid.op_endpoint' => 'http://binkley.lan/server', 'openid.pape.auth_policies' => 'none', 'openid.pape.auth_time' => '2008-01-28T20:42:36Z', 'openid.pape.nist_auth_level' => '0', 'openid.response_nonce' => '2008-01-28T21:07:04Z99Q=', 'openid.return_to' => 'http://binkley.lan:8001/process?janrain_nonce=2008-01-28T21%3A07%3A02Z0tMIKx', 'openid.sig' => 'YJlWH4U6SroB1HoPkmEKx9AyGGg=', 'openid.signed' => 'assoc_handle,identity,response_nonce,return_to,claimed_id,op_endpoint,pape.auth_time,ns.pape,pape.nist_auth_level,pape.auth_policies' } assert_equal(args['openid.ns'], OPENID2_NS) incoming = Message.from_post_args(args) assert(incoming.is_openid2) idres = IdResHandler.new(incoming, nil) car = idres.send(:create_check_auth_request) expected_args = args.dup expected_args['openid.mode'] = 'check_authentication' expected = Message.from_post_args(expected_args) assert(expected.is_openid2) assert_equal(expected, car) assert_equal(expected_args, car.to_post_args) end def test_no_signed_list msg = Message.new(OPENID2_NS) idres = IdResHandler.new(msg, nil) assert_protocol_error("Response missing signed") { idres.send(:signed_list) } end def test_success_openid1 msg = mkMsg(OPENID1_NS, OPENID1_FIELDS, OPENID1_SIGNED) idres = IdResHandler.new(msg, nil) assert_nothing_raised { idres.send(:check_for_fields) } end def test_success_openid1_1 msg = mkMsg(OPENID11_NS, OPENID1_FIELDS, OPENID1_SIGNED) idres = IdResHandler.new(msg, nil) assert_nothing_raised { idres.send(:check_for_fields) } end end class ReturnToArgsTest < Test::Unit::TestCase include OpenID::ProtocolErrorMixin def check_return_to_args(query) idres = IdResHandler.new(Message.from_post_args(query), nil) class << idres def verify_return_to_base(unused) end end idres.send(:verify_return_to) end def assert_bad_args(msg, query) assert_protocol_error(msg) { check_return_to_args(query) } end def test_return_to_args_okay assert_nothing_raised { check_return_to_args({ 'openid.mode' => 'id_res', 'openid.return_to' => 'http://example.com/?foo=bar', 'foo' => 'bar', }) } end def test_unexpected_arg_okay assert_bad_args("Unexpected parameter", { 'openid.mode' => 'id_res', 'openid.return_to' => 'http://example.com/', 'foo' => 'bar', }) end def test_return_to_mismatch assert_bad_args('Message missing ret', { 'openid.mode' => 'id_res', 'openid.return_to' => 'http://example.com/?foo=bar', }) assert_bad_args("Parameter 'foo' val", { 'openid.mode' => 'id_res', 'openid.return_to' => 'http://example.com/?foo=bar', 'foo' => 'foos', }) end end class ReturnToVerifyTest < Test::Unit::TestCase def test_bad_return_to return_to = "http://some.url/path?foo=bar" m = Message.new(OPENID1_NS) m.set_arg(OPENID_NS, 'mode', 'cancel') m.set_arg(BARE_NS, 'foo', 'bar') # Scheme, authority, and path differences are checked by # IdResHandler.verify_return_to_base. Query args checked by # IdResHandler.verify_return_to_args. [ # Scheme only "https://some.url/path?foo=bar", # Authority only "http://some.url.invalid/path?foo=bar", # Path only "http://some.url/path_extra?foo=bar", # Query args differ "http://some.url/path?foo=bar2", "http://some.url/path?foo2=bar", ].each do |bad| m.set_arg(OPENID_NS, 'return_to', bad) idres = IdResHandler.new(m, return_to) assert_raises(ProtocolError) { idres.send(:verify_return_to) } end end def test_good_return_to base = 'http://example.janrain.com/path' [ [base, {}], [base + "?another=arg", {'another' => 'arg'}], [base + "?another=arg#frag", {'another' => 'arg'}], ['HTTP'+base[4..-1], {}], [base.sub('com', 'COM'), {}], ['http://example.janrain.com:80/path', {}], ['http://example.janrain.com/p%61th', {}], ['http://example.janrain.com/./path',{}], ].each do |return_to, args| args['openid.return_to'] = return_to msg = Message.from_post_args(args) idres = IdResHandler.new(msg, base) assert_nothing_raised { idres.send(:verify_return_to) } end end end class DummyEndpoint attr_accessor :server_url def initialize(server_url) @server_url = server_url end end class CheckSigTest < Test::Unit::TestCase include ProtocolErrorMixin include TestUtil def setup @assoc = GoodAssoc.new('{not_dumb}') @store = Store::Memory.new @server_url = 'http://server.url/' @endpoint = DummyEndpoint.new(@server_url) @store.store_association(@server_url, @assoc) @message = Message.from_post_args({ 'openid.mode' => 'id_res', 'openid.identity' => '=example', 'openid.sig' => GOODSIG, 'openid.assoc_handle' => @assoc.handle, 'openid.signed' => 'mode,identity,assoc_handle,signed', 'frobboz' => 'banzit', }) end def call_idres_method(method_name) idres = IdResHandler.new(@message, nil, @store, @endpoint) idres.extend(InstanceDefExtension) yield idres idres.send(method_name) end def call_check_sig(&proc) call_idres_method(:check_signature, &proc) end def no_check_auth(idres) idres.instance_def(:check_auth) { fail "Called check_auth" } end def test_sign_good assert_nothing_raised { call_check_sig(&method(:no_check_auth)) } end def test_bad_sig @message.set_arg(OPENID_NS, 'sig', 'bad sig!') assert_protocol_error('Bad signature') { call_check_sig(&method(:no_check_auth)) } end def test_check_auth_ok @message.set_arg(OPENID_NS, 'assoc_handle', 'dumb-handle') check_auth_called = false call_check_sig do |idres| idres.instance_def(:check_auth) do check_auth_called = true end end assert(check_auth_called) end def test_check_auth_ok_no_store @store = nil check_auth_called = false call_check_sig do |idres| idres.instance_def(:check_auth) do check_auth_called = true end end assert(check_auth_called) end def test_expired_assoc @assoc.expires_in = -1 @store.store_association(@server_url, @assoc) assert_protocol_error('Association with') { call_check_sig(&method(:no_check_auth)) } end def call_check_auth(&proc) assert_log_matches("Using 'check_authentication'") { call_idres_method(:check_auth, &proc) } end def test_check_auth_create_fail assert_protocol_error("Could not generate") { call_check_auth do |idres| idres.instance_def(:create_check_auth_request) do raise Message::KeyNotFound, "Testing" end end } end def test_check_auth_okay OpenID.extend(OverrideMethodMixin) me = self send_resp = Proc.new do |req, server_url| me.assert_equal(:req, req) :expected_response end OpenID.with_method_overridden(:make_kv_post, send_resp) do final_resp = call_check_auth do |idres| idres.instance_def(:create_check_auth_request) { :req } idres.instance_def(:process_check_auth_response) do |resp| me.assert_equal(:expected_response, resp) end end end end def test_check_auth_process_fail OpenID.extend(OverrideMethodMixin) me = self send_resp = Proc.new do |req, server_url| me.assert_equal(:req, req) :expected_response end OpenID.with_method_overridden(:make_kv_post, send_resp) do assert_protocol_error("Testing") do final_resp = call_check_auth do |idres| idres.instance_def(:create_check_auth_request) { :req } idres.instance_def(:process_check_auth_response) do |resp| me.assert_equal(:expected_response, resp) raise ProtocolError, "Testing" end end end end end 1.times do # Fields from the signed list ['mode', 'identity', 'assoc_handle' ].each do |field| test = lambda do @message.del_arg(OPENID_NS, field) assert_raises(Message::KeyNotFound) { call_idres_method(:create_check_auth_request) {} } end define_method("test_create_check_auth_missing_#{field}", test) end end def test_create_check_auth_request_success ca_msg = call_idres_method(:create_check_auth_request) {} expected = @message.copy expected.set_arg(OPENID_NS, 'mode', 'check_authentication') assert_equal(expected, ca_msg) end end class CheckAuthResponseTest < Test::Unit::TestCase include TestUtil include ProtocolErrorMixin def setup @message = Message.from_openid_args({ 'is_valid' => 'true', }) @assoc = GoodAssoc.new @store = Store::Memory.new @server_url = 'http://invalid/' @endpoint = DummyEndpoint.new(@server_url) @idres = IdResHandler.new(nil, nil, @store, @endpoint) end def call_process @idres.send(:process_check_auth_response, @message) end def test_valid assert_log_matches() { call_process } end def test_invalid for is_valid in ['false', 'monkeys'] @message.set_arg(OPENID_NS, 'is_valid', 'false') assert_protocol_error("Server #{@server_url} responds") { assert_log_matches() { call_process } } end end def test_valid_invalidate @message.set_arg(OPENID_NS, 'invalidate_handle', 'cheese') assert_log_matches("Received 'invalidate_handle'") { call_process } end def test_invalid_invalidate @message.set_arg(OPENID_NS, 'invalidate_handle', 'cheese') for is_valid in ['false', 'monkeys'] @message.set_arg(OPENID_NS, 'is_valid', 'false') assert_protocol_error("Server #{@server_url} responds") { assert_log_matches("Received 'invalidate_handle'") { call_process } } end end def test_invalidate_no_store @idres.instance_variable_set(:@store, nil) @message.set_arg(OPENID_NS, 'invalidate_handle', 'cheese') assert_log_matches("Received 'invalidate_handle'", 'Unexpectedly got "invalidate_handle"') { call_process } end end class NonceTest < Test::Unit::TestCase include TestUtil include ProtocolErrorMixin def setup @store = Object.new class << @store attr_accessor :nonces, :succeed def use_nonce(server_url, time, extra) @nonces << [server_url, time, extra] @succeed end end @store.nonces = [] @nonce = Nonce.mk_nonce end def call_check_nonce(post_args, succeed=false) response = Message.from_post_args(post_args) if !@store.nil? @store.succeed = succeed end idres = IdResHandler.new(response, nil, @store, nil) idres.send(:check_nonce) end def test_openid1_success [{}, {'openid.ns' => OPENID1_NS}, {'openid.ns' => OPENID11_NS} ].each do |args| assert_nothing_raised { call_check_nonce({'rp_nonce' => @nonce}.merge(args), true) } end end def test_openid1_missing [{}, {'openid.ns' => OPENID1_NS}, {'openid.ns' => OPENID11_NS} ].each do |args| assert_protocol_error('Nonce missing') { call_check_nonce(args) } end end def test_openid2_ignore_rp_nonce assert_protocol_error('Nonce missing') { call_check_nonce({'rp_nonce' => @nonce, 'openid.ns' => OPENID2_NS}) } end def test_openid2_success assert_nothing_raised { call_check_nonce({'openid.response_nonce' => @nonce, 'openid.ns' => OPENID2_NS}, true) } end def test_openid1_ignore_response_nonce [{}, {'openid.ns' => OPENID1_NS}, {'openid.ns' => OPENID11_NS} ].each do |args| assert_protocol_error('Nonce missing') { call_check_nonce({'openid.response_nonce' => @nonce}.merge(args)) } end end def test_no_store @store = nil assert_nothing_raised { call_check_nonce({'rp_nonce' => @nonce}) } end def test_already_used assert_protocol_error('Nonce already used') { call_check_nonce({'rp_nonce' => @nonce}, false) } end def test_malformed_nonce assert_protocol_error('Malformed nonce') { call_check_nonce({'rp_nonce' => 'whee!'}) } end end class DiscoveryVerificationTest < Test::Unit::TestCase include ProtocolErrorMixin include TestUtil def setup @endpoint = OpenIDServiceEndpoint.new end def call_verify(msg_args) call_verify_modify(msg_args){} end def call_verify_modify(msg_args) msg = Message.from_openid_args(msg_args) idres = IdResHandler.new(msg, nil, nil, @endpoint) idres.extend(InstanceDefExtension) yield idres idres.send(:verify_discovery_results) idres.instance_variable_get(:@endpoint) end def assert_verify_protocol_error(error_prefix, openid_args) assert_protocol_error(error_prefix) {call_verify(openid_args)} end def test_openid1_no_local_id @endpoint.claimed_id = 'http://invalid/' assert_verify_protocol_error("Missing required field: "\ "<#{OPENID1_NS}>identity", {}) end def test_openid1_no_endpoint @endpoint = nil assert_raises(ProtocolError) { call_verify({'identity' => 'snakes on a plane'}) } end def test_openid1_fallback_1_0 [OPENID1_NS, OPENID11_NS].each do |openid1_ns| claimed_id = 'http://claimed.id/' @endpoint = nil resp_mesg = Message.from_openid_args({ 'ns' => openid1_ns, 'identity' => claimed_id, }) # Pass the OpenID 1 claimed_id this way since we're # passing None for the endpoint. resp_mesg.set_arg(BARE_NS, 'openid1_claimed_id', claimed_id) # We expect the OpenID 1 discovery verification to try # matching the discovered endpoint against the 1.1 type # and fall back to 1.0. expected_endpoint = OpenIDServiceEndpoint.new expected_endpoint.type_uris = [OPENID_1_0_TYPE] expected_endpoint.local_id = nil expected_endpoint.claimed_id = claimed_id hacked_discover = Proc.new { |_claimed_id| ['unused', [expected_endpoint]] } idres = IdResHandler.new(resp_mesg, nil, nil, @endpoint) assert_log_matches('Performing discovery') { OpenID.with_method_overridden(:discover, hacked_discover) { idres.send(:verify_discovery_results) } } actual_endpoint = idres.instance_variable_get(:@endpoint) assert_equal(actual_endpoint, expected_endpoint) end end def test_openid2_no_op_endpoint assert_protocol_error("Missing required field: "\ "<#{OPENID2_NS}>op_endpoint") { call_verify({'ns'=>OPENID2_NS}) } end def test_openid2_local_id_no_claimed assert_verify_protocol_error('openid.identity is present without', {'ns' => OPENID2_NS, 'op_endpoint' => 'Phone Home', 'identity' => 'Jorge Lius Borges'}) end def test_openid2_no_local_id_claimed assert_log_matches() { assert_protocol_error('openid.claimed_id is present without') { call_verify({'ns' => OPENID2_NS, 'op_endpoint' => 'Phone Home', 'claimed_id' => 'Manuel Noriega'}) } } end def test_openid2_no_identifiers op_endpoint = 'Phone Home' result_endpoint = assert_log_matches() { call_verify({'ns' => OPENID2_NS, 'op_endpoint' => op_endpoint}) } assert(result_endpoint.is_op_identifier) assert_equal(op_endpoint, result_endpoint.server_url) assert(result_endpoint.claimed_id.nil?) end def test_openid2_no_endpoint_does_disco endpoint = OpenIDServiceEndpoint.new endpoint.claimed_id = 'monkeysoft' @endpoint = nil result = assert_log_matches('No pre-discovered') { call_verify_modify({'ns' => OPENID2_NS, 'identity' => 'sour grapes', 'claimed_id' => 'monkeysoft', 'op_endpoint' => 'Phone Home'}) do |idres| idres.instance_def(:discover_and_verify) do |claimed_id, endpoints| @endpoint = endpoint end end } assert_equal(endpoint, result) end def test_openid2_mismatched_does_disco @endpoint.claimed_id = 'nothing special, but different' @endpoint.local_id = 'green cheese' endpoint = OpenIDServiceEndpoint.new endpoint.claimed_id = 'monkeysoft' result = assert_log_matches('Error attempting to use stored', 'Attempting discovery') { call_verify_modify({'ns' => OPENID2_NS, 'identity' => 'sour grapes', 'claimed_id' => 'monkeysoft', 'op_endpoint' => 'Green Cheese'}) do |idres| idres.instance_def(:discover_and_verify) do |claimed_id, endpoints| @endpoint = endpoint end end } assert(endpoint.equal?(result)) end def test_verify_discovery_single_claimed_id_mismatch idres = IdResHandler.new(nil, nil) @endpoint.local_id = 'my identity' @endpoint.claimed_id = 'http://i-am-sam/' @endpoint.server_url = 'Phone Home' @endpoint.type_uris = [OPENID_2_0_TYPE] to_match = @endpoint.dup to_match.claimed_id = 'http://something.else/' e = assert_raises(ProtocolError) { idres.send(:verify_discovery_single, @endpoint, to_match) } assert(e.to_s =~ /different subjects/) end def test_openid1_1_verify_discovery_single_no_server_url idres = IdResHandler.new(nil, nil) @endpoint.local_id = 'my identity' @endpoint.claimed_id = 'http://i-am-sam/' @endpoint.server_url = 'Phone Home' @endpoint.type_uris = [OPENID_1_1_TYPE] to_match = @endpoint.dup to_match.claimed_id = 'http://i-am-sam/' to_match.type_uris = [OPENID_1_1_TYPE] to_match.server_url = nil idres.send(:verify_discovery_single, @endpoint, to_match) end def test_openid2_use_pre_discovered @endpoint.local_id = 'my identity' @endpoint.claimed_id = 'http://i-am-sam/' @endpoint.server_url = 'Phone Home' @endpoint.type_uris = [OPENID_2_0_TYPE] result = assert_log_matches() { call_verify({'ns' => OPENID2_NS, 'identity' => @endpoint.local_id, 'claimed_id' => @endpoint.claimed_id, 'op_endpoint' => @endpoint.server_url }) } assert(result.equal?(@endpoint)) end def test_openid2_use_pre_discovered_wrong_type text = "verify failed" me = self @endpoint.local_id = 'my identity' @endpoint.claimed_id = 'i am sam' @endpoint.server_url = 'Phone Home' @endpoint.type_uris = [OPENID_1_1_TYPE] endpoint = @endpoint msg = Message.from_openid_args({'ns' => OPENID2_NS, 'identity' => @endpoint.local_id, 'claimed_id' => @endpoint.claimed_id, 'op_endpoint' => @endpoint.server_url}) idres = IdResHandler.new(msg, nil, nil, @endpoint) idres.extend(InstanceDefExtension) idres.instance_def(:discover_and_verify) { |claimed_id, to_match| me.assert_equal(endpoint.claimed_id, to_match[0].claimed_id) me.assert_equal(claimed_id, endpoint.claimed_id) raise ProtocolError, text } assert_log_matches('Error attempting to use stored', 'Attempting discovery') { assert_protocol_error(text) { idres.send(:verify_discovery_results) } } end def test_openid1_use_pre_discovered @endpoint.local_id = 'my identity' @endpoint.claimed_id = 'http://i-am-sam/' @endpoint.server_url = 'Phone Home' @endpoint.type_uris = [OPENID_1_1_TYPE] result = assert_log_matches() { call_verify({'ns' => OPENID1_NS, 'identity' => @endpoint.local_id}) } assert(result.equal?(@endpoint)) end def test_openid1_use_pre_discovered_wrong_type verified_error = Class.new(Exception) @endpoint.local_id = 'my identity' @endpoint.claimed_id = 'i am sam' @endpoint.server_url = 'Phone Home' @endpoint.type_uris = [OPENID_2_0_TYPE] assert_log_matches('Error attempting to use stored', 'Attempting discovery') { assert_raises(verified_error) { call_verify_modify({'ns' => OPENID1_NS, 'identity' => @endpoint.local_id}) { |idres| idres.instance_def(:discover_and_verify) do |claimed_id, endpoints| raise verified_error end } } } end def test_openid2_fragment claimed_id = "http://unittest.invalid/" claimed_id_frag = claimed_id + "#fragment" @endpoint.local_id = 'my identity' @endpoint.claimed_id = claimed_id @endpoint.server_url = 'Phone Home' @endpoint.type_uris = [OPENID_2_0_TYPE] result = assert_log_matches() { call_verify({'ns' => OPENID2_NS, 'identity' => @endpoint.local_id, 'claimed_id' => claimed_id_frag, 'op_endpoint' => @endpoint.server_url}) } [:local_id, :server_url, :type_uris].each do |sym| assert_equal(@endpoint.send(sym), result.send(sym)) end assert_equal(claimed_id_frag, result.claimed_id) end def test_endpoint_without_local_id # An endpoint like this with no local_id is generated as a result of # e.g. Yadis discovery with no LocalID tag. @endpoint.server_url = "http://localhost:8000/openidserver" @endpoint.claimed_id = "http://localhost:8000/id/id-jo" to_match = OpenIDServiceEndpoint.new to_match.server_url = "http://localhost:8000/openidserver" to_match.claimed_id = "http://localhost:8000/id/id-jo" to_match.local_id = "http://localhost:8000/id/id-jo" idres = IdResHandler.new(nil, nil) assert_log_matches() { result = idres.send(:verify_discovery_single, @endpoint, to_match) } end end class IdResTopLevelTest < Test::Unit::TestCase def test_id_res endpoint = OpenIDServiceEndpoint.new endpoint.server_url = 'http://invalid/server' endpoint.claimed_id = 'http://my.url/' endpoint.local_id = 'http://invalid/username' endpoint.type_uris = [OPENID_2_0_TYPE] assoc = GoodAssoc.new store = Store::Memory.new store.store_association(endpoint.server_url, assoc) signed_fields = [ 'response_nonce', 'op_endpoint', 'assoc_handle', 'identity', 'claimed_id', 'ns', 'return_to', ] return_to = 'http://return.to/' args = { 'ns' => OPENID2_NS, 'return_to' => return_to, 'claimed_id' => endpoint.claimed_id, 'identity' => endpoint.local_id, 'assoc_handle' => assoc.handle, 'op_endpoint' => endpoint.server_url, 'response_nonce' => Nonce.mk_nonce, 'signed' => signed_fields.join(','), 'sig' => GOODSIG, } msg = Message.from_openid_args(args) idres = OpenID::Consumer::IdResHandler.new(msg, return_to, store, endpoint) assert_equal(idres.signed_fields, signed_fields.map {|f|'openid.' + f}) end end class DiscoverAndVerifyTest < Test::Unit::TestCase include ProtocolErrorMixin include TestUtil def test_no_services me = self disco = Proc.new do |e| me.assert_equal(e, :sentinel) [:undefined, []] end endpoint = OpenIDServiceEndpoint.new endpoint.claimed_id = :sentinel idres = IdResHandler.new(nil, nil) assert_log_matches('Performing discovery on') do assert_protocol_error('No OpenID information found') do OpenID.with_method_overridden(:discover, disco) do idres.send(:discover_and_verify, :sentinel, [endpoint]) end end end end end class VerifyDiscoveredServicesTest < Test::Unit::TestCase include ProtocolErrorMixin include TestUtil def test_no_services endpoint = OpenIDServiceEndpoint.new endpoint.claimed_id = :sentinel idres = IdResHandler.new(nil, nil) assert_log_matches('Discovery verification failure') do assert_protocol_error('No matching endpoint') do idres.send(:verify_discovered_services, 'http://bogus.id/', [], [endpoint]) end end end end end end end ruby-openid-2.1.8debian.orig/test/test_consumer.rb0000644000175000017500000002037011753312574020755 0ustar paulpaulrequire "openid/consumer" require "test/unit" require "testutil" module OpenID class Consumer module TestConsumer class TestLastEndpoint < Test::Unit::TestCase def test_set_get session = {} consumer = Consumer.new(session, nil) consumer.send(:last_requested_endpoint=, :endpoint) ep = consumer.send(:last_requested_endpoint) assert_equal(:endpoint, ep) ep = consumer.send(:last_requested_endpoint) assert_equal(:endpoint, ep) consumer.send(:cleanup_last_requested_endpoint) ep = consumer.send(:last_requested_endpoint) assert_equal(nil, ep) end end class TestBegin < Test::Unit::TestCase attr_accessor :user_input, :anonymous, :services, :discovered_identifier, :checkid_request, :service def setup @discovered_identifier = 'http://discovered/' @user_input = 'user.input' @service = :service @services = [@service] @session = {} @anonymous = false @checkid_request = :checkid_request end def consumer test = self consumer = Consumer.new(@session, nil) consumer.extend(InstanceDefExtension) consumer.instance_def(:discover) do |identifier| test.assert_equal(test.user_input, identifier) [test.discovered_identifier, test.services] end consumer.instance_def(:begin_without_discovery) do |service, sent_anonymous| test.assert_equal(test.service, service) test.assert_equal(test.anonymous, sent_anonymous) test.checkid_request end consumer end def test_begin checkid_request = consumer.begin(@user_input, @anonymous) assert_equal(:checkid_request, checkid_request) assert_equal(['OpenID::Consumer::DiscoveredServices::'\ 'OpenID::Consumer::'], @session.keys.sort!) end def test_begin_failure @services = [] assert_raises(DiscoveryFailure) { consumer.begin(@user_input, @anonymous) } end def test_begin_fallback @services = [:service1, :service2] consumer = self.consumer @service = :service1 consumer.begin(@user_input, @anonymous) @service = :service2 consumer.begin(@user_input, @anonymous) @service = :service1 consumer.begin(@user_input, @anonymous) @service = :service2 consumer.begin(@user_input, @anonymous) end end class TestBeginWithoutDiscovery < Test::Unit::TestCase attr_reader :assoc def setup @session = {} @assoc = :assoc @service = OpenIDServiceEndpoint.new @claimed_id = 'http://claimed.id/' @service.claimed_id = @claimed_id @anonymous = false end def consumer test = self assoc_manager = Object.new assoc_manager.extend(InstanceDefExtension) assoc_manager.instance_def(:get_association) do test.assoc end consumer = Consumer.new(@session, nil) consumer.extend(InstanceDefExtension) consumer.instance_def(:association_manager) do |service| assoc_manager end consumer end def call_begin_without_discovery result = consumer.begin_without_discovery(@service, @anonymous) assert(result.instance_of?(CheckIDRequest)) assert_equal(@anonymous, result.anonymous) assert_equal(@service, consumer.send(:last_requested_endpoint)) assert_equal(result.instance_variable_get(:@assoc), @assoc) return result end def cid_name Consumer.openid1_return_to_claimed_id_name end def nonce_name Consumer.openid1_return_to_nonce_name end def test_begin_without_openid1 result = call_begin_without_discovery assert_equal(@claimed_id, result.return_to_args[cid_name]) assert_equal([cid_name, nonce_name].sort!, result.return_to_args.keys.sort!) end def test_begin_without_openid1_anonymous @anonymous = true assert_raises(ArgumentError) { call_begin_without_discovery } end def test_begin_without_openid2 @service.type_uris = [OPENID_2_0_TYPE] result = call_begin_without_discovery assert(result.return_to_args.empty?) end def test_begin_without_openid2_anonymous @anonymous = true @service.type_uris = [OPENID_2_0_TYPE] result = call_begin_without_discovery assert(result.return_to_args.empty?) end end class TestComplete < Test::Unit::TestCase def setup @session = {} @consumer = Consumer.new(@session, nil) end def test_bad_mode response = @consumer.complete({'openid.ns' => OPENID2_NS, 'openid.mode' => 'bad'}, nil) assert_equal(FAILURE, response.status) end def test_missing_mode response = @consumer.complete({'openid.ns' => OPENID2_NS}, nil) assert_equal(FAILURE, response.status) end def test_cancel response = @consumer.complete({'openid.mode' => 'cancel'}, nil) assert_equal(CANCEL, response.status) end def test_setup_needed_openid1 response = @consumer.complete({'openid.mode' => 'setup_needed'}, nil) assert_equal(FAILURE, response.status) end def test_setup_needed_openid2 args = {'openid.ns' => OPENID2_NS, 'openid.mode' => 'setup_needed'} response = @consumer.complete(args, nil) assert_equal(SETUP_NEEDED, response.status) end def test_idres_setup_needed_openid1 setup_url = 'http://setup.url/' args = { 'openid.user_setup_url' => setup_url, 'openid.mode' => 'id_res', } response = @consumer.complete(args, nil) assert_equal(SETUP_NEEDED, response.status) end def test_error contact = 'me' reference = 'thing thing' args = { 'openid.mode' => 'error', 'openid.contact' => contact, 'openid.reference' => reference, } response = @consumer.complete(args, nil) assert_equal(FAILURE, response.status) assert_equal(contact, response.contact) assert_equal(reference, response.reference) args['openid.ns'] = OPENID2_NS response = @consumer.complete(args, nil) assert_equal(FAILURE, response.status) assert_equal(contact, response.contact) assert_equal(reference, response.reference) end def test_idres_openid1 args = { 'openid.mode' => 'id_res', } endpoint = OpenIDServiceEndpoint.new endpoint.claimed_id = :test_claimed_id idres = Object.new idres.extend(InstanceDefExtension) idres.instance_def(:endpoint){endpoint} idres.instance_def(:signed_fields){:test_signed_fields} test = self @consumer.extend(InstanceDefExtension) @consumer.instance_def(:handle_idres) {|message, return_to| test.assert_equal(args, message.to_post_args) test.assert_equal(:test_return_to, return_to) idres } response = @consumer.complete(args, :test_return_to) assert_equal(SUCCESS, response.status, response.message) assert_equal(:test_claimed_id, response.identity_url) assert_equal(endpoint, response.endpoint) error_message = "In Soviet Russia, id_res handles you!" @consumer.instance_def(:handle_idres) {|message, return_to| raise ProtocolError, error_message } response = @consumer.complete(args, :test_return_to) assert_equal(FAILURE, response.status) assert_equal(error_message, response.message) end end end end end ruby-openid-2.1.8debian.orig/test/test_openid_yadis.rb0000644000175000017500000001132611753312574021572 0ustar paulpaul require 'test/unit' require 'openid/consumer/discovery' require 'openid/yadis/services' module OpenID XRDS_BOILERPLATE = < %s EOF def self.mkXRDS(services) return sprintf(XRDS_BOILERPLATE, services) end def self.mkService(uris=nil, type_uris=nil, local_id=nil, dent=" ") chunks = [dent, "\n"] dent2 = dent + " " if type_uris type_uris.each { |type_uri| chunks += [dent2 + "", type_uri, "\n"] } end if uris uris.each { |uri| if uri.is_a?(Array) uri, prio = uri else prio = nil end chunks += [dent2, "", uri, "\n"] } end if local_id chunks += [dent2, "", local_id, "\n"] end chunks += [dent, "\n"] return chunks.join("") end # Different sets of server URLs for use in the URI tag SERVER_URL_OPTIONS = [ [], # This case should not generate an endpoint object ['http://server.url/'], ['https://server.url/'], ['https://server.url/', 'http://server.url/'], ['https://server.url/', 'http://server.url/', 'http://example.server.url/'], ] # Used for generating test data def OpenID.subsets(l) subsets_list = [[]] l.each { |x| subsets_list += subsets_list.collect { |t| [x] + t } } return subsets_list end # A couple of example extension type URIs. These are not at all # official, but are just here for testing. EXT_TYPES = [ 'http://janrain.com/extension/blah', 'http://openid.net/sreg/1.0', ] # Range of valid Delegate tag values for generating test data LOCAL_ID_OPTIONS = [ nil, 'http://vanity.domain/', 'https://somewhere/yadis/', ] class OpenIDYadisTest def initialize(uris, type_uris, local_id) super() @uris = uris @type_uris = type_uris @local_id = local_id @yadis_url = 'http://unit.test/' # Create an XRDS document to parse services = OpenID.mkService(@uris, @type_uris, @local_id) @xrds = OpenID.mkXRDS(services) end def runTest(testcase) # Parse into endpoint objects that we will check endpoints = Yadis.apply_filter(@yadis_url, @xrds, OpenIDServiceEndpoint) # make sure there are the same number of endpoints as URIs. This # assumes that the type_uris contains at least one OpenID type. testcase.assert_equal(@uris.length, endpoints.length) # So that we can check equality on the endpoint types type_uris = @type_uris.dup type_uris.sort! seen_uris = [] endpoints.each { |endpoint| seen_uris << endpoint.server_url # All endpoints will have same yadis_url testcase.assert_equal(@yadis_url, endpoint.claimed_id) # and local_id testcase.assert_equal(@local_id, endpoint.local_id) # and types actual_types = endpoint.type_uris.dup actual_types.sort! testcase.assert_equal(type_uris, actual_types, actual_types.inspect) } # So that they will compare equal, because we don't care what # order they are in seen_uris.sort! uris = @uris.dup uris.sort! # Make sure we saw all URIs, and saw each one once testcase.assert_equal(uris, seen_uris) end end class OpenIDYadisTests < Test::Unit::TestCase def test_openid_yadis data = [] # All valid combinations of Type tags that should produce an # OpenID endpoint type_uri_options = [] OpenID.subsets([OPENID_1_0_TYPE, OPENID_1_1_TYPE]).each { |ts| OpenID.subsets(EXT_TYPES).each { |exts| if !ts.empty? type_uri_options << exts + ts end } } # All combinations of valid URIs, Type URIs and Delegate tags SERVER_URL_OPTIONS.each { |uris| type_uri_options.each { |type_uris| LOCAL_ID_OPTIONS.each { |local_id| data << [uris, type_uris, local_id] } } } data.each { |args| t = OpenIDYadisTest.new(*args) t.runTest(self) } end end end ruby-openid-2.1.8debian.orig/test/test_parsehtml.rb0000644000175000017500000000430411753312574021120 0ustar paulpaulrequire 'test/unit' require "openid/yadis/parsehtml" require "testutil" module OpenID class ParseHTMLTestCase < Test::Unit::TestCase include OpenID::TestDataMixin def test_parsehtml reserved_values = ['None', 'EOF'] chunks = read_data_file('test1-parsehtml.txt', false).split("\f\n") test_num = 1 chunks.each{|c| expected, html = c.split("\n", 2) found = Yadis::html_yadis_location(html) assert(!reserved_values.member?(found)) # this case is a little hard to detect and the distinction # seems unimportant expected = "None" if expected == "EOF" found = "None" if found.nil? assert_equal(expected, found, html.split("\n",2)[0]) } end end # the HTML tokenizer test class TC_TestHTMLTokenizer < Test::Unit::TestCase def test_bad_link toke = HTMLTokenizer.new("

    foo

    ") assert("http://bad.com/link" == toke.getTag("a").attr_hash['href']) end def test_namespace toke = HTMLTokenizer.new("") assert("http://www.com/foo" == toke.getTag("f:table").attr_hash['xmlns:f']) end def test_comment toke = HTMLTokenizer.new("") t = toke.getNextToken assert(HTMLComment == t.class) assert("comment on me" == t.contents) end def test_full page = " This is the title

    This is the header

    This is the paragraph, it contains links, images
are
really cool. Ok, here is some more text and another link.

    " toke = HTMLTokenizer.new(page) assert("

    " == toke.getTag("h1", "h2", "h3").to_s.downcase) assert(HTMLTag.new("") == toke.getTag("IMG", "A")) assert("links" == toke.getTrimmedText) assert(toke.getTag("IMG", "A").attr_hash['optional']) assert("_blank" == toke.getTag("IMG", "A").attr_hash['target']) end end end ruby-openid-2.1.8debian.orig/test/data/0000755000175000017500000000000011753312574016445 5ustar paulpaulruby-openid-2.1.8debian.orig/test/data/example-xrds.xml0000644000175000017500000000046311753312574021603 0ustar paulpaul http://example.com/ http://www.openidenabled.com/ ruby-openid-2.1.8debian.orig/test/data/test1-discover.txt0000644000175000017500000000413211753312574022062 0ustar paulpaulequiv Status: 200 OK Content-Type: text/html Joe Schmoe's Homepage

    Joe Schmoe's Homepage

    Blah blah blah blah blah blah blah

    header Status: 200 OK Content-Type: text/html YADIS_HEADER: URL_BASE/xrds Joe Schmoe's Homepage

    Joe Schmoe's Homepage

    Blah blah blah blah blah blah blah

    xrds Status: 200 OK Content-Type: application/xrds+xml xrds_ctparam Status: 200 OK Content-Type: application/xrds+xml; charset=UTF8 xrds_ctcase Status: 200 OK Content-Type: appliCATION/XRDS+xml xrds_html Status: 200 OK Content-Type: text/html redir_equiv Status: 302 Found Content-Type: text/plain Location: URL_BASE/equiv You are presently being redirected. redir_header Status: 302 Found Content-Type: text/plain Location: URL_BASE/header You are presently being redirected. redir_xrds Status: 302 Found Content-Type: application/xrds+xml Location: URL_BASE/xrds redir_xrds_html Status: 302 Found Content-Type: text/plain Location: URL_BASE/xrds_html You are presently being redirected. redir_redir_equiv Status: 302 Found Content-Type: text/plain Location: URL_BASE/redir_equiv You are presently being redirected. lowercase_header Status: 200 OK Content-Type: text/html x-xrds-location: URL_BASE/xrds Joe Schmoe's Homepage

    Joe Schmoe's Homepage

    Blah blah blah blah blah blah blah

    404_server_response Status: 404 Not Found EEk! 500_server_response Status: 500 Server error EEk! 201_server_response Status: 201 Created EEk! 404_with_header Status: 404 Not Found YADIS_HEADER: URL_BASE/xrds EEk! 404_with_meta Status: 404 Not Found Content-Type: text/html Joe Schmoe's Homepage

    Joe Schmoe's Homepage

    Blah blah blah blah blah blah blah

    ruby-openid-2.1.8debian.orig/test/data/trustroot.txt0000644000175000017500000001346411753312574021303 0ustar paulpaul======================================== Trust root parsing checking ======================================== ---------------------------------------- 23: Does not parse ---------------------------------------- baz.org *.foo.com http://*.schtuff.*/ ftp://foo.com ftp://*.foo.com http://*.foo.com:80:90/ foo.*.com http://foo.*.com http://www.* http://*foo.com/ http://foo.com/invalid#fragment http://..it/ http://.it/ http://*:8081/ http://*:80 http://localhost:1900foo/ http://foo.com\/ http://Ï€.pi.com/ http://lambda.com/Λ 5 ---------------------------------------- 14: Insane ---------------------------------------- http:/// http://*/ https://*/ http://*.com http://*.com/ https://*.com/ http://*.com.au/ http://*.co.uk/ http://*.foo.notatld/ https://*.foo.notatld/ http://*.museum/ https://*.museum/ http://www.schtuffcom/ http://it/ ---------------------------------------- 18: Sane ---------------------------------------- http://*.schtuff.com./ http://*.schtuff.com/ http://*.foo.schtuff.com/ http://*.schtuff.com http://www.schtuff.com/ http://www.schtuff.com./ http://www.schutff.com http://*.this.that.schtuff.com/ http://*.foo.com/path http://*.foo.com/path?action=foo2 http://x.foo.com/path?action=foo2 http://x.foo.com/path?action=%3D http://localhost:8081/ http://localhost:8082/?action=openid https://foo.com/ http://kink.fm/should/be/sane http://beta.lingu.no/ http://goathack.livejournal.org:8020/openid/login.bml ======================================== return_to matching ======================================== ---------------------------------------- 46: matches ---------------------------------------- http://*/ http://cnn.com/ http://*/ http://livejournal.com/ http://*/ http://met.museum/ http://localhost:8081/x?action=openid http://localhost:8081/x?action=openid http://*.foo.com http://b.foo.com http://*.foo.com http://b.foo.com/ http://*.foo.com/ http://b.foo.com http://b.foo.com http://b.foo.com http://b.foo.com http://b.foo.com/ http://b.foo.com/ http://b.foo.com http://*.b.foo.com http://b.foo.com http://*.b.foo.com http://b.foo.com/ http://*.b.foo.com/ http://b.foo.com http://*.b.foo.com http://x.b.foo.com http://*.b.foo.com http://w.x.b.foo.com http://*.bar.co.uk http://www.bar.co.uk http://*.uoregon.edu http://x.cs.uoregon.edu http://x.com/abc http://x.com/abc http://x.com/abc http://x.com/abc/def http://10.0.0.1/abc http://10.0.0.1/abc http://*.x.com http://x.com/gallery http://*.x.com http://foo.x.com/gallery http://foo.x.com http://foo.x.com/gallery/xxx http://*.x.com/gallery http://foo.x.com/gallery http://localhost:8082/?action=openid http://localhost:8082/?action=openid http://goathack.livejournal.org:8020/ http://goathack.livejournal.org:8020/openid/login.bml https://foo.com https://foo.com http://Foo.com http://foo.com http://foo.com http://Foo.com http://foo.com:80/ http://foo.com/ http://foo.com/?x=y http://foo.com/?x=y&a=b http://foo.com/x http://foo.com/x?y http://mylid.net/j3h. http://mylid.net/j3h.?x=y http://j3h.us http://j3h.us?ride=unicycle https://www.filmclans.com:443/mattmartin/FilmClans https://www.filmclans.com/mattmartin/FilmClans/Logon.aspx?nonce=BVjqSOee http://foo.com:80 http://foo.com http://foo.com http://foo.com:80 http://foo.com http://foo.com/ http://foo.com/ http://foo.com http://foo.com/ http://foo.com:80 http://foo.com:80/ http://foo.com:80/stuff http://foo.com:80/ http://foo.com/stuff http://foo.com/path http://foo.com/path/extra http://foo.com/path2 http://foo.com/path2?extra=query http://foo.com/path2 http://foo.com/path2/?extra=query http://foo.com/ HTTP://foo.com/ ---------------------------------------- 25: does not match ---------------------------------------- http://*/ ftp://foo.com/ http://*/ xxx http://foo.com/ http://oo.com/ http://*.x.com/abc http://foo.x.com http://*.x.com/abc http://*.x.com http://*.com/ http://*.com/ http://x.com/abc http://x.com/ http://x.com/abc http://x.com/a http://x.com/abc http://x.com/ab http://x.com/abc http://x.com/abcd http://*.cs.uoregon.edu http://x.uoregon.edu http://*.foo.com http://bar.com http://*.foo.com http://www.bar.com http://*.bar.co.uk http://xxx.co.uk https://foo.com http://foo.com http://foo.com https://foo.com http://foo.com:81 http://foo.com:80 http://foo.com/?a=b http://foo.com/?x=y http://foo.com/?a=b http://foo.com/?x=y&a=b http://foo.com/?a=b http://foo.com/ http://*.oo.com/ http://foo.com/ http://foo.com/* http://foo.com/anything http://foo.com http://foo.com:443 https://foo.com https://foo.com:80 http://foo.com/path/xev http://foo.com/path?extra=more ruby-openid-2.1.8debian.orig/test/data/linkparse.txt0000644000175000017500000001722411753312574021204 0ustar paulpaulNum Tests: 72 OpenID link parsing test cases Copyright (C) 2005-2008, JanRain, Inc. See COPYING for license information. File format ----------- All text before the first triple-newline (this chunk) should be ignored. This file may be interpreted as Latin-1 or UTF-8. Test cases separated by three line separators (`\n\n\n'). The test cases consist of a headers section followed by a data block. These are separated by a double newline. The headers consist of the header name, followed by a colon, a space, the value, and a newline. There must be one, and only one, `Name' header for a test case. There may be zero or more link headers. The `Link' header consists of whitespace-separated attribute pairs. A link header with an empty string as a value indicates an empty but present link tag. The attribute pairs are `=' separated and not quoted. Optional Links and attributes have a trailing `*'. A compilant implementation may produce this as output or may not. A compliant implementation will not produce any output that is absent from this file. Name: No link tag at all Name: Link element first Name: Link inside HTML, not head Name: Link inside head, not html Name: Link inside html, after head Name: Link inside html, before head Name: Link before html and head Name: Link after html document with head Name: Link inside html inside head, inside another html Name: Link inside html inside head Name: link inside body inside head inside html Name: Link inside head inside head inside html Name: Link inside script inside head inside html Name: Link inside comment inside head inside html Name: Link inside of head after short head Name: Plain vanilla Link: Name: Ignore tags in the namespace Link*: Name: Short link tag Link: Name: Spaces in the HTML tag Link: Name: Spaces in the head tag Link: Name: Spaces in the link tag Link: Name: No whitespace Link: Name: Closed head tag Link: Name: One good, one bad (after close head) Link: Name: One good, one bad (after open body) Link: Name: ill formed (missing close head) Link: Name: Ill formed (no close head, link after ) Link: Name: Ignore random tags inside of html Link: <link> Name: case-folding Link*: <HtMl> <hEaD> <LiNk> Name: unexpected tags Link: <butternut> <html> <summer> <head> <turban> <link> Name: un-closed script tags Link*: <html> <head> <script> <link> Name: un-closed script tags (no whitespace) Link*: <html><head><script><link> Name: un-closed comment Link*: <html> <head> <!-- <link> Name: un-closed CDATA Link*: <html> <head> <![CDATA[ <link> Name: cdata-like Link*: <html> <head> <![ACORN[ <link> ]]> Name: comment close only Link: <html> <head> <link> --> Name: Vanilla, two links Link: Link: <html> <head> <link> <link> Name: extra tag, two links Link: Link: <html> <gold nugget> <head> <link> <link> Name: case-fold, body ends, two links Link: Link*: <html> <head> <link> <LiNk> <body> <link> Name: simple, non-quoted rel Link: rel=openid.server <html><head><link rel=openid.server> Name: short tag has rel Link: rel=openid.server <html><head><link rel=openid.server/> Name: short tag w/space has rel Link: rel=openid.server <html><head><link rel=openid.server /> Name: extra non-attribute, has rel Link: rel=openid.server hubbard*=hubbard <html><head><link hubbard rel=openid.server> Name: non-attr, has rel, short Link: rel=openid.server hubbard*=hubbard <html><head><link hubbard rel=openid.server/> Name: non-attr, has rel, short, space Link: rel=openid.server hubbard*=hubbard <html><head><link hubbard rel=openid.server /> Name: misplaced slash has rel Link: rel=openid.server <html><head><link / rel=openid.server> Name: quoted rel Link: rel=openid.server <html><head><link rel="openid.server"> Name: single-quoted rel Link: rel=openid.server <html><head><link rel='openid.server'> Name: two links w/ rel Link: x=y Link: a=b <html><head><link x=y><link a=b> Name: non-entity Link: x=&y <html><head><link x=&y> Name: quoted non-entity Link: x=&y <html><head><link x="&y"> Name: quoted entity Link: x=& <html><head><link x="&"> Name: entity not processed Link: x= <html><head><link x=""> Name: < Link: x=< <html><head><link x="<"> Name: > Link: x=> <html><head><link x=">"> Name: " Link: x=" <html><head><link x="""> Name: &" Link: x=&" <html><head><link x="&""> Name: mixed entity and non-entity Link: x=&"…> <html><head><link x="&"…>"> Name: mixed entity and non-entity (w/normal chars) Link: x=x&"…>x <html><head><link x="x&"…>x"> Name: broken tags Link*: x=y Link*: x=y< <html><head><link x=y<> Name: missing close pointy Link*: x=y Link*: x=y<link z=y Link*: z=y <html><head><link x=y<link z=y /> Name: missing attribute value Link: x=y y*=y Link: x=y <html><head><link x=y y=><link x=y /> Name: Missing close pointy (no following) Link*: x=y <html><head><link x=y Name: Should be quoted Link*: x=< <html><head><link x="<"> Name: Should be quoted (2) Link*: x=> Link*: x=x <html><head><link x=">"> Name: Repeated attribute Link: x=y <html><head><link x=z x=y> Name: Repeated attribute (2) Link: x=y <html><head><link x=y x=y> Name: Two attributes Link: x=y y=z <html><head><link x=y y=z> Name: Well-formed link rel="openid.server" Link: rel=openid.server href=http://www.myopenid.com/server <html> <head> <link rel="openid.server" href="http://www.myopenid.com/server" /> </head> </html> Name: Well-formed link rel="openid.server" and "openid.delegate" Link: rel=openid.server href=http://www.myopenid.com/server Link: rel=openid.delegate href=http://example.myopenid.com/ <html><head><link rel="openid.server" href="http://www.myopenid.com/server" /> <link rel="openid.delegate" href="http://example.myopenid.com/" /> </head></html> Name: from brian's livejournal page Link: rel=stylesheet href=http://www.livejournal.com/~serotta/res/319998/stylesheet?1130478711 type=text/css Link: rel=openid.server href=http://www.livejournal.com/openid/server.bml <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <link rel="stylesheet" href="http://www.livejournal.com/~serotta/res/319998/stylesheet?1130478711" type="text/css" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="foaf:maker" content="foaf:mbox_sha1sum '12f8abdacb5b1a806711e23249da592c0d316260'" /> <meta name="robots" content="noindex, nofollow, noarchive" /> <meta name="googlebot" content="nosnippet" /> <link rel="openid.server" href="http://www.livejournal.com/openid/server.bml" /> <title>Brian Name: non-ascii (Latin-1 or UTF8) Link: x=® ruby-openid-2.1.8debian.orig/test/data/urinorm.txt0000644000175000017500000000311711753312574020703 0ustar paulpaulAlready normal form http://example.com/ http://example.com/ Add a trailing slash http://example.com http://example.com/ Remove an empty port segment http://example.com:/ http://example.com/ Remove a default port segment http://example.com:80/ http://example.com/ Capitalization in host names http://wWw.exaMPLE.COm/ http://www.example.com/ Capitalization in scheme names htTP://example.com/ http://example.com/ Capitalization in percent-escaped reserved characters http://example.com/foo%2cbar http://example.com/foo%2Cbar Unescape percent-encoded unreserved characters http://example.com/foo%2Dbar%2dbaz http://example.com/foo-bar-baz remove_dot_segments example 1 http://example.com/a/b/c/./../../g http://example.com/a/g remove_dot_segments example 2 http://example.com/mid/content=5/../6 http://example.com/mid/6 remove_dot_segments: single-dot http://example.com/a/./b http://example.com/a/b remove_dot_segments: double-dot http://example.com/a/../b http://example.com/b remove_dot_segments: leading double-dot http://example.com/../b http://example.com/b remove_dot_segments: trailing single-dot http://example.com/a/. http://example.com/a/ remove_dot_segments: trailing double-dot http://example.com/a/.. http://example.com/ remove_dot_segments: trailing single-dot-slash http://example.com/a/./ http://example.com/a/ remove_dot_segments: trailing double-dot-slash http://example.com/a/../ http://example.com/ Test of all kinds of syntax-based normalization hTTPS://a/./b/../b/%63/%7bfoo%7d https://a/b/c/%7Bfoo%7D Unsupported scheme ftp://example.com/ fail Non-absolute URI http:/foo failruby-openid-2.1.8debian.orig/test/data/test_discover/0000755000175000017500000000000011753312574021322 5ustar paulpaulruby-openid-2.1.8debian.orig/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml0000644000175000017500000000111111753312574030277 0ustar paulpaul http://specs.openid.net/auth/2.0/signon http://openid.net/signon/1.0 http://openid.net/signon/1.1 http://www.myopenid.com/server http://smoker.myopenid.com/ http://localid.mismatch.invalid/ ruby-openid-2.1.8debian.orig/test/data/test_discover/yadis_0entries.xml0000644000175000017500000000051611753312574024770 0ustar paulpaul http://is-not-openid.unittest/ http://noffing.unittest./ ruby-openid-2.1.8debian.orig/test/data/test_discover/yadis_no_delegate.xml0000644000175000017500000000045011753312574025502 0ustar paulpaul http://openid.net/signon/1.0 http://www.myopenid.com/server ruby-openid-2.1.8debian.orig/test/data/test_discover/yadis_2entries_idp.xml0000644000175000017500000000117111753312574025624 0ustar paulpaul =!1000 http://specs.openid.net/auth/2.0/signon http://www.myopenid.com/server http://smoker.myopenid.com/ http://specs.openid.net/auth/2.0/server http://www.livejournal.com/openid/server.bml ruby-openid-2.1.8debian.orig/test/data/test_discover/openid_no_delegate.html0000644000175000017500000000046111753312574026015 0ustar paulpaul Identity Page for Smoker

    foo

    ruby-openid-2.1.8debian.orig/test/data/test_discover/openid_1_and_2_xrds.xml0000644000175000017500000000102411753312574025642 0ustar paulpaul http://specs.openid.net/auth/2.0/signon http://openid.net/signon/1.1 http://www.myopenid.com/server http://smoker.myopenid.com/ http://smoker.myopenid.com/ ruby-openid-2.1.8debian.orig/test/data/test_discover/yadis_2_bad_local_id.xml0000644000175000017500000000073111753312574026033 0ustar paulpaul http://specs.openid.net/auth/2.0/signon http://www.myopenid.com/server http://smoker.myopenid.com/ http://localid.mismatch.invalid/ ruby-openid-2.1.8debian.orig/test/data/test_discover/openid_utf8.html0000644000175000017500000000060311753312574024433 0ustar paulpaul Identity Page for Smoker

    ã“ã‚“ã«ã¡ã¯

    ruby-openid-2.1.8debian.orig/test/data/test_discover/openid.html0000644000175000017500000000056711753312574023476 0ustar paulpaul Identity Page for Smoker

    foo

    ruby-openid-2.1.8debian.orig/test/data/test_discover/openid2.html0000644000175000017500000000057311753312574023555 0ustar paulpaul Identity Page for Smoker

    foo

    ruby-openid-2.1.8debian.orig/test/data/test_discover/malformed_meta_tag.html0000644000175000017500000000064711753312574026026 0ustar paulpaul <link rel="openid.server" href="http://www.myopenid.com/server" /> <link rel="openid.delegate" href="http://user.myopenid.com/" /> <link rel="openid2.local_id" href="http://user.myopenid.com/" /> <link rel="openid2.provider" href="http://www.myopenid.com/server" /> <meta http-equiv="X-XRDS-Location" http://www.myopenid.com/xrds?username=user.myopenid.com" /> </head> <body> </body> </html> �����������������������������������������������������������������������������������������ruby-openid-2.1.8debian.orig/test/data/test_discover/openid_and_yadis.html��������������������������0000644�0001750�0001750�00000000711�11753312574�025500� 0����������������������������������������������������������������������������������������������������ustar �paul����������������������������paul�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Identity Page for Smoker

    foo

    ruby-openid-2.1.8debian.orig/test/data/test_discover/yadis_2entries_delegate.xml0000644000175000017500000000125411753312574026624 0ustar paulpaul =!1000 http://openid.net/signon/1.0 http://www.myopenid.com/server http://smoker.myopenid.com/ http://openid.net/signon/1.0 http://www.livejournal.com/openid/server.bml http://frank.livejournal.com/ ruby-openid-2.1.8debian.orig/test/data/test_discover/yadis_idp_delegate.xml0000644000175000017500000000064011753312574025643 0ustar paulpaul http://specs.openid.net/auth/2.0/server http://www.myopenid.com/server http://smoker.myopenid.com/ ruby-openid-2.1.8debian.orig/test/data/test_discover/openid2_xrds.xml0000644000175000017500000000055011753312574024444 0ustar paulpaul http://specs.openid.net/auth/2.0/signon http://www.myopenid.com/server http://smoker.myopenid.com/ ruby-openid-2.1.8debian.orig/test/data/test_discover/yadis_another_delegate.xml0000644000175000017500000000064211753312574026531 0ustar paulpaul http://openid.net/signon/1.0 http://vroom.unittest/server http://smoker.myopenid.com/ ruby-openid-2.1.8debian.orig/test/data/test_discover/openid_1_and_2.html0000644000175000017500000000063111753312574024751 0ustar paulpaul Identity Page for Smoker

    foo

    ruby-openid-2.1.8debian.orig/test/data/test_discover/openid2_xrds_no_local_id.xml0000644000175000017500000000046311753312574026771 0ustar paulpaul http://specs.openid.net/auth/2.0/signon http://www.myopenid.com/server ruby-openid-2.1.8debian.orig/test/data/test_discover/yadis_idp.xml0000644000175000017500000000055111753312574024012 0ustar paulpaul http://specs.openid.net/auth/2.0/server http://www.myopenid.com/server ruby-openid-2.1.8debian.orig/test/data/test1-parsehtml.txt0000644000175000017500000000670211753312574022250 0ustar paulpaulfound found found found found found found found None " end end end ruby-openid-2.1.8debian.orig/lib/openid/message.rb0000644000175000017500000004015111753312574020553 0ustar paulpaulrequire 'openid/util' require 'openid/kvform' module OpenID IDENTIFIER_SELECT = 'http://specs.openid.net/auth/2.0/identifier_select' # URI for Simple Registration extension, the only commonly deployed # OpenID 1.x extension, and so a special case. SREG_URI = 'http://openid.net/sreg/1.0' # The OpenID 1.x namespace URIs OPENID1_NS = 'http://openid.net/signon/1.0' OPENID11_NS = 'http://openid.net/signon/1.1' OPENID1_NAMESPACES = [OPENID1_NS, OPENID11_NS] # The OpenID 2.0 namespace URI OPENID2_NS = 'http://specs.openid.net/auth/2.0' # The namespace consisting of pairs with keys that are prefixed with # "openid." but not in another namespace. NULL_NAMESPACE = :null_namespace # The null namespace, when it is an allowed OpenID namespace OPENID_NS = :openid_namespace # The top-level namespace, excluding all pairs with keys that start # with "openid." BARE_NS = :bare_namespace # Limit, in bytes, of identity provider and return_to URLs, # including response payload. See OpenID 1.1 specification, # Appendix D. OPENID1_URL_LIMIT = 2047 # All OpenID protocol fields. Used to check namespace aliases. OPENID_PROTOCOL_FIELDS = [ 'ns', 'mode', 'error', 'return_to', 'contact', 'reference', 'signed', 'assoc_type', 'session_type', 'dh_modulus', 'dh_gen', 'dh_consumer_public', 'claimed_id', 'identity', 'realm', 'invalidate_handle', 'op_endpoint', 'response_nonce', 'sig', 'assoc_handle', 'trust_root', 'openid', ] # Sentinel used for Message implementation to indicate that getArg # should raise an exception instead of returning a default. NO_DEFAULT = :no_default # Raised if the generic OpenID namespace is accessed when there # is no OpenID namespace set for this message. class UndefinedOpenIDNamespace < Exception; end # Raised when an alias or namespace URI has already been registered. class NamespaceAliasRegistrationError < Exception; end # Raised if openid.ns is not a recognized value. # See Message class variable @@allowed_openid_namespaces class InvalidOpenIDNamespace < Exception; end class Message attr_reader :namespaces # Raised when key lookup fails class KeyNotFound < IndexError ; end # Namespace / alias registration map. See # register_namespace_alias. @@registered_aliases = {} # Registers a (namespace URI, alias) mapping in a global namespace # alias map. Raises NamespaceAliasRegistrationError if either the # namespace URI or alias has already been registered with a # different value. This function is required if you want to use a # namespace with an OpenID 1 message. def Message.register_namespace_alias(namespace_uri, alias_) if @@registered_aliases[alias_] == namespace_uri return end if @@registered_aliases.values.include?(namespace_uri) raise NamespaceAliasRegistrationError, 'Namespace uri #{namespace_uri} already registered' end if @@registered_aliases.member?(alias_) raise NamespaceAliasRegistrationError, 'Alias #{alias_} already registered' end @@registered_aliases[alias_] = namespace_uri end @@allowed_openid_namespaces = [OPENID1_NS, OPENID2_NS, OPENID11_NS] # Raises InvalidNamespaceError if you try to instantiate a Message # with a namespace not in the above allowed list def initialize(openid_namespace=nil) @args = {} @namespaces = NamespaceMap.new if openid_namespace implicit = OPENID1_NAMESPACES.member? openid_namespace self.set_openid_namespace(openid_namespace, implicit) else @openid_ns_uri = nil end end # Construct a Message containing a set of POST arguments. # Raises InvalidNamespaceError if you try to instantiate a Message # with a namespace not in the above allowed list def Message.from_post_args(args) m = Message.new openid_args = {} args.each do |key,value| if value.is_a?(Array) raise ArgumentError, "Query dict must have one value for each key, " + "not lists of values. Query is #{args.inspect}" end prefix, rest = key.split('.', 2) if prefix != 'openid' or rest.nil? m.set_arg(BARE_NS, key, value) else openid_args[rest] = value end end m._from_openid_args(openid_args) return m end # Construct a Message from a parsed KVForm message. # Raises InvalidNamespaceError if you try to instantiate a Message # with a namespace not in the above allowed list def Message.from_openid_args(openid_args) m = Message.new m._from_openid_args(openid_args) return m end # Raises InvalidNamespaceError if you try to instantiate a Message # with a namespace not in the above allowed list def _from_openid_args(openid_args) ns_args = [] # resolve namespaces openid_args.each { |rest, value| ns_alias, ns_key = rest.split('.', 2) if ns_key.nil? ns_alias = NULL_NAMESPACE ns_key = rest end if ns_alias == 'ns' @namespaces.add_alias(value, ns_key) elsif ns_alias == NULL_NAMESPACE and ns_key == 'ns' set_openid_namespace(value, false) else ns_args << [ns_alias, ns_key, value] end } # implicitly set an OpenID 1 namespace unless get_openid_namespace set_openid_namespace(OPENID1_NS, true) end # put the pairs into the appropriate namespaces ns_args.each { |ns_alias, ns_key, value| ns_uri = @namespaces.get_namespace_uri(ns_alias) unless ns_uri ns_uri = _get_default_namespace(ns_alias) unless ns_uri ns_uri = get_openid_namespace ns_key = "#{ns_alias}.#{ns_key}" else @namespaces.add_alias(ns_uri, ns_alias, true) end end self.set_arg(ns_uri, ns_key, value) } end def _get_default_namespace(mystery_alias) # only try to map an alias to a default if it's an # OpenID 1.x namespace if is_openid1 @@registered_aliases[mystery_alias] end end def set_openid_namespace(openid_ns_uri, implicit) if !@@allowed_openid_namespaces.include?(openid_ns_uri) raise InvalidOpenIDNamespace, "Invalid null namespace: #{openid_ns_uri}" end @namespaces.add_alias(openid_ns_uri, NULL_NAMESPACE, implicit) @openid_ns_uri = openid_ns_uri end def get_openid_namespace return @openid_ns_uri end def is_openid1 return OPENID1_NAMESPACES.member?(@openid_ns_uri) end def is_openid2 return @openid_ns_uri == OPENID2_NS end # Create a message from a KVForm string def Message.from_kvform(kvform_string) return Message.from_openid_args(Util.kv_to_dict(kvform_string)) end def copy return Marshal.load(Marshal.dump(self)) end # Return all arguments with "openid." in from of namespaced arguments. def to_post_args args = {} # add namespace defs to the output @namespaces.each { |ns_uri, ns_alias| if @namespaces.implicit?(ns_uri) next end if ns_alias == NULL_NAMESPACE ns_key = 'openid.ns' else ns_key = 'openid.ns.' + ns_alias end args[ns_key] = ns_uri } @args.each { |k, value| ns_uri, ns_key = k key = get_key(ns_uri, ns_key) args[key] = value } return args end # Return all namespaced arguments, failing if any non-namespaced arguments # exist. def to_args post_args = self.to_post_args kvargs = {} post_args.each { |k,v| if !k.starts_with?('openid.') raise ArgumentError, "This message can only be encoded as a POST, because it contains arguments that are not prefixed with 'openid.'" else kvargs[k[7..-1]] = v end } return kvargs end # Generate HTML form markup that contains the values in this # message, to be HTTP POSTed as x-www-form-urlencoded UTF-8. def to_form_markup(action_url, form_tag_attrs=nil, submit_text='Continue') form_tag_attr_map = {} if form_tag_attrs form_tag_attrs.each { |name, attr| form_tag_attr_map[name] = attr } end form_tag_attr_map['action'] = action_url form_tag_attr_map['method'] = 'post' form_tag_attr_map['accept-charset'] = 'UTF-8' form_tag_attr_map['enctype'] = 'application/x-www-form-urlencoded' markup = "
    #{key} not in this message" else default end } end # Get the arguments that are defined for this namespace URI. def get_args(namespace) namespace = _fix_ns(namespace) args = {} @args.each { |k,v| pair_ns, ns_key = k args[ns_key] = v if pair_ns == namespace } return args end # Set multiple key/value pairs in one call. def update_args(namespace, updates) namespace = _fix_ns(namespace) updates.each {|k,v| set_arg(namespace, k, v)} end # Set a single argument in this namespace def set_arg(namespace, key, value) namespace = _fix_ns(namespace) @args[[namespace, key].freeze] = value if namespace != BARE_NS @namespaces.add(namespace) end end # Remove a single argument from this namespace. def del_arg(namespace, key) namespace = _fix_ns(namespace) _key = [namespace, key] @args.delete(_key) end def ==(other) other.is_a?(self.class) && @args == other.instance_eval { @args } end def get_aliased_arg(aliased_key, default=nil) if aliased_key == 'ns' return get_openid_namespace() end ns_alias, key = aliased_key.split('.', 2) if ns_alias == 'ns' uri = @namespaces.get_namespace_uri(key) if uri.nil? and default == NO_DEFAULT raise KeyNotFound, "Namespace #{key} not defined when looking "\ "for #{aliased_key}" else return (uri.nil? ? default : uri) end end if key.nil? key = aliased_key ns = nil else ns = @namespaces.get_namespace_uri(ns_alias) end if ns.nil? key = aliased_key ns = get_openid_namespace end return get_arg(ns, key, default) end end # Maintains a bidirectional map between namespace URIs and aliases. class NamespaceMap def initialize @alias_to_namespace = {} @namespace_to_alias = {} @implicit_namespaces = [] end def get_alias(namespace_uri) @namespace_to_alias[namespace_uri] end def get_namespace_uri(namespace_alias) @alias_to_namespace[namespace_alias] end # Add an alias from this namespace URI to the alias. def add_alias(namespace_uri, desired_alias, implicit=false) # Check that desired_alias is not an openid protocol field as # per the spec. Util.assert(!OPENID_PROTOCOL_FIELDS.include?(desired_alias), "#{desired_alias} is not an allowed namespace alias") # check that there is not a namespace already defined for the # desired alias current_namespace_uri = @alias_to_namespace.fetch(desired_alias, nil) if current_namespace_uri and current_namespace_uri != namespace_uri raise IndexError, "Cannot map #{namespace_uri} to alias #{desired_alias}. #{current_namespace_uri} is already mapped to alias #{desired_alias}" end # Check that desired_alias does not contain a period as per the # spec. if desired_alias.is_a?(String) Util.assert(desired_alias.index('.').nil?, "#{desired_alias} must not contain a dot") end # check that there is not already a (different) alias for this # namespace URI. _alias = @namespace_to_alias[namespace_uri] if _alias and _alias != desired_alias raise IndexError, "Cannot map #{namespace_uri} to alias #{desired_alias}. It is already mapped to alias #{_alias}" end @alias_to_namespace[desired_alias] = namespace_uri @namespace_to_alias[namespace_uri] = desired_alias @implicit_namespaces << namespace_uri if implicit return desired_alias end # Add this namespace URI to the mapping, without caring what alias # it ends up with. def add(namespace_uri) # see if this namepace is already mapped to an alias _alias = @namespace_to_alias[namespace_uri] return _alias if _alias # Fall back to generating a numberical alias i = 0 while true _alias = 'ext' + i.to_s begin add_alias(namespace_uri, _alias) rescue IndexError i += 1 else return _alias end end raise StandardError, 'Unreachable' end def member?(namespace_uri) @namespace_to_alias.has_key?(namespace_uri) end def each @namespace_to_alias.each {|k,v| yield k,v} end def namespace_uris # Return an iterator over the namespace URIs return @namespace_to_alias.keys() end def implicit?(namespace_uri) return @implicit_namespaces.member?(namespace_uri) end def aliases # Return an iterator over the aliases return @alias_to_namespace.keys() end end end ruby-openid-2.1.8debian.orig/lib/openid/consumer/0000755000175000017500000000000011753312574020434 5ustar paulpaulruby-openid-2.1.8debian.orig/lib/openid/consumer/html_parse.rb0000644000175000017500000000576511753312574023134 0ustar paulpaulrequire "openid/yadis/htmltokenizer" module OpenID # Stuff to remove before we start looking for tags REMOVED_RE = / # Comments # CDATA blocks | # script blocks | ]*>.*?<\/script> /mix 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 ruby-openid-2.1.8debian.orig/lib/openid/consumer/checkid_request.rb0000644000175000017500000001617211753312574024132 0ustar paulpaulrequire "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 ruby-openid-2.1.8debian.orig/lib/openid/consumer/discovery_manager.rb0000644000175000017500000000520411753312574024463 0ustar paulpaulmodule 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 ruby-openid-2.1.8debian.orig/lib/openid/consumer/discovery.rb0000644000175000017500000003615611753312574023003 0ustar paulpaul# 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 # # @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_xri(xri) # Normalize an XRI, stripping its scheme if present m = /^xri:\/\/(.*)/.match(xri) xri = m[1] if m return xri 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 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. # 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 = [] iname = self.normalize_xri(iname) begin canonical_id, services = Yadis::XRI::ProxyResolver.new().query( iname ) 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 ruby-openid-2.1.8debian.orig/lib/openid/consumer/responses.rb0000644000175000017500000001064711753312574023012 0ustar paulpaulmodule 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 signed?(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 ruby-openid-2.1.8debian.orig/lib/openid/consumer/idres.rb0000644000175000017500000004474711753312574022107 0ustar paulpaulrequire "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', 'op_endpoint'] when OPENID1_NS, OPENID11_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, OPENID11_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, OPENID11_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(to_match.claimed_id) when :xri to_match.claimed_id when :uri begin parsed = URI.parse(to_match.claimed_id) rescue URI::InvalidURIError to_match.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 ruby-openid-2.1.8debian.orig/lib/openid/consumer/associationmanager.rb0000644000175000017500000003142211753312574024632 0ustar paulpaulrequire "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 rescue InvalidOpenIDNamespace Util.log("Server #{@server_url} returned a malformed association " \ "response. Falling back to check_id mode for this request.") return nil 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(OPENID_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 ruby-openid-2.1.8debian.orig/lib/openid/kvpost.rb0000644000175000017500000000317111753312574020456 0ustar paulpaulrequire "openid/message" require "openid/fetchers" module OpenID # Exception that is raised when the server returns a 400 response # code to a direct request. class ServerError < OpenIDError attr_reader :error_text, :error_code, :message def initialize(error_text, error_code, message) super(error_text) @error_text = error_text @error_code = error_code @message = message end def self.from_message(msg) error_text = msg.get_arg(OPENID_NS, 'error', '') error_code = msg.get_arg(OPENID_NS, 'error_code') return self.new(error_text, error_code, msg) end end class KVPostNetworkError < OpenIDError end class HTTPStatusError < OpenIDError end class Message def self.from_http_response(response, server_url) msg = self.from_kvform(response.body) case response.code.to_i when 200 return msg when 206 return msg when 400 raise ServerError.from_message(msg) else error_message = "bad status code from server #{server_url}: "\ "#{response.code}" raise HTTPStatusError.new(error_message) end end end # Send the message to the server via HTTP POST and receive and parse # a response in KV Form def self.make_kv_post(request_message, server_url) begin http_response = self.fetch(server_url, request_message.to_url_encoded) rescue Exception raise KVPostNetworkError.new("Unable to contact OpenID server: #{$!.to_s}") end return Message.from_http_response(http_response, server_url) end end ruby-openid-2.1.8debian.orig/lib/openid/store/0000755000175000017500000000000011753312574017735 5ustar paulpaulruby-openid-2.1.8debian.orig/lib/openid/store/filesystem.rb0000644000175000017500000001621711753312574022455 0ustar paulpaulrequire 'fileutils' require 'pathname' require 'tempfile' require 'openid/util' require 'openid/store/interface' require 'openid/association' module OpenID module Store class Filesystem < Interface @@FILENAME_ALLOWED = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-".split("") # Create a Filesystem store instance, putting all data in +directory+. def initialize(directory) p_dir = Pathname.new(directory) @nonce_dir = p_dir.join('nonces') @association_dir = p_dir.join('associations') @temp_dir = p_dir.join('temp') self.ensure_dir(@nonce_dir) self.ensure_dir(@association_dir) self.ensure_dir(@temp_dir) end # Create a unique filename for a given server url and handle. The # filename that is returned will contain the domain name from the # server URL for ease of human inspection of the data dir. def get_association_filename(server_url, handle) unless server_url.index('://') raise ArgumentError, "Bad server URL: #{server_url}" end proto, rest = server_url.split('://', 2) domain = filename_escape(rest.split('/',2)[0]) url_hash = safe64(server_url) if handle handle_hash = safe64(handle) else handle_hash = '' end filename = [proto,domain,url_hash,handle_hash].join('-') @association_dir.join(filename) end # Store an association in the assoc directory def store_association(server_url, association) assoc_s = association.serialize filename = get_association_filename(server_url, association.handle) f, tmp = mktemp begin begin f.write(assoc_s) f.fsync ensure f.close end begin File.rename(tmp, filename) rescue Errno::EEXIST begin File.unlink(filename) rescue Errno::ENOENT # do nothing end File.rename(tmp, filename) end rescue self.remove_if_present(tmp) raise end end # Retrieve an association def get_association(server_url, handle=nil) # the filename with empty handle is the prefix for the associations # for a given server url filename = get_association_filename(server_url, handle) if handle return _get_association(filename) end assoc_filenames = Dir.glob(filename.to_s + '*') assocs = assoc_filenames.collect do |f| _get_association(f) end assocs = assocs.find_all { |a| not a.nil? } assocs = assocs.sort_by { |a| a.issued } return nil if assocs.empty? return assocs[-1] end def _get_association(filename) begin assoc_file = File.open(filename, "r") rescue Errno::ENOENT return nil else begin assoc_s = assoc_file.read ensure assoc_file.close end begin association = Association.deserialize(assoc_s) rescue self.remove_if_present(filename) return nil end # clean up expired associations if association.expires_in == 0 self.remove_if_present(filename) return nil else return association end end end # Remove an association if it exists, otherwise do nothing. def remove_association(server_url, handle) assoc = get_association(server_url, handle) if assoc.nil? return false else filename = get_association_filename(server_url, handle) return self.remove_if_present(filename) end end # Return whether the nonce is valid def use_nonce(server_url, timestamp, salt) return false if (timestamp - Time.now.to_i).abs > Nonce.skew if server_url and !server_url.empty? proto, rest = server_url.split('://',2) else proto, rest = '','' end raise "Bad server URL" unless proto && rest domain = filename_escape(rest.split('/',2)[0]) url_hash = safe64(server_url) salt_hash = safe64(salt) nonce_fn = '%08x-%s-%s-%s-%s'%[timestamp, proto, domain, url_hash, salt_hash] filename = @nonce_dir.join(nonce_fn) begin fd = File.new(filename, File::CREAT | File::EXCL | File::WRONLY, 0200) fd.close return true rescue Errno::EEXIST return false end end # Remove expired entries from the database. This is potentially expensive, # so only run when it is acceptable to take time. def cleanup cleanup_associations cleanup_nonces end def cleanup_associations association_filenames = Dir[@association_dir.join("*").to_s] count = 0 association_filenames.each do |af| begin f = File.open(af, 'r') rescue Errno::ENOENT next else begin assoc_s = f.read ensure f.close end begin association = OpenID::Association.deserialize(assoc_s) rescue StandardError self.remove_if_present(af) next else if association.expires_in == 0 self.remove_if_present(af) count += 1 end end end end return count end def cleanup_nonces nonces = Dir[@nonce_dir.join("*").to_s] now = Time.now.to_i count = 0 nonces.each do |filename| nonce = filename.split('/')[-1] timestamp = nonce.split('-', 2)[0].to_i(16) nonce_age = (timestamp - now).abs if nonce_age > Nonce.skew self.remove_if_present(filename) count += 1 end end return count end protected # Create a temporary file and return the File object and filename. def mktemp f = Tempfile.new('tmp', @temp_dir) [f, f.path] end # create a safe filename from a url def filename_escape(s) s = '' if s.nil? filename_chunks = [] s.split('').each do |c| if @@FILENAME_ALLOWED.index(c) filename_chunks << c else filename_chunks << sprintf("_%02X", c[0]) end end filename_chunks.join("") end def safe64(s) s = OpenID::CryptUtil.sha1(s) s = OpenID::Util.to_base64(s) s.gsub!('+', '_') s.gsub!('/', '.') s.gsub!('=', '') return s end # remove file if present in filesystem def remove_if_present(filename) begin File.unlink(filename) rescue Errno::ENOENT return false end return true end # ensure that a path exists def ensure_dir(dir_name) FileUtils::mkdir_p(dir_name) end end end end ruby-openid-2.1.8debian.orig/lib/openid/store/memory.rb0000644000175000017500000000411111753312574021567 0ustar paulpaulrequire 'openid/store/interface' module OpenID module Store # An in-memory implementation of Store. This class is mainly used # for testing, though it may be useful for long-running single # process apps. Note that this store is NOT thread-safe. # # You should probably be looking at OpenID::Store::Filesystem class Memory < Interface def initialize @associations = {} @associations.default = {} @nonces = {} end def store_association(server_url, assoc) assocs = @associations[server_url] @associations[server_url] = assocs.merge({assoc.handle => deepcopy(assoc)}) end def get_association(server_url, handle=nil) assocs = @associations[server_url] assoc = nil if handle assoc = assocs[handle] else assoc = assocs.values.sort{|a,b| a.issued <=> b.issued}[-1] end return assoc end def remove_association(server_url, handle) assocs = @associations[server_url] if assocs.delete(handle) return true else return false end end def use_nonce(server_url, timestamp, salt) return false if (timestamp - Time.now.to_i).abs > Nonce.skew nonce = [server_url, timestamp, salt].join('') return false if @nonces[nonce] @nonces[nonce] = timestamp return true end def cleanup_associations count = 0 @associations.each{|server_url, assocs| assocs.each{|handle, assoc| if assoc.expires_in == 0 assocs.delete(handle) count += 1 end } } return count end def cleanup_nonces count = 0 now = Time.now.to_i @nonces.each{|nonce, timestamp| if (timestamp - now).abs > Nonce.skew @nonces.delete(nonce) count += 1 end } return count end protected def deepcopy(o) Marshal.load(Marshal.dump(o)) end end end end ruby-openid-2.1.8debian.orig/lib/openid/store/memcache.rb0000644000175000017500000000666111753312574022035 0ustar paulpaulrequire 'openid/util' require 'openid/store/interface' require 'openid/store/nonce' require 'time' module OpenID module Store class Memcache < Interface attr_accessor :key_prefix def initialize(cache_client, key_prefix='openid-store:') @cache_client = cache_client self.key_prefix = key_prefix end # Put a Association object into storage. # When implementing a store, don't assume that there are any limitations # on the character set of the server_url. In particular, expect to see # unescaped non-url-safe characters in the server_url field. def store_association(server_url, association) serialized = serialize(association) [nil, association.handle].each do |handle| key = assoc_key(server_url, handle) @cache_client.set(key, serialized, expiry(association.lifetime)) end end # Returns a Association object from storage that matches # the server_url. Returns nil if no such association is found or if # the one matching association is expired. (Is allowed to GC expired # associations when found.) def get_association(server_url, handle=nil) serialized = @cache_client.get(assoc_key(server_url, handle)) if serialized return deserialize(serialized) else return nil end end # If there is a matching association, remove it from the store and # return true, otherwise return false. def remove_association(server_url, handle) deleted = delete(assoc_key(server_url, handle)) server_assoc = get_association(server_url) if server_assoc && server_assoc.handle == handle deleted = delete(assoc_key(server_url)) | deleted end return deleted end # Return true if the nonce has not been used before, and store it # for a while to make sure someone doesn't try to use the same value # again. Return false if the nonce has already been used or if the # timestamp is not current. # You can use OpenID::Store::Nonce::SKEW for your timestamp window. # server_url: URL of the server from which the nonce originated # timestamp: time the nonce was created in seconds since unix epoch # salt: A random string that makes two nonces issued by a server in # the same second unique def use_nonce(server_url, timestamp, salt) return false if (timestamp - Time.now.to_i).abs > Nonce.skew ts = timestamp.to_s # base 10 seconds since epoch nonce_key = key_prefix + 'N' + server_url + '|' + ts + '|' + salt result = @cache_client.add(nonce_key, '', expiry(Nonce.skew + 5)) return !!(result =~ /^STORED/) end def assoc_key(server_url, assoc_handle=nil) key = key_prefix + 'A' + server_url if assoc_handle key += '|' + assoc_handle end return key end def cleanup_nonces end def cleanup end def cleanup_associations end protected def delete(key) result = @cache_client.delete(key) return !!(result =~ /^DELETED/) end def serialize(assoc) Marshal.dump(assoc) end def deserialize(assoc_str) Marshal.load(assoc_str) end # Convert a lifetime in seconds into a memcache expiry value def expiry(t) Time.now.to_i + t end end end end ruby-openid-2.1.8debian.orig/lib/openid/store/interface.rb0000644000175000017500000000575511753312574022236 0ustar paulpaulrequire 'openid/util' module OpenID # Stores for Associations and nonces. Used by both the Consumer and # the Server. If you have a database abstraction layer or other # state storage in your application or framework already, you can # implement the store interface. module Store # Abstract Store # Changes in 2.0: # * removed store_nonce, get_auth_key, is_dumb # * changed use_nonce to support one-way nonces # * added cleanup_nonces, cleanup_associations, cleanup class Interface < Object # Put a Association object into storage. # When implementing a store, don't assume that there are any limitations # on the character set of the server_url. In particular, expect to see # unescaped non-url-safe characters in the server_url field. def store_association(server_url, association) raise NotImplementedError end # Returns a Association object from storage that matches # the server_url. Returns nil if no such association is found or if # the one matching association is expired. (Is allowed to GC expired # associations when found.) def get_association(server_url, handle=nil) raise NotImplementedError end # If there is a matching association, remove it from the store and # return true, otherwise return false. def remove_association(server_url, handle) raise NotImplementedError end # Return true if the nonce has not been used before, and store it # for a while to make sure someone doesn't try to use the same value # again. Return false if the nonce has already been used or if the # timestamp is not current. # You can use OpenID::Store::Nonce::SKEW for your timestamp window. # server_url: URL of the server from which the nonce originated # timestamp: time the nonce was created in seconds since unix epoch # salt: A random string that makes two nonces issued by a server in # the same second unique def use_nonce(server_url, timestamp, salt) raise NotImplementedError end # Remove expired nonces from the store # Discards any nonce that is old enough that it wouldn't pass use_nonce # Not called during normal library operation, this method is for store # admins to keep their storage from filling up with expired data def cleanup_nonces raise NotImplementedError end # Remove expired associations from the store # Not called during normal library operation, this method is for store # admins to keep their storage from filling up with expired data def cleanup_associations raise NotImplementedError end # Remove expired nonces and associations from the store # Not called during normal library operation, this method is for store # admins to keep their storage from filling up with expired data def cleanup return cleanup_nonces, cleanup_associations end end end end ruby-openid-2.1.8debian.orig/lib/openid/store/nonce.rb0000644000175000017500000000367011753312574021372 0ustar paulpaulrequire 'openid/cryptutil' require 'date' require 'time' module OpenID module Nonce DEFAULT_SKEW = 60*60*5 TIME_FMT = '%Y-%m-%dT%H:%M:%SZ' TIME_STR_LEN = '0000-00-00T00:00:00Z'.size @@NONCE_CHRS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" TIME_VALIDATOR = /\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ/ @skew = DEFAULT_SKEW # The allowed nonce time skew in seconds. Defaults to 5 hours. # Used for checking nonce validity, and by stores' cleanup methods. def Nonce.skew @skew end def Nonce.skew=(new_skew) @skew = new_skew end # Extract timestamp from a nonce string def Nonce.split_nonce(nonce_str) timestamp_str = nonce_str[0...TIME_STR_LEN] raise ArgumentError if timestamp_str.size < TIME_STR_LEN raise ArgumentError unless timestamp_str.match(TIME_VALIDATOR) ts = Time.parse(timestamp_str).to_i raise ArgumentError if ts < 0 return ts, nonce_str[TIME_STR_LEN..-1] end # Is the timestamp that is part of the specified nonce string # within the allowed clock-skew of the current time? def Nonce.check_timestamp(nonce_str, allowed_skew=nil, now=nil) allowed_skew = skew if allowed_skew.nil? begin stamp, foo = split_nonce(nonce_str) rescue ArgumentError # bad timestamp return false end now = Time.now.to_i unless now # times before this are too old past = now - allowed_skew # times newer than this are too far in the future future = now + allowed_skew return (past <= stamp and stamp <= future) end # generate a nonce with the specified timestamp (defaults to now) def Nonce.mk_nonce(time = nil) salt = CryptUtil::random_string(6, @@NONCE_CHRS) if time.nil? t = Time.now.getutc else t = Time.at(time).getutc end time_str = t.strftime(TIME_FMT) return time_str + salt end end end ruby-openid-2.1.8debian.orig/lib/openid/trustroot.rb0000644000175000017500000002515211753312574021220 0ustar paulpaulrequire 'uri' require 'openid/urinorm' module OpenID class RealmVerificationRedirected < Exception # Attempting to verify this realm resulted in a redirect. def initialize(relying_party_url, rp_url_after_redirects) @relying_party_url = relying_party_url @rp_url_after_redirects = rp_url_after_redirects end def to_s return "Attempting to verify #{@relying_party_url} resulted in " + "redirect to #{@rp_url_after_redirects}" end end module TrustRoot TOP_LEVEL_DOMAINS = %w' ac ad ae aero af ag ai al am an ao aq ar arpa as asia at au aw ax az ba bb bd be bf bg bh bi biz bj bm bn bo br bs bt bv bw by bz ca cat cc cd cf cg ch ci ck cl cm cn co com coop cr cu cv cx cy cz de dj dk dm do dz ec edu ee eg er es et eu fi fj fk fm fo fr ga gb gd ge gf gg gh gi gl gm gn gov gp gq gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in info int io iq ir is it je jm jo jobs jp ke kg kh ki km kn kp kr kw ky kz la lb lc li lk lr ls lt lu lv ly ma mc md me mg mh mil mk ml mm mn mo mobi mp mq mr ms mt mu museum mv mw mx my mz na name nc ne net nf ng ni nl no np nr nu nz om org pa pe pf pg ph pk pl pm pn pr pro ps pt pw py qa re ro rs ru rw sa sb sc sd se sg sh si sj sk sl sm sn so sr st su sv sy sz tc td tel tf tg th tj tk tl tm tn to tp tr travel tt tv tw tz ua ug uk us uy uz va vc ve vg vi vn vu wf ws xn--0zwm56d xn--11b5bs3a9aj6g xn--80akhbyknj4f xn--9t4b11yi5a xn--deba0ad xn--g6w251d xn--hgbk6aj7f53bba xn--hlcj6aya9esc7a xn--jxalpdlp xn--kgbechtv xn--zckzah ye yt yu za zm zw' ALLOWED_PROTOCOLS = ['http', 'https'] # The URI for relying party discovery, used in realm verification. # # XXX: This should probably live somewhere else (like in # OpenID or OpenID::Yadis somewhere) RP_RETURN_TO_URL_TYPE = 'http://specs.openid.net/auth/2.0/return_to' # If the endpoint is a relying party OpenID return_to endpoint, # return the endpoint URL. Otherwise, return None. # # This function is intended to be used as a filter for the Yadis # filtering interface. # # endpoint: An XRDS BasicServiceEndpoint, as returned by # performing Yadis dicovery. # # returns the endpoint URL or None if the endpoint is not a # relying party endpoint. def TrustRoot._extract_return_url(endpoint) if endpoint.matchTypes([RP_RETURN_TO_URL_TYPE]) return endpoint.uri else return nil end end # Is the return_to URL under one of the supplied allowed # return_to URLs? def TrustRoot.return_to_matches(allowed_return_to_urls, return_to) allowed_return_to_urls.each { |allowed_return_to| # A return_to pattern works the same as a realm, except that # it's not allowed to use a wildcard. We'll model this by # parsing it as a realm, and not trying to match it if it has # a wildcard. return_realm = TrustRoot.parse(allowed_return_to) if (# Parses as a trust root !return_realm.nil? and # Does not have a wildcard !return_realm.wildcard and # Matches the return_to that we passed in with it return_realm.validate_url(return_to) ) return true end } # No URL in the list matched return false end # Given a relying party discovery URL return a list of return_to # URLs. def TrustRoot.get_allowed_return_urls(relying_party_url) rp_url_after_redirects, return_to_urls = services.get_service_endpoints( relying_party_url, _extract_return_url) if rp_url_after_redirects != relying_party_url # Verification caused a redirect raise RealmVerificationRedirected.new( relying_party_url, rp_url_after_redirects) end return return_to_urls end # Verify that a return_to URL is valid for the given realm. # # This function builds a discovery URL, performs Yadis discovery # on it, makes sure that the URL does not redirect, parses out # the return_to URLs, and finally checks to see if the current # return_to URL matches the return_to. # # raises DiscoveryFailure when Yadis discovery fails returns # true if the return_to URL is valid for the realm def TrustRoot.verify_return_to(realm_str, return_to, _vrfy=nil) # _vrfy parameter is there to make testing easier if _vrfy.nil? _vrfy = self.method('get_allowed_return_urls') end if !(_vrfy.is_a?(Proc) or _vrfy.is_a?(Method)) raise ArgumentError, "_vrfy must be a Proc or Method" end realm = TrustRoot.parse(realm_str) if realm.nil? # The realm does not parse as a URL pattern return false end begin allowable_urls = _vrfy.call(realm.build_discovery_url()) rescue RealmVerificationRedirected => err Util.log(err.to_s) return false end if return_to_matches(allowable_urls, return_to) return true else Util.log("Failed to validate return_to #{return_to} for " + "realm #{realm_str}, was not in #{allowable_urls}") return false end end class TrustRoot attr_reader :unparsed, :proto, :wildcard, :host, :port, :path @@empty_re = Regexp.new('^http[s]*:\/\/\*\/$') def TrustRoot._build_path(path, query=nil, frag=nil) s = path.dup frag = nil if frag == '' query = nil if query == '' if query s << "?" << query end if frag s << "#" << frag end return s end def TrustRoot._parse_url(url) begin url = URINorm.urinorm(url) rescue URI::InvalidURIError => err nil end begin parsed = URI::parse(url) rescue URI::InvalidURIError return nil end path = TrustRoot._build_path(parsed.path, parsed.query, parsed.fragment) return [parsed.scheme || '', parsed.host || '', parsed.port || '', path || ''] end def TrustRoot.parse(trust_root) trust_root = trust_root.dup unparsed = trust_root.dup # look for wildcard wildcard = (not trust_root.index('://*.').nil?) trust_root.sub!('*.', '') if wildcard # handle http://*/ case if not wildcard and @@empty_re.match(trust_root) proto = trust_root.split(':')[0] port = proto == 'http' ? 80 : 443 return new(unparsed, proto, true, '', port, '/') end parts = TrustRoot._parse_url(trust_root) return nil if parts.nil? proto, host, port, path = parts # check for URI fragment if path and !path.index('#').nil? return nil end return nil unless ['http', 'https'].member?(proto) return new(unparsed, proto, wildcard, host, port, path) end def TrustRoot.check_sanity(trust_root_string) trust_root = TrustRoot.parse(trust_root_string) if trust_root.nil? return false else return trust_root.sane? end end # quick func for validating a url against a trust root. See the # TrustRoot class if you need more control. def self.check_url(trust_root, url) tr = self.parse(trust_root) return (!tr.nil? and tr.validate_url(url)) end # Return a discovery URL for this realm. # # This function does not check to make sure that the realm is # valid. Its behaviour on invalid inputs is undefined. # # return_to:: The relying party return URL of the OpenID # authentication request # # Returns the URL upon which relying party discovery should be # run in order to verify the return_to URL def build_discovery_url if self.wildcard # Use "www." in place of the star www_domain = 'www.' + @host port = (!@port.nil? and ![80, 443].member?(@port)) ? (":" + @port.to_s) : '' return "#{@proto}://#{www_domain}#{port}#{@path}" else return @unparsed end end def initialize(unparsed, proto, wildcard, host, port, path) @unparsed = unparsed @proto = proto @wildcard = wildcard @host = host @port = port @path = path end def sane? return true if @host == 'localhost' host_parts = @host.split('.') # a note: ruby string split does not put an empty string at # the end of the list if the split element is last. for # example, 'foo.com.'.split('.') => ['foo','com']. Mentioned # because the python code differs here. return false if host_parts.length == 0 # no adjacent dots return false if host_parts.member?('') # last part must be a tld tld = host_parts[-1] return false unless TOP_LEVEL_DOMAINS.member?(tld) return false if host_parts.length == 1 if @wildcard if tld.length == 2 and host_parts[-2].length <= 3 # It's a 2-letter tld with a short second to last segment # so there needs to be more than two segments specified # (e.g. *.co.uk is insane) return host_parts.length > 2 end end return true end def validate_url(url) parts = TrustRoot._parse_url(url) return false if parts.nil? proto, host, port, path = parts return false unless proto == @proto return false unless port == @port return false unless host.index('*').nil? if !@wildcard if host != @host return false end elsif ((@host != '') and (!host.ends_with?('.' + @host)) and (host != @host)) return false end if path != @path path_len = @path.length trust_prefix = @path[0...path_len] url_prefix = path[0...path_len] # must be equal up to the length of the path, at least if trust_prefix != url_prefix return false end # These characters must be on the boundary between the end # of the trust root's path and the start of the URL's path. if !@path.index('?').nil? allowed = '&' else allowed = '?/' end return (!allowed.index(@path[-1]).nil? or !allowed.index(path[path_len]).nil?) end return true end end end end