Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' into i18n

Conflicts:
	app/controllers/sessions_controller.rb
	app/controllers/users_controller.rb
	app/views/forums/index.html.erb
	app/views/forums/show.html.erb
	app/views/layouts/_head.html.erb
	app/views/sessions/new.html.erb
	app/views/topics/show.html.erb
	app/views/users/new.rhtml
	public/javascripts/application.js
  • Loading branch information...
commit 4c237a2ed5deec7725ccbb87d49cf9af4ece3387 2 parents 7a8990e + 726bb9c
@courtenay courtenay authored
Showing with 1,183 additions and 99 deletions.
  1. 0  app/controllers/{application.rb → application_controller.rb}
  2. +20 −0 app/controllers/monitorships_controller.rb
  3. +10 −14 app/controllers/posts_controller.rb
  4. +58 −12 app/controllers/sessions_controller.rb
  5. +6 −4 app/controllers/users_controller.rb
  6. +1 −1  app/helpers/application_helper.rb
  7. +9 −0 app/models/monitorships_sweeper.rb
  8. +12 −0 app/models/posts_sweeper.rb
  9. +1 −1  app/models/topic.rb
  10. +16 −1 app/models/user.rb
  11. +1 −1  app/models/user/activation.rb
  12. +2 −2 app/models/user/states.rb
  13. +8 −1 app/models/user/validation.rb
  14. +3 −5 app/views/forums/index.html.erb
  15. +1 −1  app/views/forums/show.html.erb
  16. +2 −3 app/views/layouts/_head.html.erb
  17. +4 −0 app/views/monitorships/create.rjs
  18. +4 −0 app/views/monitorships/destroy.rjs
  19. +32 −3 app/views/sessions/new.html.erb
  20. +1 −1  app/views/topics/show.html.erb
  21. +5 −0 app/views/users/new.rhtml
  22. +1 −1  app/views/users/show.html.erb
  23. +3 −1 config/environment.rb
  24. +1 −0  config/initializers/application.rb
  25. +12 −3 config/routes.rb
  26. +28 −0 db/migrate/20090317123901_add_open_id_authentication_tables.rb
  27. +20 −1 db/schema.rb
  28. +1 −1  lib/authenticated_system.rb
  29. +2 −1  lib/html_formatting.rb
  30. +25 −3 public/javascripts/application.js
  31. +2 −0  spec/controllers/monitorships_controller_spec.rb
  32. +1 −26 spec/controllers/posts_controller_spec.rb
  33. +1 −1  spec/controllers/sessions_controller_spec.rb
  34. +2 −0  spec/controllers/sites_controller_spec.rb
  35. +17 −11 spec/controllers/users_controller_spec.rb
  36. +6 −0 spec/models/user_spec.rb
  37. +35 −0 vendor/plugins/open_id_authentication/CHANGELOG
  38. +231 −0 vendor/plugins/open_id_authentication/README
  39. +22 −0 vendor/plugins/open_id_authentication/Rakefile
  40. +11 −0 .../open_id_authentication/generators/open_id_authentication_tables/open_id_authentication_tables_generator.rb
  41. +20 −0 vendor/plugins/open_id_authentication/generators/open_id_authentication_tables/templates/migration.rb
  42. +26 −0 vendor/plugins/open_id_authentication/generators/upgrade_open_id_authentication_tables/templates/migration.rb
  43. +11 −0 ...ication/generators/upgrade_open_id_authentication_tables/upgrade_open_id_authentication_tables_generator.rb
  44. +18 −0 vendor/plugins/open_id_authentication/init.rb
  45. +240 −0 vendor/plugins/open_id_authentication/lib/open_id_authentication.rb
  46. +9 −0 vendor/plugins/open_id_authentication/lib/open_id_authentication/association.rb
  47. +55 −0 vendor/plugins/open_id_authentication/lib/open_id_authentication/db_store.rb
  48. +5 −0 vendor/plugins/open_id_authentication/lib/open_id_authentication/nonce.rb
  49. +23 −0 vendor/plugins/open_id_authentication/lib/open_id_authentication/request.rb
  50. +20 −0 vendor/plugins/open_id_authentication/lib/open_id_authentication/timeout_fixes.rb
  51. +30 −0 vendor/plugins/open_id_authentication/tasks/open_id_authentication_tasks.rake
  52. +32 −0 vendor/plugins/open_id_authentication/test/normalize_test.rb
  53. +46 −0 vendor/plugins/open_id_authentication/test/open_id_authentication_test.rb
  54. +14 −0 vendor/plugins/open_id_authentication/test/status_test.rb
  55. +17 −0 vendor/plugins/open_id_authentication/test/test_helper.rb
View
0  app/controllers/application.rb → app/controllers/application_controller.rb
File renamed without changes
View
20 app/controllers/monitorships_controller.rb
@@ -0,0 +1,20 @@
+class MonitorshipsController < ApplicationController
+ before_filter :login_required
+
+ def create
+ @monitorship = Monitorship.find_or_initialize_by_user_id_and_topic_id(current_user.id, params[:topic_id])
+ @monitorship.update_attribute :active, true
+ respond_to do |format|
+ format.html { redirect_to topic_path(params[:forum_id], params[:topic_id]) }
+ format.js
+ end
+ end
+
+ def destroy
+ Monitorship.update_all ['active = ?', false], ['user_id = ? and topic_id = ?', current_user.id, params[:topic_id]]
+ respond_to do |format|
+ format.html { redirect_to topic_path(params[:forum_id], params[:topic_id]) }
+ format.js
+ end
+ end
+end
View
24 app/controllers/posts_controller.rb
@@ -26,15 +26,6 @@ def show
end
end
- def new
- @post = Post.new
-
- respond_to do |format|
- format.html # new.html.erb
- format.xml { render :xml => @post }
- end
- end
-
def edit
respond_to do |format|
format.html # edit.html.erb
@@ -47,7 +38,7 @@ def create
respond_to do |format|
if @post.new_record?
- format.html { render :action => "new" }
+ format.html { redirect_to forum_topic_path(@forum, @topic) }
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
else
flash[:notice] = 'Post was successfully created.'
@@ -82,14 +73,19 @@ def destroy
protected
def find_parents
if params[:user_id]
- @parent = @user = User.find_by_permalink(params[:user_id])
+ @parent = @user = User.find(params[:user_id])
elsif params[:forum_id]
@parent = @forum = Forum.find_by_permalink(params[:forum_id])
@parent = @topic = @forum.topics.find_by_permalink(params[:topic_id]) if params[:topic_id]
end
end
-
+
def find_post
- @post = @topic.posts.find(params[:id])
+ post = @topic.posts.find(params[:id])
+ if post.user == current_user || current_user.admin?
+ @post = post
+ else
+ raise ActiveRecord::RecordNotFound
+ end
end
-end
+end
View
70 app/controllers/sessions_controller.rb
@@ -7,19 +7,14 @@ def new
end
def create
- params[:login] = params[:login].downcase
- self.current_user = current_site.users.authenticate(params[:login], params[:password])
-
- if logged_in?
- if params[:remember_me] == "1"
- current_user.remember_me
- cookies[:auth_token] = { :value => current_user.remember_token , :expires => current_user.remember_token_expires_at }
- end
- redirect_back_or_default('/')
- flash[:notice] = I18n.t 'txt.successful_login', :default => "Logged in successfully"
+ reset_session
+ if using_open_id?
+ cookies[:use_open_id] = {:value => '1', :expires => 1.year.from_now.utc}
+ open_id_authentication(params[:openid_url])
else
- flash[:error] = I18n.t 'txt.invalid_login', :default => "Invalid login"
- redirect_back_or_default(login_path)
+ cookies[:use_open_id] = {:value => '0', :expires => 1.year.ago.utc}
+ password_authentication params[:login].downcase, params[:password]
+ flash[:error] = "Invalid login"
end
end
@@ -30,4 +25,55 @@ def destroy
flash[:notice] = I18n.t 'txt.logged_out', :default => "You have been logged out."
redirect_back_or_default('/')
end
+
+ protected
+
+ def password_authentication(name, password)
+ if @current_user = current_site.users.authenticate(name, password)
+ successful_login
+ else
+ failed_login I18n.t 'txt.invalid_login', :default => "Invalid login"
+ end
+ end
+
+ def open_id_authentication(openid_url)
+ authenticate_with_open_id(openid_url, :required => [:nickname, :email]) do |result, openid_url, registration|
+ if result.successful?
+ @user = User.find_or_initialize_by_openid_url(openid_url)
+ @current_user = @user
+ if @current_user
+ if @user.new_record?
+ @user.login = openid_url
+ @user.email = registration['email']
+ @user.password = 123456
+ @user.site = Site.find(:first)
+ @user.display_name = registration['nickname']
+ @user.save(false)
+ end
+ successful_login
+ else
+ failed_login I18n.t 'txt.invalid_openid', :default => "Sorry, no user by the identity URL #{openid_url} exists"
+ end
+ else
+ failed_login result.message
+ end
+ end
+ end
+
+
+ private
+ def successful_login
+ flash[:notice] = I18n.t 'txt.successful_login', :default => "Logged in successfully"
+ new_cookie_flag = (params[:remember_me] == "1")
+ handle_remember_cookie! new_cookie_flag
+ session[:user_id] = @current_user.id
+ redirect_back_or_default('/')
+ end
+
+ def failed_login(message)
+ @remember_me = params[:remember_me]
+ flash[:error] = message
+ render :action => "new"
+ end
+
end
View
10 app/controllers/users_controller.rb
@@ -101,17 +101,19 @@ def make_admin
protected
def find_user
@user = if admin?
- current_site.all_users.find_by_permalink(params[:id])
+ current_site.all_users.find params[:id]
+ elsif params[:id] == current_user.id
+ current_user
else
- current_site.users.find_by_permalink(params[:id])
+ current_site.users.find params[:id]
end or raise ActiveRecord::RecordNotFound
end
def authorized?
- admin? || params[:id].blank? || params[:id] == current_user.permalink
+ admin? || params[:id].blank? || params[:id] == current_user.id.to_s
end
def render_or_redirect_for_captcha_failure
render :action => 'new'
end
-end
+end
View
2  app/helpers/application_helper.rb
@@ -58,7 +58,7 @@ def search_path(atom = false)
else
:search
end
- atom ? send("formatted_#{prefix}_posts_path", options.update(:format => :atom)) : send("#{prefix}_posts_path", options)
+ atom ? send("#{prefix}_posts_path", options.update(:format => :atom)) : send("#{prefix}_posts_path", options)
end
def for_moderators_of(record, &block)
View
9 app/models/monitorships_sweeper.rb
@@ -0,0 +1,9 @@
+class MonitorshipsSweeper < ActionController::Caching::Sweeper
+ observe Monitorship
+
+ def after_save(monitorship)
+ FileUtils.rm_rf File.join(RAILS_ROOT, 'public', 'users', monitorship.user_id.to_s)
+ end
+
+ alias_method :after_destroy, :after_save
+end
View
12 app/models/posts_sweeper.rb
@@ -0,0 +1,12 @@
+class PostsSweeper < ActionController::Caching::Sweeper
+ observe Post
+
+ def after_save(post)
+ FileUtils.rm_rf File.join(RAILS_ROOT, 'public', 'forums', post.forum_id.to_s, 'posts.rss')
+ FileUtils.rm_rf File.join(RAILS_ROOT, 'public', 'forums', post.forum_id.to_s, 'topics', "#{post.topic_id}.rss")
+ FileUtils.rm_rf File.join(RAILS_ROOT, 'public', 'users')
+ FileUtils.rm_rf File.join(RAILS_ROOT, 'public', 'posts.rss')
+ end
+
+ alias_method :after_destroy, :after_save
+end
View
2  app/models/topic.rb
@@ -65,7 +65,7 @@ def update_cached_post_fields(post)
self.class.update_all(['last_updated_at = ?, last_user_id = ?, last_post_id = ?, posts_count = ?',
remaining_post.created_at, remaining_post.user_id, remaining_post.id, posts.count], ['id = ?', id])
else
- self.destroy
+ destroy
end
end
View
17 app/models/user.rb
@@ -65,6 +65,21 @@ def seen!
end
def to_param
- permalink
+ id.to_s # permalink || login
end
+
+ def openid_url=(value)
+ write_attribute :openid_url, value.blank? ? nil : OpenIdAuthentication.normalize_identifier(value)
+ end
+
+ def using_openid
+ self.openid_url.blank? ? false : true
+ end
+
+ def to_xml(options = {})
+ options[:except] ||= []
+ options[:except] << :email << :login_key << :login_key_expires_at << :password_hash << :openid_url << :activated << :admin
+ super
+ end
+
end
View
2  app/models/user/activation.rb
@@ -5,7 +5,7 @@ class User
after_create :set_first_user_as_activated
def set_first_user_as_activated
- register! && activate! if site.nil? or site.users.size <= 1
+ register! && activate! if site.nil? or site.users.count <= 1
end
# Returns true if the user has just been activated.
View
4 app/models/user/states.rb
@@ -38,7 +38,7 @@ def do_activation
self.deleted_at = nil
self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
- UserMailer.deliver_signup_notification(self)
+ UserMailer.deliver_signup_notification(self) unless using_openid
end
protected
@@ -52,7 +52,7 @@ def do_activate
self.deleted_at = nil
self.activation_code = ""
- UserMailer.deliver_activation(self)
+ UserMailer.deliver_activation(self) unless using_openid
end
def remove_moderatorships
View
9 app/models/user/validation.rb
@@ -14,6 +14,8 @@ class User
validates_uniqueness_of :login, :email, :scope => :site_id
before_save :encrypt_password
before_create :set_first_user_as_admin
+ # validates_email_format_of :email, :message=>"is invalid"
+ validates_uniqueness_of :openid_url, :case_sensitive => false, :allow_nil => true
# prevents a user from submitting a crafted form that bypasses activation
# anything else you want your user to change should be added here.
@@ -42,7 +44,12 @@ def encrypt_password
self.crypted_password = encrypt(password)
end
- def password_required?
+ def using_openid
+ self.openid_url.blank? ? false : true
+ end
+
+ def password_required?
+ return false if using_openid
crypted_password.blank? || !password.blank?
end
View
8 app/views/forums/index.html.erb
@@ -15,11 +15,9 @@
<h1 style="margin-top:0;"><%= I18n.t 'txt.forums', :default => 'Forums' %></h1>
<p class="subtitle">
-<%= feed_icon_tag I18n.t('txt.recent_posts', :default => "Recent Posts"), formatted_posts_path(:format => 'atom') %>
+<%= feed_icon_tag I18n.t('txt.recent_posts', :default => "Recent Posts"), posts_path(:format => 'atom') %>
<%= I18n.t 'txt.count_topics', :count => current_site.topics.size, :num => number_with_delimiter(current_site.topics.size) %>,
-<%= I18n.t 'txt.count_posts', :count => current_site.posts.size, :num => number_with_delimiter(current_site.posts.size) %>
-<%# OPTIMIZE ME voice_count %>
-
+<%= I18n.t 'txt.count_posts', :count => current_site.posts.size, :num => number_with_delimiter(current_site.posts.size) %>
</p>
<table border="0" cellspacing="0" cellpadding="0" class="wide forums">
@@ -28,7 +26,7 @@
<th class="la" width="30%" colspan="1"><%= I18n.t 'txt.latest_post', :default => 'Last Post' %></th>
</tr>
<% for forum in @forums do %>
- <tr>
+ <tr class="forum" id="forum_<%= forum.id %>_row">
<td class="vat c1">
<% if recent_forum_activity(forum) %>
View
2  app/views/forums/show.html.erb
@@ -27,7 +27,7 @@
<h1 style="margin-top:0.5em"><%= h @forum.name %></h1>
<p class="subtitle">
- <%= feed_icon_tag @forum.name, formatted_forum_posts_path(@forum, :atom) %>
+ <%= feed_icon_tag @forum.name, forum_posts_path(@forum, :format => :atom) %>
<%= I18n.t 'txt.count_topics', :count => @forum.topics.size, :num => number_with_delimiter(@forum.topics.size) %>,
<%= I18n.t 'txt.count_posts', :count => @forum.posts.size, :num => number_with_delimiter(@forum.posts.size) %>
</p>
View
5 app/views/layouts/_head.html.erb
@@ -1,12 +1,11 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title><%=h @current_site && current_site.name || I18n.t('txt.beast_title', :default => 'Altered Beast') %><%= " - #{h @page_title}" if @page_title %></title>
<%= stylesheet_link_tag 'display' %>
<%= stylesheet_link_tag 'captcha' %>
- <%= javascript_include_tag "prototype", "effects", "lowpro", "application", :cache => 'beast' %>
+ <%= javascript_include_tag "prototype", "effects", "lowpro", "time", "application", :cache => "beast" %>
<% unless @feed_icons.blank? -%>
<% @feed_icons.each do |feed| -%>
<%= auto_discovery_link_tag :atom, feed[:url], :title => "Subscribe to '#{feed[:title]}'" %>
@@ -28,7 +27,7 @@
<li><%= link_to_function I18n.t('txt.search', :default => 'Search'), "#", :href => root_path, :id => 'search-link' %></li>
<% if @current_site and logged_in? -%>
- <li class="login"><%= link_to current_user.login, user_path(current_user) %></li>
+ <li class="login"><%= link_to current_user.display_name, user_path(current_user) %></li>
<li class="logout"><%= link_to I18n.t('txt.settings', :default => 'Settings'), settings_path %></li>
<li class="logout"><%= link_to I18n.t('txt.logout', :default => 'Logout'), logout_path(:to => CGI.escape(request.request_uri)) %></li>
<% else -%>
View
4 app/views/monitorships/create.rjs
@@ -0,0 +1,4 @@
+#page["monitorship-icon-topics-#{params[:topic_id]}"].remove_class_name(:grey)
+#page["monitorship-icon-topics-#{params[:topic_id]}"].add_class_name(:green)
+
+page[:monitor_label].innerHTML = "Monitoring topic"[]
View
4 app/views/monitorships/destroy.rjs
@@ -0,0 +1,4 @@
+#page["monitorship-icon-topics-#{params[:topic_id]}"].remove_class_name(:green)
+#page["monitorship-icon-topics-#{params[:topic_id]}"].add_class_name(:darkgrey)
+
+page[:monitor_label].innerHTML = "Monitor topic"[]
View
35 app/views/sessions/new.html.erb
@@ -1,16 +1,45 @@
<h1><%= I18n.t('txt.login', :default => 'Login') %></h1>
<% form_tag session_path do -%>
+<div id="openid_fields" style="display:none">
+<p>
+ <label for="openid_url">Login with OpenID</label>
+ <br />
+ <%= text_field_tag 'openid_url', params[:openid_url], :class => "openid_url" %>
+</p>
+<p>or login with <a href="#" onclick="LoginForm.setToPassword()">username/password</a></p>
+</div>
+
+<div id="password_fields">
<p><%= label_tag I18n.t('txt.login_name', :default => 'Login') %><br />
<%= text_field_tag 'login', @login %></p>
<p><%= label_tag I18n.t('txt.password', :default => 'Password') %><br/>
<%= password_field_tag 'password', nil %></p>
-<!-- Uncomment this if you want this functionality
<p><%= label_tag 'remember_me', I18n.t('txt.remember_me', :default => 'Remember me') %>
<%= check_box_tag 'remember_me', '1', @remember_me %></p>
--->
-<p><%= submit_tag I18n.t('txt.log_in', :default => 'Log in') %></p>
+<p><%= submit_tag I18n.t('txt.log_in', :default => 'Log in') %>
+ <%= link_to_function(I18n.t('reset password', :default => "reset password"), "$('reset-password').toggle();") %>
+ <a href="#" onclick="LoginForm.setToOpenID()"><%= I18n.t('txt.or_login_with_openid', :default => 'or login with OpenID') %></a>
+</p>
+</div>
+
+<p><%= submit_tag 'Log in' %></p>
+
<% end -%>
+
+<% form_tag users_path, :id => 'reset-password', :style => 'display:none' do -%>
+
+ <hr />
+ <h5><%= 'Reset Password'[] %></h5>
+
+ <p><%= 'Enter your email, and a brand new login key will be sent to you. Click the link in the email to log in, and then change your password.'[:email_directions] %></p>
+ <p><%= text_field_tag :email, "", :size => 30 %></p>
+ <p><%= submit_tag 'E-mail me the link'[:email_submit] %>
+ or <%= link_to_function('cancel'[], "$('reset-password').hide()") %></p>
+
+<% end -%>
+
+<%= javascript_tag "$('openid_input').focus();"%>
View
2  app/views/topics/show.html.erb
@@ -13,7 +13,7 @@
<% end # right content -%>
-<% if false#logged_in? %>
+<% if logged_in? %>
<% form_tag monitorship_path(@forum, @topic), :style => 'margin-top:0em; float:right;' do -%>
<div>
View
5 app/views/users/new.rhtml
@@ -12,7 +12,12 @@
<p><label for="password_confirmation"><%= I18n.t 'txt.password_confirm', :default => 'Confirm Password' %></label><br/>
<%= f.password_field :password_confirmation %></p>
+<p><label for="user_openid_url"><%= I18n.t 'txt.openid_url', :default => "OpenID URL" %></label><br />
+<%= f.text_field :openid_url %>
+</p>
+
<%= render :partial => '/captcha' %>
<p><%= submit_tag I18n.t('txt.sign_up', :default => 'Sign up') %></p>
+
<% end -%>
View
2  app/views/users/show.html.erb
@@ -46,7 +46,7 @@
<h1><%=h @user.display_name %> </h1>
<p class="subtitle">
- <%= feed_icon_tag @user.display_name, formatted_user_posts_path(:user_id => @user, :format => :rss) %>
+ <%= feed_icon_tag @user.display_name, user_posts_path(:user_id => @user, :format => :rss) %>
<span>
<%= I18n.t 'txt.count_topics', :count => @user.topics.size, :num => number_with_delimiter(@user.topics.size) %>,
<%= I18n.t 'txt.count_posts', :count => @user.posts.size, :num => number_with_delimiter(@user.posts.size) %>
View
4 config/environment.rb
@@ -5,7 +5,7 @@
# ENV['RAILS_ENV'] ||= 'production'
# Specifies gem version of Rails to use when vendor/rails is not present
-RAILS_GEM_VERSION = '2.2.2' unless defined? RAILS_GEM_VERSION
+RAILS_GEM_VERSION = '2.3.3' unless defined? RAILS_GEM_VERSION
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
@@ -18,6 +18,8 @@
config.gem 'mislav-will_paginate', :lib => "will_paginate",
:source => "http://gems.github.com"
+
+ config.gem "bluecloth"
config.gem 'RedCloth', :lib => "redcloth",
:source => "http://code.whytheluckystiff.net"
View
1  config/initializers/application.rb
@@ -0,0 +1 @@
+OpenIdAuthentication.store = :file
View
15 config/routes.rb
@@ -1,7 +1,10 @@
ActionController::Routing::Routes.draw do |map|
- map.resource :session
+ map.open_id_complete '/session',
+ :controller => "sessions", :action => "create",
+ :requirements => { :method => :get }
- map.resources :sites, :moderatorships
+ map.resources :sites, :moderatorships, :monitorship
+
map.resources :forums, :has_many => :posts do |forum|
forum.resources :topics do |topic|
@@ -11,8 +14,8 @@
forum.resources :posts
end
+ map.user '/users/:id', :controller => "users", :action => "show"
map.resources :posts, :collection => {:search => :get}
-
map.resources :users, :member => { :suspend => :put,
:settings => :get,
:make_admin => :put,
@@ -26,5 +29,11 @@
map.logout '/logout', :controller => 'sessions', :action => 'destroy'
map.settings '/settings', :controller => 'users', :action => 'settings'
map.resource :session
+
+ map.with_options :controller => 'posts', :action => 'monitored' do |map|
+ map.formatted_monitored_posts 'users/:user_id/monitored.:format'
+ map.monitored_posts 'users/:user_id/monitored'
+ end
+
map.root :controller => 'forums', :action => 'index'
end
View
28 db/migrate/20090317123901_add_open_id_authentication_tables.rb
@@ -0,0 +1,28 @@
+class AddOpenIdAuthenticationTables < ActiveRecord::Migration
+ def self.up
+ create_table "open_id_authentication_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_authentication_nonces", :force => true do |t|
+ t.column "nonce", :string
+ t.column "created", :integer
+ end
+
+ create_table "open_id_authentication_settings", :force => true do |t|
+ t.column "setting", :string
+ t.column "value", :binary
+ end
+ end
+
+ def self.down
+ drop_table "open_id_authentication_associations"
+ drop_table "open_id_authentication_nonces"
+ drop_table "open_id_authentication_settings"
+ end
+end
View
21 db/schema.rb
@@ -9,7 +9,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20081213180202) do
+ActiveRecord::Schema.define(:version => 20090317123901) do
create_table "brain_busters", :force => true do |t|
t.string "question"
@@ -46,6 +46,25 @@
t.boolean "active", :default => true
end
+ create_table "open_id_authentication_associations", :force => true do |t|
+ t.binary "server_url"
+ t.string "handle"
+ t.binary "secret"
+ t.integer "issued"
+ t.integer "lifetime"
+ t.string "assoc_type"
+ end
+
+ create_table "open_id_authentication_nonces", :force => true do |t|
+ t.string "nonce"
+ t.integer "created"
+ end
+
+ create_table "open_id_authentication_settings", :force => true do |t|
+ t.string "setting"
+ t.binary "value"
+ end
+
create_table "posts", :force => true do |t|
t.integer "user_id"
t.integer "topic_id"
View
2  lib/authenticated_system.rb
@@ -201,7 +201,7 @@ def kill_remember_cookie!
end
def send_remember_cookie!
- cookies[:auth_token] = {
+ cookies['auth_token'] = {
:value => @current_user.remember_token,
:expires => @current_user.remember_token_expires_at }
end
View
3  lib/html_formatting.rb
@@ -1,3 +1,4 @@
+require 'redcloth'
module HtmlFormatting
protected
@@ -5,7 +6,7 @@ def format_attributes
self.class.formatted_attributes.each do |attr|
raw = read_attribute attr
linked = auto_link(raw) { |text| truncate(text, :length => 50) }
- textilized = RedCloth.new(linked, [:hard_breaks])
+ textilized = ::RedCloth.new(linked, [:hard_breaks])
textilized.hard_breaks = true if textilized.respond_to?("hard_breaks=")
write_attribute "#{attr}_html", white_list_sanitizer.sanitize(textilized.to_html)
end
View
28 public/javascripts/application.js
@@ -80,6 +80,19 @@ var PostForm = {
}
}
+var RowManager = {
+ addMouseBehavior : function(ele){
+ ele.onmouseover = function(e){
+ ele.addClassName('topic_over');
+ }
+
+ ele.onmouseout = function(e){
+ ele.removeClassName('topic_over');
+ }
+ }
+};
+
+
Event.addBehavior({
'#search, #reply': function() { this.hide() },
'#search-link:click': function() {
@@ -87,11 +100,20 @@ Event.addBehavior({
$('search_box').focus();
return false
},
-
+
+ 'tr.forum' : function() {
+ RowManager.addMouseBehavior(this);
+ },
+
+ 'tr.topic' : function(){
+ RowManager.addMouseBehavior(this);
+ },
+
'tr.post': function() {
var postId = this.id.match(/^post_(\d+)-/)[1]
- var anchor = this.down(".edit a")
- if(anchor) { PostForm.edit.attach(anchor, postId) }
+ var anchor = this.down(".edit a")
+ if(anchor) { PostForm.edit.attach(anchor, postId) };
+ RowManager.addMouseBehavior(this);
},
'#reply-link': function() {
View
2  spec/controllers/monitorships_controller_spec.rb
@@ -0,0 +1,2 @@
+
+# TODO
View
27 spec/controllers/posts_controller_spec.rb
@@ -132,31 +132,6 @@ def self.included(base)
end
end
-describe PostsController, "GET #new" do
- include PostsControllerParentObjects
- define_models
- act! { get :new, :forum_id => @forum.to_param, :topic_id => @topic.to_param }
- before do
- @post = Post.new
- end
-
- it_assigns :forum, :topic, :parent => lambda { @topic }
-
- it "assigns @post" do
- act!
- assigns[:post].should be_new_record
- end
-
- it_renders :template, :new, :pending => true
-
- describe PostsController, "(xml)" do
- define_models
- act! { get :new, :forum_id => @forum.to_param, :topic_id => @topic.to_param, :format => 'xml' }
- it_assigns :forum, :topic, :parent => lambda { @topic }
- it_renders :xml, :post
- end
-end
-
describe PostsController, "GET #edit" do
include PostsControllerParentObjects
act! { get :edit, :forum_id => @forum.to_param, :topic_id => @topic.to_param, :id => @post.to_param }
@@ -185,7 +160,7 @@ def self.included(base)
act! { post :create, :forum_id => @forum.to_param, :topic_id => @topic.to_param, :post => {:body => ''} }
it_assigns :post, :forum, :topic, :parent => lambda { @topic }
- it_renders :template, :new, :pending => true
+ it_redirects_to { forum_topic_url(@forum, @topic) }
end
describe PostsController, "(successful creation, xml)" do
View
2  spec/controllers/sessions_controller_spec.rb
@@ -57,7 +57,7 @@ def attempt_login(user = nil, password = nil, options = {})
it_redirects_to { '/' }
it 'deletes token on logout' do
- acting.cookies["auth_token"].should == []
+ acting.cookies["auth_token"].should be_nil
end
end
View
2  spec/controllers/sites_controller_spec.rb
@@ -51,6 +51,7 @@
act! { get :new }
before do
+ @controller.stub!(:admin_required).and_return(true)
login_as :default
end
@@ -95,6 +96,7 @@
before do
login_as :default
@attributes = {:name => 'yow'}
+ @controller.stub!(:admin_required).and_return(true)
end
describe SitesController, "(successful creation)" do
View
28 spec/controllers/users_controller_spec.rb
@@ -71,7 +71,8 @@
end
it "sends an email to the user on create" do
- pending "Email functionality has not been written"
+ create_user :login => "admin", :email => "admin@example.com"
+ response.should be_redirect
lambda{ create_user }.should change(ActionMailer::Base.deliveries, :size).by(1)
end
@@ -133,7 +134,7 @@ def create_user(options = {})
it "sets admin" do
user = users(:default)
user.admin.should be_false
- put :make_admin, :id => users(:default).to_param, :user => { :admin => "1" }
+ put :make_admin, :id => users(:default).id, :user => { :admin => "1" }
user.reload.admin.should be_true
end
@@ -141,13 +142,14 @@ def create_user(options = {})
user = users(:default)
user.update_attribute :admin, true
user.admin.should be_true
- put :make_admin, :id => users(:default).to_param, :user => { }
+ put :make_admin, :id => users(:default).id, :user => { }
user.reload.admin.should be_false
end
end
end
describe UsersController, "PUT #update" do
+ define_models :users
before do
login_as :default
current_site :default
@@ -156,7 +158,7 @@ def create_user(options = {})
describe UsersController, "(successful save)" do
define_models
- act! { put :update, :id => @user.to_param, :user => @attributes }
+ act! { put :update,{ :id => @user.id, :user => @attributes }}
before do
@user.stub!(:save).and_return(true)
@@ -167,18 +169,22 @@ def create_user(options = {})
describe "updating from edit form" do
define_models :stubbed
- %w(display_name openid_url website bio).each do |field|
- it "should update #{field}" do
- put :update, :id => @user.to_param, :user => { field, "test" }
- assigns(:user).attributes[field].should == "test"
+ %w(display_name website bio).each do |field|
+ it "should update #{field}" do
+ put :update, :id => @user.id, :user => { field => "test" }
+ assigns(:user).attributes[field].should == "test"
+ end
end
+ it "should update openid_url" do
+ put :update, :id => @user.id, :user => { 'openid_url' => 'test' }
+ assigns(:user).attributes['openid_url'].should == 'http://test/'
end
end
end
describe UsersController, "(successful save, xml)" do
define_models
- act! { put :update, :id => @user.to_param, :user => @attributes, :format => 'xml' }
+ act! { put :update, :id => @user.id, :user => @attributes, :format => 'xml' }
before do
@user.stub!(:save).and_return(true)
@@ -190,7 +196,7 @@ def create_user(options = {})
describe UsersController, "(unsuccessful save)" do
define_models
- act! { put :update, :id => @user.to_param, :user => {:email => ''} }
+ act! { put :update, :id => @user.id, :user => {:email => ''} }
it_assigns :user
it_renders :template, :edit
@@ -198,7 +204,7 @@ def create_user(options = {})
describe UsersController, "(unsuccessful save, xml)" do
define_models
- act! { put :update, :id => @user.to_param, :user => {:email => ''}, :format => 'xml' }
+ act! { put :update, :id => @user.id, :user => {:email => ''}, :format => 'xml' }
it_assigns :user
it_renders :xml, :status => :unprocessable_entity do
View
6 spec/models/user_spec.rb
@@ -13,6 +13,12 @@
end
end
+ it 'logs in with openid' do
+ u = sites(:default).users.new(:openid_url => 'http://foo', :email => 'zoe@girl.com')
+ u.login = 'zoegirl'
+ assert u.valid?, u.errors.inspect
+ end
+
it 'increments User.count' do
@creating_user.should change(User, :count).by(1)
end
View
35 vendor/plugins/open_id_authentication/CHANGELOG
@@ -0,0 +1,35 @@
+* Fake HTTP method from OpenID server since they only support a GET. Eliminates the need to set an extra route to match the server's reply. [Josh Peek]
+
+* OpenID 2.0 recommends that forms should use the field name "openid_identifier" rather than "openid_url" [Josh Peek]
+
+* Return open_id_response.display_identifier to the application instead of .endpoints.claimed_id. [nbibler]
+
+* Add Timeout protection [Rick]
+
+* An invalid identity url passed through authenticate_with_open_id will no longer raise an InvalidOpenId exception. Instead it will return Result[:missing] to the completion block.
+
+* Allow a return_to option to be used instead of the requested url [Josh Peek]
+
+* Updated plugin to use Ruby OpenID 2.x.x [Josh Peek]
+
+* Tied plugin to ruby-openid 1.1.4 gem until we can make it compatible with 2.x [DHH]
+
+* Use URI instead of regexps to normalize the URL and gain free, better matching #8136 [dkubb]
+
+* Allow -'s in #normalize_url [Rick]
+
+* remove instance of mattr_accessor, it was breaking tests since they don't load ActiveSupport. Fix Timeout test [Rick]
+
+* Throw a InvalidOpenId exception instead of just a RuntimeError when the URL can't be normalized [DHH]
+
+* Just use the path for the return URL, so extra query parameters don't interfere [DHH]
+
+* Added a new default database-backed store after experiencing trouble with the filestore on NFS. The file store is still available as an option [DHH]
+
+* Added normalize_url and applied it to all operations going through the plugin [DHH]
+
+* Removed open_id? as the idea of using the same input box for both OpenID and username has died -- use using_open_id? instead (which checks for the presence of params[:openid_url] by default) [DHH]
+
+* Added OpenIdAuthentication::Result to make it easier to deal with default situations where you don't care to do something particular for each error state [DHH]
+
+* Stop relying on root_url being defined, we can just grab the current url instead [DHH]
View
231 vendor/plugins/open_id_authentication/README
@@ -0,0 +1,231 @@
+OpenIdAuthentication
+====================
+
+Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first:
+
+ gem install ruby-openid
+
+To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb
+from that gem.
+
+The specification used is http://openid.net/specs/openid-authentication-2_0.html.
+
+
+Prerequisites
+=============
+
+OpenID authentication uses the session, so be sure that you haven't turned that off. It also relies on a number of
+database tables to store the authentication keys. So you'll have to run the migration to create these before you get started:
+
+ rake open_id_authentication:db:create
+
+Or, use the included generators to install or upgrade:
+
+ ./script/generate open_id_authentication_tables MigrationName
+ ./script/generate upgrade_open_id_authentication_tables MigrationName
+
+Alternatively, you can use the file-based store, which just relies on on tmp/openids being present in RAILS_ROOT. But be aware that this store only works if you have a single application server. And it's not safe to use across NFS. It's recommended that you use the database store if at all possible. To use the file-based store, you'll also have to add this line to your config/environment.rb:
+
+ OpenIdAuthentication.store = :file
+
+This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
+If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb.
+
+The plugin also expects to find a root_url method that points to the home page of your site. You can accomplish this by using a root route in config/routes.rb:
+
+ map.root :controller => 'articles'
+
+This plugin relies on Rails Edge revision 6317 or newer.
+
+
+Example
+=======
+
+This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
+salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
+not a destination.
+
+Note that the User model referenced in the simple example below has an 'identity_url' attribute. You will want to add the same or similar field to whatever
+model you are using for authentication.
+
+Also of note is the following code block used in the example below:
+
+ authenticate_with_open_id do |result, identity_url|
+ ...
+ end
+
+In the above code block, 'identity_url' will need to match user.identity_url exactly. 'identity_url' will be a string in the form of 'http://example.com' -
+If you are storing just 'example.com' with your user, the lookup will fail.
+
+There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
+
+ OpenIdAuthentication.normalize_url(user.identity_url)
+
+The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
+It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
+Use the above code in your User model and validate OpenID URLs before saving them.
+
+config/routes.rb
+
+ map.root :controller => 'articles'
+ map.resource :session
+
+
+app/views/sessions/new.erb
+
+ <% form_tag(session_url) do %>
+ <p>
+ <label for="name">Username:</label>
+ <%= text_field_tag "name" %>
+ </p>
+
+ <p>
+ <label for="password">Password:</label>
+ <%= password_field_tag %>
+ </p>
+
+ <p>
+ ...or use:
+ </p>
+
+ <p>
+ <label for="openid_identifier">OpenID:</label>
+ <%= text_field_tag "openid_identifier" %>
+ </p>
+
+ <p>
+ <%= submit_tag 'Sign in', :disable_with => "Signing in&hellip;" %>
+ </p>
+ <% end %>
+
+app/controllers/sessions_controller.rb
+ class SessionsController < ApplicationController
+ def create
+ if using_open_id?
+ open_id_authentication
+ else
+ password_authentication(params[:name], params[:password])
+ end
+ end
+
+
+ protected
+ def password_authentication(name, password)
+ if @current_user = @account.users.authenticate(params[:name], params[:password])
+ successful_login
+ else
+ failed_login "Sorry, that username/password doesn't work"
+ end
+ end
+
+ def open_id_authentication
+ authenticate_with_open_id do |result, identity_url|
+ if result.successful?
+ if @current_user = @account.users.find_by_identity_url(identity_url)
+ successful_login
+ else
+ failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
+ end
+ else
+ failed_login result.message
+ end
+ end
+ end
+
+
+ private
+ def successful_login
+ session[:user_id] = @current_user.id
+ redirect_to(root_url)
+ end
+
+ def failed_login(message)
+ flash[:error] = message
+ redirect_to(new_session_url)
+ end
+ end
+
+
+
+If you're fine with the result messages above and don't need individual logic on a per-failure basis,
+you can collapse the case into a mere boolean:
+
+ def open_id_authentication
+ authenticate_with_open_id do |result, identity_url|
+ if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
+ successful_login
+ else
+ failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
+ end
+ end
+ end
+
+
+Simple Registration OpenID Extension
+====================================
+
+Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension
+
+You can support it in your app by changing #open_id_authentication
+
+ def open_id_authentication(identity_url)
+ # Pass optional :required and :optional keys to specify what sreg fields you want.
+ # Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
+ authenticate_with_open_id(identity_url,
+ :required => [ :nickname, :email ],
+ :optional => :fullname) do |result, identity_url, registration|
+ case result.status
+ when :missing
+ failed_login "Sorry, the OpenID server couldn't be found"
+ when :invalid
+ failed_login "Sorry, but this does not appear to be a valid OpenID"
+ when :canceled
+ failed_login "OpenID verification was canceled"
+ when :failed
+ failed_login "Sorry, the OpenID verification failed"
+ when :successful
+ if @current_user = @account.users.find_by_identity_url(identity_url)
+ assign_registration_attributes!(registration)
+
+ if current_user.save
+ successful_login
+ else
+ failed_login "Your OpenID profile registration failed: " +
+ @current_user.errors.full_messages.to_sentence
+ end
+ else
+ failed_login "Sorry, no user by that identity URL exists"
+ end
+ end
+ end
+ end
+
+ # registration is a hash containing the valid sreg keys given above
+ # use this to map them to fields of your user model
+ def assign_registration_attributes!(registration)
+ model_to_registration_mapping.each do |model_attribute, registration_attribute|
+ unless registration[registration_attribute].blank?
+ @current_user.send("#{model_attribute}=", registration[registration_attribute])
+ end
+ end
+ end
+
+ def model_to_registration_mapping
+ { :login => 'nickname', :email => 'email', :display_name => 'fullname' }
+ end
+
+Attribute Exchange OpenID Extension
+===================================
+
+Some OpenID providers also support the OpenID AX (attribute exchange) protocol for exchanging identity information between endpoints. See more: http://openid.net/specs/openid-attribute-exchange-1_0.html
+
+Accessing AX data is very similar to the Simple Registration process, described above -- just add the URI identifier for the AX field to your :optional or :required parameters. For example:
+
+ authenticate_with_open_id(identity_url,
+ :required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration|
+
+This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate'
+
+
+
+Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
View
22 vendor/plugins/open_id_authentication/Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the open_id_authentication 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_authentication plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'OpenIdAuthentication'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
View
11 ...en_id_authentication/generators/open_id_authentication_tables/open_id_authentication_tables_generator.rb
@@ -0,0 +1,11 @@
+class OpenIdAuthenticationTablesGenerator < Rails::Generator::NamedBase
+ def initialize(runtime_args, runtime_options = {})
+ super
+ end
+
+ def manifest
+ record do |m|
+ m.migration_template 'migration.rb', 'db/migrate'
+ end
+ end
+end
View
20 vendor/plugins/open_id_authentication/generators/open_id_authentication_tables/templates/migration.rb
@@ -0,0 +1,20 @@
+class <%= class_name %> < ActiveRecord::Migration
+ def self.up
+ create_table :open_id_authentication_associations, :force => true do |t|
+ t.integer :issued, :lifetime
+ t.string :handle, :assoc_type
+ t.binary :server_url, :secret
+ end
+
+ create_table :open_id_authentication_nonces, :force => true do |t|
+ t.integer :timestamp, :null => false
+ t.string :server_url, :null => true
+ t.string :salt, :null => false
+ end
+ end
+
+ def self.down
+ drop_table :open_id_authentication_associations
+ drop_table :open_id_authentication_nonces
+ end
+end
View
26 ...r/plugins/open_id_authentication/generators/upgrade_open_id_authentication_tables/templates/migration.rb
@@ -0,0 +1,26 @@
+class <%= class_name %> < ActiveRecord::Migration
+ def self.up
+ drop_table :open_id_authentication_settings
+ drop_table :open_id_authentication_nonces
+
+ create_table :open_id_authentication_nonces, :force => true do |t|
+ t.integer :timestamp, :null => false
+ t.string :server_url, :null => true
+ t.string :salt, :null => false
+ end
+ end
+
+ def self.down
+ drop_table :open_id_authentication_nonces
+
+ create_table :open_id_authentication_nonces, :force => true do |t|
+ t.integer :created
+ t.string :nonce
+ end
+
+ create_table :open_id_authentication_settings, :force => true do |t|
+ t.string :setting
+ t.binary :value
+ end
+ end
+end
View
11 ...tion/generators/upgrade_open_id_authentication_tables/upgrade_open_id_authentication_tables_generator.rb
@@ -0,0 +1,11 @@
+class UpgradeOpenIdAuthenticationTablesGenerator < Rails::Generator::NamedBase
+ def initialize(runtime_args, runtime_options = {})
+ super
+ end
+
+ def manifest
+ record do |m|
+ m.migration_template 'migration.rb', 'db/migrate'
+ end
+ end
+end
View
18 vendor/plugins/open_id_authentication/init.rb
@@ -0,0 +1,18 @@
+if config.respond_to?(:gems)
+ config.gem 'ruby-openid', :lib => 'openid', :version => '>=2.0.4'
+else
+ begin
+ require 'openid'
+ rescue LoadError
+ begin
+ gem 'ruby-openid', '>=2.0.4'
+ rescue Gem::LoadError
+ puts "Install the ruby-openid gem to enable OpenID support"
+ end
+ end
+end
+
+config.to_prepare do
+ OpenID::Util.logger = Rails.logger
+ ActionController::Base.send :include, OpenIdAuthentication
+end
View
240 vendor/plugins/open_id_authentication/lib/open_id_authentication.rb
@@ -0,0 +1,240 @@
+require 'uri'
+require 'openid/extensions/sreg'
+require 'openid/extensions/ax'
+require 'openid/store/filesystem'
+
+require File.dirname(__FILE__) + '/open_id_authentication/association'
+require File.dirname(__FILE__) + '/open_id_authentication/nonce'
+require File.dirname(__FILE__) + '/open_id_authentication/db_store'
+require File.dirname(__FILE__) + '/open_id_authentication/request'
+require File.dirname(__FILE__) + '/open_id_authentication/timeout_fixes' if OpenID::VERSION == "2.0.4"
+
+module OpenIdAuthentication
+ OPEN_ID_AUTHENTICATION_DIR = RAILS_ROOT + "/tmp/openids"
+
+ def self.store
+ @@store
+ end
+
+ def self.store=(*store_option)
+ store, *parameters = *([ store_option ].flatten)
+
+ @@store = case store
+ when :db
+ OpenIdAuthentication::DbStore.new
+ when :file
+ OpenID::Store::Filesystem.new(OPEN_ID_AUTHENTICATION_DIR)
+ else
+ store
+ end
+ end
+
+ self.store = :db
+
+ class InvalidOpenId < StandardError
+ end
+
+ class Result
+ ERROR_MESSAGES = {
+ :missing => "Sorry, the OpenID server couldn't be found",
+ :invalid => "Sorry, but this does not appear to be a valid OpenID",
+ :canceled => "OpenID verification was canceled",
+ :failed => "OpenID verification failed",
+ :setup_needed => "OpenID verification needs setup"
+ }
+
+ def self.[](code)
+ new(code)
+ end
+
+ def initialize(code)
+ @code = code
+ end
+
+ def status
+ @code
+ end
+
+ ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
+
+ def successful?
+ @code == :successful
+ end
+
+ def unsuccessful?
+ ERROR_MESSAGES.keys.include?(@code)
+ end
+
+ def message
+ ERROR_MESSAGES[@code]
+ end
+ end
+
+ # normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
+ def self.normalize_identifier(identifier)
+ # clean up whitespace
+ identifier = identifier.to_s.strip
+
+ # if an XRI has a prefix, strip it.
+ identifier.gsub!(/xri:\/\//i, '')
+
+ # dodge XRIs -- TODO: validate, don't just skip.
+ unless ['=', '@', '+', '$', '!', '('].include?(identifier.at(0))
+ # does it begin with http? if not, add it.
+ identifier = "http://#{identifier}" unless identifier =~ /^http/i
+
+ # strip any fragments
+ identifier.gsub!(/\#(.*)$/, '')
+
+ begin
+ uri = URI.parse(identifier)
+ uri.scheme = uri.scheme.downcase # URI should do this
+ identifier = uri.normalize.to_s
+ rescue URI::InvalidURIError
+ raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
+ end
+ end
+
+ return identifier
+ end
+
+ # deprecated for OpenID 2.0, where not all OpenIDs are URLs
+ def self.normalize_url(url)
+ ActiveSupport::Deprecation.warn "normalize_url has been deprecated, use normalize_identifier instead"
+ self.normalize_identifier(url)
+ end
+
+ protected
+ def normalize_url(url)
+ OpenIdAuthentication.normalize_url(url)
+ end
+
+ def normalize_identifier(url)
+ OpenIdAuthentication.normalize_identifier(url)
+ end
+
+ # The parameter name of "openid_identifier" is used rather than the Rails convention "open_id_identifier"
+ # because that's what the specification dictates in order to get browser auto-complete working across sites
+ def using_open_id?(identity_url = nil) #:doc:
+ identity_url ||= params[:openid_identifier] || params[:openid_url]
+ !identity_url.blank? || params[:open_id_complete]
+ end
+
+ def authenticate_with_open_id(identity_url = nil, options = {}, &block) #:doc:
+ identity_url ||= params[:openid_identifier] || params[:openid_url]
+
+ if params[:open_id_complete].nil?
+ begin_open_id_authentication(identity_url, options, &block)
+ else
+ complete_open_id_authentication(&block)
+ end
+ end
+
+ private
+ def begin_open_id_authentication(identity_url, options = {})
+ identity_url = normalize_identifier(identity_url)
+ return_to = options.delete(:return_to)
+ method = options.delete(:method)
+
+ options[:required] ||= [] # reduces validation later
+ options[:optional] ||= []
+
+ open_id_request = open_id_consumer.begin(identity_url)
+ add_simple_registration_fields(open_id_request, options)
+ add_ax_fields(open_id_request, options)
+ redirect_to(open_id_redirect_url(open_id_request, return_to, method))
+ rescue OpenIdAuthentication::InvalidOpenId => e
+ yield Result[:invalid], identity_url, nil
+ rescue OpenID::OpenIDError, Timeout::Error => e
+ logger.error("[OPENID] #{e}")
+ yield Result[:missing], identity_url, nil
+ end
+
+ def complete_open_id_authentication
+ params_with_path = params.reject { |key, value| request.path_parameters[key] }
+ params_with_path.delete(:format)
+ open_id_response = timeout_protection_from_identity_server { open_id_consumer.complete(params_with_path, requested_url) }
+ identity_url = normalize_identifier(open_id_response.display_identifier) if open_id_response.display_identifier
+
+ case open_id_response.status
+ when OpenID::Consumer::SUCCESS
+ profile_data = {}
+
+ # merge the SReg data and the AX data into a single hash of profile data
+ [ OpenID::SReg::Response, OpenID::AX::FetchResponse ].each do |data_response|
+ if data_response.from_success_response( open_id_response )
+ profile_data.merge! data_response.from_success_response( open_id_response ).data
+ end
+ end
+
+ yield Result[:successful], identity_url, profile_data
+ when OpenID::Consumer::CANCEL
+ yield Result[:canceled], identity_url, nil
+ when OpenID::Consumer::FAILURE
+ yield Result[:failed], identity_url, nil
+ when OpenID::Consumer::SETUP_NEEDED
+ yield Result[:setup_needed], open_id_response.setup_url, nil
+ end
+ end
+
+ def open_id_consumer
+ OpenID::Consumer.new(session, OpenIdAuthentication.store)
+ end
+
+ def add_simple_registration_fields(open_id_request, fields)
+ sreg_request = OpenID::SReg::Request.new
+
+ # filter out AX identifiers (URIs)
+ required_fields = fields[:required].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
+ optional_fields = fields[:optional].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
+
+ sreg_request.request_fields(required_fields, true) unless required_fields.blank?
+ sreg_request.request_fields(optional_fields, false) unless optional_fields.blank?
+ sreg_request.policy_url = fields[:policy_url] if fields[:policy_url]
+ open_id_request.add_extension(sreg_request)
+ end
+
+ def add_ax_fields( open_id_request, fields )
+ ax_request = OpenID::AX::FetchRequest.new
+
+ # look through the :required and :optional fields for URIs (AX identifiers)
+ fields[:required].each do |f|
+ next unless f =~ /^https?:\/\//
+ ax_request.add( OpenID::AX::AttrInfo.new( f, nil, true ) )
+ end
+
+ fields[:optional].each do |f|
+ next unless f =~ /^https?:\/\//
+ ax_request.add( OpenID::AX::AttrInfo.new( f, nil, false ) )
+ end
+
+ open_id_request.add_extension( ax_request )
+ end
+
+ def open_id_redirect_url(open_id_request, return_to = nil, method = nil)
+ open_id_request.return_to_args['_method'] = (method || request.method).to_s
+ open_id_request.return_to_args['open_id_complete'] = '1'
+ open_id_request.redirect_url(root_url, return_to || requested_url)
+ end
+
+ def requested_url
+ relative_url_root = self.class.respond_to?(:relative_url_root) ?
+ self.class.relative_url_root.to_s :
+ request.relative_url_root
+ "#{request.protocol}#{request.host_with_port}#{ActionController::Base.relative_url_root}#{request.path}"
+ end
+
+ def timeout_protection_from_identity_server
+ yield
+ rescue Timeout::Error
+ Class.new do
+ def status
+ OpenID::FAILURE
+ end
+
+ def msg
+ "Identity server timed out"
+ end
+ end.new
+ end
+end
View
9 vendor/plugins/open_id_authentication/lib/open_id_authentication/association.rb
@@ -0,0 +1,9 @@
+module OpenIdAuthentication
+ class Association < ActiveRecord::Base
+ set_table_name :open_id_authentication_associations
+
+ def from_record
+ OpenID::Association.new(handle, secret, issued, lifetime, assoc_type)
+ end
+ end
+end
View
55 vendor/plugins/open_id_authentication/lib/open_id_authentication/db_store.rb
@@ -0,0 +1,55 @@
+require 'openid/store/interface'
+
+module OpenIdAuthentication
+ class DbStore < OpenID::Store::Interface
+ def self.cleanup_nonces
+ now = Time.now.to_i
+ Nonce.delete_all(["timestamp > ? OR timestamp < ?", now + OpenID::Nonce.skew, now - OpenID::Nonce.skew])
+ end
+
+ def self.cleanup_associations
+ now = Time.now.to_i
+ Association.delete_all(['issued + lifetime > ?',now])
+ 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 = if handle.blank?
+ Association.find_all_by_server_url(server_url)
+ else
+ Association.find_all_by_server_url_and_handle(server_url, handle)
+ end
+
+ assocs.reverse.each do |assoc|
+ a = assoc.from_record
+ if a.expires_in == 0
+ assoc.destroy
+ else
+ return a
+ end
+ end if assocs.any?
+
+ return nil
+ end
+
+ def remove_association(server_url, handle)
+ Association.delete_all(['server_url = ? AND handle = ?', server_url, handle]) > 0
+ end
+
+ def use_nonce(server_url, timestamp, salt)
+ return false if Nonce.find_by_server_url_and_timestamp_and_salt(server_url, timestamp, salt)
+ return false if (timestamp - Time.now.to_i).abs > OpenID::Nonce.skew
+ Nonce.create(:server_url => server_url, :timestamp => timestamp, :salt => salt)
+ return true
+ end
+ end
+end
View
5 vendor/plugins/open_id_authentication/lib/open_id_authentication/nonce.rb
@@ -0,0 +1,5 @@
+module OpenIdAuthentication
+ class Nonce < ActiveRecord::Base
+ set_table_name :open_id_authentication_nonces
+ end
+end
View
23 vendor/plugins/open_id_authentication/lib/open_id_authentication/request.rb
@@ -0,0 +1,23 @@
+module OpenIdAuthentication
+ module Request
+ def self.included(base)
+ base.alias_method_chain :request_method, :openid
+ end
+
+ def request_method_with_openid
+ if !parameters[:_method].blank? && parameters[:open_id_complete] == '1'
+ parameters[:_method].to_sym
+ else
+ request_method_without_openid
+ end
+ end
+ end
+end
+
+# In Rails 2.3, the request object has been renamed
+# from AbstractRequest to Request
+if defined? ActionController::Request
+ ActionController::Request.send :include, OpenIdAuthentication::Request
+else
+ ActionController::AbstractRequest.send :include, OpenIdAuthentication::Request
+end
View
20 vendor/plugins/open_id_authentication/lib/open_id_authentication/timeout_fixes.rb
@@ -0,0 +1,20 @@
+# http://trac.openidenabled.com/trac/ticket/156
+module OpenID
+ @@timeout_threshold = 20
+
+ def self.timeout_threshold
+ @@timeout_threshold
+ end
+
+ def self.timeout_threshold=(value)
+ @@timeout_threshold = value
+ end
+
+ class StandardFetcher
+ def make_http(uri)
+ http = @proxy.new(uri.host, uri.port)
+ http.read_timeout = http.open_timeout = OpenID.timeout_threshold
+ http
+ end
+ end
+end
View
30 vendor/plugins/open_id_authentication/tasks/open_id_authentication_tasks.rake
@@ -0,0 +1,30 @@
+namespace :open_id_authentication do
+ namespace :db do
+ desc "Creates authentication tables for use with OpenIdAuthentication"
+ task :create => :environment do
+ generate_migration(["open_id_authentication_tables", "add_open_id_authentication_tables"])
+ end
+
+ desc "Upgrade authentication tables from ruby-openid 1.x.x to 2.x.x"
+ task :upgrade => :environment do
+ generate_migration(["upgrade_open_id_authentication_tables", "upgrade_open_id_authentication_tables"])
+ end
+
+ def generate_migration(args)
+ require 'rails_generator'
+ require 'rails_generator/scripts/generate'
+
+ if ActiveRecord::Base.connection.supports_migrations?
+ Rails::Generator::Scripts::Generate.new.run(args)
+ else
+ raise "Task unavailable to this database (no migration support)"
+ end
+ end
+
+ desc "Clear the authentication tables"
+ task :clear => :environment do
+ OpenIdAuthentication::DbStore.cleanup_nonces
+ OpenIdAuthentication::DbStore.cleanup_associations
+ end
+ end
+end
View
32 vendor/plugins/open_id_authentication/test/normalize_test.rb
@@ -0,0 +1,32 @@
+require File.dirname(__FILE__) + '/test_helper'
+
+class NormalizeTest < Test::Unit::TestCase
+ include OpenIdAuthentication
+
+ NORMALIZATIONS = {
+ "openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
+ "http://openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
+ "https://openid.aol.com/nextangler" => "https://openid.aol.com/nextangler",
+ "HTTP://OPENID.AOL.COM/NEXTANGLER" => "http://openid.aol.com/NEXTANGLER",
+ "HTTPS://OPENID.AOL.COM/NEXTANGLER" => "https://openid.aol.com/NEXTANGLER",
+ "loudthinking.com" => "http://loudthinking.com/",
+ "http://loudthinking.com" => "http://loudthinking.com/",
+ "http://loudthinking.com:80" => "http://loudthinking.com/",
+ "https://loudthinking.com:443" => "https://loudthinking.com/",
+ "http://loudthinking.com:8080" => "http://loudthinking.com:8080/",
+ "techno-weenie.net" => "http://techno-weenie.net/",
+ "http://techno-weenie.net" => "http://techno-weenie.net/",
+ "http://techno-weenie.net " => "http://techno-weenie.net/",
+ "=name" => "=name"
+ }
+
+ def test_normalizations
+ NORMALIZATIONS.each do |from, to|
+ assert_equal to, normalize_identifier(from)
+ end
+ end
+
+ def test_broken_open_id
+ assert_raises(InvalidOpenId) { normalize_identifier(nil) }
+ end
+end
View
46 vendor/plugins/open_id_authentication/test/open_id_authentication_test.rb
@@ -0,0 +1,46 @@
+require File.dirname(__FILE__) + '/test_helper'
+
+class OpenIdAuthenticationTest < Test::Unit::TestCase
+ def setup
+ @controller = Class.new do
+ include OpenIdAuthentication
+ def params() {} end
+ end.new
+ end
+
+ def test_authentication_should_fail_when_the_identity_server_is_missing
+ open_id_consumer = mock()
+ open_id_consumer.expects(:begin).raises(OpenID::OpenIDError)
+ @controller.expects(:open_id_consumer).returns(open_id_consumer)
+ @controller.expects(:logger).returns(mock(:error => true))
+
+ @controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
+ assert result.missing?
+ assert_equal "Sorry, the OpenID server couldn't be found", result.message
+ end
+ end
+
+ def test_authentication_should_be_invalid_when_the_identity_url_is_invalid
+ @controller.send(:authenticate_with_open_id, "!") do |result, identity_url|
+ assert result.invalid?, "Result expected to be invalid but was not"
+ assert_equal "Sorry, but this does not appear to be a valid OpenID", result.message
+ end
+ end
+
+ def test_authentication_should_fail_when_the_identity_server_times_out
+ open_id_consumer = mock()
+ open_id_consumer.expects(:begin).raises(Timeout::Error, "Identity Server took too long.")
+ @controller.expects(:open_id_consumer).returns(open_id_consumer)
+ @controller.expects(:logger).returns(mock(:error => true))
+
+ @controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
+ assert result.missing?
+ assert_equal "Sorry, the OpenID server couldn't be found", result.message
+ end
+ end
+
+ def test_authentication_should_begin_when_the_identity_server_is_present
+ @controller.expects(:begin_open_id_authentication)
+ @controller.send(:authenticate_with_open_id, "http://someone.example.com")
+ end
+end
View
14 vendor/plugins/open_id_authentication/test/status_test.rb
@@ -0,0 +1,14 @@
+require File.dirname(__FILE__) + '/test_helper'
+
+class StatusTest < Test::Unit::TestCase
+ include OpenIdAuthentication
+
+ def test_state_conditional
+ assert Result[:missing].missing?
+ assert Result[:missing].unsuccessful?
+ assert !Result[:missing].successful?
+
+ assert Result[:successful].successful?
+ assert !Result[:successful].unsuccessful?
+ end
+end
View
17 vendor/plugins/open_id_authentication/test/test_helper.rb
@@ -0,0 +1,17 @@
+require 'test/unit'
+require 'rubygems'
+
+gem 'activesupport'
+require 'active_support'
+
+gem 'actionpack'
+require 'action_controller'
+
+gem 'mocha'
+require 'mocha'
+
+gem 'ruby-openid'
+require 'openid'
+
+RAILS_ROOT = File.dirname(__FILE__) unless defined? RAILS_ROOT
+require File.dirname(__FILE__) + "/../lib/open_id_authentication"
Please sign in to comment.
Something went wrong with that request. Please try again.