diff --git a/tracks/app/controllers/context_controller.rb b/tracks/app/controllers/context_controller.rb
index de167a9d..ae4c6c83 100644
--- a/tracks/app/controllers/context_controller.rb
+++ b/tracks/app/controllers/context_controller.rb
@@ -86,10 +86,10 @@ class ContextController < ApplicationController
# fallback for standard requests
if @saved
- flash["notice"] = 'Added new next action.'
+ flash[:notice] = 'Added new next action.'
redirect_to :controller => 'todo', :action => 'list'
else
- flash["warning"] = 'The next action was not added. Please try again.'
+ flash[:warning] = 'The next action was not added. Please try again.'
redirect_to :controller => 'todo', :action => 'list'
end
@@ -97,7 +97,7 @@ class ContextController < ApplicationController
if request.xhr? # be sure to include an error.rjs
render :action => 'error'
else
- flash["warning"] = 'An error occurred on the server.'
+ flash[:warning] = 'An error occurred on the server.'
redirect_to :controller => 'todo', :action => 'list'
end
end
@@ -118,9 +118,9 @@ class ContextController < ApplicationController
return if request.xhr?
if @saved
- flash['notice'] = "The action '#{@item.description}' was marked as #{@item.done? ? 'complete' : 'incomplete' }"
+ flash[:notice] = "The action '#{@item.description}' was marked as #{@item.done? ? 'complete' : 'incomplete' }"
else
- flash['notice'] = "The action '#{@item.description}' was NOT marked as #{@item.done? ? 'complete' : 'incomplete' } due to an error on the server."
+ flash[:notice] = "The action '#{@item.description}' was NOT marked as #{@item.done? ? 'complete' : 'incomplete' } due to an error on the server."
end
redirect_to :action => "list"
end
@@ -133,9 +133,9 @@ class ContextController < ApplicationController
@context.attributes = params["context"]
@context.name = deurlize(@context.name)
if @context.save
- render_partial 'context_listing', @context
+ render :partial => 'context_listing', :object => @context
else
- flash["warning"] = "Couldn't update new context"
+ flash[:warning] = "Couldn't update new context"
render :text => ""
end
end
@@ -148,7 +148,7 @@ class ContextController < ApplicationController
if @context.destroy
render_text ""
else
- flash["warning"] = "Couldn't delete context \"#{@context.name}\""
+ flash[:warning] = "Couldn't delete context \"#{@context.name}\""
redirect_to( :controller => "context", :action => "list" )
end
end
@@ -178,7 +178,7 @@ class ContextController < ApplicationController
return @context
else
@context = nil # Should be nil anyway.
- flash["warning"] = "Item and session user mis-match: #{@context.user_id} and #{@user.id}!"
+ flash[:warning] = "Item and session user mis-match: #{@context.user_id} and #{@user.id}!"
render_text ""
end
end
@@ -189,7 +189,7 @@ class ContextController < ApplicationController
return @context
else
@context = nil
- flash["warning"] = "Project and session user mis-match: #{@context.user_id} and #{@user.id}!"
+ flash[:warning] = "Project and session user mis-match: #{@context.user_id} and #{@user.id}!"
render_text ""
end
end
@@ -199,7 +199,7 @@ class ContextController < ApplicationController
if @user == item.user
return item
else
- flash["warning"] = "Item and session user mis-match: #{item.user.name} and #{@user.name}!"
+ flash[:warning] = "Item and session user mis-match: #{item.user.name} and #{@user.name}!"
render_text ""
end
end
diff --git a/tracks/app/controllers/login_controller.rb b/tracks/app/controllers/login_controller.rb
index 3bf88bbe..6d89c35f 100644
--- a/tracks/app/controllers/login_controller.rb
+++ b/tracks/app/controllers/login_controller.rb
@@ -2,7 +2,8 @@ class LoginController < ApplicationController
model :user, :preference
layout 'login'
skip_before_filter :set_session_expiration
-
+ open_id_consumer if Tracks::Config.auth_schemes.include?('open_id')
+
def login
@page_title = "TRACKS::Login"
case request.method
@@ -13,15 +14,68 @@ class LoginController < ApplicationController
# of inactivity
session['noexpiry'] = params['user_noexpiry']
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
- flash['notice'] = "Login successful: session #{msg}"
+ flash[:notice] = "Login successful: session #{msg}"
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year }
redirect_back_or_default :controller => "todo", :action => "index"
else
@login = params['user_login']
- flash['warning'] = "Login unsuccessful"
+ flash[:warning] = "Login unsuccessful"
end
end
end
+
+ def begin
+ # If the URL was unusable (either because of network conditions,
+ # a server error, or that the response returned was not an OpenID
+ # identity page), the library will return HTTP_FAILURE or PARSE_ERROR.
+ # Let the user know that the URL is unusable.
+ case open_id_response.status
+ when OpenID::SUCCESS
+ # The URL was a valid identity URL. Now we just need to send a redirect
+ # to the server using the redirect_url the library created for us.
+
+ # redirect to the server
+ redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), url_for(:action => 'complete'))
+ else
+ flash[:warning] = "Unable to find openid server for #{params[:openid_url]}"
+ redirect_to :action => 'login'
+ end
+ end
+
+ def complete
+ case open_id_response.status
+ when OpenID::FAILURE
+ # In the case of failure, if info is non-nil, it is the
+ # URL that we were verifying. We include it in the error
+ # message to help the user figure out what happened.
+ if open_id_response.identity_url
+ flash[:message] = "Verification of #{open_id_response.identity_url} failed. "
+ else
+ flash[:message] = "Verification failed. "
+ end
+ flash[:message] += open_id_response.msg.to_s
+
+ when OpenID::SUCCESS
+ # Success means that the transaction completed without
+ # error. If info is nil, it means that the user cancelled
+ # the verification.
+ @user = User.find_by_open_id_url(open_id_response.identity_url)
+ unless (@user.nil?)
+ flash[:message] = "You have successfully verified #{open_id_response.identity_url} as your identity."
+ session['user_id'] = @user.id
+ redirect_back_or_default :controller => 'todo', :action => 'index'
+ else
+ flash[:warning] = "You have successfully verified #{open_id_response.identity_url} as your identity, but you do not have a Tracks account. Please ask your administrator to sign you up."
+ end
+
+ when OpenID::CANCEL
+ flash[:message] = "Verification cancelled."
+
+ else
+ flash[:warning] = "Unknown response status: #{open_id_response.status}"
+ end
+ redirect_to :action => 'login' unless performed?
+ end
def signup
if User.find_all.empty? # the first user of the system
@@ -53,7 +107,7 @@ class LoginController < ApplicationController
@user = User.authenticate(user.login, params['user']['password'])
@user.create_preference
@user.save
- flash['notice'] = "Signup successful for user #{@user.login}."
+ flash[:notice] = "Signup successful for user #{@user.login}."
redirect_back_or_default :controller => "todo", :action => "index"
end
end
@@ -70,8 +124,8 @@ class LoginController < ApplicationController
def logout
session['user_id'] = nil
reset_session
- flash['notice'] = "You have been logged out of Tracks."
- redirect_to :controller => "login", :action => "login"
+ flash[:notice] = "You have been logged out of Tracks."
+ redirect_to :action => "login"
end
def check_expiry
diff --git a/tracks/app/controllers/note_controller.rb b/tracks/app/controllers/note_controller.rb
index 964e9f78..8efa2bce 100644
--- a/tracks/app/controllers/note_controller.rb
+++ b/tracks/app/controllers/note_controller.rb
@@ -26,19 +26,19 @@ class NoteController < ApplicationController
note.attributes = params["new_note"]
if note.save
- render_partial 'notes_summary', note
+ render :partial => 'notes_summary', :object => note
else
- render_text ""
+ render :text => ''
end
end
def delete
note = check_user_return_note
if note.destroy
- render_text ""
+ render :text => ''
else
- flash["warning"] = "Couldn't delete note \"#{note.id.to_s}\""
- render_text ""
+ flash[:warning] = "Couldn't delete note \"#{note.id.to_s}\""
+ render :text => ''
end
end
@@ -46,10 +46,10 @@ class NoteController < ApplicationController
note = check_user_return_note
note.attributes = params["note"]
if note.save
- render_partial 'notes', note
+ render :partial => 'notes', :object => note
else
flash["warning"] = "Couldn't update note \"#{note.id.to_s}\""
- render_text ""
+ render :text => ''
end
end
@@ -60,7 +60,7 @@ class NoteController < ApplicationController
if @user == note.user
return note
else
- render_text ""
+ render :text => ''
end
end
end
diff --git a/tracks/app/controllers/project_controller.rb b/tracks/app/controllers/project_controller.rb
index 9f1bdfb8..b352ba8b 100644
--- a/tracks/app/controllers/project_controller.rb
+++ b/tracks/app/controllers/project_controller.rb
@@ -34,7 +34,7 @@ class ProjectController < ApplicationController
@page_title = "TRACKS::Project: #{@project.name}"
if @contexts.empty?
- flash['warning'] = 'You must add at least one context before adding next actions.'
+ flash[:warning] = 'You must add at least one context before adding next actions.'
end
if @not_done.empty?
@@ -108,10 +108,10 @@ class ProjectController < ApplicationController
# fallback for standard requests
if @saved
- flash["notice"] = 'Added new next action.'
+ flash[:notice] = 'Added new next action.'
redirect_to :controller => 'todo', :action => 'index'
else
- flash["warning"] = 'The next action was not added. Please try again.'
+ flash[:warning] = 'The next action was not added. Please try again.'
redirect_to :controller => 'todo', :action => 'index'
end
@@ -119,7 +119,7 @@ class ProjectController < ApplicationController
if request.xhr? # be sure to include an error.rjs
render :action => 'error'
else
- flash["warning"] = 'An error occurred on the server.'
+ flash[:warning] = 'An error occurred on the server.'
redirect_to :controller => 'todo', :action => 'index'
end
end
@@ -140,9 +140,9 @@ class ProjectController < ApplicationController
return if request.xhr?
if @saved
- flash['notice'] = "The action '#{@item.description}' was marked as #{@item.done? ? 'complete' : 'incomplete' }"
+ flash[:notice] = "The action '#{@item.description}' was marked as #{@item.done? ? 'complete' : 'incomplete' }"
else
- flash['notice'] = "The action '#{@item.description}' was NOT marked as #{@item.done? ? 'complete' : 'incomplete' } due to an error on the server."
+ flash[:notice] = "The action '#{@item.description}' was NOT marked as #{@item.done? ? 'complete' : 'incomplete' } due to an error on the server."
end
redirect_to :action => "list"
end
@@ -152,13 +152,13 @@ class ProjectController < ApplicationController
def update
self.init
check_user_set_project
- @project.attributes = params["project"]
+ @project.attributes = params['project']
@project.name = deurlize(@project.name)
if @project.save
- render_partial 'project_listing', @project
+ render :partial => 'project_listing', :object => @project
else
- flash["warning"] = "Couldn't update project"
- render_text ""
+ flash[:warning] = "Couldn't update project"
+ render :text => ''
end
end
@@ -178,9 +178,9 @@ class ProjectController < ApplicationController
def destroy
check_user_set_project
if @project.destroy
- render_text ""
+ render :text => ''
else
- flash["warning"] = "Couldn't delete project \"#{@project.name}\""
+ flash[:warning] = "Couldn't delete project \"#{@project.name}\""
redirect_to( :controller => "project", :action => "list" )
end
end
@@ -210,8 +210,8 @@ class ProjectController < ApplicationController
return @project
else
@project = nil # Should be nil anyway
- flash["warning"] = "Project and session user mis-match: #{@project.user_id} and #{@user.id}!"
- render_text ""
+ flash[:warning] = "Project and session user mis-match: #{@project.user_id} and #{@user.id}!"
+ render :text => ''
end
end
@@ -221,8 +221,8 @@ class ProjectController < ApplicationController
return @project
else
@project = nil
- flash["warning"] = "Project and session user mis-match: #{@project.user_id} and #{@user.id}!"
- render_text ""
+ flash[:warning] = "Project and session user mis-match: #{@project.user_id} and #{@user.id}!"
+ render :text => ''
end
end
@@ -231,8 +231,8 @@ class ProjectController < ApplicationController
if @user == item.user
return item
else
- flash["warning"] = "Item and session user mis-match: #{item.user.name} and #{@user.name}!"
- render_text ""
+ flash[:warning] = "Item and session user mis-match: #{item.user.name} and #{@user.name}!"
+ render :text => ''
end
end
diff --git a/tracks/app/controllers/user_controller.rb b/tracks/app/controllers/user_controller.rb
index 4046f1cd..1c0d9df2 100644
--- a/tracks/app/controllers/user_controller.rb
+++ b/tracks/app/controllers/user_controller.rb
@@ -1,13 +1,9 @@
class UserController < ApplicationController
layout 'standard'
prepend_before_filter :login_required
-
- def index
- render_text "This will be our jumping-off point for managing user functions!"
- end
-
- def admin
- render_text "You'll only be allowed to go here if you're an administrator."
+ if Tracks::Config.auth_schemes.include?('open_id')
+ open_id_consumer
+ before_filter :begin_open_id_auth, :only => :update_auth_type
end
verify :method => :post,
@@ -22,7 +18,7 @@ class UserController < ApplicationController
#
def create
admin = User.find_admin
- #render_text "user is " + session["user_id"].to_s + " and admin is " + a.id.to_s
+ #logger.debug "user is " + session["user_id"].to_s + " and admin is " + a.id.to_s
unless session["user_id"].to_i == admin.id.to_i
access_denied
return
@@ -81,6 +77,71 @@ class UserController < ApplicationController
redirect_to :controller => 'user', :action => 'change_password'
end
end
+
+ def change_auth_type
+ @page_title = "TRACKS::Change authentication type"
+ end
+
+ def update_auth_type
+ if (params[:user][:auth_type] == 'open_id')
+ case open_id_response.status
+ when OpenID::SUCCESS
+ # The URL was a valid identity URL. Now we just need to send a redirect
+ # to the server using the redirect_url the library created for us.
+
+ # redirect to the server
+ redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), url_for(:action => 'complete'))
+ else
+ flash[:warning] = "Unable to find openid server for #{params[:openid_url]}"
+ redirect_to :action => 'change_auth_type'
+ end
+ return
+ end
+ @user.auth_type = params[:user][:auth_type]
+ if @user.save
+ flash[:notice] = "Authentication type updated."
+ redirect_to :controller => 'user', :action => 'preferences'
+ else
+ flash[:warning] = "There was a problem updating your authentication type: #{ @user.errors.full_messages.join(', ')}"
+ redirect_to :controller => 'user', :action => 'change_auth_type'
+ end
+ end
+
+ def complete
+ case open_id_response.status
+ when OpenID::FAILURE
+ # In the case of failure, if info is non-nil, it is the
+ # URL that we were verifying. We include it in the error
+ # message to help the user figure out what happened.
+ if open_id_response.identity_url
+ flash[:message] = "Verification of #{open_id_response.identity_url} failed. "
+ else
+ flash[:message] = "Verification failed. "
+ end
+ flash[:message] += open_id_response.msg.to_s
+
+ when OpenID::SUCCESS
+ # Success means that the transaction completed without
+ # error. If info is nil, it means that the user cancelled
+ # the verification.
+ @user.auth_type = 'open_id'
+ @user.open_id_url = open_id_response.identity_url
+ if @user.save
+ flash[:message] = "You have successfully verified #{open_id_response.identity_url} as your identity and set your authentication type to Open ID."
+ else
+ flash[:warning] = "You have successfully verified #{open_id_response.identity_url} as your identity but there was a problem saving your authentication preferences."
+ end
+ redirect_to :action => 'preferences'
+
+ when OpenID::CANCEL
+ flash[:message] = "Verification cancelled."
+
+ else
+ flash[:warning] = "Unknown response status: #{open_id_response.status}"
+ end
+ redirect_to :action => 'change_auth_type' unless performed?
+ end
+
def refresh_token
@user.crypt_word
@@ -93,10 +154,10 @@ class UserController < ApplicationController
def do_change_password_for(user)
user.change_password(params[:updateuser][:password], params[:updateuser][:password_confirmation])
if user.save
- flash["notice"] = "Password updated."
+ flash[:notice] = "Password updated."
return true
else
- flash["warning"] = 'There was a problem saving the password. Please retry.'
+ flash[:warning] = 'There was a problem saving the password. Please retry.'
return false
end
end
diff --git a/tracks/app/helpers/application_helper.rb b/tracks/app/helpers/application_helper.rb
index d281f905..f32b36e5 100644
--- a/tracks/app/helpers/application_helper.rb
+++ b/tracks/app/helpers/application_helper.rb
@@ -140,4 +140,8 @@ module ApplicationHelper
link_to( descriptor, { :controller => "project", :action => "show", :name => urlize(item.project.name) }, :title => "View project: #{item.project.name}" )
end
+ def render_flash
+ render :partial => 'shared/flash'
+ end
+
end
diff --git a/tracks/app/models/user.rb b/tracks/app/models/user.rb
index 5f4a91d7..98c583ad 100644
--- a/tracks/app/models/user.rb
+++ b/tracks/app/models/user.rb
@@ -10,17 +10,20 @@ class User < ActiveRecord::Base
attr_protected :is_admin
def self.authenticate(login, pass)
- find_first(["login = ? AND password = ?", login, sha1(pass)])
+ candidate = find(:first, :conditions => ["login = ?", login])
+ return nil if candidate.nil?
+ if candidate.auth_type == 'database'
+ return candidate if candidate.password == sha1(pass)
+ elsif candidate.auth_type == 'ldap' && Tracks::Config.auth_schemes.include?('ldap')
+ return candidate if SimpleLdapAuthenticator.valid?(login, pass)
+ end
+ nil
end
def self.find_admin
find_first([ "is_admin = ?", true ])
end
- def self.get_salt
- SALT
- end
-
def display_name
if first_name.blank? && last_name.blank?
return login
@@ -44,7 +47,7 @@ class User < ActiveRecord::Base
protected
def self.sha1(pass)
- Digest::SHA1.hexdigest("#{get_salt}--#{pass}--")
+ Digest::SHA1.hexdigest("#{Tracks::Config.salt}--#{pass}--")
end
before_create :crypt_password, :crypt_word
@@ -54,10 +57,12 @@ protected
write_attribute("password", self.class.sha1(password)) if password == @password_confirmation
end
- validates_presence_of :password, :login
+ validates_presence_of :login
+ validates_presence_of :password, :if => Proc.new{|user| user.auth_type == 'database'}
validates_length_of :password, :within => 5..40
validates_confirmation_of :password
validates_length_of :login, :within => 3..80
validates_uniqueness_of :login, :on => :create
-
+ validates_inclusion_of :auth_type, :in => Tracks::Config.auth_schemes, :message=>"not a valid authentication type"
+ validates_presence_of :open_id_url, :if => Proc.new{|user| user.auth_type == 'open_id'}
end
diff --git a/tracks/app/views/context/_context_listing.rhtml b/tracks/app/views/context/_context_listing.rhtml
index 1cbb6ae2..718f38e4 100644
--- a/tracks/app/views/context/_context_listing.rhtml
+++ b/tracks/app/views/context/_context_listing.rhtml
@@ -32,7 +32,7 @@
:update => "container_#{context.id}",
:complete => visual_effect(:appear, 'container_#{context.id}') %>
\ No newline at end of file
diff --git a/tracks/app/views/user/change_password.rhtml b/tracks/app/views/user/change_password.rhtml
index 14ffc6de..5de032f6 100644
--- a/tracks/app/views/user/change_password.rhtml
+++ b/tracks/app/views/user/change_password.rhtml
@@ -2,15 +2,11 @@
<%= @page_title %>
-<% for name in ["notice", "warning", "message"] %>
- <% if flash[name] %>
- <%= "
Enter your new password in the fields below and click 'Change Password' to replace your current password with your new one (note that this will also change the URL you use to subscribe to your RSS or text field).
+
Enter your new password in the fields below and click 'Change Password' to replace your current password with your new one.
- <%= button_to "Generate a new token", { :controller => 'user', :action => 'refresh_token'},
- :confirm => "Are you sure? Generating a new token will replace the existing one and break any external usages of this token." %>
-
-
-
+
Token (for feeds and API use):
+
<%= @user.word %>
+
+ <%= button_to "Generate a new token", { :controller => 'user', :action => 'refresh_token'},
+ :confirm => "Are you sure? Generating a new token will replace the existing one and break any external usages of this token." %>
+
+
+
Your authentication
+
+ <% if Tracks::Config.auth_schemes.length > 1 %>
+
Your authentication type is <%= @user.auth_type %>.
+
+ <%= link_to "Change your authentication type »", { :controller => 'user', :action => 'change_auth_type'}, :class => 'edit_link' %>
+
+ <% end %>
+ <% if @user.auth_type == 'database' %>
+
diff --git a/tracks/config/environment.rb.tmpl b/tracks/config/environment.rb.tmpl
index 846f3a8e..7d99bd99 100644
--- a/tracks/config/environment.rb.tmpl
+++ b/tracks/config/environment.rb.tmpl
@@ -60,9 +60,23 @@ SALT = "change-me"
require 'acts_as_namepart_finder'
require 'acts_as_todo_container'
+require 'config'
ActiveRecord::Base.class_eval do
include Tracks::Acts::NamepartFinder
include Tracks::Acts::TodoContainer
end
+AUTHENTICATION_SCHEMES = ['database'] #one or more of ['database', 'ldap', 'open_id']
+if (AUTHENTICATION_SCHEMES.include? 'ldap')
+ require 'net/ldap' #requires ruby-net-ldap gem be installed
+ require 'simple_ldap_authenticator'
+ SimpleLdapAuthenticator.ldap_library = 'net/ldap'
+ SimpleLdapAuthenticator.servers = %w'localhost'
+ SimpleLdapAuthenticator.use_ssl = false
+ SimpleLdapAuthenticator.login_format = 'cn=%s,dc=example,dc=com'
+end
+if (AUTHENTICATION_SCHEMES.include? 'open_id')
+ #requires ruby-openid gem to be installed
+end
+
diff --git a/tracks/db/migrate/016_add_user_auth_type.rb b/tracks/db/migrate/016_add_user_auth_type.rb
new file mode 100644
index 00000000..5c3efe01
--- /dev/null
+++ b/tracks/db/migrate/016_add_user_auth_type.rb
@@ -0,0 +1,9 @@
+class AddUserAuthType < ActiveRecord::Migration
+ def self.up
+ add_column :users, :auth_type, :string, :default => 'database', :null => false
+ end
+
+ def self.down
+ remove_column :users, :auth_type
+ end
+end
diff --git a/tracks/db/migrate/017_add_open_id_tables.rb b/tracks/db/migrate/017_add_open_id_tables.rb
new file mode 100644
index 00000000..bbc20af5
--- /dev/null
+++ b/tracks/db/migrate/017_add_open_id_tables.rb
@@ -0,0 +1,45 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+class AddOpenIdTables < ActiveRecord::Migration
+ def self.up
+ create_table "open_id_associations", :force => true do |t|
+ t.column "server_url", :binary
+ t.column "handle", :string
+ t.column "secret", :binary
+ t.column "issued", :integer
+ t.column "lifetime", :integer
+ t.column "assoc_type", :string
+ end
+
+ 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
+
+ def self.down
+ drop_table "open_id_associations"
+ drop_table "open_id_nonces"
+ drop_table "open_id_settings"
+ end
+end
diff --git a/tracks/db/migrate/018_add_user_open_id_url.rb b/tracks/db/migrate/018_add_user_open_id_url.rb
new file mode 100644
index 00000000..71c06100
--- /dev/null
+++ b/tracks/db/migrate/018_add_user_open_id_url.rb
@@ -0,0 +1,9 @@
+class AddUserOpenIdUrl < ActiveRecord::Migration
+ def self.up
+ add_column :users, :open_id_url, :string
+ end
+
+ def self.down
+ remove_column :users, :open_id_url
+ end
+end
diff --git a/tracks/db/schema.rb b/tracks/db/schema.rb
index f2d13e39..321056ee 100644
--- a/tracks/db/schema.rb
+++ b/tracks/db/schema.rb
@@ -2,7 +2,7 @@
# migrations feature of ActiveRecord to incrementally modify your database, and
# then regenerate this schema definition.
-ActiveRecord::Schema.define(:version => 15) do
+ActiveRecord::Schema.define(:version => 18) do
create_table "contexts", :force => true do |t|
t.column "name", :string, :default => "", :null => false
@@ -19,6 +19,25 @@ ActiveRecord::Schema.define(:version => 15) do
t.column "updated_at", :datetime
end
+ create_table "open_id_associations", :force => true do |t|
+ t.column "server_url", :binary
+ t.column "handle", :string
+ t.column "secret", :binary
+ t.column "issued", :integer
+ t.column "lifetime", :integer
+ t.column "assoc_type", :string
+ end
+
+ 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
+
create_table "preferences", :force => true do |t|
t.column "user_id", :integer, :default => 0, :null => false
t.column "date_format", :string, :limit => 40, :default => "%d/%m/%Y", :null => false
@@ -70,6 +89,8 @@ ActiveRecord::Schema.define(:version => 15) do
t.column "is_admin", :integer, :limit => 4, :default => 0, :null => false
t.column "first_name", :string
t.column "last_name", :string
+ t.column "auth_type", :string, :default => "database", :null => false
+ t.column "open_id_url", :string
end
end
diff --git a/tracks/lib/config.rb b/tracks/lib/config.rb
new file mode 100644
index 00000000..3fa0fb8a
--- /dev/null
+++ b/tracks/lib/config.rb
@@ -0,0 +1,15 @@
+module Tracks
+
+ class Config
+
+ def self.salt
+ SALT
+ end
+
+ def self.auth_schemes
+ AUTHENTICATION_SCHEMES
+ end
+
+ end
+
+end
\ No newline at end of file
diff --git a/tracks/public/images/open-id-login-bg.gif b/tracks/public/images/open-id-login-bg.gif
new file mode 100644
index 00000000..cde836c8
Binary files /dev/null and b/tracks/public/images/open-id-login-bg.gif differ
diff --git a/tracks/public/stylesheets/scaffold.css b/tracks/public/stylesheets/scaffold.css
index 74c6ffab..1f32a2ac 100644
--- a/tracks/public/stylesheets/scaffold.css
+++ b/tracks/public/stylesheets/scaffold.css
@@ -132,4 +132,15 @@ div.memo {
#errorExplanation ul li {
font-size: 1em;
list-style: disc;
+}
+input.login_text {
+ width:200px;
+}
+input.open_id {
+ background: url(../images/open-id-login-bg.gif) no-repeat;
+ background-color: #fff;
+ background-position: 0 50%;
+ color: #000;
+ padding-left: 18px;
+ width:182px;
}
\ No newline at end of file
diff --git a/tracks/public/stylesheets/standard.css b/tracks/public/stylesheets/standard.css
index 840795c9..6e752707 100644
--- a/tracks/public/stylesheets/standard.css
+++ b/tracks/public/stylesheets/standard.css
@@ -643,9 +643,10 @@ div.message {
}
ul#prefs {list-style-type: disc; margin-left: 5px;}
-#token_area {
+#token_area, #authentication_area {
text-align:center;
margin-top:20px;
+ margin-bottom:10px;
}
#token_area .description{
font-weight:bold;
@@ -654,7 +655,14 @@ ul#prefs {list-style-type: disc; margin-left: 5px;}
width:100%;
text-align:center;
}
-
+.prefscontainer .actions {
+ text-align:center;
+ margin-bottom:20px;
+}
+.authtype_container .actions {
+ margin-top:20px;
+ margin-bottom:20px;
+}
#feedlegend {
padding: 2px;
border: 1px solid #CCC;
@@ -688,3 +696,11 @@ ul#prefs {list-style-type: disc; margin-left: 5px;}
font-weight: normal;
font-style:oblique;
}
+input.open_id {
+ background: url(../images/open-id-login-bg.gif) no-repeat;
+ background-color: #fff;
+ background-position: 0 50%;
+ color: #000;
+ padding-left: 18px;
+ width:182px;
+}
\ No newline at end of file
diff --git a/tracks/test/fixtures/users.yml b/tracks/test/fixtures/users.yml
index b613202f..1e9d1b2a 100644
--- a/tracks/test/fixtures/users.yml
+++ b/tracks/test/fixtures/users.yml
@@ -2,17 +2,29 @@
admin_user:
id: 1
login: admin
- password: <%= Digest::SHA1.hexdigest("#{User.get_salt()}--abracadabra--") %>
+ password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--abracadabra--") %>
word: <%= Digest::SHA1.hexdigest("adminSat Feb 25 17:14:00 GMT 20060.236961325863376") %>
is_admin: true
first_name: Admin
last_name: Schmadmin
+ auth_type: database
other_user:
id: 2
login: jane
- password: <%= Digest::SHA1.hexdigest("#{User.get_salt()}--sesame--") %>
+ password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--") %>
word: <%= Digest::SHA1.hexdigest("janeSun Feb 19 14:42:45 GMT 20060.408173979260027") %>
is_admin: false
first_name: Jane
last_name: Doe
+ auth_type: database
+
+ldap_user:
+ id: 3
+ login: john
+ password:
+ word: <%= Digest::SHA1.hexdigest("johnSun Feb 19 14:42:45 GMT 20060.408173979260027") %>
+ is_admin: false
+ first_name: John
+ last_name: Deere
+ auth_type: ldap
diff --git a/tracks/test/functional/backend_controller_test.rb b/tracks/test/functional/backend_controller_test.rb
index 8ca029b7..a42ae13c 100644
--- a/tracks/test/functional/backend_controller_test.rb
+++ b/tracks/test/functional/backend_controller_test.rb
@@ -10,7 +10,7 @@ class BackendControllerTest < Test::Unit::TestCase
def setup
@controller = BackendController.new
request, response = ActionController::TestRequest.new, ActionController::TestResponse.new
- assert_equal "change-me", User.get_salt()
+ assert_equal "change-me", Tracks::Config.salt
end
def test_new_todo_fails_with_incorrect_token
diff --git a/tracks/test/functional/login_controller_test.rb b/tracks/test/functional/login_controller_test.rb
index d11d0b7a..e0e66580 100644
--- a/tracks/test/functional/login_controller_test.rb
+++ b/tracks/test/functional/login_controller_test.rb
@@ -10,10 +10,11 @@ class LoginControllerTest < Test::Unit::TestCase
def setup
assert_equal "test", ENV['RAILS_ENV']
- assert_equal "change-me", User.get_salt()
+ assert_equal "change-me", Tracks::Config.salt
@controller = LoginController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
+ @num_users_in_fixture = User.count
end
#============================================
@@ -33,7 +34,7 @@ class LoginControllerTest < Test::Unit::TestCase
assert_equal user.id, @response.session['user_id']
assert_equal user.login, "admin"
assert user.is_admin
- assert_equal "Login successful: session will not expire.", flash['notice']
+ assert_equal "Login successful: session will not expire.", flash[:notice]
assert_redirect_url "http://#{@request.host}/bogus/location"
end
@@ -43,7 +44,7 @@ class LoginControllerTest < Test::Unit::TestCase
assert_equal user.id, @response.session['user_id']
assert_equal user.login, "jane"
assert user.is_admin == false || user.is_admin == 0
- assert_equal "Login successful: session will expire after 1 hour of inactivity.", flash['notice']
+ assert_equal "Login successful: session will expire after 1 hour of inactivity.", flash[:notice]
assert_redirected_to :controller => 'todo', :action => 'index'
end
@@ -59,14 +60,14 @@ class LoginControllerTest < Test::Unit::TestCase
def test_login_bad_password
post :login, {:user_login => 'jane', :user_password => 'wrong', :user_noexpiry => 'on'}
assert_session_has_no :user
- assert_equal "Login unsuccessful", flash['warning']
+ assert_equal "Login unsuccessful", flash[:warning]
assert_response :success
end
def test_login_bad_login
post :login, {:user_login => 'blah', :user_password => 'sesame', :user_noexpiry => 'on'}
assert_session_has_no :user
- assert_equal "Login unsuccessful", flash['warning']
+ assert_equal "Login unsuccessful", flash[:warning]
assert_response :success
end
@@ -81,7 +82,7 @@ class LoginControllerTest < Test::Unit::TestCase
admin = login('admin', 'abracadabra', 'on')
assert admin.is_admin
newbie = create('newbie', 'newbiepass')
- assert_equal "Signup successful for user newbie.", flash['notice']
+ assert_equal "Signup successful for user newbie.", flash[:notice]
assert_redirected_to :controller => 'todo', :action => 'index'
assert_valid newbie
get :logout # logout the admin user
@@ -92,8 +93,7 @@ class LoginControllerTest < Test::Unit::TestCase
assert_redirected_to :controller => 'todo', :action => 'index'
assert_equal 'newbie', user.login
assert user.is_admin == false || user.is_admin == 0
- num_users = User.find(:all)
- assert_equal num_users.length, 3
+ assert_equal User.count, @num_users_in_fixture + 1
end
# Test whether signup of new users is denied to a non-admin user
@@ -103,9 +103,7 @@ class LoginControllerTest < Test::Unit::TestCase
assert non_admin.is_admin == false || non_admin.is_admin == 0
post :signup, :user => {:login => 'newbie2', :password => 'newbiepass2', :password_confirmation => 'newbiepass2'}
assert_template 'login/nosignup'
-
- num_users = User.find(:all)
- assert_equal num_users.length, 2
+ assert_number_of_users_is_unchanged
end
# ============================================
@@ -117,8 +115,7 @@ class LoginControllerTest < Test::Unit::TestCase
assert admin.is_admin
assert_equal admin.id, @response.session['user_id']
post :create, :user => {:login => 'newbie', :password => '', :password_confirmation => ''}
- num_users = User.find(:all)
- assert_equal num_users.length, 2
+ assert_number_of_users_is_unchanged
assert_redirected_to :controller => 'login', :action => 'signup'
end
@@ -127,8 +124,7 @@ class LoginControllerTest < Test::Unit::TestCase
assert admin.is_admin
assert_equal admin.id, @response.session['user_id']
post :create, :user => {:login => 'n', :password => 'newbiepass', :password_confirmation => 'newbiepass'}
- num_users = User.find(:all)
- assert_equal num_users.length, 2
+ assert_number_of_users_is_unchanged
assert_redirected_to :controller => 'login', :action => 'signup'
end
@@ -140,8 +136,13 @@ class LoginControllerTest < Test::Unit::TestCase
assert_equal admin.id, @response.session['user_id']
post :create, :user => {:login => 'jane', :password => 'newbiepass', :password_confirmation => 'newbiepass'}
num_users = User.find(:all)
- assert_equal num_users.length, 2
+ assert_number_of_users_is_unchanged
assert_redirected_to :controller => 'login', :action => 'signup'
end
+ private
+
+ def assert_number_of_users_is_unchanged
+ assert_equal User.count, @num_users_in_fixture
+ end
end
diff --git a/tracks/test/functional/user_controller_test.rb b/tracks/test/functional/user_controller_test.rb
index 39b2a426..4e69f1b2 100644
--- a/tracks/test/functional/user_controller_test.rb
+++ b/tracks/test/functional/user_controller_test.rb
@@ -10,32 +10,12 @@ class UserControllerTest < Test::Unit::TestCase
def setup
assert_equal "test", ENV['RAILS_ENV']
- assert_equal "change-me", User.get_salt()
+ assert_equal "change-me", Tracks::Config.salt
@controller = UserController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
- # Test index with and without login
- #
- def test_index
- get :index # should fail because no login
- assert_redirected_to :controller => 'login', :action => 'login'
- @request.session['user_id'] = users(:admin_user).id # log in the admin user
- get :index
- assert_response :success
- end
-
- # Test admin with and without login
- #
- def test_admin
- get :admin # should fail because no login
- assert_redirected_to :controller => 'login', :action => 'login'
- @request.session['user_id'] = users(:admin_user).id # log in the admin user
- get :admin
- assert_response :success
- end
-
def test_preferences
get :preferences # should fail because no login
assert_redirected_to :controller => 'login', :action => 'login'
@@ -81,8 +61,8 @@ class UserControllerTest < Test::Unit::TestCase
post :update_password, :updateuser => {:password => 'newpassword', :password_confirmation => 'newpassword'}
assert_redirected_to :controller => 'user', :action => 'preferences'
@updated_user = User.find(users(:admin_user).id)
- assert_equal @updated_user.password, Digest::SHA1.hexdigest("#{User.get_salt()}--newpassword--")
- assert_equal flash['notice'], "Password updated."
+ assert_equal @updated_user.password, Digest::SHA1.hexdigest("#{Tracks::Config.salt}--newpassword--")
+ assert_equal flash[:notice], "Password updated."
end
def test_update_password_no_confirmation
@@ -92,7 +72,7 @@ class UserControllerTest < Test::Unit::TestCase
post :update_password, :updateuser => {:password => 'newpassword', :password_confirmation => 'wrong'}
assert_redirected_to :controller => 'user', :action => 'change_password'
assert users(:admin_user).save, false
- assert_equal flash['warning'], 'There was a problem saving the password. Please retry.'
+ assert_equal flash[:warning], 'There was a problem saving the password. Please retry.'
end
def test_update_password_validation_errors
@@ -105,7 +85,7 @@ class UserControllerTest < Test::Unit::TestCase
# For some reason, no errors are being raised now.
#assert_equal 1, users(:admin_user).errors.count
#assert_equal users(:admin_user).errors.on(:password), "is too short (min is 5 characters)"
- assert_equal flash['warning'], 'There was a problem saving the password. Please retry.'
+ assert_equal flash[:warning], 'There was a problem saving the password. Please retry.'
end
end
diff --git a/tracks/test/integration/create_user_api_test.rb b/tracks/test/integration/create_user_api_test.rb
index aa6ded6a..4112122f 100644
--- a/tracks/test/integration/create_user_api_test.rb
+++ b/tracks/test/integration/create_user_api_test.rb
@@ -10,7 +10,7 @@ class CreateUserControllerTest < ActionController::IntegrationTest
fixtures :users
@@foobar_postdata = "foobar"
- @@john_postdata = "johnbarracuda"
+ @@johnny_postdata = "johnnybarracuda"
def setup
assert_test_environment_ok
@@ -63,13 +63,13 @@ class CreateUserControllerTest < ActionController::IntegrationTest
def test_creates_new_user
initial_count = User.count
- authenticated_post_xml_to_user_create @@john_postdata
+ authenticated_post_xml_to_user_create @@johnny_postdata
assert_response_and_body 200, "User created."
assert_equal initial_count + 1, User.count
- john1 = User.find_by_login('john')
- assert_not_nil john1, "expected user john to be created"
- john2 = User.authenticate('john','barracuda')
- assert_not_nil john2, "expected user john to be created"
+ johnny1 = User.find_by_login('johnny')
+ assert_not_nil johnny1, "expected user johnny to be created"
+ johnny2 = User.authenticate('johnny','barracuda')
+ assert_not_nil johnny2, "expected user johnny to be created"
end
def test_fails_with_get_verb
diff --git a/tracks/test/integration/ldap_auth_test.rb b/tracks/test/integration/ldap_auth_test.rb
new file mode 100755
index 00000000..57363787
--- /dev/null
+++ b/tracks/test/integration/ldap_auth_test.rb
@@ -0,0 +1,117 @@
+require "#{File.dirname(__FILE__)}/../test_helper"
+require 'tempfile'
+require 'user'
+
+class LdapAuthTest < Test::Unit::TestCase
+
+ fixtures :users
+
+ SLAPD_BIN = "/usr/libexec/slapd" #You may need to adjust this
+ SLAPD_SCHEMA_DIR = "/etc/openldap/schema/" #You may need to adjust this
+ SLAPD_TEST_PORT = 10389
+ OUTPUT_DEBUG_INFO = false
+
+ def setup
+ assert_equal "test", ENV['RAILS_ENV']
+ assert_equal "change-me", Tracks::Config.salt
+
+ setup_ldap_server_conf
+ start_ldap_server
+ end
+
+ def teardown
+ stop_ldap_server
+ end
+
+ def test_authenticate_against_ldap
+ add_ldap_user_to_ldap_repository
+ user = User.authenticate('john', 'deere')
+ assert_not_nil(user)
+ assert_equal user.login, 'john'
+ end
+
+ def setup_ldap_server_conf
+ @slapd_conf = create_slapd_conf()
+ open(@slapd_conf.path) { |f| f.read }
+ unless File.exist?(SLAPD_BIN)
+ assert false, "slapd could not be found at #{SLAPD_BIN}. Adjust the path in #{__FILE__}"
+ end
+ end
+
+ def start_ldap_server
+ t = Thread.new(@slapd_conf.path) { |slapd_conf_path|
+ puts "starting slapd..." if OUTPUT_DEBUG_INFO
+ run_cmd %Q{/usr/libexec/slapd -f #{slapd_conf_path} -h "ldap://127.0.0.1:10389/"}
+ }
+ sleep(2)
+ run_cmd %Q{ldapsearch -H "ldap://127.0.0.1:10389/" -x -b '' -s base '(objectclass=*)' namingContexts}
+ end
+
+ def add_ldap_user_to_ldap_repository
+ ldif_file = create_ldif()
+ run_cmd %Q{ldapadd -H "ldap://127.0.0.1:10389/" -f #{ldif_file.path} -cxv -D "cn=Manager,dc=lukemelia,dc=com" -w secret}
+ puts `cat #{ldif_file.path}` if OUTPUT_DEBUG_INFO
+ end
+
+ def stop_ldap_server
+ pid = open(get_pid_file_path(@slapd_conf)) { |f| f.read }
+ run_cmd "kill -TERM #{pid}"
+ end
+
+ def create_slapd_conf
+ slapd_conf = Tempfile.new("slapd.conf")
+ slapd_conf.path
+ data_dir = slapd_conf.path + '-data'
+ pid_file = get_pid_file_path(slapd_conf)
+ Dir.mkdir(data_dir)
+ encrypted_password = `slappasswd -s secret`
+ open(slapd_conf.path, 'w') do |f|
+ f.puts %Q{include #{SLAPD_SCHEMA_DIR}core.schema
+pidfile #{pid_file}
+database ldbm
+suffix "dc=lukemelia,dc=com"
+rootdn "cn=Manager,dc=lukemelia,dc=com"
+rootpw #{encrypted_password}
+directory #{data_dir}
+
+access to *
+ by self write
+ by users read
+ by anonymous auth
+}
+ end
+ puts `cat #{slapd_conf.path}` if OUTPUT_DEBUG_INFO
+ slapd_conf
+ end
+
+ def create_ldif
+ ldif_file = Tempfile.new("ldap_user.ldif")
+ encrypted_password = `slappasswd -s deere`
+ open(ldif_file.path, 'w') do |f|
+ f.puts %Q{dn: dc=lukemelia,dc=com
+objectclass: dcObject
+objectclass: organization
+o: Luke Melia DotCom
+dc: lukemelia
+
+dn: cn=john,dc=lukemelia,dc=com
+cn: john
+sn: john
+objectclass: person
+userPassword: #{encrypted_password}
+}
+ end
+ ldif_file
+ end
+
+ def run_cmd(cmd)
+ puts cmd if OUTPUT_DEBUG_INFO
+ cmd_out = `#{cmd}`
+ puts cmd_out if OUTPUT_DEBUG_INFO
+ end
+
+ def get_pid_file_path(tempfile)
+ tempfile.path + '.pid'
+ end
+
+end
diff --git a/tracks/test/test_helper.rb b/tracks/test/test_helper.rb
index ac0b55bd..5778815f 100644
--- a/tracks/test/test_helper.rb
+++ b/tracks/test/test_helper.rb
@@ -2,9 +2,11 @@ ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'test_help'
-class User < ActiveRecord::Base
- def self.get_salt
- "change-me"
+module Tracks
+ class Config
+ def self.salt
+ "change-me"
+ end
end
end
@@ -50,7 +52,7 @@ class ActionController::IntegrationTest
def assert_test_environment_ok
assert_equal "test", ENV['RAILS_ENV']
- assert_equal "change-me", User.get_salt()
+ assert_equal "change-me", Tracks::Config.salt
end
def authenticated_post_xml(url, username, password, parameters, headers = {})
diff --git a/tracks/test/unit/user_test.rb b/tracks/test/unit/user_test.rb
index 8c57323b..eda75dae 100644
--- a/tracks/test/unit/user_test.rb
+++ b/tracks/test/unit/user_test.rb
@@ -5,7 +5,7 @@ class UserTest < Test::Unit::TestCase
def setup
assert_equal "test", ENV['RAILS_ENV']
- assert_equal "change-me", User.get_salt()
+ assert_equal "change-me", Tracks::Config.salt
@admin_user = User.find(1)
@other_user = User.find(2)
end
@@ -16,7 +16,7 @@ class UserTest < Test::Unit::TestCase
assert_kind_of User, @admin_user
assert_equal 1, @admin_user.id
assert_equal "admin", @admin_user.login
- assert_equal "#{Digest::SHA1.hexdigest("#{User.get_salt()}--abracadabra--")}", @admin_user.password
+ assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--abracadabra--")}", @admin_user.password
assert_not_nil @admin_user.word
assert @admin_user.is_admin
end
@@ -26,7 +26,7 @@ class UserTest < Test::Unit::TestCase
assert_kind_of User, @other_user
assert_equal 2, @other_user.id
assert_equal "jane", @other_user.login
- assert_equal "#{Digest::SHA1.hexdigest("#{User.get_salt()}--sesame--")}", @other_user.password
+ assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
assert_not_nil @other_user.word
assert @other_user.is_admin == false || @other_user.is_admin == 0
end
@@ -38,7 +38,7 @@ class UserTest < Test::Unit::TestCase
# Test a password shorter than 5 characters
#
def test_validate_short_password
- assert_equal "#{Digest::SHA1.hexdigest("#{User.get_salt()}--sesame--")}", @other_user.password
+ assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
@other_user.password = "four"
assert !@other_user.save
assert_equal 1, @other_user.errors.count
@@ -48,7 +48,7 @@ class UserTest < Test::Unit::TestCase
# Test a password longer than 40 characters
#
def test_validate_long_password
- assert_equal "#{Digest::SHA1.hexdigest("#{User.get_salt()}--sesame--")}", @other_user.password
+ assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
@other_user.password = generate_random_string(41)
assert !@other_user.save
assert_equal 1, @other_user.errors.count
@@ -58,7 +58,7 @@ class UserTest < Test::Unit::TestCase
# Test that correct length password is valid
#
def test_validate_correct_length_password
- assert_equal "#{Digest::SHA1.hexdigest("#{User.get_salt()}--sesame--")}", @other_user.password
+ assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
@other_user.password = generate_random_string(6)
assert @other_user.save
end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/LICENSE b/tracks/vendor/plugins/openid_consumer_plugin/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/LICENSE
@@ -0,0 +1,202 @@
+
+ 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.
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/README b/tracks/vendor/plugins/openid_consumer_plugin/README
new file mode 100644
index 00000000..a5a01bfd
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/README
@@ -0,0 +1,22 @@
+OpenID Consumer
+===============
+
+Enable OpenID authentication and profile exchange from your application.
+
+PRE-REQUISITES
+--------------
+
+* JanRain's Yadis and OpenID 1.2 libraries in Ruby.
+ * These can be obtained using 'gem install ruby-openid'
+
+
+INSTALLATION
+------------
+
+To install you need to create a migration and add a controller.
+
+ ./script/generate open_id_migration add_open_id_tables
+ ./script/generate open_id_consumer_controller open_id
+
+This can be used well in conjunction with a login system such as ActsAsAuthenticated
+
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/Rakefile b/tracks/vendor/plugins/openid_consumer_plugin/Rakefile
new file mode 100644
index 00000000..79ede457
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/Rakefile
@@ -0,0 +1,39 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the open_id_consumer plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the open_id_consumer plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'OpenIdConsumer'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/open_id_consumer_controller_generator.rb b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/open_id_consumer_controller_generator.rb
new file mode 100644
index 00000000..ad63aaaa
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/open_id_consumer_controller_generator.rb
@@ -0,0 +1,86 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+class OpenIdConsumerControllerGenerator < Rails::Generator::NamedBase
+ attr_reader :controller_name,
+ :controller_class_path,
+ :controller_file_path,
+ :controller_class_nesting,
+ :controller_class_nesting_depth,
+ :controller_class_name,
+ :controller_singular_name,
+ :controller_plural_name
+ alias_method :controller_file_name, :controller_singular_name
+ alias_method :controller_table_name, :controller_plural_name
+
+ def initialize(runtime_args, runtime_options = {})
+ runtime_args << 'open_id' if runtime_args.empty?
+ super
+
+ # Take controller name from the next argument. Default to the pluralized model name.
+ @controller_name = args.shift
+ @controller_name ||= ActiveRecord::Base.pluralize_table_names ? @name.pluralize : @name
+
+ base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
+ @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
+
+ if @controller_class_nesting.empty?
+ @controller_class_name = @controller_class_name_without_nesting
+ else
+ @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
+ end
+ end
+
+ def manifest
+ record do |m|
+ # Check for class naming collisions.
+ m.class_collisions controller_class_path, "#{controller_class_name}Controller",
+ "#{controller_class_name}Helper"
+
+ # Controller, helper, views, and test directories.
+ m.directory File.join('app/controllers', controller_class_path)
+ m.directory File.join('app/helpers', controller_class_path)
+ m.directory File.join('app/views', controller_class_path, controller_file_name)
+ m.directory File.join('test/functional', controller_class_path)
+
+ m.template 'controller.rb',
+ File.join('app/controllers',
+ controller_class_path,
+ "#{controller_file_name}_controller.rb")
+
+ m.template 'functional_test.rb',
+ File.join('test/functional',
+ controller_class_path,
+ "#{controller_file_name}_controller_test.rb")
+
+ m.template 'helper.rb',
+ File.join('app/helpers',
+ controller_class_path,
+ "#{controller_file_name}_helper.rb")
+
+ # Controller templates
+ m.template "index.rhtml",
+ File.join('app/views', controller_class_path, controller_file_name, "index.rhtml")
+ end
+ end
+
+ protected
+ # Override with your own usage banner.
+ def banner
+ "Usage: #{$0} open_id_consumer_controller [open_id]"
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/controller.rb b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/controller.rb
new file mode 100644
index 00000000..50717e3d
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/controller.rb
@@ -0,0 +1,74 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+class <%= controller_class_name %>Controller < ApplicationController
+ open_id_consumer :required => [:email, :nickname], :optional => [:fullname, :dob, :gender, :country]
+
+ def index
+ @title = 'Welcome'
+ end
+
+ def begin
+ # If the URL was unusable (either because of network conditions,
+ # a server error, or that the response returned was not an OpenID
+ # identity page), the library will return HTTP_FAILURE or PARSE_ERROR.
+ # Let the user know that the URL is unusable.
+ case open_id_response.status
+ when OpenID::SUCCESS
+ # The URL was a valid identity URL. Now we just need to send a redirect
+ # to the server using the redirect_url the library created for us.
+
+ # redirect to the server
+ redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + '/'), url_for(:action => 'complete'))
+ else
+ flash[:error] = "Unable to find openid server for #{params[:openid_url]}"
+ render :action => :index
+ end
+ end
+
+ def complete
+ case open_id_response.status
+ when OpenID::FAILURE
+ # In the case of failure, if info is non-nil, it is the
+ # URL that we were verifying. We include it in the error
+ # message to help the user figure out what happened.
+ if open_id_response.identity_url
+ flash[:message] = "Verification of #{open_id_response.identity_url} failed. "
+ else
+ flash[:message] = "Verification failed. "
+ end
+ flash[:message] += open_id_response.msg.to_s
+
+ when OpenID::SUCCESS
+ # Success means that the transaction completed without
+ # error. If info is nil, it means that the user cancelled
+ # the verification.
+ flash[:message] = "You have successfully verified #{open_id_response.identity_url} as your identity."
+ if open_id_fields.any?
+ flash[:message] << " With simple registration fields: "
+ open_id_fields.each {|k,v| flash[:message] << " #{k}: #{v}"}
+ end
+
+ when OpenID::CANCEL
+ flash[:message] = "Verification cancelled."
+
+ else
+ flash[:message] = "Unknown response status: #{open_id_response.status}"
+ end
+ redirect_to :action => 'index'
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/functional_test.rb b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/functional_test.rb
new file mode 100644
index 00000000..c787b010
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/functional_test.rb
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+require File.dirname(__FILE__) + '/../test_helper'
+require '<%= controller_file_name %>_controller'
+
+# Re-raise errors caught by the controller.
+class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
+
+class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase
+ def setup
+ @controller = <%= controller_class_name %>Controller.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_truth
+ assert true
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/helper.rb b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/helper.rb
new file mode 100644
index 00000000..1c518a5c
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/helper.rb
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+module <%= controller_class_name %>Helper
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/index.rhtml b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/index.rhtml
new file mode 100644
index 00000000..4f7d22e0
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_consumer_controller/templates/index.rhtml
@@ -0,0 +1,32 @@
+<% # Licensed to the Apache Software Foundation (ASF) under one
+ # or more contributor license agreements. See the NOTICE file
+ # distributed with this work for additional information
+ # regarding copyright ownership. The ASF licenses this file
+ # to you 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.
+%>
+
+
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_migration/open_id_migration_generator.rb b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_migration/open_id_migration_generator.rb
new file mode 100644
index 00000000..f9cd2e99
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_migration/open_id_migration_generator.rb
@@ -0,0 +1,29 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+class OpenIdMigrationGenerator < Rails::Generator::NamedBase
+ def initialize(runtime_args, runtime_options = {})
+ runtime_args << 'add_open_id_tables' if runtime_args.empty?
+ super
+ end
+
+ def manifest
+ record do |m|
+ m.migration_template 'migration.rb', 'db/migrate'
+ end
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_migration/templates/migration.rb b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_migration/templates/migration.rb
new file mode 100644
index 00000000..d7569bd5
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/generators/open_id_migration/templates/migration.rb
@@ -0,0 +1,45 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+class <%= class_name %> < ActiveRecord::Migration
+ def self.up
+ create_table "open_id_associations", :force => true do |t|
+ t.column "server_url", :binary
+ t.column "handle", :string
+ t.column "secret", :binary
+ t.column "issued", :integer
+ t.column "lifetime", :integer
+ t.column "assoc_type", :string
+ end
+
+ 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
+
+ def self.down
+ drop_table "open_id_associations"
+ drop_table "open_id_nonces"
+ drop_table "open_id_settings"
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/init.rb b/tracks/vendor/plugins/openid_consumer_plugin/init.rb
new file mode 100644
index 00000000..d3d8f53e
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/init.rb
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+class << ActionController::Base
+ def open_id_consumer(options = {})
+ include OpenIdConsumer::ControllerMethods
+ self.open_id_consumer_options = options
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/install.rb b/tracks/vendor/plugins/openid_consumer_plugin/install.rb
new file mode 100644
index 00000000..ba7dcb48
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/install.rb
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+puts IO.read(File.join(File.dirname(__FILE__), 'README'))
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/active_record_open_id_store.rb b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/active_record_open_id_store.rb
new file mode 100644
index 00000000..9b7bfbe0
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/active_record_open_id_store.rb
@@ -0,0 +1,103 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+begin
+ require_gem "ruby-openid", ">= 1.0"
+rescue LoadError
+ require "openid"
+end
+
+module OpenIdConsumer
+ class ActiveRecordOpenIdStore < OpenID::Store
+ def get_auth_key
+ setting = Setting.find_by_setting 'auth_key'
+ if setting.nil?
+ auth_key = OpenID::Util.random_string(20)
+ setting = Setting.create :setting => 'auth_key', :value => auth_key
+ end
+ setting.value
+ end
+
+ 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,
+ :lifetime => assoc.lifetime,
+ :assoc_type => assoc.assoc_type)
+ end
+
+ def get_association(server_url, handle=nil)
+ assocs = handle.blank? ?
+ Association.find_all_by_server_url(server_url) :
+ Association.find_all_by_server_url_and_handle(server_url, handle)
+
+ assocs.reverse.each do |assoc|
+ a = assoc.from_record
+ if a.expired?
+ assoc.destroy
+ else
+ return a
+ end
+ end if assocs.any?
+
+ return nil
+ end
+
+ def remove_association(server_url, handle)
+ assoc = Association.find_by_server_url_and_handle(server_url, handle)
+ unless assoc.nil?
+ assoc.destroy
+ return true
+ end
+ false
+ end
+
+ def store_nonce(nonce)
+ use_nonce(nonce)
+ Nonce.create :nonce => nonce, :created => Time.now.to_i
+ end
+
+ def use_nonce(nonce)
+ nonce = Nonce.find_by_nonce(nonce)
+ return false if nonce.nil?
+
+ age = Time.now.to_i - nonce.created
+ nonce.destroy
+
+ age < 6.hours # max nonce age of 6 hours
+ end
+
+ def dumb?
+ false
+ end
+
+ # not part of the api, but useful
+ def gc
+ now = Time.now.to_i
+
+ # remove old nonces
+ nonces = Nonce.find(:all)
+ nonces.each {|n| n.destroy if now - n.created > 6.hours} unless nonces.nil?
+
+ # remove expired assocs
+ assocs = Association.find(:all)
+ assocs.each { |a| a.destroy if a.from_record.expired? } unless assocs.nil?
+ end
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/association.rb b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/association.rb
new file mode 100644
index 00000000..9d09ec47
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/association.rb
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+begin
+ require_gem "ruby-openid", ">= 1.0"
+rescue LoadError
+ require "openid"
+end
+
+module OpenIdConsumer
+ class Association < ActiveRecord::Base
+ set_table_name 'open_id_associations'
+ def from_record
+ OpenID::Association.new(handle, secret, issued, lifetime, assoc_type)
+ end
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/controller_methods.rb b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/controller_methods.rb
new file mode 100644
index 00000000..9a61aec9
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/controller_methods.rb
@@ -0,0 +1,69 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+begin
+ require_gem "ruby-openid", ">= 1.0"
+rescue LoadError
+ require "openid"
+end
+
+module OpenIdConsumer
+ module ControllerMethods
+ def self.included(controller)
+ controller.class_eval do
+ verify :method => :post, :only => :begin, :params => :openid_url, :redirect_to => { :action => 'index' },
+ :add_flash => { :error => "Enter an Identity URL to verify." }
+ verify :method => :get, :only => :complete, :redirect_to => { :action => 'index' }
+ before_filter :begin_open_id_auth, :only => :begin
+ before_filter :complete_open_id_auth, :only => :complete
+ attr_reader :open_id_response
+ attr_reader :open_id_fields
+ cattr_accessor :open_id_consumer_options
+ end
+ end
+
+ protected
+ def open_id_consumer
+ @open_id_consumer ||= OpenID::Consumer.new(
+ session[:openid_session] ||= {},
+ ActiveRecordOpenIdStore.new)
+ end
+
+ def begin_open_id_auth
+ @open_id_response = open_id_consumer.begin(params[:openid_url])
+ add_sreg_params!(@open_id_response) if @open_id_response.status == OpenID::SUCCESS
+ end
+
+ def complete_open_id_auth
+ @open_id_response = open_id_consumer.complete(params)
+ return unless open_id_response.status == OpenID::SUCCESS
+
+ @open_id_fields = open_id_response.extension_response('sreg')
+ logger.debug "***************** sreg params ***************"
+ logger.debug @open_id_fields.inspect
+ logger.debug "***************** sreg params ***************"
+ end
+
+ def add_sreg_params!(openid_response)
+ open_id_consumer_options.keys.inject({}) do |params, key|
+ value = open_id_consumer_options[key]
+ value = value.collect { |v| v.to_s.strip } * ',' if value.respond_to?(:collect)
+ openid_response.add_extension_arg('sreg', key.to_s, value.to_s)
+ end
+ end
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/nonce.rb b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/nonce.rb
new file mode 100644
index 00000000..67a7893d
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/nonce.rb
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+module OpenIdConsumer
+ class Nonce < ActiveRecord::Base
+ set_table_name 'open_id_nonces'
+ end
+end
diff --git a/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/setting.rb b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/setting.rb
new file mode 100644
index 00000000..6616dd6e
--- /dev/null
+++ b/tracks/vendor/plugins/openid_consumer_plugin/lib/open_id_consumer/setting.rb
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+module OpenIdConsumer
+ class Setting < ActiveRecord::Base
+ set_table_name 'open_id_settings'
+ end
+end
diff --git a/tracks/vendor/plugins/simple_ldap_authenticator/README b/tracks/vendor/plugins/simple_ldap_authenticator/README
new file mode 100644
index 00000000..dc8ca509
--- /dev/null
+++ b/tracks/vendor/plugins/simple_ldap_authenticator/README
@@ -0,0 +1,5 @@
+SimpleLdapAuthenticator
+=======================
+
+Allows for simple authentication to an LDAP server with a minimum of
+configuration. See the RDoc for details.
diff --git a/tracks/vendor/plugins/simple_ldap_authenticator/Rakefile b/tracks/vendor/plugins/simple_ldap_authenticator/Rakefile
new file mode 100644
index 00000000..f7c3459e
--- /dev/null
+++ b/tracks/vendor/plugins/simple_ldap_authenticator/Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the simple_ldap_authenticator plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the simple_ldap_authenticator plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'SimpleLdapAuthenticator'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
diff --git a/tracks/vendor/plugins/simple_ldap_authenticator/init.rb b/tracks/vendor/plugins/simple_ldap_authenticator/init.rb
new file mode 100644
index 00000000..85917669
--- /dev/null
+++ b/tracks/vendor/plugins/simple_ldap_authenticator/init.rb
@@ -0,0 +1,2 @@
+# Include hook code here
+#require 'simple_ldap_authenticator'
diff --git a/tracks/vendor/plugins/simple_ldap_authenticator/install.rb b/tracks/vendor/plugins/simple_ldap_authenticator/install.rb
new file mode 100644
index 00000000..f7732d37
--- /dev/null
+++ b/tracks/vendor/plugins/simple_ldap_authenticator/install.rb
@@ -0,0 +1 @@
+# Install hook code here
diff --git a/tracks/vendor/plugins/simple_ldap_authenticator/lib/simple_ldap_authenticator.rb b/tracks/vendor/plugins/simple_ldap_authenticator/lib/simple_ldap_authenticator.rb
new file mode 100644
index 00000000..2992d892
--- /dev/null
+++ b/tracks/vendor/plugins/simple_ldap_authenticator/lib/simple_ldap_authenticator.rb
@@ -0,0 +1,127 @@
+# SimpleLdapAuthenticator
+#
+# This plugin supports both Ruby/LDAP and Net::LDAP, defaulting to Ruby/LDAP
+# if it is available. If both are installed and you want to force the use of
+# Net::LDAP, set SimpleLdapAuthenticator.ldap_library = 'net/ldap'.
+
+# Allows for easily authenticating users via LDAP (or LDAPS). If authenticating
+# via LDAP to a server running on localhost, you should only have to configure
+# the login_format.
+#
+# Can be configured using the following accessors (with examples):
+# * login_format = '%s@domain.com' # Active Directory, OR
+# * login_format = 'cn=%s,cn=users,o=organization,c=us' # Other LDAP servers
+# * servers = ['dc1.domain.com', 'dc2.domain.com'] # names/addresses of LDAP servers to use
+# * use_ssl = true # for logging in via LDAPS
+# * port = 3289 # instead of 389 for LDAP or 636 for LDAPS
+# * logger = RAILS_DEFAULT_LOGGER # for logging authentication successes/failures
+#
+# The class is used as a global variable, you are not supposed to create an
+# instance of it. For example:
+#
+# require 'simple_ldap_authenticator'
+# SimpleLdapAuthenticator.servers = %w'dc1.domain.com dc2.domain.com'
+# SimpleLdapAuthenticator.use_ssl = true
+# SimpleLdapAuthenticator.login_format = '%s@domain.com'
+# SimpleLdapAuthenticator.logger = RAILS_DEFAULT_LOGGER
+# class LoginController < ApplicationController
+# def login
+# return redirect_to(:action=>'try_again') unless SimpleLdapAuthenticator.valid?(params[:username], params[:password])
+# session[:username] = params[:username]
+# end
+# end
+class SimpleLdapAuthenticator
+ class << self
+ @servers = ['127.0.0.1']
+ @use_ssl = false
+ @login_format = '%s'
+ attr_accessor :servers, :use_ssl, :port, :login_format, :logger, :connection, :ldap_library
+
+ # Load the required LDAP library, either 'ldap' or 'net/ldap'
+ def load_ldap_library
+ return if @ldap_library_loaded
+ if ldap_library
+ if ldap_library == 'net/ldap'
+ require 'net/ldap'
+ else
+ require 'ldap'
+ require 'ldap/control'
+ end
+ else
+ begin
+ require 'ldap'
+ require 'ldap/control'
+ ldap_library = 'ldap'
+ rescue LoadError
+ require 'net/ldap'
+ ldap_library = 'net/ldap'
+ end
+ end
+ @ldap_library_loaded = true
+ end
+
+ # The next LDAP server to which to connect
+ def server
+ servers[0]
+ end
+
+ # The connection to the LDAP server. A single connection is made and the
+ # connection is only changed if a server returns an error other than
+ # invalid password.
+ def connection
+ return @connection if @connection
+ load_ldap_library
+ @connection = if ldap_library == 'net/ldap'
+ Net::LDAP.new(:host=>server, :port=>(port), :encryption=>(:simple_tls if use_ssl))
+ else
+ (use_ssl ? LDAP::SSLConn : LDAP::Conn).new(server, port)
+ end
+ end
+
+ # The port to use. Defaults to 389 for LDAP and 636 for LDAPS.
+ def port
+ @port ||= use_ssl ? 636 : 389
+ end
+
+ # Disconnect from current LDAP server and use a different LDAP server on the
+ # next authentication attempt
+ def switch_server
+ self.connection = nil
+ servers << servers.shift
+ end
+
+ # Check the validity of a login/password combination
+ def valid?(login, password)
+ if ldap_library == 'net/ldap'
+ connection.authenticate(login_format % login.to_s, password.to_s)
+ begin
+ if connection.bind
+ logger.info("Authenticated #{login.to_s} by #{server}") if logger
+ true
+ else
+ logger.info("Error attempting to authenticate #{login.to_s} by #{server}: #{connection.get_operation_result.code} #{connection.get_operation_result.message}") if logger
+ switch_server unless connection.get_operation_result.code == 49
+ false
+ end
+ rescue Net::LDAP::LdapError => error
+ logger.info("Error attempting to authenticate #{login.to_s} by #{server}: #{error.message}") if logger
+ switch_server
+ false
+ end
+ else
+ connection.unbind if connection.bound?
+ begin
+ connection.bind(login_format % login.to_s, password.to_s)
+ connection.unbind
+ logger.info("Authenticated #{login.to_s} by #{server}") if logger
+ true
+ rescue LDAP::ResultError => error
+ connection.unbind if connection.bound?
+ logger.info("Error attempting to authenticate #{login.to_s} by #{server}: #{error.message}") if logger
+ switch_server unless error.message == 'Invalid credentials'
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/tracks/vendor/plugins/simple_ldap_authenticator/tasks/simple_ldap_authenticator_tasks.rake b/tracks/vendor/plugins/simple_ldap_authenticator/tasks/simple_ldap_authenticator_tasks.rake
new file mode 100644
index 00000000..1916c233
--- /dev/null
+++ b/tracks/vendor/plugins/simple_ldap_authenticator/tasks/simple_ldap_authenticator_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :simple_ldap_authenticator do
+# # Task goes here
+# end
\ No newline at end of file
diff --git a/tracks/vendor/plugins/simple_ldap_authenticator/test/simple_ldap_authenticator_test.rb b/tracks/vendor/plugins/simple_ldap_authenticator/test/simple_ldap_authenticator_test.rb
new file mode 100644
index 00000000..dfd92dae
--- /dev/null
+++ b/tracks/vendor/plugins/simple_ldap_authenticator/test/simple_ldap_authenticator_test.rb
@@ -0,0 +1,8 @@
+require 'test/unit'
+
+class SimpleLdapAuthenticatorTest < Test::Unit::TestCase
+ # Replace this with your real tests.
+ def test_this_plugin
+ flunk
+ end
+end