Adding initial version of the OAuth token authentication method. This adds basic OAuth support for registering apps, getting and revoking keys, etc... The tokens come with 6 separate permissions bits; read/write user preferences, write diaries, write API and read/write GPS traces. Needs more tests.

This commit is contained in:
Matt Amos 2009-06-22 16:54:37 +00:00
parent 2ad330d642
commit b8f6dbd403
85 changed files with 3277 additions and 9 deletions

View file

@ -22,19 +22,61 @@ class ApplicationController < ActionController::Base
redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri unless @user redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri unless @user
end end
##
# requires the user to be logged in by the token or HTTP methods, or have an
# OAuth token with the right capability. this method is a bit of a pain to call
# directly, since it's cumbersome to call filters with arguments in rails. to
# make it easier to read and write the code, there are some utility methods
# below.
def require_capability(cap)
# when the current token is nil, it means the user logged in with a different
# method, otherwise an OAuth token was used, which has to be checked.
unless @current_token.nil?
unless @current_token.read_attribute(cap)
render :text => "OAuth token doesn't have that capability.", :status => :forbidden
return false
end
end
end
# Utility methods to make the controller filter methods easier to read and write.
def require_allow_read_prefs
require_capability(:allow_read_prefs)
end
def require_allow_write_prefs
require_capability(:allow_write_prefs)
end
def require_allow_write_diary
require_capability(:allow_write_diary)
end
def require_allow_write_api
require_capability(:allow_write_api)
end
def require_allow_read_gpx
require_capability(:allow_read_gpx)
end
def require_allow_write_gpx
require_capability(:allow_write_gpx)
end
## ##
# sets up the @user object for use by other methods. this is mostly called # sets up the @user object for use by other methods. this is mostly called
# from the authorize method, but can be called elsewhere if authorisation # from the authorize method, but can be called elsewhere if authorisation
# is optional. # is optional.
def setup_user_auth def setup_user_auth
username, passwd = get_auth_data # parse from headers # try and setup using OAuth
# authenticate per-scheme if oauthenticate
if username.nil? @user = @current_token.user
@user = nil # no authentication provided - perhaps first connect (client should retry after 401)
elsif username == 'token'
@user = User.authenticate(:token => passwd) # preferred - random token for user from db, passed in basic auth
else else
@user = User.authenticate(:username => username, :password => passwd) # basic auth username, passwd = get_auth_data # parse from headers
# authenticate per-scheme
if username.nil?
@user = nil # no authentication provided - perhaps first connect (client should retry after 401)
elsif username == 'token'
@user = User.authenticate(:token => passwd) # preferred - random token for user from db, passed in basic auth
else
@user = User.authenticate(:username => username, :password => passwd) # basic auth
end
end end
end end

View file

@ -7,6 +7,7 @@ class ChangesetController < ApplicationController
before_filter :authorize_web, :only => [:list, :list_user, :list_bbox] before_filter :authorize_web, :only => [:list, :list_user, :list_bbox]
before_filter :set_locale, :only => [:list, :list_user, :list_bbox] before_filter :set_locale, :only => [:list, :list_user, :list_bbox]
before_filter :authorize, :only => [:create, :update, :delete, :upload, :include, :close] before_filter :authorize, :only => [:create, :update, :delete, :upload, :include, :close]
before_filter :require_allow_write_api, :only => [:create, :update, :delete, :upload, :include, :close]
before_filter :require_public_data, :only => [:create, :update, :delete, :upload, :include, :close] before_filter :require_public_data, :only => [:create, :update, :delete, :upload, :include, :close]
before_filter :check_api_writable, :only => [:create, :update, :delete, :upload, :include] before_filter :check_api_writable, :only => [:create, :update, :delete, :upload, :include]
before_filter :check_api_readable, :except => [:create, :update, :delete, :upload, :download, :query] before_filter :check_api_readable, :except => [:create, :update, :delete, :upload, :download, :query]

View file

@ -4,6 +4,7 @@ class NodeController < ApplicationController
require 'xml/libxml' require 'xml/libxml'
before_filter :authorize, :only => [:create, :update, :delete] before_filter :authorize, :only => [:create, :update, :delete]
before_filter :require_allow_write_api, :only => [:create, :update, :delete]
before_filter :require_public_data, :only => [:create, :update, :delete] before_filter :require_public_data, :only => [:create, :update, :delete]
before_filter :check_api_writable, :only => [:create, :update, :delete] before_filter :check_api_writable, :only => [:create, :update, :delete]
before_filter :check_api_readable, :except => [:create, :update, :delete] before_filter :check_api_readable, :except => [:create, :update, :delete]

View file

@ -0,0 +1,53 @@
class OauthClientsController < ApplicationController
layout 'site'
before_filter :authorize_web
before_filter :require_user
def index
@client_applications = @user.client_applications
@tokens = @user.oauth_tokens.find :all, :conditions => 'oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null'
end
def new
@client_application = ClientApplication.new
end
def create
@client_application = @user.client_applications.build(params[:client_application])
if @client_application.save
flash[:notice] = "Registered the information successfully"
redirect_to :action => "show", :id => @client_application.id
else
render :action => "new"
end
end
def show
@client_application = @user.client_applications.find(params[:id])
rescue ActiveRecord::RecordNotFound
@type = "client application"
render :action => "not_found", :status => :not_found
end
def edit
@client_application = @user.client_applications.find(params[:id])
end
def update
@client_application = @user.client_applications.find(params[:id])
if @client_application.update_attributes(params[:client_application])
flash[:notice] = "Updated the client information successfully"
redirect_to :action => "show", :id => @client_application.id
else
render :action => "edit"
end
end
def destroy
@client_application = @user.client_applications.find(params[:id])
@client_application.destroy
flash[:notice] = "Destroyed the client application registration"
redirect_to :action => "index"
end
end

View file

@ -0,0 +1,83 @@
class OauthController < ApplicationController
layout 'site'
before_filter :authorize_web, :except => [:request_token, :access_token]
before_filter :require_user, :only => [:oauthorize]
before_filter :verify_oauth_consumer_signature, :only => [:request_token]
before_filter :verify_oauth_request_token, :only => [:access_token]
# Uncomment the following if you are using restful_open_id_authentication
# skip_before_filter :verify_authenticity_token
def request_token
@token = current_client_application.create_request_token
logger.info "in REQUEST TOKEN"
if @token
logger.info "request token params: #{params.inspect}"
# request tokens indicate what permissions the client *wants*, not
# necessarily the same as those which the user allows.
current_client_application.permissions.each do |pref|
logger.info "PARAMS found #{pref}"
@token.write_attribute(pref, true)
end
@token.save!
render :text => @token.to_query
else
render :nothing => true, :status => 401
end
end
def access_token
@token = current_token && current_token.exchange!
if @token
render :text => @token.to_query
else
render :nothing => true, :status => 401
end
end
def oauthorize
@token = RequestToken.find_by_token params[:oauth_token]
unless @token.invalidated?
if request.post?
any_auth = false
@token.client_application.permissions.each do |pref|
if params[pref]
logger.info "OAUTHORIZE PARAMS found #{pref}"
@token.write_attribute(pref, true)
any_auth ||= true
else
@token.write_attribute(pref, false)
end
end
if any_auth
@token.authorize!(@user)
redirect_url = params[:oauth_callback] || @token.client_application.callback_url
if redirect_url
redirect_to "#{redirect_url}?oauth_token=#{@token.token}"
else
render :action => "authorize_success"
end
else
@token.invalidate!
render :action => "authorize_failure"
end
end
else
render :action => "authorize_failure"
end
end
def revoke
@token = @user.oauth_tokens.find_by_token params[:token]
if @token
@token.invalidate!
flash[:notice] = "You've revoked the token for #{@token.client_application.name}"
end
logger.info "about to redirect"
redirect_to :controller => 'oauth_clients', :action => 'index'
end
end

View file

@ -2,6 +2,7 @@ class RelationController < ApplicationController
require 'xml/libxml' require 'xml/libxml'
before_filter :authorize, :only => [:create, :update, :delete] before_filter :authorize, :only => [:create, :update, :delete]
before_filter :require_allow_write_api, :only => [:create, :update, :delete]
before_filter :require_public_data, :only => [:create, :update, :delete] before_filter :require_public_data, :only => [:create, :update, :delete]
before_filter :check_api_writable, :only => [:create, :update, :delete] before_filter :check_api_writable, :only => [:create, :update, :delete]
before_filter :check_api_readable, :except => [:create, :update, :delete] before_filter :check_api_readable, :except => [:create, :update, :delete]

View file

@ -9,6 +9,8 @@ class TraceController < ApplicationController
before_filter :check_database_writable, :only => [:create, :edit, :delete, :make_public] before_filter :check_database_writable, :only => [:create, :edit, :delete, :make_public]
before_filter :check_api_readable, :only => [:api_details, :api_data] before_filter :check_api_readable, :only => [:api_details, :api_data]
before_filter :check_api_writable, :only => [:api_create] before_filter :check_api_writable, :only => [:api_create]
before_filter :require_allow_read_gpx, :only => [:api_details, :api_data]
before_filter :require_allow_write_gpx, :only => [:api_create]
# Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.). # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
# target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces # target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces

View file

@ -8,6 +8,8 @@ class UserController < ApplicationController
before_filter :check_database_readable, :except => [:api_details, :api_gpx_files] before_filter :check_database_readable, :except => [:api_details, :api_gpx_files]
before_filter :check_database_writable, :only => [:login, :new, :set_home, :account, :go_public, :make_friend, :remove_friend, :upload_image, :delete_image] before_filter :check_database_writable, :only => [:login, :new, :set_home, :account, :go_public, :make_friend, :remove_friend, :upload_image, :delete_image]
before_filter :check_api_readable, :only => [:api_details, :api_gpx_files] before_filter :check_api_readable, :only => [:api_details, :api_gpx_files]
before_filter :require_allow_read_prefs, :only => [:api_details]
before_filter :require_allow_read_gpx, :only => [:api_gpx_files]
filter_parameter_logging :password, :pass_crypt, :pass_crypt_confirmation filter_parameter_logging :password, :pass_crypt, :pass_crypt_confirmation
@ -37,6 +39,7 @@ class UserController < ApplicationController
def account def account
@title = t 'user.account.title' @title = t 'user.account.title'
@tokens = @user.oauth_tokens.find :all, :conditions => 'oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null'
if params[:user] and params[:user][:display_name] and params[:user][:description] if params[:user] and params[:user][:display_name] and params[:user][:description]
if params[:user][:email] != @user.email if params[:user][:email] != @user.email

View file

@ -1,6 +1,8 @@
# Update and read user preferences, which are arbitrayr key/val pairs # Update and read user preferences, which are arbitrayr key/val pairs
class UserPreferenceController < ApplicationController class UserPreferenceController < ApplicationController
before_filter :authorize before_filter :authorize
before_filter :require_allow_read_prefs, :only => [:read_one, :read]
before_filter :require_allow_write_prefs, :except => [:read_one, :read]
def read_one def read_one
pref = UserPreference.find(@user.id, params[:preference_key]) pref = UserPreference.find(@user.id, params[:preference_key])

View file

@ -2,6 +2,7 @@ class WayController < ApplicationController
require 'xml/libxml' require 'xml/libxml'
before_filter :authorize, :only => [:create, :update, :delete] before_filter :authorize, :only => [:create, :update, :delete]
before_filter :require_allow_write_api, :only => [:create, :update, :delete]
before_filter :require_public_data, :only => [:create, :update, :delete] before_filter :require_public_data, :only => [:create, :update, :delete]
before_filter :check_api_writable, :only => [:create, :update, :delete] before_filter :check_api_writable, :only => [:create, :update, :delete]
before_filter :check_api_readable, :except => [:create, :update, :delete] before_filter :check_api_readable, :except => [:create, :update, :delete]

View file

@ -0,0 +1,2 @@
module OauthClientsHelper
end

View file

@ -0,0 +1,2 @@
module OauthHelper
end

View file

@ -0,0 +1,10 @@
class AccessToken<OauthToken
validates_presence_of :user
before_create :set_authorized_at
protected
def set_authorized_at
self.authorized_at = Time.now
end
end

View file

@ -0,0 +1,69 @@
require 'oauth'
class ClientApplication < ActiveRecord::Base
belongs_to :user
has_many :tokens, :class_name => "OauthToken"
validates_presence_of :name, :url, :key, :secret
validates_uniqueness_of :key
before_validation_on_create :generate_keys
def self.find_token(token_key)
token = OauthToken.find_by_token(token_key, :include => :client_application)
if token && token.authorized?
logger.info "Loaded #{token.token} which was authorized by (user_id=#{token.user_id}) on the #{token.authorized_at}"
token
else
nil
end
end
def self.verify_request(request, options = {}, &block)
begin
signature = OAuth::Signature.build(request, options, &block)
logger.info "Signature Base String: #{signature.signature_base_string}"
logger.info "Consumer: #{signature.send :consumer_key}"
logger.info "Token: #{signature.send :token}"
return false unless OauthNonce.remember(signature.request.nonce, signature.request.timestamp)
value = signature.verify
logger.info "Signature verification returned: #{value.to_s}"
value
rescue OAuth::Signature::UnknownSignatureMethod => e
logger.info "ERROR"+e.to_s
false
end
end
def self.all_permissions
PERMISSIONS
end
def oauth_server
@oauth_server ||= OAuth::Server.new("http://" + SERVER_URL)
end
def credentials
@oauth_client ||= OAuth::Consumer.new(key, secret)
end
def create_request_token
RequestToken.create :client_application => self
end
# the permissions that this client would like from the user
def permissions
ClientApplication.all_permissions.select { |p| self[p] }
end
protected
# this is the set of permissions that the client can ask for. clients
# have to say up-front what permissions they want and when users sign up they
# can agree or not agree to each of them.
PERMISSIONS = [:allow_read_prefs, :allow_write_prefs, :allow_write_diary,
:allow_write_api, :allow_read_gpx, :allow_write_gpx ]
def generate_keys
@oauth_client = oauth_server.generate_consumer_credentials
self.key = @oauth_client.key
self.secret = @oauth_client.secret
end
end

13
app/models/oauth_nonce.rb Normal file
View file

@ -0,0 +1,13 @@
# Simple store of nonces. The OAuth Spec requires that any given pair of nonce and timestamps are unique.
# Thus you can use the same nonce with a different timestamp and viceversa.
class OauthNonce < ActiveRecord::Base
validates_presence_of :nonce, :timestamp
validates_uniqueness_of :nonce, :scope => :timestamp
# Remembers a nonce and it's associated timestamp. It returns false if it has already been used
def self.remember(nonce, timestamp)
oauth_nonce = OauthNonce.create(:nonce => nonce, :timestamp => timestamp)
return false if oauth_nonce.new_record?
oauth_nonce
end
end

31
app/models/oauth_token.rb Normal file
View file

@ -0,0 +1,31 @@
class OauthToken < ActiveRecord::Base
belongs_to :client_application
belongs_to :user
validates_uniqueness_of :token
validates_presence_of :client_application, :token, :secret
before_validation_on_create :generate_keys
def invalidated?
invalidated_at != nil
end
def invalidate!
update_attribute(:invalidated_at, Time.now)
end
def authorized?
authorized_at != nil && !invalidated?
end
def to_query
"oauth_token=#{token}&oauth_token_secret=#{secret}"
end
protected
def generate_keys
@oauth_token = client_application.oauth_server.generate_credentials
self.token = @oauth_token[0]
self.secret = @oauth_token[1]
end
end

View file

@ -0,0 +1,25 @@
class RequestToken < OauthToken
def authorize!(user)
return false if authorized?
self.user = user
self.authorized_at = Time.now
self.save
end
def exchange!
return false unless authorized?
RequestToken.transaction do
logger.info("£££ In exchange!")
params = { :user => user, :client_application => client_application }
# copy the permissions from the authorised request token to the access token
client_application.permissions.each { |p|
logger.info("£££ copying permission #{p} = #{read_attribute(p).inspect}")
params[p] = read_attribute(p)
}
access_token = AccessToken.create(params)
invalidate!
access_token
end
end
end

View file

@ -11,6 +11,9 @@ class User < ActiveRecord::Base
has_many :preferences, :class_name => "UserPreference" has_many :preferences, :class_name => "UserPreference"
has_many :changesets has_many :changesets
has_many :client_applications
has_many :oauth_tokens, :class_name => "OauthToken", :order => "authorized_at desc", :include => [:client_application]
validates_presence_of :email, :display_name validates_presence_of :email, :display_name
validates_confirmation_of :email#, :message => ' addresses must match' validates_confirmation_of :email#, :message => ' addresses must match'
validates_confirmation_of :pass_crypt#, :message => ' must match the confirmation password' validates_confirmation_of :pass_crypt#, :message => ' must match the confirmation password'

View file

@ -0,0 +1,14 @@
<h1>Authorize access to your account</h1>
<p>Would you like to authorize <%= link_to @token.client_application.name,@token.client_application.url %> (<%= link_to @token.client_application.url,@token.client_application.url %>) to access your account?</p>
<% form_tag authorize_url do %>
<%= hidden_field_tag "oauth_token", @token.token %>
<%- if params[:oauth_callback] -%>
<%= hidden_field_tag "oauth_callback", params[:oauth_callback] %>
<%- end -%>
<p>
<%= check_box_tag 'authorize' %> authorize access
</p>
<p>
<%= submit_tag %>
</p>
<% end %>

View file

@ -0,0 +1 @@
<h1>You have disallowed this request</h1>

View file

@ -0,0 +1 @@
<h1>You have allowed this request</h1>

View file

@ -0,0 +1,17 @@
<h1>Authorize access to your account</h1>
<p><%= t('oauth.client_application.request_access', :app_name => link_to(@token.client_application.name,@token.client_application.url)) %></p>
<% form_tag authorize_url do %>
<%= hidden_field_tag "oauth_token", @token.token %>
<%- if params[:oauth_callback] -%>
<%= hidden_field_tag "oauth_callback", params[:oauth_callback] %>
<%- end -%>
<p><%= t 'oauth.client_application.allow_to' %></p>
<ul style="list-style:none">
<% @token.client_application.permissions.each do |perm| %>
<li><%= check_box_tag perm.to_s, "yes", @token.read_attribute(perm) %><%= t "oauth.client_application.#{perm}" %></li>
<% end %>
</ul>
<p>
<%= submit_tag %>
</p>
<% end %>

View file

@ -0,0 +1,40 @@
<div class="field">
<label for="client_application_name">Name*</label><br/>
<%= f.text_field :name %>
</div>
<div class="field">
<label for="client_application_url">Main Application URL*</label><br/>
<%= f.text_field :url %>
</div>
<div class="field">
<label for="client_application_callback_url">Callback URL*</label><br/>
<%= f.text_field :callback_url %>
</div>
<div class="field">
<label for="client_application_support_url">Support URL</label><br/>
<%= f.text_field :support_url %>
</div>
<div class="field">
<label for="client_application_allow_read_prefs">Request permission to read the user preferences.</label><br/>
<%= f.check_box :allow_read_prefs %>
</div>
<div class="field">
<label for="client_application_allow_write_prefs">Request permission to write the user preferences.</label><br/>
<%= f.check_box :allow_write_prefs %>
</div>
<div class="field">
<label for="client_application_allow_write_diary">Request permission to create diary entries, comments and friends.</label><br/>
<%= f.check_box :allow_write_diary %>
</div>
<div class="field">
<label for="client_application_allow_write_api">Request permission to write the API on the user's behalf.</label><br/>
<%= f.check_box :allow_write_api %>
</div>
<div class="field">
<label for="client_application_allow_read_gpx">Request permission to read the user's private GPS traces.</label><br/>
<%= f.check_box :allow_read_gpx %>
</div>
<div class="field">
<label for="client_application_allow_write_gpx">Request permission to upload GPS traces as the user.</label><br/>
<%= f.check_box :allow_write_gpx %>
</div>

View file

@ -0,0 +1,5 @@
<h1>Edit your application</h1>
<% form_for :client_application do |f| %>
<%= render :partial => "form", :locals => { :f => f } %>
<%= submit_tag "Edit" %>
<% end %>

View file

@ -0,0 +1,39 @@
<div class="flash"><%= flash[:notice] %></div>
<h1>OAuth Client Applications</h1>
<% unless @tokens.empty? %>
<p>The following tokens have been issued to applications in your name</p>
<table>
<tr><th>Application</th><th>Issued</th><th>&nbsp;</th></tr>
<% @tokens.each do |token|%>
<% content_tag_for :tr, token do %>
<td><%= link_to token.client_application.name, token.client_application.url %></td>
<td><%= token.authorized_at %></td>
<td>
<% form_tag :controller => 'oauth', :action => 'revoke' do %>
<%= hidden_field_tag 'token', token.token %>
<%= submit_tag "Revoke!" %>
<% end %>
</td>
<% end %>
<% end %>
</table>
<% end %>
<h3>Application Developers</h3>
<% if @client_applications.empty? %>
<p>
Do you have an application you would like to register for use with us using the <a href="http://oauth.net">OAuth</a> standard?
</p>
<p>
You must register your web application before it can make OAuth requests to this service
</p>
<% else %>
<p>
You have the following client applications registered:
</p>
<% @client_applications.each do |client|%>
<% div_for client do %>
<%= link_to client.name, :action => :show, :id => client.id %>
<% end %>
<% end %>
<% end %>
<h3><%= link_to "Register your application", :action => :new %></h3>

View file

@ -0,0 +1,5 @@
<h1>Register a new application</h1>
<% form_for :client_application, :url => { :action => :create } do |f| %>
<%= render :partial => "form", :locals => { :f => f } %>
<%= submit_tag "Register" %>
<% end %>

View file

@ -0,0 +1 @@
<p>Sorry, that <%= @type -%> could not be found.</p>

View file

@ -0,0 +1,20 @@
<h1>OAuth details for <%=@client_application.name %></h1>
<p>
<b>Consumer Key:</b> <%=@client_application.key %>
</p>
<p>
<b>Consumer Secret:</b> <%=@client_application.secret %>
</p>
<p>
<b>Request Token URL</b> http<%='s' if request.ssl? %>://<%= request.host_with_port %><%=@client_application.oauth_server.request_token_path %>
</p>
<p>
<b>Access Token URL</b> http<%='s' if request.ssl? %>://<%= request.host_with_port %><%=@client_application.oauth_server.access_token_path %>
</p>
<p>
<b>Authorize URL</b> http<%='s' if request.ssl? %>://<%= request.host_with_port %><%=@client_application.oauth_server.authorize_path %>
</p>
<p>
We support hmac-sha1 (recommended) as well as plain text in ssl mode.
</p>

View file

@ -29,7 +29,7 @@
<div id="map" style="border:1px solid black; position:relative; width:500px; height:400px;"></div> <div id="map" style="border:1px solid black; position:relative; width:500px; height:400px;"></div>
</td></tr> </td></tr>
<tr><td></td><td align=right><br/></br><%= submit_tag t('user.account.save changes button') %></td></tr> <tr><td></td><td align=right><br/><%= submit_tag t('user.account.save changes button') %></td></tr>
</table> </table>
<br/> <br/>
@ -46,6 +46,39 @@
<br /><br /> <br /><br />
<%= button_to t('user.account.make all my edits public button'), :action => :go_public %> <%= button_to t('user.account.make all my edits public button'), :action => :go_public %>
<% end %> <% end %>
<h2><%= t 'user.account.my apps' %></h2>
<% if @tokens.empty? %>
<p><%= t 'oauth.token.none' %></p>
<% else %>
<table>
<tr><th><%= t 'oauth.token.application' %></th><th><%= t 'oauth.token.issued' %></th><th>&nbsp;</th></tr>
<% @tokens.each do |token|%>
<% content_tag_for :tr, token do %>
<td><%= link_to token.client_application.name, token.client_application.url %></td>
<td><%= token.authorized_at %></td>
<td>
<% form_tag :controller => 'oauth', :action => 'revoke' do %>
<%= hidden_field_tag 'token', token.token %>
<%= submit_tag t('oauth.token.revoke') %>
<% end %>
</td>
<% end %>
<% end %>
</table>
<% end %>
<h2><%= t 'user.account.developers' %></h2>
<% if @user.client_applications.empty? %>
<p><%= t 'user.account.dev_intro', :link => "<a href=\"http://oauth.net\">OAuth</a>" %></p>
<p><%= link_to t('user.account.register_app'), :controller => 'oauth_clients', :action => :new %></p>
<% else %>
<p><%= t 'user.account.apps_registered' %></p>
<ul><% @user.client_applications.each do |client| %>
<li><% div_for client do %>
<%= link_to client.name, :controller => 'oauth_clients', :action => :show, :id => client.id %>
<% end %></li>
<% end %></ul>
<p><%= link_to t('user.account.register_another_app'), :controller => 'oauth_clients', :action => :new %></p>
<% end %>
<br/> <br/>
<br/> <br/>
<%= link_to t('user.account.return to profile'), :controller => 'user', :action => @user.display_name %> <%= link_to t('user.account.return to profile'), :controller => 'user', :action => @user.display_name %>

View file

@ -49,6 +49,7 @@ Rails::Initializer.run do |config|
config.gem 'composite_primary_keys', :version => '2.2.2' config.gem 'composite_primary_keys', :version => '2.2.2'
config.gem 'libxml-ruby', :version => '>= 1.1.1', :lib => 'libxml' config.gem 'libxml-ruby', :version => '>= 1.1.1', :lib => 'libxml'
config.gem 'rmagick', :lib => 'RMagick' config.gem 'rmagick', :lib => 'RMagick'
config.gem 'oauth', :version => '0.3.5'
# Only load the plugins named here, in the order given. By default, all plugins # Only load the plugins named here, in the order given. By default, all plugins
# in vendor/plugins are loaded in alphabetical order. # in vendor/plugins are loaded in alphabetical order.

View file

@ -644,6 +644,21 @@ en:
scheduled_for_deletion: "Track scheduled for deletion" scheduled_for_deletion: "Track scheduled for deletion"
make_public: make_public:
made_public: "Track made public" made_public: "Track made public"
oauth:
client_application:
request_access: "The application {{app_name}} is requesting access to your account. Please check whether you would like the application to have the following capabilities. You may choose as many or as few as you like."
allow_to: "Allow the client application to:"
allow_read_prefs: "read your user preferences."
allow_write_prefs: "modify your user preferences."
allow_write_diary: "create diary entries, comments and make friends."
allow_write_api: "modify the map."
allow_read_gpx: "read your private GPS traces."
allow_write_gpx: "upload GPS traces."
token:
none: "You have not authorised any clients to act on your behalf. You do not have to do anything now to authorise them, as they will ask for authorisation when they need it. After that time you can return here to revoke those permissions if you do not want the clients to have your authorisation any more."
application: "Application"
issued: "Issued"
revoke: "Revoke!"
user: user:
login: login:
title: "Login" title: "Login"
@ -741,6 +756,12 @@ en:
return to profile: Return to profile return to profile: Return to profile
flash update success confirm needed: "User information updated successfully. Check your email for a note to confirm your new email address." flash update success confirm needed: "User information updated successfully. Check your email for a note to confirm your new email address."
flash update success: "User information updated successfully." flash update success: "User information updated successfully."
my apps: "My client applications"
developers: "Application Developers"
dev_intro: "Have you written an application which you would like to register to make {{link}} requests to the OpenStreetMap server?"
register_app: "Register your application"
apps_registered: "You have the following client applications registered:"
register_another_app: "Register another application"
confirm: confirm:
heading: Confirm a user account heading: Confirm a user account
press confirm button: "Press the confirm button below to activate your account." press confirm button: "Press the confirm button below to activate your account."

View file

@ -172,7 +172,6 @@ ActionController::Routing::Routes.draw do |map|
map.connect '/export/finish', :controller => 'export', :action => 'finish' map.connect '/export/finish', :controller => 'export', :action => 'finish'
# messages # messages
map.connect '/user/:display_name/inbox', :controller => 'message', :action => 'inbox' map.connect '/user/:display_name/inbox', :controller => 'message', :action => 'inbox'
map.connect '/user/:display_name/outbox', :controller => 'message', :action => 'outbox' map.connect '/user/:display_name/outbox', :controller => 'message', :action => 'outbox'
map.connect '/message/new/:display_name', :controller => 'message', :action => 'new' map.connect '/message/new/:display_name', :controller => 'message', :action => 'new'
@ -180,6 +179,14 @@ ActionController::Routing::Routes.draw do |map|
map.connect '/message/mark/:message_id', :controller => 'message', :action => 'mark' map.connect '/message/mark/:message_id', :controller => 'message', :action => 'mark'
map.connect '/message/reply/:message_id', :controller => 'message', :action => 'reply' map.connect '/message/reply/:message_id', :controller => 'message', :action => 'reply'
# oauth admin pages (i.e: for setting up new clients, etc...)
map.resources :oauth_clients
map.connect '/oauth/revoke', :controller => 'oauth', :action => 'revoke'
map.authorize '/oauth/authorize', :controller => 'oauth', :action => 'oauthorize'
map.request_token '/oauth/request_token', :controller => 'oauth', :action => 'request_token'
map.access_token '/oauth/access_token', :controller => 'oauth', :action => 'access_token'
map.test_request '/oauth/test_request', :controller => 'oauth', :action => 'test_request'
# fall through # fall through
map.connect ':controller/:id/:action' map.connect ':controller/:id/:action'
map.connect ':controller/:action' map.connect ':controller/:action'

View file

@ -0,0 +1,44 @@
class CreateOauthTables < ActiveRecord::Migration
def self.up
create_table :client_applications do |t|
t.string :name
t.string :url
t.string :support_url
t.string :callback_url
t.string :key, :limit => 50
t.string :secret, :limit => 50
t.integer :user_id
t.timestamps
end
add_index :client_applications, :key, :unique => true
create_table :oauth_tokens do |t|
t.integer :user_id
t.string :type, :limit => 20
t.integer :client_application_id
t.string :token, :limit => 50
t.string :secret, :limit => 50
t.timestamp :authorized_at, :invalidated_at
t.timestamps
end
add_index :oauth_tokens, :token, :unique => true
create_table :oauth_nonces do |t|
t.string :nonce
t.integer :timestamp
t.timestamps
end
add_index :oauth_nonces, [:nonce, :timestamp], :unique => true
end
def self.down
drop_table :client_applications
drop_table :oauth_tokens
drop_table :oauth_nonces
end
end

View file

@ -0,0 +1,23 @@
class AddFineOAuthPermissions < ActiveRecord::Migration
PERMISSIONS = [:allow_read_prefs, :allow_write_prefs, :allow_write_diary,
:allow_write_api, :allow_read_gpx, :allow_write_gpx ]
def self.up
PERMISSIONS.each do |perm|
# add fine-grained permissions columns for OAuth tokens, allowing people to
# give permissions to parts of the site only.
add_column :oauth_tokens, perm, :boolean, :null => false, :default => false
# add fine-grained permissions columns for client applications, allowing the
# client applications to request particular privileges.
add_column :client_applications, perm, :boolean, :null => false, :default => false
end
end
def self.down
PERMISSIONS.each do |perm|
remove_column :oauth_tokens, perm
remove_column :client_applications, perm
end
end
end

12
test/fixtures/client_applications.yml vendored Normal file
View file

@ -0,0 +1,12 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
oauth_web_app:
name: Some OAuth Web App
created_at: "2009-04-21 00:00:00"
support_url: http://some.web.app.org/support
updated_at: "2009-04-21 00:00:00"
callback_url: http://some.web.app.org/callback
url: http://some.web.app.org/
user_id: 2
secret: Ur1s9LWWJJuYBiV9cDi3za3OV8TGCoRgUvVXJ5zp7pc
key: ewvENqsaTXFnZbMWmGDX2g

View file

@ -0,0 +1,75 @@
require File.dirname(__FILE__) + '/../test_helper'
class ClientApplicationTest < ActionController::IntegrationTest
fixtures :users, :client_applications
##
# run through the procedure of creating a client application and checking
# that it shows up on the user's account page.
def test_create_application
post '/login', {'user[email]' => "test@example.com", 'user[password]' => "test", :referer => '/user/test/account'}
assert_response :redirect
follow_redirect!
assert_response :success
assert_template 'user/account'
# check that the form to allow new client application creations exists
assert_in_body do
assert_select "h2", "Application Developers"
assert_select "a[href='/oauth_clients/new']"
end
# now we follow the link to the new oauth client page
get '/oauth_clients/new'
assert_response :success
assert_in_body do
assert_select "h1", "Register a new application"
assert_select "form[action='/oauth_clients']" do
[ :name, :url, :callback_url, :support_url ].each do |inp|
assert_select "input[name=?]", "client_application[#{inp}]"
end
ClientApplication.all_permissions.each do |perm|
assert_select "input[name=?]", "client_application[#{perm}]"
end
end
end
post '/oauth_clients', {
'client_application[name]' => 'My New App',
'client_application[url]' => 'http://my.new.app.org/',
'client_application[callback_url]' => 'http://my.new.app.org/callback',
'client_application[support_url]' => 'http://my.new.app.org/support'}
assert_response :redirect
follow_redirect!
assert_response :success
assert_template 'oauth_clients/show'
assert_equal 'Registered the information successfully', flash[:notice]
# now go back to the account page and check its listed under this user
get '/user/test/account'
assert_response :success
assert_template 'user/account'
assert_in_body { assert_select "li>div>a", "My New App" }
end
##
# fake client workflow.
# this acts like a 3rd party client trying to access the site.
def test_3rd_party_token
# apparently the oauth gem doesn't really support being used inside integration
# tests, as its too tied into the HTTP headers and stuff that it signs.
end
##
# utility method to make the HTML screening easier to read.
def assert_in_body
assert_select "html:root" do
assert_select "body" do
assert_select "div#content" do
yield
end
end
end
end
end

View file

@ -71,6 +71,8 @@ class ActiveSupport::TestCase
set_fixture_class :gpx_files => 'Trace' set_fixture_class :gpx_files => 'Trace'
set_fixture_class :gps_points => 'Tracepoint' set_fixture_class :gps_points => 'Tracepoint'
set_fixture_class :gpx_file_tags => 'Tracetag' set_fixture_class :gpx_file_tags => 'Tracetag'
fixtures :client_applications
end end
## ##

View file

@ -0,0 +1,17 @@
require File.dirname(__FILE__) + '/../test_helper'
class ClientApplicationTest < ActiveSupport::TestCase
api_fixtures
##
# test that tokens can't be found unless they're authorised
def test_find_token
tok = client_applications(:oauth_web_app).create_request_token
assert_equal false, tok.authorized?, "Token should be created unauthorised."
assert_equal nil, ClientApplication.find_token(tok.token), "Shouldn't be able to find unauthorised token"
tok.authorize!(users(:public_user))
assert_equal true, tok.authorized?, "Token should now be authorised."
assert_not_equal nil, ClientApplication.find_token(tok.token), "Should be able to find authorised token"
end
end

View file

@ -0,0 +1,20 @@
require File.dirname(__FILE__) + '/../test_helper'
class OauthNonceTest < ActiveSupport::TestCase
api_fixtures
##
# the nonce has only one property, that it is a unique pair of
# string and timestamp.
def test_nonce_uniqueness
string = "0123456789ABCDEF"
timestamp = Time.now
nonce1 = OauthNonce.remember(string, timestamp)
assert_not_equal false, nonce1, "First nonce should be unique. Check your test database is empty."
nonce2 = OauthNonce.remember(string, timestamp)
assert_equal false, nonce2, "Shouldn't be able to remember the same nonce twice."
end
end

View file

@ -0,0 +1,26 @@
require File.dirname(__FILE__) + '/../test_helper'
class OauthTokenTest < ActiveSupport::TestCase
api_fixtures
##
# check that after calling invalidate! on a token, it is invalid.
def test_token_invalidation
tok = OauthToken.new
assert_equal false, tok.invalidated?, "Token should be created valid."
tok.invalidate!
assert_equal true, tok.invalidated?, "Token should now be invalid."
end
##
# check that an authorized token is authorised and can be invalidated
def test_token_authorisation
tok = RequestToken.create :client_application => client_applications(:oauth_web_app)
assert_equal false, tok.authorized?, "Token should be created unauthorised."
tok.authorize!(users(:public_user))
assert_equal true, tok.authorized?, "Token should now be authorised."
tok.invalidate!
assert_equal false, tok.authorized?, "Token should now be invalid."
end
end

View file

@ -0,0 +1,5 @@
doc
pkg
*.log
.DS_Store
.svn

61
vendor/plugins/oauth-plugin/CHANGELOG vendored Normal file
View file

@ -0,0 +1,61 @@
2/11/2009
- Fixed escaping error and file path error in the generator simultaneously reported and fixed by Ivan Valdes and Mike Demers thanks
2/9/2009
- Fixed compatibility issue with OAuth Gem 3.1 (wr0ngway and aeden)
- Added Test:Unit tests to generator (Ed Hickey)
- added missing oauth_clients/edit.html.erb view template (Ed Hickey)
- added missing :oauth_clients resource route in USAGE (Ed Hickey)
- Don't throw NPE it token is not in db (Haruska)
- Cleaned up whitespace (bricolage, Nicholas Nam)
- Fixed bug in default verify_oauth_signature (igrigorik)
- Doc fixes (skippy)
6/23/2008
- Split OAuth controller into two parts: OAuth and OAuth clients. [jcrosby]
revision 31
- patch that fixes a problem in oauth_required from Hannes Tyden and Sean Treadway from SoundCloud. Thanks.
revision 30
- updated to use oauth gem 0.2.1
revision 23
- removed all core libraries from plugin. They are now in the oauth gem.
# oauth-plugin-pre-gem Branch created
revision 18
- added a generator for creation oauth_providers
revision 12
- the bug with post and put has now been fixed.
- better documentation
revision 9
- added a test helper. Include OAuth::TestHelper in your tests or specs to mock incoming requests
revision: 8
- moved tests into oauth folder and renamed them to make them work with autotest by default
- Refactored the request methods to make them more flexible and ready for integrating with ActiveResource
- There are a few tests that fail. All of them to do with put and post requests with payload data. I decided to commit anyway, to get the new api out.
revision: 7
- Done a lot of work on the Server side of things. The Server class has changed a lot and is likely to be incompatible with previous versions
revision: 6
- Throws InsecureSignatureMethod exception if attempting to use straight sha1 or md5.
- Disables plaintext signature over http (throws an InsecureSignatureMethod)
- Better testing of signature methods - the prior tests were seriously flawed.
revision: 5
- Removed support for sha1 and md5
- Implemented draft 6 support of OAuth removing secrets from base string

20
vendor/plugins/oauth-plugin/MIT-LICENSE vendored Normal file
View file

@ -0,0 +1,20 @@
Copyright (c) 2007 [name of plugin creator]
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

110
vendor/plugins/oauth-plugin/README.rdoc vendored Normal file
View file

@ -0,0 +1,110 @@
= OAuth Plugin
This is the beginning of a plugin for implementing OAuth Providers in Rails applications.
See the OAuth specs at:
http://oauth.net/core/1.0/
and the OAuth site at:
http://oauth.net
== Requirements
You need to install the oauth gem (0.2.1) which is the core OAuth ruby library. It will NOT work on any previous version of the gem.
sudo gem install oauth
The Generator currently creates code (in particular views) that only work in Rails 2.
It should not be difficult to manually modify the code to work on Rails 1.2.x
I think the only real issue is that the views have .html.erb extensions. So these could theoretically just be renamed to .rhtml.
Please let me know if this works and I will see if I can make the generator conditionally create .rhtml for pre 2.0 versions of RAILS.
== OAuth Provider generator
While it isn't very flexible at the moment there is an oauth_provider generator which you can use like this:
./script/generate oauth_provider
This generates OAuth and OAuth client controllers as well as the required models.
It requires an authentication framework such as acts_as_authenticated, restful_authentication or restful_open_id_authentication. It also requires Rails 2.0.
=== Routes
You need to add the following to your routes (config/routes.rb)
map.resources :oauth_clients
map.authorize '/oauth/authorize',:controller=>'oauth',:action=>'authorize'
map.request_token '/oauth/request_token',:controller=>'oauth',:action=>'request_token'
map.access_token '/oauth/access_token',:controller=>'oauth',:action=>'access_token'
map.test_request '/oauth/test_request',:controller=>'oauth',:action=>'test_request'
=== User Model
Add the following lines to your user model:
has_many :client_applications
has_many :tokens, :class_name=>"OauthToken",:order=>"authorized_at desc",:include=>[:client_application]
=== Migrate database
The database is defined in:
db/migrate/XXX_create_oauth_tables.rb
Run them as any other normal migration in rails with:
rake db:migrate
=== RSpec
The generator installs a collection of RSpec (http://rspec.info) specs instead of normal unit_tests. If you don't use RSpec (and really why aren't you?) feel free to remove the spec folder.
If you would like to contribute regular unit tests I will accept them with a smile.
== Protecting your actions
I recommend that you think about what your users would want to provide access to and limit oauth for those only. For example in a CRUD controller you may think about if you want to let consumer applications do the create, update or delete actions. For your application this might make sense, but for others maybe not.
If you want to give oauth access to everything a registered user can do, just replace the filter you have in your controllers with:
before_filter :login_or_oauth_required
If you want to restrict consumers to the index and show methods of your controller do the following:
before_filter :login_required,:except=>[:show,:index]
before_filter :login_or_oauth_required,:only=>[:show,:index]
If you have an action you only want used via oauth:
before_filter :oauth_required
All of these places the tokens user in current_user as you would expect. It also exposes the following methods:
* current_token - for accessing the token used to authorize the current request
* current_client_application - for accessing information about which consumer is currently accessing your request
You could add application specific information to the OauthToken and ClientApplication model for such things as object level access control, billing, expiry etc. Be creative and you can create some really cool applications here.
== More
The Google Code project is http://code.google.com/p/oauth-plugin/
The Mailing List for all things OAuth in Ruby is:
http://groups.google.com/group/oauth-ruby
The Mailing list for everything else OAuth is:
http://groups.google.com/group/oauth
The OAuth Ruby Gem home page is http://oauth.rubyforge.org
Please help documentation, patches and testing.
Copyright (c) 2007-2008 Pelle Braendgaard, released under the MIT license

22
vendor/plugins/oauth-plugin/Rakefile vendored Normal file
View file

@ -0,0 +1,22 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test the oauth plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for the oauth plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'Oauth'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end

View file

@ -0,0 +1,24 @@
./script/generate oauth_provider
This creates an OAuth Provider controller as well as the requisite models.
It requires an authentication framework such as acts_as_authenticated, restful_authentication or restful_open_id_authentication.
If you generated the migration file (true by default), make sure you run
rake db:migrate
You need to add the following routes to your config/routes.rb file:
map.resources :oauth_clients
map.oauth '/oauth',:controller=>'oauth',:action=>'index'
map.authorize '/oauth/authorize',:controller=>'oauth',:action=>'authorize'
map.request_token '/oauth/request_token',:controller=>'oauth',:action=>'request_token'
map.access_token '/oauth/access_token',:controller=>'oauth',:action=>'access_token'
map.test_request '/oauth/test_request',:controller=>'oauth',:action=>'test_request'
include the following in your user.rb
has_many :client_applications
has_many :tokens, :class_name=>"OauthToken",:order=>"authorized_at desc",:include=>[:client_application]

View file

@ -0,0 +1,116 @@
class OauthProviderGenerator < Rails::Generator::Base
default_options :skip_migration => false
attr_reader :class_path,
:controller_name,
:controller_class_path,
:controller_file_path,
:controller_class_name,
:controller_singular_name,
:controller_plural_name
alias_method :controller_file_name, :controller_singular_name
def initialize(runtime_args, runtime_options = {})
super
@controller_name = args.shift || 'oauth'
@controller_singular_name = 'oauth'
@controller_plural_name = 'oauth'
@controller_file_name = 'oauth'
@controller_class_name="Oauth"
@class_path=''
@controller_class_path=''
end
def manifest
record do |m|
# Check for class naming collisions.
# Check for class naming collisions.
m.class_collisions controller_class_path, "#{controller_class_name}Controller", # Oauth Controller
"#{controller_class_name}Helper",
"#{controller_class_name}ClientsController",
"#{controller_class_name}ClientsHelper"
m.class_collisions class_path, "ClientApplication","OauthNonce","RequestToken","AccessToken","OauthToken"
# Controller, helper, views, and test directories.
m.directory File.join('app/models', class_path)
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('app/views', controller_class_path, 'oauth_clients')
m.template 'client_application.rb',File.join('app/models',"client_application.rb")
m.template 'oauth_token.rb', File.join('app/models',"oauth_token.rb")
m.template 'request_token.rb', File.join('app/models',"request_token.rb")
m.template 'access_token.rb', File.join('app/models',"access_token.rb")
m.template 'oauth_nonce.rb', File.join('app/models',"oauth_nonce.rb")
m.template 'controller.rb',File.join('app/controllers',controller_class_path,"#{controller_file_name}_controller.rb")
m.template 'helper.rb',File.join('app/helpers',controller_class_path,"#{controller_file_name}_helper.rb")
m.template 'clients_controller.rb',File.join('app/controllers',controller_class_path,"#{controller_file_name}_clients_controller.rb")
m.template 'clients_helper.rb',File.join('app/helpers',controller_class_path,"#{controller_file_name}_clients_helper.rb")
if !options[:test_unit]
m.directory File.join('spec')
m.directory File.join('spec/models')
m.directory File.join('spec/fixtures', class_path)
m.directory File.join('spec/controllers', controller_class_path)
m.template 'client_application_spec.rb',File.join('spec/models',"client_application_spec.rb")
m.template 'oauth_token_spec.rb', File.join('spec/models',"oauth_token_spec.rb")
m.template 'oauth_nonce_spec.rb', File.join('spec/models',"oauth_nonce_spec.rb")
m.template 'client_applications.yml',File.join('spec/fixtures',"client_applications.yml")
m.template 'oauth_tokens.yml', File.join('spec/fixtures',"oauth_tokens.yml")
m.template 'oauth_nonces.yml', File.join('spec/fixtures',"oauth_nonces.yml")
m.template 'controller_spec_helper.rb', File.join('spec/controllers', controller_class_path,"#{controller_file_name}_controller_spec_helper.rb")
m.template 'controller_spec.rb',File.join('spec/controllers',controller_class_path,"#{controller_file_name}_controller_spec.rb")
m.template 'clients_controller_spec.rb',File.join('spec/controllers',controller_class_path,"#{controller_file_name}_clients_controller_spec.rb")
else
m.directory File.join('test')
m.directory File.join('test/unit')
m.directory File.join('test/fixtures', class_path)
m.directory File.join('test/functional', controller_class_path)
m.template 'client_application_test.rb',File.join('test/unit',"client_application_test.rb")
m.template 'oauth_token_test.rb', File.join('test/unit',"oauth_token_test.rb")
m.template 'oauth_nonce_test.rb', File.join('test/unit',"oauth_nonce_test.rb")
m.template 'client_applications.yml',File.join('test/fixtures',"client_applications.yml")
m.template 'oauth_tokens.yml', File.join('test/fixtures',"oauth_tokens.yml")
m.template 'oauth_nonces.yml', File.join('test/fixtures',"oauth_nonces.yml")
m.template 'controller_test_helper.rb', File.join('test', controller_class_path,"#{controller_file_name}_controller_test_helper.rb")
m.template 'controller_test.rb',File.join('test/functional',controller_class_path,"#{controller_file_name}_controller_test.rb")
m.template 'clients_controller_test.rb',File.join('test/functional',controller_class_path,"#{controller_file_name}_clients_controller_test.rb")
end
m.template '_form.html.erb', File.join('app/views', controller_class_path, 'oauth_clients', "_form.html.erb")
m.template 'new.html.erb', File.join('app/views', controller_class_path, 'oauth_clients', "new.html.erb")
m.template 'index.html.erb', File.join('app/views', controller_class_path, 'oauth_clients', "index.html.erb")
m.template 'show.html.erb', File.join('app/views', controller_class_path, 'oauth_clients', "show.html.erb")
m.template 'edit.html.erb', File.join('app/views', controller_class_path, 'oauth_clients', "edit.html.erb")
m.template 'authorize.html.erb', File.join('app/views', controller_class_path, controller_file_name, "authorize.html.erb")
m.template 'authorize_success.html.erb', File.join('app/views', controller_class_path, controller_file_name, "authorize_success.html.erb")
m.template 'authorize_failure.html.erb', File.join('app/views', controller_class_path, controller_file_name, "authorize_failure.html.erb")
unless options[:skip_migration]
m.migration_template 'migration.rb', 'db/migrate', :assigns => {
:migration_name => "CreateOauthTables"
}, :migration_file_name => "create_oauth_tables"
end
end
end
protected
def banner
"Usage: #{$0} #{spec.name}"
end
def add_options!(opt)
opt.separator ''
opt.separator 'Options:'
opt.on("--skip-migration",
"Don't generate a migration file") { |v| options[:skip_migration] = v }
opt.on("--test-unit",
"Generate the Test::Unit compatible tests instead of RSpec") { |v| options[:test_unit] = v }
end
end

View file

@ -0,0 +1,16 @@
<div class="field">
<label for="client_application_name">Name*</label><br/>
<%%= f.text_field :name %>
</div>
<div class="field">
<label for="client_application_url">Main Application URL*</label><br/>
<%%= f.text_field :url %>
</div>
<div class="field">
<label for="client_application_callback_url">Callback URL*</label><br/>
<%%= f.text_field :callback_url %>
</div>
<div class="field">
<label for="client_application_support_url">Support URL</label><br/>
<%%= f.text_field :support_url %>
</div>

View file

@ -0,0 +1,10 @@
class AccessToken<OauthToken
validates_presence_of :user
before_create :set_authorized_at
protected
def set_authorized_at
self.authorized_at = Time.now
end
end

View file

@ -0,0 +1,14 @@
<h1>Authorize access to your account</h1>
<p>Would you like to authorize <%%= link_to @token.client_application.name,@token.client_application.url %> (<%%= link_to @token.client_application.url,@token.client_application.url %>) to access your account?</p>
<%% form_tag authorize_url do %>
<%%= hidden_field_tag "oauth_token", @token.token %>
<%%- if params[:oauth_callback] -%>
<%%= hidden_field_tag "oauth_callback", params[:oauth_callback] %>
<%%- end -%>
<p>
<%%= check_box_tag 'authorize' %> authorize access
</p>
<p>
<%%= submit_tag %>
</p>
<%% end %>

View file

@ -0,0 +1 @@
<h1>You have disallowed this request</h1>

View file

@ -0,0 +1 @@
<h1>You have allowed this request</h1>

View file

@ -0,0 +1,54 @@
require 'oauth'
class ClientApplication < ActiveRecord::Base
belongs_to :user
has_many :tokens, :class_name => "OauthToken"
validates_presence_of :name, :url, :key, :secret
validates_uniqueness_of :key
before_validation_on_create :generate_keys
def self.find_token(token_key)
token = OauthToken.find_by_token(token_key, :include => :client_application)
if token && token.authorized?
logger.info "Loaded #{token.token} which was authorized by (user_id=#{token.user_id}) on the #{token.authorized_at}"
token
else
nil
end
end
def self.verify_request(request, options = {}, &block)
begin
signature = OAuth::Signature.build(request, options, &block)
logger.info "Signature Base String: #{signature.signature_base_string}"
logger.info "Consumer: #{signature.send :consumer_key}"
logger.info "Token: #{signature.send :token}"
return false unless OauthNonce.remember(signature.request.nonce, signature.request.timestamp)
value = signature.verify
logger.info "Signature verification returned: #{value.to_s}"
value
rescue OAuth::Signature::UnknownSignatureMethod => e
logger.info "ERROR"+e.to_s
false
end
end
def oauth_server
@oauth_server ||= OAuth::Server.new("http://your.site")
end
def credentials
@oauth_client ||= OAuth::Consumer.new(key, secret)
end
def create_request_token
RequestToken.create :client_application => self
end
protected
def generate_keys
@oauth_client = oauth_server.generate_consumer_credentials
self.key = @oauth_client.key
self.secret = @oauth_client.secret
end
end

View file

@ -0,0 +1,60 @@
require File.dirname(__FILE__) + '/../spec_helper'
module OAuthSpecHelpers
def create_consumer
@consumer = OAuth::Consumer.new(@application.key,@application.secret,
{
:site => @application.oauth_server.base_url
})
end
def create_test_request
end
def create_oauth_request
@token = AccessToken.create :client_application => @application, :user => users(:quentin)
@request = @consumer.create_signed_request(:get, "/hello", @token)
end
def create_request_token_request
@request = @consumer.create_signed_request(:get, @application.oauth_server.request_token_path, @token)
end
def create_access_token_request
@token = RequestToken.create :client_application => @application
@request = @consumer.create_signed_request(:get, @application.oauth_server.request_token_path, @token)
end
end
describe ClientApplication do #, :shared => true do
include OAuthSpecHelpers
fixtures :users, :client_applications, :oauth_tokens
before(:each) do
@application = ClientApplication.create :name => "Agree2", :url => "http://agree2.com", :user => users(:quentin)
create_consumer
end
it "should be valid" do
@application.should be_valid
end
it "should not have errors" do
@application.errors.full_messages.should == []
end
it "should have key and secret" do
@application.key.should_not be_nil
@application.secret.should_not be_nil
end
it "should have credentials" do
@application.credentials.should_not be_nil
@application.credentials.key.should == @application.key
@application.credentials.secret.should == @application.secret
end
end

View file

@ -0,0 +1,42 @@
require File.dirname(__FILE__) + '/../test_helper'
module OAuthHelpers
def create_consumer
@consumer=OAuth::Consumer.new(@application.key,@application.secret,
{
:site=>@application.oauth_server.base_url
})
end
end
class ClientApplicationTest < ActiveSupport::TestCase
include OAuthHelpers
fixtures :users,:client_applications,:oauth_tokens
def setup
@application = ClientApplication.create :name=>"Agree2",:url=>"http://agree2.com",:user=>users(:quentin)
create_consumer
end
def test_should_be_valid
assert @application.valid?
end
def test_should_not_have_errors
assert_equal [], @application.errors.full_messages
end
def test_should_have_key_and_secret
assert_not_nil @application.key
assert_not_nil @application.secret
end
def test_should_have_credentials
assert_not_nil @application.credentials
assert_equal @application.key, @application.credentials.key
assert_equal @application.secret, @application.credentials.secret
end
end

View file

@ -0,0 +1,23 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
id: 1
name: MyString
url: MyString
support_url: MyString
callback_url: MyString
key: one_key
secret: MyString
user_id: 1
created_at: 2007-11-17 16:56:51
updated_at: 2007-11-17 16:56:51
two:
id: 2
name: MyString
url: MyString
support_url: MyString
callback_url: MyString
key: two_key
secret: MyString
user_id: 1
created_at: 2007-11-17 16:56:51
updated_at: 2007-11-17 16:56:51

View file

@ -0,0 +1,47 @@
class OauthClientsController < ApplicationController
before_filter :login_required
def index
@client_applications = current_user.client_applications
@tokens = current_user.tokens.find :all, :conditions => 'oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null'
end
def new
@client_application = ClientApplication.new
end
def create
@client_application = current_user.client_applications.build(params[:client_application])
if @client_application.save
flash[:notice] = "Registered the information successfully"
redirect_to :action => "show", :id => @client_application.id
else
render :action => "new"
end
end
def show
@client_application = current_user.client_applications.find(params[:id])
end
def edit
@client_application = current_user.client_applications.find(params[:id])
end
def update
@client_application = current_user.client_applications.find(params[:id])
if @client_application.update_attributes(params[:client_application])
flash[:notice] = "Updated the client information successfully"
redirect_to :action => "show", :id => @client_application.id
else
render :action => "edit"
end
end
def destroy
@client_application = current_user.client_applications.find(params[:id])
@client_application.destroy
flash[:notice] = "Destroyed the client application registration"
redirect_to :action => "index"
end
end

View file

@ -0,0 +1,239 @@
require File.dirname(__FILE__) + '/../spec_helper'
require File.dirname(__FILE__) + '/oauth_controller_spec_helper'
require 'oauth/client/action_controller_request'
describe OauthClientsController, "index" do
include OAuthControllerSpecHelper
before(:each) do
login_as_application_owner
end
def do_get
get :index
end
it "should be successful" do
do_get
response.should be_success
end
it "should query current_users client applications" do
@user.should_receive(:client_applications).and_return(@client_applications)
do_get
end
it "should assign client_applications" do
do_get
assigns[:client_applications].should equal(@client_applications)
end
it "should render index template" do
do_get
response.should render_template('index')
end
end
describe OauthClientsController, "show" do
include OAuthControllerSpecHelper
before(:each) do
login_as_application_owner
end
def do_get
get :show, :id => '3'
end
it "should be successful" do
do_get
response.should be_success
end
it "should query current_users client applications" do
@user.should_receive(:client_applications).and_return(@client_applications)
@client_applications.should_receive(:find).with('3').and_return(@client_application)
do_get
end
it "should assign client_applications" do
do_get
assigns[:client_application].should equal(@client_application)
end
it "should render show template" do
do_get
response.should render_template('show')
end
end
describe OauthClientsController, "new" do
include OAuthControllerSpecHelper
before(:each) do
login_as_application_owner
ClientApplication.stub!(:new).and_return(@client_application)
end
def do_get
get :new
end
it "should be successful" do
do_get
response.should be_success
end
it "should assign client_applications" do
do_get
assigns[:client_application].should equal(@client_application)
end
it "should render show template" do
do_get
response.should render_template('new')
end
end
describe OauthClientsController, "edit" do
include OAuthControllerSpecHelper
before(:each) do
login_as_application_owner
end
def do_get
get :edit, :id => '3'
end
it "should be successful" do
do_get
response.should be_success
end
it "should query current_users client applications" do
@user.should_receive(:client_applications).and_return(@client_applications)
@client_applications.should_receive(:find).with('3').and_return(@client_application)
do_get
end
it "should assign client_applications" do
do_get
assigns[:client_application].should equal(@client_application)
end
it "should render edit template" do
do_get
response.should render_template('edit')
end
end
describe OauthClientsController, "create" do
include OAuthControllerSpecHelper
before(:each) do
login_as_application_owner
@client_applications.stub!(:build).and_return(@client_application)
@client_application.stub!(:save).and_return(true)
end
def do_valid_post
@client_application.should_receive(:save).and_return(true)
post :create, 'client_application'=>{'name' => 'my site'}
end
def do_invalid_post
@client_application.should_receive(:save).and_return(false)
post :create, :client_application=>{:name => 'my site'}
end
it "should query current_users client applications" do
@client_applications.should_receive(:build).and_return(@client_application)
do_valid_post
end
it "should redirect to new client_application" do
do_valid_post
response.should be_redirect
response.should redirect_to(:action => "show", :id => @client_application.id)
end
it "should assign client_applications" do
do_invalid_post
assigns[:client_application].should equal(@client_application)
end
it "should render show template" do
do_invalid_post
response.should render_template('new')
end
end
describe OauthClientsController, "destroy" do
include OAuthControllerSpecHelper
before(:each) do
login_as_application_owner
@client_application.stub!(:destroy)
end
def do_delete
delete :destroy, :id => '3'
end
it "should query current_users client applications" do
@user.should_receive(:client_applications).and_return(@client_applications)
@client_applications.should_receive(:find).with('3').and_return(@client_application)
do_delete
end
it "should destroy client applications" do
@client_application.should_receive(:destroy)
do_delete
end
it "should redirect to list" do
do_delete
response.should be_redirect
response.should redirect_to(:action => 'index')
end
end
describe OauthClientsController, "update" do
include OAuthControllerSpecHelper
before(:each) do
login_as_application_owner
end
def do_valid_update
@client_application.should_receive(:update_attributes).and_return(true)
put :update, :id => '1', 'client_application'=>{'name' => 'my site'}
end
def do_invalid_update
@client_application.should_receive(:update_attributes).and_return(false)
put :update, :id => '1', 'client_application'=>{'name' => 'my site'}
end
it "should query current_users client applications" do
@user.should_receive(:client_applications).and_return(@client_applications)
@client_applications.should_receive(:find).with('1').and_return(@client_application)
do_valid_update
end
it "should redirect to new client_application" do
do_valid_update
response.should be_redirect
response.should redirect_to(:action => "show", :id => @client_application.id)
end
it "should assign client_applications" do
do_invalid_update
assigns[:client_application].should equal(@client_application)
end
it "should render show template" do
do_invalid_update
response.should render_template('edit')
end
end

View file

@ -0,0 +1,280 @@
require File.dirname(__FILE__) + '/../test_helper'
require File.dirname(__FILE__) + '/../oauth_controller_test_helper'
require 'oauth/client/action_controller_request'
class OauthClientsController; def rescue_action(e) raise e end; end
class OauthClientsControllerIndexTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthClientsController
def setup
@controller = OauthClientsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
login_as_application_owner
end
def do_get
get :index
end
def test_should_be_successful
do_get
assert @response.success?
end
def test_should_query_current_users_client_applications
@user.expects(:client_applications).returns(@client_applications)
do_get
end
def test_should_assign_client_applications
do_get
assert_equal @client_applications, assigns(:client_applications)
end
def test_should_render_index_template
do_get
assert_template 'index'
end
end
class OauthClientsControllerShowTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthClientsController
def setup
@controller = OauthClientsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
login_as_application_owner
end
def do_get
get :show, :id=>'3'
end
def test_should_be_successful
do_get
assert @response.success?
end
def test_should_query_current_users_client_applications
@user.expects(:client_applications).returns(@client_applications)
@client_applications.expects(:find).with('3').returns(@client_application)
do_get
end
def test_should_assign_client_applications
do_get
assert_equal @client_application, assigns(:client_application)
end
def test_should_render_show_template
do_get
assert_template 'show'
end
end
class OauthClientsControllerNewTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthClientsController
def setup
@controller = OauthClientsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
login_as_application_owner
ClientApplication.stubs(:new).returns(@client_application)
end
def do_get
get :new
end
def test_should_be_successful
do_get
assert @response.success?
end
def test_should_assign_client_applications
do_get
assert_equal @client_application, assigns(:client_application)
end
def test_should_render_show_template
do_get
assert_template 'new'
end
end
class OauthClientsControllerEditTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthClientsController
def setup
@controller = OauthClientsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
login_as_application_owner
end
def do_get
get :edit, :id=>'3'
end
def test_should_be_successful
do_get
assert @response.success?
end
def test_should_query_current_users_client_applications
@user.expects(:client_applications).returns(@client_applications)
@client_applications.expects(:find).with('3').returns(@client_application)
do_get
end
def test_should_assign_client_applications
do_get
assert_equal @client_application, assigns(:client_application)
end
def test_should_render_edit_template
do_get
assert_template 'edit'
end
end
class OauthClientsControllerCreateTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthClientsController
def setup
@controller = OauthClientsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
login_as_application_owner
@client_applications.stubs(:build).returns(@client_application)
@client_application.stubs(:save).returns(true)
end
def do_valid_post
@client_application.expects(:save).returns(true)
post :create,'client_application'=>{'name'=>'my site'}
end
def do_invalid_post
@client_application.expects(:save).returns(false)
post :create,:client_application=>{:name=>'my site'}
end
def test_should_query_current_users_client_applications
@client_applications.expects(:build).returns(@client_application)
do_valid_post
end
def test_should_redirect_to_new_client_application
do_valid_post
assert_response :redirect
assert_redirected_to(:action => "show", :id => @client_application.id)
end
def test_should_assign_client_applications
do_invalid_post
assert_equal @client_application, assigns(:client_application)
end
def test_should_render_show_template
do_invalid_post
assert_template('new')
end
end
class OauthClientsControllerDestroyTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthClientsController
def setup
@controller = OauthClientsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
login_as_application_owner
@client_application.stubs(:destroy)
end
def do_delete
delete :destroy,:id=>'3'
end
def test_should_query_current_users_client_applications
@user.expects(:client_applications).returns(@client_applications)
@client_applications.expects(:find).with('3').returns(@client_application)
do_delete
end
def test_should_destroy_client_applications
@client_application.expects(:destroy)
do_delete
end
def test_should_redirect_to_list
do_delete
assert_response :redirect
assert_redirected_to :action => 'index'
end
end
class OauthClientsControllerUpdateTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthClientsController
def setup
@controller = OauthClientsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
login_as_application_owner
end
def do_valid_update
@client_application.expects(:update_attributes).returns(true)
put :update, :id => '1', 'client_application' => {'name'=>'my site'}
end
def do_invalid_update
@client_application.expects(:update_attributes).returns(false)
put :update, :id=>'1', 'client_application' => {'name'=>'my site'}
end
def test_should_query_current_users_client_applications
@user.expects(:client_applications).returns(@client_applications)
@client_applications.expects(:find).with('1').returns(@client_application)
do_valid_update
end
def test_should_redirect_to_new_client_application
do_valid_update
assert_response :redirect
assert_redirected_to :action => "show", :id => @client_application.id
end
def test_should_assign_client_applications
do_invalid_update
assert_equal @client_application, assigns(:client_application)
end
def test_should_render_show_template
do_invalid_update
assert_template('edit')
end
end

View file

@ -0,0 +1,2 @@
module OauthClientsHelper
end

View file

@ -0,0 +1,62 @@
class OauthController < ApplicationController
before_filter :login_required, :except => [:request_token, :access_token, :test_request]
before_filter :login_or_oauth_required, :only => [:test_request]
before_filter :verify_oauth_consumer_signature, :only => [:request_token]
before_filter :verify_oauth_request_token, :only => [:access_token]
# Uncomment the following if you are using restful_open_id_authentication
# skip_before_filter :verify_authenticity_token
def request_token
@token = current_client_application.create_request_token
if @token
render :text => @token.to_query
else
render :nothing => true, :status => 401
end
end
def access_token
@token = current_token && current_token.exchange!
if @token
render :text => @token.to_query
else
render :nothing => true, :status => 401
end
end
def test_request
render :text => params.collect{|k,v|"#{k}=#{v}"}.join("&")
end
def authorize
@token = RequestToken.find_by_token params[:oauth_token]
unless @token.invalidated?
if request.post?
if params[:authorize] == '1'
@token.authorize!(current_user)
redirect_url = params[:oauth_callback] || @token.client_application.callback_url
if redirect_url
redirect_to "#{redirect_url}?oauth_token=#{@token.token}"
else
render :action => "authorize_success"
end
elsif params[:authorize] == "0"
@token.invalidate!
render :action => "authorize_failure"
end
end
else
render :action => "authorize_failure"
end
end
def revoke
@token = current_user.tokens.find_by_token params[:token]
if @token
@token.invalidate!
flash[:notice] = "You've revoked the token for #{@token.client_application.name}"
end
redirect_to oauth_clients_url
end
end

View file

@ -0,0 +1,296 @@
require File.dirname(__FILE__) + '/../spec_helper'
require File.dirname(__FILE__) + '/oauth_controller_spec_helper'
require 'oauth/client/action_controller_request'
describe OauthController, "getting a request token" do
include OAuthControllerSpecHelper
before(:each) do
setup_oauth
sign_request_with_oauth
@client_application.stub!(:create_request_token).and_return(@request_token)
end
def do_get
get :request_token
end
it "should be successful" do
do_get
response.should be_success
end
it "should query for client_application" do
ClientApplication.should_receive(:find_by_key).with('key').and_return(@client_application)
do_get
end
it "should request token from client_application" do
@client_application.should_receive(:create_request_token).and_return(@request_token)
do_get
end
it "should return token string" do
do_get
response.body.should == @request_token_string
end
end
describe OauthController, "token authorization" do
include OAuthControllerSpecHelper
before(:each) do
login
setup_oauth
RequestToken.stub!(:find_by_token).and_return(@request_token)
end
def do_get
get :authorize, :oauth_token => @request_token.token
end
def do_post
@request_token.should_receive(:authorize!).with(@user)
post :authorize, :oauth_token => @request_token.token, :authorize => "1"
end
def do_post_without_user_authorization
@request_token.should_receive(:invalidate!)
post :authorize, :oauth_token => @request_token.token, :authorize => "0"
end
def do_post_with_callback
@request_token.should_receive(:authorize!).with(@user)
post :authorize, :oauth_token => @request_token.token, :oauth_callback => "http://application/alternative", :authorize => "1"
end
def do_post_with_no_application_callback
@request_token.should_receive(:authorize!).with(@user)
@client_application.stub!(:callback_url).and_return(nil)
post :authorize, :oauth_token => @request_token.token, :authorize => "1"
end
it "should be successful" do
do_get
response.should be_success
end
it "should query for client_application" do
RequestToken.should_receive(:find_by_token).and_return(@request_token)
do_get
end
it "should assign token" do
do_get
assigns[:token].should equal(@request_token)
end
it "should render authorize template" do
do_get
response.should render_template('authorize')
end
it "should redirect to default callback" do
do_post
response.should be_redirect
response.should redirect_to("http://application/callback?oauth_token=#{@request_token.token}")
end
it "should redirect to callback in query" do
do_post_with_callback
response.should be_redirect
response.should redirect_to("http://application/alternative?oauth_token=#{@request_token.token}")
end
it "should be successful on authorize without any application callback" do
do_post_with_no_application_callback
response.should be_success
end
it "should be successful on authorize without any application callback" do
do_post_with_no_application_callback
response.should render_template('authorize_success')
end
it "should render failure screen on user invalidation" do
do_post_without_user_authorization
response.should render_template('authorize_failure')
end
it "should render failure screen if token is invalidated" do
@request_token.should_receive(:invalidated?).and_return(true)
do_get
response.should render_template('authorize_failure')
end
end
describe OauthController, "getting an access token" do
include OAuthControllerSpecHelper
before(:each) do
setup_oauth
sign_request_with_oauth @request_token
@request_token.stub!(:exchange!).and_return(@access_token)
end
def do_get
get :access_token
end
it "should be successful" do
do_get
response.should be_success
end
it "should query for client_application" do
ClientApplication.should_receive(:find_token).with(@request_token.token).and_return(@request_token)
do_get
end
it "should request token from client_application" do
@request_token.should_receive(:exchange!).and_return(@access_token)
do_get
end
it "should return token string" do
do_get
response.body.should == @access_token_string
end
end
class OauthorizedController<ApplicationController
before_filter :login_or_oauth_required, :only => :both
before_filter :login_required, :only => :interactive
before_filter :oauth_required, :only => :token_only
def interactive
end
def token_only
end
def both
end
end
describe OauthorizedController, " access control" do
include OAuthControllerSpecHelper
before(:each) do
end
it "should have access_token set up correctly" do
setup_to_authorize_request
@access_token.is_a?(AccessToken).should == true
@access_token.should be_authorized
@access_token.should_not be_invalidated
@access_token.user.should == @user
@access_token.client_application.should == @client_application
end
it "should return false for oauth? by default" do
controller.send(:oauth?).should == false
end
it "should return nil for current_token by default" do
controller.send(:current_token).should be_nil
end
it "should allow oauth when using login_or_oauth_required" do
setup_to_authorize_request
sign_request_with_oauth(@access_token)
ClientApplication.should_receive(:find_token).with(@access_token.token).and_return(@access_token)
get :both
controller.send(:current_token).should == @access_token
controller.send(:current_token).is_a?(AccessToken).should == true
controller.send(:current_user).should == @user
controller.send(:current_client_application).should == @client_application
response.code.should == '200'
response.should be_success
end
it "should allow interactive when using login_or_oauth_required" do
login
get :both
response.should be_success
controller.send(:current_user).should == @user
controller.send(:current_token).should be_nil
end
it "should allow oauth when using oauth_required" do
setup_to_authorize_request
sign_request_with_oauth(@access_token)
ClientApplication.should_receive(:find_token).with(@access_token.token).and_return(@access_token)
get :token_only
controller.send(:current_token).should == @access_token
controller.send(:current_client_application).should == @client_application
controller.send(:current_user).should == @user
response.code.should == '200'
response.should be_success
end
it "should disallow oauth using RequestToken when using oauth_required" do
setup_to_authorize_request
ClientApplication.should_receive(:find_token).with(@request_token.token).and_return(@request_token)
sign_request_with_oauth(@request_token)
get :token_only
response.code.should == '401'
end
it "should disallow interactive when using oauth_required" do
login
get :token_only
response.code.should == '401'
controller.send(:current_user).should == @user
controller.send(:current_token).should be_nil
end
it "should disallow oauth when using login_required" do
setup_to_authorize_request
sign_request_with_oauth(@access_token)
get :interactive
response.code.should == "302"
controller.send(:current_user).should be_nil
controller.send(:current_token).should be_nil
end
it "should allow interactive when using login_required" do
login
get :interactive
response.should be_success
controller.send(:current_user).should == @user
controller.send(:current_token).should be_nil
end
end
describe OauthController, "revoke" do
include OAuthControllerSpecHelper
before(:each) do
setup_oauth_for_user
@request_token.stub!(:invalidate!)
end
def do_post
post :revoke, :token => "TOKEN STRING"
end
it "should redirect to index" do
do_post
response.should be_redirect
response.should redirect_to('http://test.host/oauth_clients')
end
it "should query current_users tokens" do
@tokens.should_receive(:find_by_token).and_return(@request_token)
do_post
end
it "should call invalidate on token" do
@request_token.should_receive(:invalidate!)
do_post
end
end

View file

@ -0,0 +1,74 @@
module OAuthControllerSpecHelper
def login
controller.stub!(:local_request?).and_return(true)
@user = mock_model(User)
controller.stub!(:current_user).and_return(@user)
@tokens = []
@tokens.stub!(:find).and_return(@tokens)
@user.stub!(:tokens).and_return(@tokens)
User.stub!(:find_by_id).and_return(@user)
end
def login_as_application_owner
login
@client_application = mock_model(ClientApplication)
@client_applications = [@client_application]
@user.stub!(:client_applications).and_return(@client_applications)
@client_applications.stub!(:find).and_return(@client_application)
end
def setup_oauth
controller.stub!(:local_request?).and_return(true)
@user||=mock_model(User)
User.stub!(:find_by_id).and_return(@user)
@server = OAuth::Server.new "http://test.host"
@consumer = OAuth::Consumer.new('key', 'secret',{:site => "http://test.host"})
@client_application = mock_model(ClientApplication)
controller.stub!(:current_client_application).and_return(@client_application)
ClientApplication.stub!(:find_by_key).and_return(@client_application)
@client_application.stub!(:key).and_return(@consumer.key)
@client_application.stub!(:secret).and_return(@consumer.secret)
@client_application.stub!(:name).and_return("Client Application name")
@client_application.stub!(:callback_url).and_return("http://application/callback")
@request_token = mock_model(RequestToken, :token => 'request_token', :client_application => @client_application, :secret => "request_secret", :user => @user)
@request_token.stub!(:invalidated?).and_return(false)
ClientApplication.stub!(:find_token).and_return(@request_token)
@request_token_string = "oauth_token = request_token&oauth_token_secret = request_secret"
@request_token.stub!(:to_query).and_return(@request_token_string)
@access_token = mock_model(AccessToken, :token => 'access_token', :client_application => @client_application, :secret => "access_secret", :user => @user)
@access_token.stub!(:invalidated?).and_return(false)
@access_token.stub!(:authorized?).and_return(true)
@access_token_string = "oauth_token = access_token&oauth_token_secret = access_secret"
@access_token.stub!(:to_query).and_return(@access_token_string)
@client_application.stub!(:authorize_request?).and_return(true)
# @client_application.stub!(:sign_request_with_oauth_token).and_return(@request_token)
@client_application.stub!(:exchange_for_access_token).and_return(@access_token)
end
def setup_oauth_for_user
login
setup_oauth
@tokens = [@request_token]
@tokens.stub!(:find).and_return(@tokens)
@tokens.stub!(:find_by_token).and_return(@request_token)
@user.stub!(:tokens).and_return(@tokens)
end
def sign_request_with_oauth(token = nil)
ActionController::TestRequest.use_oauth = true
@request.configure_oauth(@consumer,token)
end
def setup_to_authorize_request
setup_oauth
OauthToken.stub!(:find_by_token).with( @access_token.token).and_return(@access_token)
@access_token.stub!(:is_a?).and_return(true)
end
end

View file

@ -0,0 +1,310 @@
require File.dirname(__FILE__) + '/../test_helper'
require File.dirname(__FILE__) + '/../oauth_controller_test_helper'
require 'oauth/client/action_controller_request'
class OauthController; def rescue_action(e) raise e end; end
class OauthControllerRequestTokenTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthController
def setup
@controller = OauthController.new
setup_oauth
sign_request_with_oauth
@client_application.stubs(:create_request_token).returns(@request_token)
end
def do_get
get :request_token
end
def test_should_be_successful
do_get
assert @response.success?
end
def test_should_query_for_client_application
ClientApplication.expects(:find_by_key).with('key').returns(@client_application)
do_get
end
def test_should_request_token_from_client_application
@client_application.expects(:create_request_token).returns(@request_token)
do_get
end
def test_should_return_token_string
do_get
assert_equal @request_token_string, @response.body
end
end
class OauthControllerTokenAuthorizationTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthController
def setup
@controller = OauthController.new
login
setup_oauth
RequestToken.stubs(:find_by_token).returns(@request_token)
end
def do_get
get :authorize, :oauth_token => @request_token.token
end
def do_post
@request_token.expects(:authorize!).with(@user)
post :authorize,:oauth_token=>@request_token.token,:authorize=>"1"
end
def do_post_without_user_authorization
@request_token.expects(:invalidate!)
post :authorize,:oauth_token=>@request_token.token,:authorize=>"0"
end
def do_post_with_callback
@request_token.expects(:authorize!).with(@user)
post :authorize,:oauth_token=>@request_token.token,:oauth_callback=>"http://application/alternative",:authorize=>"1"
end
def do_post_with_no_application_callback
@request_token.expects(:authorize!).with(@user)
@client_application.stubs(:callback_url).returns(nil)
post :authorize, :oauth_token => @request_token.token, :authorize=>"1"
end
def test_should_be_successful
do_get
assert @response.success?
end
def test_should_query_for_client_application
RequestToken.expects(:find_by_token).returns(@request_token)
do_get
end
def test_should_assign_token
do_get
assert_equal @request_token, assigns(:token)
end
def test_should_render_authorize_template
do_get
assert_template('authorize')
end
def test_should_redirect_to_default_callback
do_post
assert_response :redirect
assert_redirected_to("http://application/callback?oauth_token=#{@request_token.token}")
end
def test_should_redirect_to_callback_in_query
do_post_with_callback
assert_response :redirect
assert_redirected_to("http://application/alternative?oauth_token=#{@request_token.token}")
end
def test_should_be_successful_on_authorize_without_any_application_callback
do_post_with_no_application_callback
assert @response.success?
assert_template('authorize_success')
end
def test_should_render_failure_screen_on_user_invalidation
do_post_without_user_authorization
assert_template('authorize_failure')
end
def test_should_render_failure_screen_if_token_is_invalidated
@request_token.expects(:invalidated?).returns(true)
do_get
assert_template('authorize_failure')
end
end
class OauthControllerGetAccessTokenTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthController
def setup
@controller = OauthController.new
setup_oauth
sign_request_with_oauth @request_token
@request_token.stubs(:exchange!).returns(@access_token)
end
def do_get
get :access_token
end
def test_should_be_successful
do_get
assert @response.success?
end
def test_should_query_for_client_application
ClientApplication.expects(:find_token).with(@request_token.token).returns(@request_token)
do_get
end
def test_should_request_token_from_client_application
@request_token.expects(:exchange!).returns(@access_token)
do_get
end
def test_should__return_token_string
do_get
assert_equal @access_token_string, @response.body
end
end
class OauthorizedController < ApplicationController
before_filter :login_or_oauth_required,:only=>:both
before_filter :login_required,:only=>:interactive
before_filter :oauth_required,:only=>:token_only
def interactive
render :text => "interactive"
end
def token_only
render :text => "token"
end
def both
render :text => "both"
end
end
class OauthControllerAccessControlTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthorizedController
def setup
@controller = OauthorizedController.new
end
def test_should__have_access_token_set_up_correctly
setup_to_authorize_request
assert @access_token.is_a?(AccessToken)
assert @access_token.authorized?
assert !@access_token.invalidated?
assert_equal @user, @access_token.user
assert_equal @client_application, @access_token.client_application
end
def test_should_return_false_for_oauth_by_default
assert_equal false, @controller.send(:oauth?)
end
def test_should_return_nil_for_current_token_by_default
assert_nil @controller.send(:current_token)
end
def test_should_allow_oauth_when_using_login_or_oauth_required
setup_to_authorize_request
sign_request_with_oauth(@access_token)
ClientApplication.expects(:find_token).with(@access_token.token).returns(@access_token)
get :both
assert_equal @access_token, @controller.send(:current_token)
assert @controller.send(:current_token).is_a?(AccessToken)
assert_equal @user, @controller.send(:current_user)
assert_equal @client_application, @controller.send(:current_client_application)
assert_equal '200', @response.code
assert @response.success?
end
def test_should_allow_interactive_when_using_login_or_oauth_required
login
get :both
assert @response.success?
assert_equal @user, @controller.send(:current_user)
assert_nil @controller.send(:current_token)
end
def test_should_allow_oauth_when_using_oauth_required
setup_to_authorize_request
sign_request_with_oauth(@access_token)
ClientApplication.expects(:find_token).with(@access_token.token).returns(@access_token)
get :token_only
assert_equal @access_token, @controller.send(:current_token)
assert_equal @client_application, @controller.send(:current_client_application)
assert_equal @user, @controller.send(:current_user)
assert_equal '200', @response.code
assert @response.success?
end
def test_should_disallow_oauth_using_request_token_when_using_oauth_required
setup_to_authorize_request
ClientApplication.expects(:find_token).with(@request_token.token).returns(@request_token)
sign_request_with_oauth(@request_token)
get :token_only
assert_equal '401', @response.code
end
def test_should_disallow_interactive_when_using_oauth_required
login
get :token_only
assert_equal '401', @response.code
assert_equal @user, @controller.send(:current_user)
assert_nil @controller.send(:current_token)
end
def test_should_disallow_oauth_when_using_login_required
setup_to_authorize_request
sign_request_with_oauth(@access_token)
get :interactive
assert_equal "302",@response.code
assert_nil @controller.send(:current_user)
assert_nil @controller.send(:current_token)
end
def test_should_allow_interactive_when_using_login_required
login
get :interactive
assert @response.success?
assert_equal @user, @controller.send(:current_user)
assert_nil @controller.send(:current_token)
end
end
class OauthControllerRevokeTest < ActionController::TestCase
include OAuthControllerTestHelper
tests OauthController
def setup
@controller = OauthController.new
setup_oauth_for_user
@request_token.stubs(:invalidate!)
end
def do_post
post :revoke, :token => "TOKEN STRING"
end
def test_should_redirect_to_index
do_post
assert_response :redirect
assert_redirected_to('http://test.host/oauth_clients')
end
def test_should_query_current_users_tokens
@tokens.expects(:find_by_token).returns(@request_token)
do_post
end
def test_should_call_invalidate_on_token
@request_token.expects(:invalidate!)
do_post
end
end

View file

@ -0,0 +1,115 @@
require "mocha"
module OAuthControllerTestHelper
# Some custom stuff since we're using Mocha
def mock_model(model_class, options_and_stubs = {})
id = rand(10000)
options_and_stubs.reverse_merge! :id => id,
:to_param => id.to_s,
:new_record? => false,
:errors => stub("errors", :count => 0)
m = stub("#{model_class.name}_#{options_and_stubs[:id]}", options_and_stubs)
m.instance_eval <<-CODE
def is_a?(other)
#{model_class}.ancestors.include?(other)
end
def kind_of?(other)
#{model_class}.ancestors.include?(other)
end
def instance_of?(other)
other == #{model_class}
end
def class
#{model_class}
end
CODE
yield m if block_given?
m
end
def mock_full_client_application
mock_model(ClientApplication,
:name => "App1",
:url => "http://app.com",
:callback_url => "http://app.com/callback",
:support_url => "http://app.com/support",
:key => "asd23423yy",
:secret => "secret",
:oauth_server => OAuth::Server.new("http://kowabunga.com")
)
end
def login
@controller.stubs(:local_request?).returns(true)
@user = mock_model(User, :login => "ron")
@controller.stubs(:current_user).returns(@user)
@tokens=[]
@tokens.stubs(:find).returns(@tokens)
@user.stubs(:tokens).returns(@tokens)
User.stubs(:find_by_id).returns(@user)
end
def login_as_application_owner
login
@client_application = mock_full_client_application
@client_applications = [@client_application]
@user.stubs(:client_applications).returns(@client_applications)
@client_applications.stubs(:find).returns(@client_application)
end
def setup_oauth
@controller.stubs(:local_request?).returns(true)
@user||=mock_model(User)
User.stubs(:find_by_id).returns(@user)
@server=OAuth::Server.new "http://test.host"
@consumer=OAuth::Consumer.new('key','secret',{:site=>"http://test.host"})
@client_application = mock_full_client_application
@controller.stubs(:current_client_application).returns(@client_application)
ClientApplication.stubs(:find_by_key).returns(@client_application)
@client_application.stubs(:key).returns(@consumer.key)
@client_application.stubs(:secret).returns(@consumer.secret)
@client_application.stubs(:name).returns("Client Application name")
@client_application.stubs(:callback_url).returns("http://application/callback")
@request_token=mock_model(RequestToken,:token=>'request_token',:client_application=>@client_application,:secret=>"request_secret",:user=>@user)
@request_token.stubs(:invalidated?).returns(false)
ClientApplication.stubs(:find_token).returns(@request_token)
@request_token_string="oauth_token=request_token&oauth_token_secret=request_secret"
@request_token.stubs(:to_query).returns(@request_token_string)
@access_token=mock_model(AccessToken,:token=>'access_token',:client_application=>@client_application,:secret=>"access_secret",:user=>@user)
@access_token.stubs(:invalidated?).returns(false)
@access_token.stubs(:authorized?).returns(true)
@access_token_string="oauth_token=access_token&oauth_token_secret=access_secret"
@access_token.stubs(:to_query).returns(@access_token_string)
@client_application.stubs(:authorize_request?).returns(true)
# @client_application.stubs(:sign_request_with_oauth_token).returns(@request_token)
@client_application.stubs(:exchange_for_access_token).returns(@access_token)
end
def setup_oauth_for_user
login
setup_oauth
@tokens=[@request_token]
@tokens.stubs(:find).returns(@tokens)
@tokens.stubs(:find_by_token).returns(@request_token)
@user.stubs(:tokens).returns(@tokens)
end
def sign_request_with_oauth(token=nil)
ActionController::TestRequest.use_oauth=true
@request.configure_oauth(@consumer, token)
end
def setup_to_authorize_request
setup_oauth
OauthToken.stubs(:find_by_token).with( @access_token.token).returns(@access_token)
@access_token.stubs(:is_a?).returns(true)
end
end

View file

@ -0,0 +1,5 @@
<h1>Edit your application</h1>
<%% form_for :client_application do |f| %>
<%%= render :partial => "form", :locals => { :f => f } %>
<%%= submit_tag "Edit" %>
<%% end %>

View file

@ -0,0 +1,2 @@
module OauthHelper
end

View file

@ -0,0 +1,40 @@
<div class="flash"><%%= flash[:notice] %></div>
<h1>OAuth Client Applications</h1>
<%% unless @tokens.empty? %>
<p>The following tokens have been issued to applications in your name</p>
<table>
<tr><th>Application</th><th>Issued</th><th>&nbsp;</th></tr>
<%% @tokens.each do |token|%>
<%% content_tag_for :tr, token do %>
<td><%%= link_to token.client_application.name, token.client_application.url %></td>
<td><%%= token.authorized_at %></td>
<td>
<%% form_tag :controller => 'oauth', :action => 'revoke' do %>
<%%= hidden_field_tag 'token', token.token %>
<%%= submit_tag "Revoke!" %>
<%% end %>
</td>
<%% end %>
<%% end %>
</table>
<%% end %>
<h3>Application Developers</h3>
<%% if @client_applications.empty? %>
<p>
Do you have an application you would like to register for use with us using the <a href="http://oauth.net">OAuth</a> standard?
</p>
<p>
You must register your web application before it can make OAuth requests to this service
</p>
<%% else %>
<p>
You have the following client applications registered:
</p>
<%% @client_applications.each do |client|%>
<%% div_for client do %>
<%%= link_to client.name, :action => :show, :id => client.id %>
<%% end %>
<%% end %>
<%% end %>
<h3><%%= link_to "Register your application", :action => :new %></h3>

View file

@ -0,0 +1,44 @@
class CreateOauthTables < ActiveRecord::Migration
def self.up
create_table :client_applications do |t|
t.string :name
t.string :url
t.string :support_url
t.string :callback_url
t.string :key, :limit => 50
t.string :secret, :limit => 50
t.integer :user_id
t.timestamps
end
add_index :client_applications, :key, :unique
create_table :oauth_tokens do |t|
t.integer :user_id
t.string :type, :limit => 20
t.integer :client_application_id
t.string :token, :limit => 50
t.string :secret, :limit => 50
t.timestamp :authorized_at, :invalidated_at
t.timestamps
end
add_index :oauth_tokens, :token, :unique
create_table :oauth_nonces do |t|
t.string :nonce
t.integer :timestamp
t.timestamps
end
add_index :oauth_nonces,[:nonce, :timestamp], :unique
end
def self.down
drop_table :client_applications
drop_table :oauth_tokens
drop_table :oauth_nonces
end
end

View file

@ -0,0 +1,5 @@
<h1>Register a new application</h1>
<%% form_for :client_application, :url => { :action => :create } do |f| %>
<%%= render :partial => "form", :locals => { :f => f } %>
<%%= submit_tag "Register" %>
<%% end %>

View file

@ -0,0 +1,13 @@
# Simple store of nonces. The OAuth Spec requires that any given pair of nonce and timestamps are unique.
# Thus you can use the same nonce with a different timestamp and viceversa.
class OauthNonce < ActiveRecord::Base
validates_presence_of :nonce, :timestamp
validates_uniqueness_of :nonce, :scope => :timestamp
# Remembers a nonce and it's associated timestamp. It returns false if it has already been used
def self.remember(nonce, timestamp)
oauth_nonce = OauthNonce.create(:nonce => nonce, :timestamp => timestamp)
return false if oauth_nonce.new_record?
oauth_nonce
end
end

View file

@ -0,0 +1,24 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'oauth/helper'
describe OauthNonce do
include OAuth::Helper
before(:each) do
@oauth_nonce = OauthNonce.remember(generate_key, Time.now.to_i)
end
it "should be valid" do
@oauth_nonce.should be_valid
end
it "should not have errors" do
@oauth_nonce.errors.full_messages.should == []
end
it "should not be a new record" do
@oauth_nonce.should_not be_new_record
end
it "should not allow a second one with the same values" do
OauthNonce.remember(@oauth_nonce.nonce,@oauth_nonce.timestamp).should == false
end
end

View file

@ -0,0 +1,26 @@
require 'oauth/helper'
require File.dirname(__FILE__) + '/../test_helper'
class ClientNoneTest < ActiveSupport::TestCase
include OAuth::Helper
def setup
@oauth_nonce = OauthNonce.remember(generate_key,Time.now.to_i)
end
def test_should_be_valid
assert @oauth_nonce.valid?
end
def test_should_not_have_errors
assert_equal [], @oauth_nonce.errors.full_messages
end
def test_should_not_be_a_new_record
assert !@oauth_nonce.new_record?
end
def test_shuold_not_allow_a_second_one_with_the_same_values
assert_equal false, OauthNonce.remember(@oauth_nonce.nonce, @oauth_nonce.timestamp)
end
end

View file

@ -0,0 +1,13 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
id: 1
nonce: a_nonce
timestamp: 1
created_at: 2007-11-25 17:27:04
updated_at: 2007-11-25 17:27:04
two:
id: 2
nonce: b_nonce
timestamp: 2
created_at: 2007-11-25 17:27:04
updated_at: 2007-11-25 17:27:04

View file

@ -0,0 +1,31 @@
class OauthToken < ActiveRecord::Base
belongs_to :client_application
belongs_to :user
validates_uniqueness_of :token
validates_presence_of :client_application, :token, :secret
before_validation_on_create :generate_keys
def invalidated?
invalidated_at != nil
end
def invalidate!
update_attribute(:invalidated_at, Time.now)
end
def authorized?
authorized_at != nil && !invalidated?
end
def to_query
"oauth_token=#{token}&oauth_token_secret=#{secret}"
end
protected
def generate_keys
@oauth_token = client_application.oauth_server.generate_credentials
self.token = @oauth_token[0]
self.secret = @oauth_token[1]
end
end

View file

@ -0,0 +1,55 @@
require File.dirname(__FILE__) + '/../spec_helper'
describe RequestToken do
fixtures :client_applications, :users, :oauth_tokens
before(:each) do
@token = RequestToken.create :client_application => client_applications(:one)
end
it "should be valid" do
@token.should be_valid
end
it "should not have errors" do
@token.errors.should_not == []
end
it "should have a token" do
@token.token.should_not be_nil
end
it "should have a secret" do
@token.secret.should_not be_nil
end
it "should not be authorized" do
@token.should_not be_authorized
end
it "should not be invalidated" do
@token.should_not be_invalidated
end
it "should authorize request" do
@token.authorize!(users(:quentin))
@token.should be_authorized
@token.authorized_at.should_not be_nil
@token.user.should == users(:quentin)
end
it "should not exchange without approval" do
@token.exchange!.should == false
@token.should_not be_invalidated
end
it "should not exchange without approval" do
@token.authorize!(users(:quentin))
@access = @token.exchange!
@access.should_not == false
@token.should be_invalidated
@access.user.should == users(:quentin)
@access.should be_authorized
end
end

View file

@ -0,0 +1,57 @@
require File.dirname(__FILE__) + '/../test_helper'
class RequestTokenTest < ActiveSupport::TestCase
fixtures :client_applications, :users, :oauth_tokens
def setup
@token = RequestToken.create :client_application=>client_applications(:one)
end
def test_should_be_valid
assert @token.valid?
end
def test_should_not_have_errors
assert @token.errors.empty?
end
def test_should_have_a_token
assert_not_nil @token.token
end
def test_should_have_a_secret
assert_not_nil @token.secret
end
def test_should_not_be_authorized
assert !@token.authorized?
end
def test_should_not_be_invalidated
assert !@token.invalidated?
end
def test_should_authorize_request
@token.authorize!(users(:quentin))
assert @token.authorized?
assert_not_nil @token.authorized_at
assert_equal users(:quentin), @token.user
end
def test_should_not_exchange_without_approval
assert_equal false, @token.exchange!
assert_equal false, @token.invalidated?
end
def test_should_not_exchange_without_approval
@token.authorize!(users(:quentin))
@access = @token.exchange!
assert_not_equal false, @access
assert @token.invalidated?
assert_equal users(:quentin), @access.user
assert @access.authorized?
end
end

View file

@ -0,0 +1,17 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
id: 1
user_id: 1
client_application_id: 1
token: one
secret: MyString
created_at: 2007-11-19 07:31:46
updated_at: 2007-11-19 07:31:46
two:
id: 2
user_id: 1
client_application_id: 1
token: two
secret: MyString
created_at: 2007-11-19 07:31:46
updated_at: 2007-11-19 07:31:46

View file

@ -0,0 +1,17 @@
class RequestToken < OauthToken
def authorize!(user)
return false if authorized?
self.user = user
self.authorized_at = Time.now
self.save
end
def exchange!
return false unless authorized?
RequestToken.transaction do
access_token = AccessToken.create(:user => user, :client_application => client_application)
invalidate!
access_token
end
end
end

View file

@ -0,0 +1,20 @@
<h1>OAuth details for <%%=@client_application.name %></h1>
<p>
<b>Consumer Key:</b> <%%=@client_application.key %>
</p>
<p>
<b>Consumer Secret:</b> <%%=@client_application.secret %>
</p>
<p>
<b>Request Token URL</b> http<%%='s' if request.ssl? %>://<%%= request.host_with_port %><%%=@client_application.oauth_server.request_token_path %>
</p>
<p>
<b>Access Token URL</b> http<%%='s' if request.ssl? %>://<%%= request.host_with_port %><%%=@client_application.oauth_server.access_token_path %>
</p>
<p>
<b>Authorize URL</b> http<%%='s' if request.ssl? %>://<%%= request.host_with_port %><%%=@client_application.oauth_server.authorize_path %>
</p>
<p>
We support hmac-sha1 (recommended) as well as plain text in ssl mode.
</p>

6
vendor/plugins/oauth-plugin/init.rb vendored Normal file
View file

@ -0,0 +1,6 @@
gem 'oauth', '>=0.2.1'
require 'oauth/signature/hmac/sha1'
require 'oauth/request_proxy/action_controller_request'
require 'oauth/server'
require 'oauth/rails/controller_methods'
ActionController::Base.send :include, OAuth::Rails::ControllerMethods

View file

@ -0,0 +1,2 @@
#should we do any text formatting?
puts IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))

View file

@ -0,0 +1,114 @@
require 'oauth/signature'
module OAuth
module Rails
module ControllerMethods
protected
def current_token
@current_token
end
def current_client_application
@current_client_application
end
def oauthenticate
logger.info "entering oauthenticate"
verified=verify_oauth_signature
logger.info "verified=#{verified.to_s}"
return verified && current_token.is_a?(::AccessToken)
end
def oauth?
current_token!=nil
end
# use in a before_filter
def oauth_required
logger.info "Current_token=#{@current_token.inspect}"
if oauthenticate
logger.info "passed oauthenticate"
if authorized?
logger.info "passed authorized"
return true
else
logger.info "failed authorized"
invalid_oauth_response
end
else
logger.info "failed oauthenticate"
invalid_oauth_response
end
end
# This requies that you have an acts_as_authenticated compatible authentication plugin installed
def login_or_oauth_required
if oauthenticate
if authorized?
return true
else
invalid_oauth_response
end
else
login_required
end
end
# verifies a request token request
def verify_oauth_consumer_signature
begin
valid = ClientApplication.verify_request(request) do |token, consumer_key|
@current_client_application = ClientApplication.find_by_key(consumer_key)
# return the token secret and the consumer secret
[nil, @current_client_application.secret]
end
rescue
valid=false
end
invalid_oauth_response unless valid
end
def verify_oauth_request_token
verify_oauth_signature && current_token.is_a?(RequestToken)
end
def invalid_oauth_response(code=401,message="Invalid OAuth Request")
render :text => message, :status => code
end
private
def current_token=(token)
@current_token=token
if @current_token
@current_user=@current_token.user
@current_client_application=@current_token.client_application
end
@current_token
end
# Implement this for your own application using app-specific models
def verify_oauth_signature
begin
valid = ClientApplication.verify_request(request) do |request|
self.current_token = ClientApplication.find_token(request.token)
logger.info "self=#{self.class.to_s}"
logger.info "token=#{self.current_token}"
# return the token secret and the consumer secret
[(current_token.nil? ? nil : current_token.secret), (current_client_application.nil? ? nil : current_client_application.secret)]
end
# reset @current_user to clear state for restful_...._authentication
@current_user = nil if (!valid)
valid
rescue
false
end
end
end
end
end

View file

@ -0,0 +1,4 @@
# desc "Explaining what the task does"
# task :oauth do
# # Task goes here
# end

View file

@ -0,0 +1 @@
# Uninstall hook code here