oauth2_provider 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/CHANGELOG +306 -0
  2. data/HACKING.textile +45 -0
  3. data/NOTICE.textile +6 -0
  4. data/README.textile +11 -3
  5. data/WHAT_IS_OAUTH.textile +165 -0
  6. data/app/controllers/oauth_authorize_controller.rb +69 -0
  7. data/app/controllers/oauth_clients_controller.rb +79 -0
  8. data/app/controllers/oauth_token_controller.rb +59 -0
  9. data/app/controllers/oauth_user_tokens_controller.rb +61 -0
  10. data/app/models/oauth2/provider/oauth_authorization.rb +4 -0
  11. data/app/models/oauth2/provider/oauth_client.rb +14 -3
  12. data/app/views/{oauth2/provider/layouts → layouts}/oauth_clients.html.erb +0 -0
  13. data/app/views/oauth_authorize/index.html.erb +17 -0
  14. data/app/views/oauth_clients/_form.html.erb +27 -0
  15. data/app/views/oauth_clients/edit.html.erb +7 -0
  16. data/app/views/oauth_clients/index.html.erb +53 -0
  17. data/app/views/oauth_clients/new.html.erb +7 -0
  18. data/app/views/{oauth2/provider/oauth_clients → oauth_clients}/show.html.erb +0 -0
  19. data/app/views/oauth_user_tokens/index.html.erb +28 -0
  20. data/config/routes.rb +15 -9
  21. data/generators/oauth2_provider/USAGE +12 -0
  22. data/generators/oauth2_provider/templates/config/initializers/oauth2_provider.rb +3 -0
  23. data/lib/ext/validatable_ext.rb +27 -0
  24. data/lib/oauth2/provider/a_r_datasource.rb +13 -1
  25. data/lib/oauth2/provider/application_controller_methods.rb +32 -20
  26. data/lib/oauth2/provider/configuration.rb +39 -0
  27. data/lib/oauth2/provider/in_memory_datasource.rb +8 -0
  28. data/lib/oauth2/provider/model_base.rb +59 -10
  29. data/lib/oauth2/provider/ssl_helper.rb +42 -0
  30. data/lib/oauth2/provider/transaction_helper.rb +24 -0
  31. data/lib/oauth2/provider/url_parser.rb +17 -0
  32. data/lib/oauth2_provider.rb +3 -6
  33. data/oauth2_provider.gemspec +15 -6
  34. metadata +81 -26
  35. data/app/controllers/oauth2/provider/oauth_authorize_controller.rb +0 -68
  36. data/app/controllers/oauth2/provider/oauth_clients_controller.rb +0 -56
  37. data/app/controllers/oauth2/provider/oauth_token_controller.rb +0 -58
  38. data/app/controllers/oauth2/provider/oauth_user_tokens_controller.rb +0 -29
  39. data/app/views/oauth2/provider/oauth_authorize/index.html.erb +0 -8
  40. data/app/views/oauth2/provider/oauth_clients/edit.html.erb +0 -20
  41. data/app/views/oauth2/provider/oauth_clients/index.html.erb +0 -28
  42. data/app/views/oauth2/provider/oauth_clients/new.html.erb +0 -21
  43. data/app/views/oauth2/provider/oauth_user_tokens/index.html.erb +0 -14
  44. data/tasks/gem.rake +0 -88
@@ -0,0 +1,69 @@
1
+ # Copyright (c) 2010 ThoughtWorks Inc. (http://thoughtworks.com)
2
+ # Licenced under the MIT License (http://www.opensource.org/licenses/mit-license.php)
3
+
4
+ class OauthAuthorizeController < ::ApplicationController
5
+
6
+ include Oauth2::Provider::SslHelper
7
+ include Oauth2::Provider::TransactionHelper
8
+
9
+ transaction_actions :authorize
10
+
11
+ def index
12
+ return unless validate_params
13
+ end
14
+
15
+ def authorize
16
+ return unless validate_params
17
+
18
+ unless params[:authorize] == 'Yes'
19
+ redirect_to "#{params[:redirect_uri]}?error=access-denied"
20
+ return
21
+ end
22
+
23
+ authorization = @client.create_authorization_for_user_id(current_user_id_for_oauth)
24
+ state_param = if params[:state].blank?
25
+ ""
26
+ else
27
+ "&state=#{CGI.escape(params[:state])}"
28
+ end
29
+
30
+ redirect_to "#{params[:redirect_uri]}?code=#{authorization.code}&expires_in=#{authorization.expires_in}#{state_param}"
31
+ end
32
+
33
+ private
34
+
35
+ # TODO: support 'code', 'token', 'code-and-token'
36
+ VALID_RESPONSE_TYPES = ['code']
37
+
38
+ def validate_params
39
+ if params[:client_id].blank? || params[:response_type].blank?
40
+ redirect_to "#{params[:redirect_uri]}?error=invalid-request"
41
+ return false
42
+ end
43
+
44
+ unless VALID_RESPONSE_TYPES.include?(params[:response_type])
45
+ redirect_to "#{params[:redirect_uri]}?error=unsupported-response-type"
46
+ return
47
+ end
48
+
49
+ if params[:redirect_uri].blank?
50
+ render :text => "You did not specify the 'redirect_uri' parameter!", :status => :bad_request
51
+ return false
52
+ end
53
+
54
+ @client = Oauth2::Provider::OauthClient.find_one(:client_id, params[:client_id])
55
+
56
+ if @client.nil?
57
+ redirect_to "#{params[:redirect_uri]}?error=invalid-client-id"
58
+ return false
59
+ end
60
+
61
+ if @client.redirect_uri != params[:redirect_uri]
62
+ redirect_to "#{params[:redirect_uri]}?error=redirect-uri-mismatch"
63
+ return false
64
+ end
65
+
66
+ true
67
+ end
68
+
69
+ end
@@ -0,0 +1,79 @@
1
+ # Copyright (c) 2010 ThoughtWorks Inc. (http://thoughtworks.com)
2
+ # Licenced under the MIT License (http://www.opensource.org/licenses/mit-license.php)
3
+
4
+ class OauthClientsController < ApplicationController
5
+
6
+ include Oauth2::Provider::SslHelper
7
+ include Oauth2::Provider::TransactionHelper
8
+
9
+ transaction_actions :create, :update, :destroy
10
+
11
+
12
+ def index
13
+ @oauth_clients = Oauth2::Provider::OauthClient.all.sort{|a, b| a.name.casecmp(b.name)}
14
+ respond_to do |format|
15
+ format.html
16
+ format.xml { render :xml => @oauth_clients.to_xml(:root => 'oauth_clients', :dasherize => false) }
17
+ end
18
+ end
19
+
20
+ def show
21
+ @oauth_client = Oauth2::Provider::OauthClient.find(params[:id])
22
+ respond_to do |format|
23
+ format.html
24
+ format.xml { render :xml => @oauth_client.to_xml(:dasherize => false) }
25
+ end
26
+ end
27
+
28
+ def new
29
+ @oauth_client = Oauth2::Provider::OauthClient.new
30
+ end
31
+
32
+ def edit
33
+ @oauth_client = Oauth2::Provider::OauthClient.find(params[:id])
34
+ end
35
+
36
+ def create
37
+ @oauth_client = Oauth2::Provider::OauthClient.new(params[:oauth_client])
38
+
39
+ respond_to do |format|
40
+ if @oauth_client.save
41
+ flash[:notice] = 'OAuth client was successfully created.'
42
+ format.html { redirect_to :action => 'index' }
43
+ format.xml { render :xml => @oauth_client, :status => :created, :location => @oauth_client }
44
+ else
45
+ flash.now[:error] = @oauth_client.errors.full_messages
46
+ format.html { render :action => "new" }
47
+ format.xml { render :xml => @oauth_client.errors, :status => :unprocessable_entity }
48
+ end
49
+ end
50
+ end
51
+
52
+ def update
53
+ @oauth_client = Oauth2::Provider::OauthClient.find(params[:id])
54
+
55
+ respond_to do |format|
56
+ if @oauth_client.update_attributes(params[:oauth_client])
57
+ flash[:notice] = 'OAuth client was successfully updated.'
58
+ format.html { redirect_to :action => 'index' }
59
+ format.xml { head :ok }
60
+ else
61
+ flash.now[:error] = @oauth_client.errors.full_messages
62
+ format.html { render :action => "edit" }
63
+ format.xml { render :xml => @oauth_client.errors, :status => :unprocessable_entity }
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ def destroy
70
+ @oauth_client = Oauth2::Provider::OauthClient.find(params[:id])
71
+ @oauth_client.destroy
72
+
73
+ respond_to do |format|
74
+ flash[:notice] = 'OAuth client was successfully deleted.'
75
+ format.html { redirect_to(oauth_clients_url) }
76
+ format.xml { head :ok }
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,59 @@
1
+ # Copyright (c) 2010 ThoughtWorks Inc. (http://thoughtworks.com)
2
+ # Licenced under the MIT License (http://www.opensource.org/licenses/mit-license.php)
3
+
4
+ class OauthTokenController < ApplicationController
5
+ skip_before_filter :verify_authenticity_token
6
+
7
+ include Oauth2::Provider::SslHelper
8
+ include Oauth2::Provider::TransactionHelper
9
+
10
+ transaction_actions :get_token
11
+
12
+ def get_token
13
+
14
+ authorization = Oauth2::Provider::OauthAuthorization.find_one(:code, params[:code])
15
+ authorization.destroy unless authorization.nil?
16
+
17
+ original_token = Oauth2::Provider::OauthToken.find_one(:refresh_token, params[:refresh_token])
18
+ original_token.destroy unless original_token.nil?
19
+
20
+ unless ['authorization-code', 'refresh-token'].include?(params[:grant_type])
21
+ render_error('unsupported-grant-type', "Grant type #{params[:grant_type]} is not supported!")
22
+ return
23
+ end
24
+
25
+ client = Oauth2::Provider::OauthClient.find_one(:client_id, params[:client_id])
26
+
27
+ if client.nil? || client.client_secret != params[:client_secret]
28
+ render_error('invalid-client-credentials', 'Invalid client credentials!')
29
+ return
30
+ end
31
+
32
+ if client.redirect_uri != params[:redirect_uri]
33
+ render_error('invalid-grant', 'Redirect uri mismatch!')
34
+ return
35
+ end
36
+
37
+ if params[:grant_type] == 'authorization-code'
38
+ if authorization.nil? || authorization.expired? || authorization.oauth_client.id != client.id
39
+ render_error('invalid-grant', "Authorization expired or invalid!")
40
+ return
41
+ end
42
+ token = authorization.generate_access_token
43
+ else # refresh-token
44
+ if original_token.nil? || original_token.oauth_client.id != client.id
45
+ render_error('invalid-grant', 'Refresh token is invalid!')
46
+ return
47
+ end
48
+ token = original_token.refresh
49
+ end
50
+
51
+ render :content_type => 'application/json', :text => token.access_token_attributes.to_json
52
+ end
53
+
54
+ private
55
+ def render_error(error_code, description)
56
+ render :status => :bad_request, :json => {:error => error_code, :error_description => description}.to_json
57
+ end
58
+
59
+ end
@@ -0,0 +1,61 @@
1
+ # Copyright (c) 2010 ThoughtWorks Inc. (http://thoughtworks.com)
2
+ # Licenced under the MIT License (http://www.opensource.org/licenses/mit-license.php)
3
+
4
+ class OauthUserTokensController < ApplicationController
5
+
6
+ include Oauth2::Provider::TransactionHelper
7
+
8
+ transaction_actions :revoke, :revoke_by_admin
9
+
10
+ def index
11
+ @tokens = Oauth2::Provider::OauthToken.find_all_with(:user_id, current_user_id_for_oauth)
12
+ end
13
+
14
+ def revoke
15
+ token = Oauth2::Provider::OauthToken.find_by_id(params[:token_id])
16
+ if token.nil?
17
+ render_not_authorized
18
+ return
19
+ end
20
+ if token.user_id.to_s != current_user_id_for_oauth
21
+ render_not_authorized
22
+ return
23
+ end
24
+
25
+ token.destroy
26
+ redirect_after_revoke
27
+ end
28
+
29
+ def revoke_by_admin
30
+
31
+ if params[:token_id].blank? && params[:user_id].blank?
32
+ render_not_authorized
33
+ return
34
+ end
35
+
36
+ if !params[:token_id].blank?
37
+ token = Oauth2::Provider::OauthToken.find_by_id(params[:token_id])
38
+ if token.nil?
39
+ render_not_authorized
40
+ return
41
+ end
42
+ token.destroy
43
+ else
44
+ Oauth2::Provider::OauthToken.find_all_with(:user_id, params[:user_id]).map(&:destroy)
45
+ end
46
+
47
+ redirect_after_revoke
48
+ end
49
+
50
+ private
51
+
52
+ def render_not_authorized
53
+ render :text => "You are not authorized to perform this action!", :status => :bad_request
54
+ end
55
+
56
+ def redirect_after_revoke
57
+ flash[:notice] = "OAuth access token was successfully deleted."
58
+ redirect_to params[:redirect_url] || {:action => 'index'}
59
+ end
60
+
61
+ end
@@ -13,6 +13,10 @@ module Oauth2
13
13
  end
14
14
 
15
15
  def generate_access_token
16
+ OauthToken.find_all_with(:user_id, user_id).each do |token|
17
+ token.destroy if token.oauth_client_id == oauth_client_id
18
+ end
19
+
16
20
  token = oauth_client.create_token_for_user_id(user_id)
17
21
  self.destroy
18
22
  token
@@ -6,7 +6,8 @@ module Oauth2
6
6
  class OauthClient < ModelBase
7
7
 
8
8
  validates_presence_of :name, :redirect_uri
9
- validates_format_of :redirect_uri, :with => Regexp.new("^(https|http)://.+$")
9
+ validates_format_of :redirect_uri, :with => Regexp.new("^(https|http)://.+$"), :if => proc { |client| !client.redirect_uri.blank? }
10
+ validates_uniqueness_of :name
10
11
 
11
12
  columns :name, :client_id, :client_secret, :redirect_uri
12
13
 
@@ -15,8 +16,12 @@ module Oauth2
15
16
  end
16
17
 
17
18
  def create_authorization_for_user_id(user_id)
19
+ oauth_authorizations.each do |authorization|
20
+ authorization.destroy if authorization.user_id == user_id
21
+ end
18
22
  OauthAuthorization.create!(:user_id => user_id, :oauth_client_id => id)
19
23
  end
24
+
20
25
  def self.model_name
21
26
  ActiveSupport::ModelName.new('OauthClient')
22
27
  end
@@ -38,7 +43,13 @@ module Oauth2
38
43
  oauth_tokens.each(&:destroy)
39
44
  oauth_authorizations.each(&:destroy)
40
45
  end
41
-
46
+
47
+ def before_save
48
+ self.name.strip! if self.name
49
+ self.redirect_uri.strip! if self.redirect_uri
50
+ end
51
+
42
52
  end
43
53
  end
44
- end
54
+ end
55
+
@@ -0,0 +1,17 @@
1
+ <div class="content_wrapper_outer">
2
+ <div class="content_wrapper_inner">
3
+ <% form_for(url_for(:action => :authorize), :html => {:id => 'oauth_authorize_form'}) do |f| -%>
4
+ <div class="label">Do you wish to allow the service named '<%= @client.name %>' to access this application on your behalf?</div>
5
+ <div>
6
+ <%= link_to_function 'Yes', 'document.getElementById("oauth_authorize_allow").value="Yes";document.getElementById("oauth_authorize_form").submit()', {:class => 'link_as_button primary_link_as_button primary', :href => 'javascript:void(0)', :id => 'authorize-yes'} %>
7
+ <%= link_to_function 'No', 'document.getElementById("oauth_authorize_allow").value="No";document.getElementById("oauth_authorize_form").submit()', {:class => 'link_as_button link_as_button', :href => 'javascript:void(0)', :id => "authorize-no"} %>
8
+ </div>
9
+
10
+ <input type="hidden" name="authorize" id="oauth_authorize_allow" value="No"/>
11
+ <input type="hidden" name="client_id" id="client_id" value="<%= params[:client_id] %>" />
12
+ <input type="hidden" name="redirect_uri" id="redirect_uri" value="<%= params[:redirect_uri] %>" />
13
+ <input type="hidden" name="response_type" id="response_type" value="<%= params[:response_type] %>" />
14
+ <input type="hidden" name="state" id="state" value="<%= params[:state] %>" />
15
+ <% end -%>
16
+ </div>
17
+ </div>
@@ -0,0 +1,27 @@
1
+ <% form_for(@oauth_client, :html => {:id => 'oauth_client_edit_form', :class => nil}) do |f| %>
2
+
3
+ <div class="form_item">
4
+ <label for='name'>
5
+ Name:
6
+ <span class="notes">
7
+ (The name of the client which will be accessing data from this server.)
8
+ </span>
9
+ </label>
10
+ <%= f.text_field :name, :class => 'width-large' %>
11
+ </div>
12
+
13
+ <div class="form_item">
14
+ <label for='redirect_uri'>
15
+ OAuth Redirect URI:
16
+ <span class="notes">
17
+ (The redirect URI provided by the client.)
18
+ </span>
19
+ </label>
20
+ <%= f.text_field :redirect_uri, :class => 'width-large' %>
21
+ </div>
22
+
23
+ <div class="action-bar">
24
+ <%= link_to_function 'Submit', 'document.getElementById("oauth_client_edit_form").submit()', {:class => 'link_as_button primary', :href => 'javascript:void(0)'} %>
25
+ <%= link_to 'Cancel', {:controller => 'oauth_clients'}, {:class => 'link_as_button back_link'} %>
26
+ </div>
27
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <div id="oauth_page">
2
+ <%= render_help_link('OAuth 2.0 Application', :class => 'page-help-at-action-bar') if defined?(render_help_link) %>
3
+
4
+ <h1>Edit OAuth client</h1>
5
+
6
+ <%= render :partial => "form", :locals => { :oauth_client => @oauth_client } %>
7
+ </div>
@@ -0,0 +1,53 @@
1
+ <div id="oauth_page">
2
+ <%= render_help_link('OAuth 2.0 Clients', :class => 'page-help-at-action-bar') if defined?(render_help_link) %>
3
+
4
+ <h1>OAuth clients configuration</h1>
5
+
6
+ <% if request.ssl? || !::Oauth2::Provider::Configuration.require_ssl_for_oauth %>
7
+ <h2>OAuth 2.0 (v9) provider endpoints for this server</h2>
8
+
9
+ <% if Oauth2::Provider::Configuration.ssl_base_url_as_url_options[:protocol] == 'https' %>
10
+ <div class='summary info-box oauth_config_info'>
11
+ <p><span>Authorization URL:</span> <%= url_for Oauth2::Provider::Configuration.ssl_base_url_as_url_options.merge(:controller => :oauth_authorize, :action => :index) %></p>
12
+ <p><span>Token URL:</span> <%= url_for Oauth2::Provider::Configuration.ssl_base_url_as_url_options.merge(:controller => :oauth_token, :action => :get_token) %></p>
13
+ </div>
14
+ <% else %>
15
+ <div class="error-box">Please set mingle.secureSiteURL property in mingle.properties in order to see both the Authorization URL and Token URL in this box.</div>
16
+ <% end %>
17
+ <% end %>
18
+
19
+ <% if @oauth_clients.empty? %>
20
+ <div class="info-box">You have not configured any OAuth clients. Click the 'Add client' button to add one.</div>
21
+ <% else %>
22
+
23
+ <h2>OAuth clients configured for this server</h2>
24
+
25
+ <table id="oauth_clients_table" class="list_table">
26
+ <tr>
27
+ <th>Name</th>
28
+ <th>OAuth Redirect URI</th>
29
+ <th>OAuth Client Credentials</th>
30
+ <th>Action</th>
31
+ </tr>
32
+
33
+ <% @oauth_clients.each do |oauth_client| %>
34
+ <tr>
35
+ <td><%= link_to h(oauth_client.name), {:controller => 'oauth_clients', :action => :edit, :id => oauth_client.id}, {:class => 'edit_link'} %></td>
36
+ <td><%=h oauth_client.redirect_uri %></td>
37
+ <td>
38
+ <p><b>Client ID</b>: <%=h oauth_client.client_id %></p>
39
+ <p><b>Client Secret</b>: <%=h oauth_client.client_secret %></p>
40
+ </td>
41
+ <td>
42
+ <%= link_to '<span>Delete</span>', {:controller => 'oauth_clients', :action => :destroy, :id => oauth_client.id}, {:confirm => 'Are you sure?', :method => :delete, :class => 'delete_link', :title => 'Click to delete this client'} %>
43
+ </td>
44
+ </tr>
45
+ <% end %>
46
+ </table>
47
+
48
+ <% end %>
49
+
50
+ <div class="table_actions">
51
+ <%= link_to 'Add client', {:controller => 'oauth_clients', :action => :new}, {:class => 'link_as_button primary', :id => 'new_oauth_client_button'} %>
52
+ </div>
53
+ </div>
@@ -0,0 +1,7 @@
1
+ <div id="oauth_page">
2
+ <%= render_help_link('OAuth 2.0 Application', :class => 'page-help-at-action-bar') if defined?(render_help_link) %>
3
+
4
+ <h1>New OAuth client</h1>
5
+
6
+ <%= render :partial => "form", :locals => { :oauth_client => @oauth_client } %>
7
+ </div>
@@ -0,0 +1,28 @@
1
+ <div id="oauth_page">
2
+ <%= render_help_link('OAuth 2.0 User Token', :class => 'page-help-at-action-bar') if defined?(render_help_link) %>
3
+
4
+ <div class="page_header"><h1 class="entity_title">OAuth Tokens</h1></div>
5
+
6
+ <div class="content_wrapper_outer">
7
+ <div class="content_wrapper_inner">
8
+ <% if @tokens.empty? %>
9
+ <div class="info-box">There are no tokens.</div>
10
+ <% else %>
11
+ <table id="oauth_user_token_table" class="list_table">
12
+ <tr>
13
+ <th>Client</th>
14
+ <th>Action</th>
15
+ </tr>
16
+ <% @tokens.each do |token| -%>
17
+ <tr>
18
+ <td><%= h(token.oauth_client.name) %></td>
19
+ <td><%= link_to '<span>Delete</span>',
20
+ {:action => :revoke, :token_id => token.id, :controller => 'oauth_user_tokens'},
21
+ {:confirm => 'Are you sure?', :method => :delete, :class => 'delete_link', :title => 'Click to revoke this token'}%></td>
22
+ </tr>
23
+ <% end -%>
24
+ </table>
25
+ <% end %>
26
+ </div>
27
+ </div>
28
+ </div>