insrc_devise_oauth2_providable 1.1.2

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 (89) hide show
  1. data/.gitignore +35 -0
  2. data/.rvmrc +1 -0
  3. data/CONTRIBUTORS.txt +6 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +158 -0
  7. data/Rakefile +8 -0
  8. data/app/controllers/devise/oauth2_providable/authorizations_controller.rb +59 -0
  9. data/app/controllers/devise/oauth2_providable/tokens_controller.rb +17 -0
  10. data/app/models/devise/oauth2_providable/access_token.rb +24 -0
  11. data/app/models/devise/oauth2_providable/authorization_code.rb +3 -0
  12. data/app/models/devise/oauth2_providable/client.rb +24 -0
  13. data/app/models/devise/oauth2_providable/refresh_token.rb +8 -0
  14. data/app/views/devise/oauth2_providable/authorizations/_form.html.erb +7 -0
  15. data/app/views/devise/oauth2_providable/authorizations/error.html.erb +4 -0
  16. data/app/views/devise/oauth2_providable/authorizations/new.html.erb +4 -0
  17. data/config/routes.rb +7 -0
  18. data/db/migrate/20111014160714_create_devise_oauth2_providable_schema.rb +54 -0
  19. data/devise_oauth2_providable.gemspec +32 -0
  20. data/lib/devise/oauth2_providable/engine.rb +16 -0
  21. data/lib/devise/oauth2_providable/expirable_token.rb +58 -0
  22. data/lib/devise/oauth2_providable/models/oauth2_authorization_code_grantable.rb +6 -0
  23. data/lib/devise/oauth2_providable/models/oauth2_password_grantable.rb +6 -0
  24. data/lib/devise/oauth2_providable/models/oauth2_providable.rb +13 -0
  25. data/lib/devise/oauth2_providable/models/oauth2_refresh_token_grantable.rb +6 -0
  26. data/lib/devise/oauth2_providable/strategies/oauth2_authorization_code_grant_type_strategy.rb +21 -0
  27. data/lib/devise/oauth2_providable/strategies/oauth2_grant_type_strategy.rb +39 -0
  28. data/lib/devise/oauth2_providable/strategies/oauth2_password_grant_type_strategy.rb +22 -0
  29. data/lib/devise/oauth2_providable/strategies/oauth2_providable_strategy.rb +25 -0
  30. data/lib/devise/oauth2_providable/strategies/oauth2_refresh_token_grant_type_strategy.rb +22 -0
  31. data/lib/devise/oauth2_providable/version.rb +5 -0
  32. data/lib/devise_oauth2_providable.rb +41 -0
  33. data/script/rails +6 -0
  34. data/spec/controllers/authorizations_controller_spec.rb +32 -0
  35. data/spec/controllers/protected_controller_spec.rb +42 -0
  36. data/spec/dummy/Rakefile +7 -0
  37. data/spec/dummy/app/assets/javascripts/application.js +7 -0
  38. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  39. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  40. data/spec/dummy/app/controllers/protected_controller.rb +6 -0
  41. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  42. data/spec/dummy/app/mailers/.gitkeep +0 -0
  43. data/spec/dummy/app/models/.gitkeep +0 -0
  44. data/spec/dummy/app/models/user.rb +3 -0
  45. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  46. data/spec/dummy/config.ru +4 -0
  47. data/spec/dummy/config/application.rb +51 -0
  48. data/spec/dummy/config/boot.rb +10 -0
  49. data/spec/dummy/config/database.yml +25 -0
  50. data/spec/dummy/config/environment.rb +5 -0
  51. data/spec/dummy/config/environments/development.rb +30 -0
  52. data/spec/dummy/config/environments/production.rb +60 -0
  53. data/spec/dummy/config/environments/test.rb +39 -0
  54. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  55. data/spec/dummy/config/initializers/devise.rb +210 -0
  56. data/spec/dummy/config/initializers/inflections.rb +10 -0
  57. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  58. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  59. data/spec/dummy/config/initializers/session_store.rb +8 -0
  60. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  61. data/spec/dummy/config/locales/devise.en.yml +58 -0
  62. data/spec/dummy/config/locales/en.yml +5 -0
  63. data/spec/dummy/config/routes.rb +7 -0
  64. data/spec/dummy/db/migrate/20111014142838_create_users.rb +9 -0
  65. data/spec/dummy/db/migrate/20111014161437_create_devise_oauth2_providable_schema.rb +55 -0
  66. data/spec/dummy/db/schema.rb +78 -0
  67. data/spec/dummy/lib/assets/.gitkeep +0 -0
  68. data/spec/dummy/public/404.html +26 -0
  69. data/spec/dummy/public/422.html +26 -0
  70. data/spec/dummy/public/500.html +26 -0
  71. data/spec/dummy/public/favicon.ico +0 -0
  72. data/spec/dummy/script/rails +6 -0
  73. data/spec/factories/client_factory.rb +5 -0
  74. data/spec/factories/user_factory.rb +4 -0
  75. data/spec/integration/oauth2_authorization_token_grant_type_strategy_spec.rb +136 -0
  76. data/spec/integration/oauth2_password_grant_type_strategy_spec.rb +174 -0
  77. data/spec/integration/oauth2_refresh_token_grant_type_strategy_spec.rb +138 -0
  78. data/spec/lib/devise_oauth2_providable_spec.rb +7 -0
  79. data/spec/models/access_token_spec.rb +53 -0
  80. data/spec/models/authorization_code_spec.rb +23 -0
  81. data/spec/models/client_spec.rb +22 -0
  82. data/spec/models/refresh_token_spec.rb +26 -0
  83. data/spec/models/user_spec.rb +6 -0
  84. data/spec/routing/authorizations_routing_spec.rb +16 -0
  85. data/spec/routing/tokens_routing_spec.rb +9 -0
  86. data/spec/spec_helper.rb +29 -0
  87. data/spec/support/inject_engine_routes_into_application.rb +74 -0
  88. data/spec/support/match_json.rb +6 -0
  89. metadata +374 -0
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "devise/oauth2_providable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "insrc_devise_oauth2_providable"
7
+ s.version = Devise::Oauth2Providable::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ryan Sonnek"]
10
+ s.email = ["ryan@socialcast.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{OAuth2 Provider for Rails3 applications}
13
+ s.description = %q{Rails3 engine that adds OAuth2 Provider support to any application built with Devise authentication}
14
+
15
+ s.rubyforge_project = "devise_oauth2_providable"
16
+
17
+ s.add_runtime_dependency(%q<rails>, [">= 3.1.0"])
18
+ s.add_runtime_dependency(%q<devise>, [">= 1.4.3"])
19
+ s.add_runtime_dependency(%q<rack-oauth2>, ["~> 1.0.5"])
20
+ s.add_development_dependency(%q<rspec-rails>, ['2.6.1'])
21
+ s.add_development_dependency(%q<sqlite3>, ['1.3.5'])
22
+ s.add_development_dependency(%q<shoulda-matchers>, ['1.0.0.beta3'])
23
+ s.add_development_dependency(%q<pry>, ['0.9.6.2'])
24
+ s.add_development_dependency(%q<factory_girl>, ['2.2.0'])
25
+ s.add_development_dependency(%q<factory_girl_rspec>, ['0.0.1'])
26
+ s.add_development_dependency(%q<rake>, ['0.9.2.2'])
27
+
28
+ s.files = `git ls-files`.split("\n")
29
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
31
+ s.require_paths = ["lib"]
32
+ end
@@ -0,0 +1,16 @@
1
+ module Devise
2
+ module Oauth2Providable
3
+ class Engine < Rails::Engine
4
+ config.devise_oauth2_providable = ActiveSupport::OrderedOptions.new
5
+ config.devise_oauth2_providable.access_token_expires_in = 15.minutes
6
+ config.devise_oauth2_providable.refresh_token_expires_in = 1.month
7
+ config.devise_oauth2_providable.authorization_code_expires_in = 1.minute
8
+
9
+ engine_name 'oauth2'
10
+ isolate_namespace Devise::Oauth2Providable
11
+ initializer "devise_oauth2_providable.initialize_application", :before=> :load_config_initializers do |app|
12
+ app.config.filter_parameters << :client_secret
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,58 @@
1
+ require 'active_support/concern'
2
+ require 'active_record'
3
+
4
+ module Devise
5
+ module Oauth2Providable
6
+ module ExpirableToken
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ def expires_according_to(config_name)
11
+ cattr_accessor :default_lifetime
12
+ self.default_lifetime = Rails.application.config.devise_oauth2_providable[config_name]
13
+
14
+ belongs_to :user
15
+ belongs_to :client
16
+
17
+ # attr_accessible :user, :client
18
+
19
+ after_initialize :init_token, :on => :create, :unless => :token?
20
+ after_initialize :init_expires_at, :on => :create, :unless => :expires_at?
21
+ validates :expires_at, :presence => true
22
+ validates :client, :presence => true
23
+ validates :token, :presence => true, :uniqueness => true
24
+
25
+ default_scope lambda {
26
+ where(self.arel_table[:expires_at].gteq(Time.now.utc))
27
+ }
28
+
29
+ include LocalInstanceMethods
30
+ end
31
+ end
32
+
33
+ module LocalInstanceMethods
34
+ # number of seconds until the token expires
35
+ def expires_in
36
+ (expires_at - Time.now.utc).to_i
37
+ end
38
+
39
+ # forcefully expire the token
40
+ def expired!
41
+ self.expires_at = Time.now.utc
42
+ self.save!
43
+ end
44
+
45
+ private
46
+
47
+ def init_token
48
+ self.token = Devise::Oauth2Providable.random_id
49
+ end
50
+ def init_expires_at
51
+ self.expires_at = self.default_lifetime.from_now
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ ActiveRecord::Base.send :include, Devise::Oauth2Providable::ExpirableToken
@@ -0,0 +1,6 @@
1
+ module Devise
2
+ module Models
3
+ module Oauth2AuthorizationCodeGrantable
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Devise
2
+ module Models
3
+ module Oauth2PasswordGrantable
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ require 'devise/models'
2
+
3
+ module Devise
4
+ module Models
5
+ module Oauth2Providable
6
+ extend ActiveSupport::Concern
7
+ included do
8
+ has_many :access_tokens, :class_name => 'Devise::Oauth2Providable::AccessToken'
9
+ has_many :authorization_codes, :class_name => 'Devise::Oauth2Providable::AuthorizationCode'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ module Devise
2
+ module Models
3
+ module Oauth2RefreshTokenGrantable
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ require 'devise/oauth2_providable/strategies/oauth2_grant_type_strategy'
2
+
3
+ module Devise
4
+ module Strategies
5
+ class Oauth2AuthorizationCodeGrantTypeStrategy < Oauth2GrantTypeStrategy
6
+ def grant_type
7
+ 'authorization_code'
8
+ end
9
+
10
+ def authenticate_grant_type(client)
11
+ if code = client.authorization_codes.find_by_token(params[:code])
12
+ success! code.user
13
+ else
14
+ oauth_error! :invalid_grant, 'invalid authorization code request'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Warden::Strategies.add(:oauth2_authorization_code_grantable, Devise::Strategies::Oauth2AuthorizationCodeGrantTypeStrategy)
@@ -0,0 +1,39 @@
1
+ require 'devise/strategies/base'
2
+
3
+ module Devise
4
+ module Strategies
5
+ class Oauth2GrantTypeStrategy < Authenticatable
6
+ def valid?
7
+ params[:controller] == 'devise/oauth2_providable/tokens' && request.post? && params[:grant_type] == grant_type
8
+ end
9
+
10
+ # defined by subclass
11
+ def grant_type
12
+ end
13
+
14
+ # defined by subclass
15
+ def authenticate_grant_type(client)
16
+ end
17
+
18
+ def authenticate!
19
+ client_id, client_secret = request.authorization ? decode_credentials : [params[:client_id], params[:client_secret]]
20
+ client = Devise::Oauth2Providable::Client.find_by_identifier client_id
21
+ if client && client.secret == client_secret
22
+ env[Devise::Oauth2Providable::CLIENT_ENV_REF] = client
23
+ authenticate_grant_type(client)
24
+ else
25
+ oauth_error! :invalid_client, 'invalid client credentials'
26
+ end
27
+ end
28
+
29
+ # return custom error response in accordance with the oauth spec
30
+ # see http://tools.ietf.org/html/draft-ietf-oauth-v2-16#section-4.3
31
+ def oauth_error!(error_code = :invalid_request, description = nil)
32
+ body = {:error => error_code}
33
+ body[:error_description] = description if description
34
+ custom! [400, {'Content-Type' => 'application/json'}, [body.to_json]]
35
+ throw :warden
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ require 'devise/oauth2_providable/strategies/oauth2_grant_type_strategy'
2
+
3
+ module Devise
4
+ module Strategies
5
+ class Oauth2PasswordGrantTypeStrategy < Oauth2GrantTypeStrategy
6
+ def grant_type
7
+ 'password'
8
+ end
9
+
10
+ def authenticate_grant_type(client)
11
+ resource = mapping.to.find_for_authentication(mapping.to.authentication_keys.first => params[:username])
12
+ if validate(resource) { resource.valid_password?(params[:password]) }
13
+ success! resource
14
+ else
15
+ oauth_error! :invalid_grant, 'invalid password authentication request'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ Warden::Strategies.add(:oauth2_password_grantable, Devise::Strategies::Oauth2PasswordGrantTypeStrategy)
@@ -0,0 +1,25 @@
1
+ require 'devise/strategies/base'
2
+
3
+ module Devise
4
+ module Strategies
5
+ class Oauth2Providable < Authenticatable
6
+ def valid?
7
+ @req = Rack::OAuth2::Server::Resource::Bearer::Request.new(env)
8
+ @req.oauth2?
9
+ end
10
+ def authenticate!
11
+ @req.setup!
12
+ token = Devise::Oauth2Providable::AccessToken.find_by_token @req.access_token
13
+ env[Devise::Oauth2Providable::CLIENT_ENV_REF] = token.client if token
14
+ resource = token ? token.user : nil
15
+ if validate(resource)
16
+ success! resource
17
+ else
18
+ fail(:invalid_token)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Warden::Strategies.add(:oauth2_providable, Devise::Strategies::Oauth2Providable)
@@ -0,0 +1,22 @@
1
+ require 'devise/oauth2_providable/strategies/oauth2_grant_type_strategy'
2
+
3
+ module Devise
4
+ module Strategies
5
+ class Oauth2RefreshTokenGrantTypeStrategy < Oauth2GrantTypeStrategy
6
+ def grant_type
7
+ 'refresh_token'
8
+ end
9
+
10
+ def authenticate_grant_type(client)
11
+ if refresh_token = client.refresh_tokens.find_by_token(params[:refresh_token])
12
+ env[Devise::Oauth2Providable::REFRESH_TOKEN_ENV_REF] = refresh_token
13
+ success! refresh_token.user
14
+ else
15
+ oauth_error! :invalid_grant, 'invalid refresh token'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ Warden::Strategies.add(:oauth2_refresh_token_grantable, Devise::Strategies::Oauth2RefreshTokenGrantTypeStrategy)
@@ -0,0 +1,5 @@
1
+ module Devise
2
+ module Oauth2Providable
3
+ VERSION = "1.1.2"
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ require 'devise'
2
+ require 'rack/oauth2'
3
+ require 'devise/oauth2_providable/engine'
4
+ require 'devise/oauth2_providable/expirable_token'
5
+ require 'devise/oauth2_providable/strategies/oauth2_providable_strategy'
6
+ require 'devise/oauth2_providable/strategies/oauth2_password_grant_type_strategy'
7
+ require 'devise/oauth2_providable/strategies/oauth2_refresh_token_grant_type_strategy'
8
+ require 'devise/oauth2_providable/strategies/oauth2_authorization_code_grant_type_strategy'
9
+ require 'devise/oauth2_providable/models/oauth2_providable'
10
+ require 'devise/oauth2_providable/models/oauth2_password_grantable'
11
+ require 'devise/oauth2_providable/models/oauth2_refresh_token_grantable'
12
+ require 'devise/oauth2_providable/models/oauth2_authorization_code_grantable'
13
+
14
+ module Devise
15
+ module Oauth2Providable
16
+ CLIENT_ENV_REF = 'oauth2.client'
17
+ REFRESH_TOKEN_ENV_REF = "oauth2.refresh_token"
18
+
19
+ class << self
20
+ def random_id
21
+ SecureRandom.hex
22
+ end
23
+ def table_name_prefix
24
+ 'oauth2_'
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ Devise.add_module(:oauth2_providable,
31
+ :strategy => true,
32
+ :model => 'devise/oauth2_providable/models/oauth2_providable')
33
+ Devise.add_module(:oauth2_password_grantable,
34
+ :strategy => true,
35
+ :model => 'devise/oauth2_providable/models/oauth2_password_grantable')
36
+ Devise.add_module(:oauth2_refresh_token_grantable,
37
+ :strategy => true,
38
+ :model => 'devise/oauth2_providable/models/oauth2_refresh_token_grantable')
39
+ Devise.add_module(:oauth2_authorization_code_grantable,
40
+ :strategy => true,
41
+ :model => 'devise/oauth2_providable/models/oauth2_authorization_code_grantable')
data/script/rails ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ #!/usr/bin/env ruby
3
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
4
+
5
+ ENGINE_PATH = File.expand_path('../..', __FILE__)
6
+ load File.expand_path('../../spec/dummy/script/rails', __FILE__)
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Devise::Oauth2Providable::AuthorizationsController do
4
+ describe 'GET #new' do
5
+ context 'with valid redirect_uri' do
6
+ with :user
7
+ with :client
8
+ let(:redirect_uri) { client.redirect_uri }
9
+ before do
10
+ sign_in user
11
+ get :new, :client_id => client.identifier, :redirect_uri => redirect_uri, :response_type => 'code', :use_route => 'devise_oauth2_providable'
12
+ end
13
+ it { should respond_with :ok }
14
+ it { should respond_with_content_type :html }
15
+ it { should assign_to(:redirect_uri).with(redirect_uri) }
16
+ it { should assign_to(:response_type) }
17
+ it { should render_template 'devise/oauth2_providable/authorizations/new' }
18
+ it { should render_with_layout 'application' }
19
+ end
20
+ context 'with invalid redirect_uri' do
21
+ with :user
22
+ with :client
23
+ let(:redirect_uri) { 'http://example.com/foo/bar' }
24
+ before do
25
+ sign_in user
26
+ get :new, :client_id => client.identifier, :redirect_uri => redirect_uri, :response_type => 'code', :use_route => 'devise_oauth2_providable'
27
+ end
28
+ it { should respond_with :bad_request }
29
+ it { should respond_with_content_type :html }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe ProtectedController do
4
+
5
+ describe 'get :index' do
6
+ with :client
7
+ with :user
8
+ before do
9
+ @token = Devise::Oauth2Providable::AccessToken.create! :client => client, :user => user
10
+ end
11
+ context 'with valid bearer token in header' do
12
+ before do
13
+ @request.env['HTTP_AUTHORIZATION'] = "Bearer #{@token.token}"
14
+ get :index, :format => 'json'
15
+ end
16
+ it { should respond_with :ok }
17
+ end
18
+ context 'with valid bearer token in query string' do
19
+ before do
20
+ get :index, :access_token => @token.token, :format => 'json'
21
+ end
22
+ it { should respond_with :ok }
23
+ end
24
+
25
+ context 'with invalid bearer token in query param' do
26
+ before do
27
+ get :index, :access_token => 'invalid', :format => 'json'
28
+ end
29
+ it { should respond_with :unauthorized }
30
+ end
31
+ context 'with valid bearer token in header and query string' do
32
+ before do
33
+ end
34
+ it 'raises error' do
35
+ lambda {
36
+ @request.env['HTTP_AUTHORIZATION'] = "Bearer #{@token.token}"
37
+ get :index, :access_token => @token.token, :format => 'json'
38
+ }.should raise_error
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
3
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4
+
5
+ require File.expand_path('../config/application', __FILE__)
6
+
7
+ Dummy::Application.load_tasks
@@ -0,0 +1,7 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require_tree .
@@ -0,0 +1,7 @@
1
+ /*
2
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
3
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
5
+ *= require_self
6
+ *= require_tree .
7
+ */