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}') %> - <%= render_partial 'context_form', context %> + <%= render :partial => 'context_form', :object => context %> diff --git a/tracks/app/views/context/list.rhtml b/tracks/app/views/context/list.rhtml index 3d31d992..ff489173 100644 --- a/tracks/app/views/context/list.rhtml +++ b/tracks/app/views/context/list.rhtml @@ -1,12 +1,8 @@
- <% for name in ["notice", "warning", "message"] %> -
><%= flash[name] %>
- <% end %> + <%= render_flash %>
- <% for context in @contexts %> - <%= render_partial( 'context_listing', context ) %> - <% end %> + <%= render :partial => 'context_listing', :collection => @contexts %>
<%= sortable_element 'list-contexts', get_listing_sortable_options %> diff --git a/tracks/app/views/context/show.rhtml b/tracks/app/views/context/show.rhtml index fa0db281..9e9d5e82 100644 --- a/tracks/app/views/context/show.rhtml +++ b/tracks/app/views/context/show.rhtml @@ -1,10 +1,6 @@
- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> +<%= render_flash %> <%= render :partial => "context/context", :locals => { :context => @context, :collapsible => false } %> <% unless @done.empty? -%> diff --git a/tracks/app/views/deferred/index.rhtml b/tracks/app/views/deferred/index.rhtml index 7613ee4f..29545190 100644 --- a/tracks/app/views/deferred/index.rhtml +++ b/tracks/app/views/deferred/index.rhtml @@ -1,10 +1,6 @@
- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> + <%= render_flash %> <%= render :partial => "items" %> diff --git a/tracks/app/views/feed/index.rhtml b/tracks/app/views/feed/index.rhtml index 863d9247..1fec9489 100644 --- a/tracks/app/views/feed/index.rhtml +++ b/tracks/app/views/feed/index.rhtml @@ -1,10 +1,6 @@
- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> + <%= render_flash %>
diff --git a/tracks/app/views/login/login.rhtml b/tracks/app/views/login/login.rhtml index 767c07c0..ee257dfb 100644 --- a/tracks/app/views/login/login.rhtml +++ b/tracks/app/views/login/login.rhtml @@ -1,29 +1,22 @@ -<%= start_form_tag :action=> "login" %> - +<% auth_schemes = Tracks::Config.auth_schemes -%>
- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> + <%= render_flash %> -

Please log in to use Tracks:

- - <% if @message %> -
<%= @message %>
- <% end %> +

Please log in to use Tracks:

+ <% if auth_schemes.include?('database') || auth_schemes.include?('open_id') %> + <%= start_form_tag :action=> 'login' %>
   Cancel
- + - + - + @@ -32,8 +25,28 @@
+ <%= end_form_tag %> + <% end %> + + <% if auth_schemes.include?('open_id') %> + <%= start_form_tag :action=> 'login', :action => 'begin' %> + + + + + + + + + + + + + +
+ <%= end_form_tag %> + <% end %> -<%= end_form_tag %> diff --git a/tracks/app/views/login/signup.rhtml b/tracks/app/views/login/signup.rhtml index a970e1f0..80d4db4e 100644 --- a/tracks/app/views/login/signup.rhtml +++ b/tracks/app/views/login/signup.rhtml @@ -3,11 +3,7 @@ <%= error_messages_for 'user' %>
- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> + <%= render_flash %>

<%= @page_title -%>

diff --git a/tracks/app/views/note/_notes.rhtml b/tracks/app/views/note/_notes.rhtml index cbb3a936..b561ff5c 100644 --- a/tracks/app/views/note/_notes.rhtml +++ b/tracks/app/views/note/_notes.rhtml @@ -26,7 +26,7 @@ :html => { :id => "form-note-#{note.id}", :class => "inline-form" }, :update => "note-#{note.id}-container", :complete => visual_effect(:appear, "note-#{note.id}-container") %> - <%= render_partial "note_edit_form", note %> + <%= render :partial => "note_edit_form", :object => note %> <%= end_form_tag %> diff --git a/tracks/app/views/note/index.rhtml b/tracks/app/views/note/index.rhtml index 653b23fc..24a1ebc1 100644 --- a/tracks/app/views/note/index.rhtml +++ b/tracks/app/views/note/index.rhtml @@ -4,7 +4,7 @@ <% else -%> <% for notes in @all_notes -%>
- <%= render_partial "notes", notes %> + <%= render :partial => 'notes', :object => notes %>
<% end -%> <% end -%> diff --git a/tracks/app/views/note/show.rhtml b/tracks/app/views/note/show.rhtml index 157ce3b4..424d3665 100644 --- a/tracks/app/views/note/show.rhtml +++ b/tracks/app/views/note/show.rhtml @@ -1,5 +1,5 @@
- <%= render_partial "notes", @note %> + <%= render :partial => 'notes', :object => @note %>
\ No newline at end of file diff --git a/tracks/app/views/project/_project_listing.rhtml b/tracks/app/views/project/_project_listing.rhtml index 62b748a3..c2f0c01e 100644 --- a/tracks/app/views/project/_project_listing.rhtml +++ b/tracks/app/views/project/_project_listing.rhtml @@ -32,7 +32,7 @@ :update => "container_#{project.id}", :complete => visual_effect(:appear, 'container_#{project.id}') %> - <%= render_partial 'project_form', project %> + <%= render :partial => 'project_form', :object => project %> diff --git a/tracks/app/views/project/_show_items.rhtml b/tracks/app/views/project/_show_items.rhtml index 67760371..747e800d 100644 --- a/tracks/app/views/project/_show_items.rhtml +++ b/tracks/app/views/project/_show_items.rhtml @@ -51,7 +51,7 @@ :html => { :id => "form-action-#{item.id}", :class => "inline-form" }, :update => "item-#{item.id}-container", :complete => visual_effect(:appear, "item-#{item.id}-container") %> - <%= render_partial 'todo/action_edit_form', item %> + <%= render :partial => 'todo/action_edit_form', :object => item %> <%= end_form_tag %> diff --git a/tracks/app/views/project/list.rhtml b/tracks/app/views/project/list.rhtml index b8c570bb..1867cd28 100644 --- a/tracks/app/views/project/list.rhtml +++ b/tracks/app/views/project/list.rhtml @@ -1,12 +1,8 @@
- <% for name in ["notice", "warning", "message"] %> -
><%= flash[name] %>
- <% end %> + <%= render_flash %>
-<% for project in @projects %> - <%= render_partial( 'project_listing', project ) %> -<% end %> + <%= render :partial => 'project_listing', :collection => @projects %>
<%= sortable_element 'list-projects', get_listing_sortable_options %>
diff --git a/tracks/app/views/project/show.rhtml b/tracks/app/views/project/show.rhtml index 7ba0ac83..1e6fbd70 100644 --- a/tracks/app/views/project/show.rhtml +++ b/tracks/app/views/project/show.rhtml @@ -1,10 +1,6 @@
- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> +<%= render_flash %> <%= render :partial => "project/project", :locals => { :project => @project, :collapsible => false } %> <% unless @done.empty? -%> diff --git a/tracks/app/views/shared/_flash.rhtml b/tracks/app/views/shared/_flash.rhtml new file mode 100644 index 00000000..f160b2ce --- /dev/null +++ b/tracks/app/views/shared/_flash.rhtml @@ -0,0 +1,7 @@ +<% for flash_key in [:notice, :warning, :message] %> + <% if flash.has_key?(flash_key) %> +
<%= flash[flash_key] %>
+ <% else%> + + <% end %> +<% end %> \ No newline at end of file diff --git a/tracks/app/views/todo/edit.rjs b/tracks/app/views/todo/edit.rjs index 779634c4..321e3c91 100644 --- a/tracks/app/views/todo/edit.rjs +++ b/tracks/app/views/todo/edit.rjs @@ -1,5 +1,4 @@ page["form-action-#{@item.id}"].replace_html :partial => 'todo/edit_form' page["item-#{@item.id}"].hide page["action-#{@item.id}-edit-form"].show -page.visual_effect :appear, "action-#{@item.id}-edit-form", :duration => 0.2 page.call "Form.focusFirstElement", "form-action-#{@item.id}" diff --git a/tracks/app/views/todo/index.rhtml b/tracks/app/views/todo/index.rhtml index 31d91b61..2644a1a6 100644 --- a/tracks/app/views/todo/index.rhtml +++ b/tracks/app/views/todo/index.rhtml @@ -1,10 +1,6 @@
- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> + <%= render_flash %> <%= render :partial => "context/context", :collection => @contexts_to_show, :locals => { :collapsible => true } %> diff --git a/tracks/app/views/todo/tickler.rhtml b/tracks/app/views/todo/tickler.rhtml index f8c98046..96b0a527 100644 --- a/tracks/app/views/todo/tickler.rhtml +++ b/tracks/app/views/todo/tickler.rhtml @@ -1,10 +1,6 @@
- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> + <%= render_flash %> <%= render :partial => "tickler_items" %> diff --git a/tracks/app/views/user/change_auth_type.rhtml b/tracks/app/views/user/change_auth_type.rhtml new file mode 100644 index 00000000..11d3aab5 --- /dev/null +++ b/tracks/app/views/user/change_auth_type.rhtml @@ -0,0 +1,20 @@ +
+ +

Change authentication type

+ + <%= render_flash %> + + <%= error_messages_for 'user' %> + +

Select your new authentication type and click 'Change Authentication Type' to replace your current settings.

+ + <%= start_form_tag :action => 'update_auth_type' %> +
<%= select('user', 'auth_type', Tracks::Config.auth_schemes.collect {|p| [ p, p ] }) %>
+
+
<%= submit_tag 'Change Authentication Type' %> <%= link_to 'Cancel', :action => 'preferences' %>
+ + <%= observe_field( :user_auth_type, :function => "$('open_id').style.display = value == 'open_id' ? 'block' : 'none'") %> + + <%= end_form_tag %> + +
\ 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] %> - <%= "
#{flash[name]}
" %> - <% end %> -<% end %> + <%= render_flash %> -<%= error_messages_for 'user' %> + <%= error_messages_for 'user' %> -

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.

<%= start_form_tag :action => 'update_password' %>
   Cancel
diff --git a/tracks/app/views/user/preferences.rhtml b/tracks/app/views/user/preferences.rhtml index 878f5c24..d3f19e5b 100644 --- a/tracks/app/views/user/preferences.rhtml +++ b/tracks/app/views/user/preferences.rhtml @@ -1,13 +1,9 @@ -
+
+ + <%= render_flash %>

Your preferences

- <% for name in ["notice", "warning", "message"] %> - <% if flash[name] %> - <%= "
#{flash[name]}
" %> - <% end %> - <% end %> - - <%= link_to "Edit preferences", :controller => 'user', :action => 'edit_preferences' %> | - <%= link_to 'Change password', :controller => 'user', :action => 'change_password' %> +
+ <%= link_to "Edit preferences »", { :controller => 'user', :action => 'edit_preferences'}, :class => 'edit_link' %> +
+ +

Your 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." %> -
- -
+
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' %> +
+ <%= link_to 'Change your password »', :controller => 'user', :action => 'change_password' %> +
+ <% end %> + <% if @user.auth_type == 'open_id' %> +

Your Open ID URL is <%= @user.open_id_url %>. +

+ <%= link_to 'Change Your Identity URL »', :controller => 'user', :action => 'change_auth_type' %>

+
+ <% end %>
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. +%> + +

<%%= @title %>

+ +
<%%= flash[:message] %>
+
<%%= flash[:error] %>
+ +

Please login with your OpenID Identity URL

+ +
+ <%%= start_form_tag :action => 'begin' %> + Identity URL: + + + +
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