doorkeeper 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (92) hide show
  1. data/CHANGELOG.md +13 -0
  2. data/README.md +32 -5
  3. data/app/controllers/doorkeeper/application_controller.rb +4 -11
  4. data/app/controllers/doorkeeper/authorizations_controller.rb +11 -2
  5. data/app/controllers/doorkeeper/tokens_controller.rb +19 -5
  6. data/app/models/doorkeeper/access_grant.rb +1 -8
  7. data/app/models/doorkeeper/access_token.rb +2 -10
  8. data/app/models/doorkeeper/application.rb +4 -0
  9. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  10. data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
  11. data/config/locales/en.yml +3 -0
  12. data/doorkeeper.gemspec +2 -1
  13. data/lib/doorkeeper.rb +23 -3
  14. data/lib/doorkeeper/config.rb +73 -12
  15. data/lib/doorkeeper/doorkeeper_for.rb +1 -1
  16. data/lib/doorkeeper/engine.rb +28 -0
  17. data/lib/doorkeeper/models/scopes.rb +13 -0
  18. data/lib/doorkeeper/oauth/access_token_request.rb +5 -16
  19. data/lib/doorkeeper/oauth/authorization/code.rb +1 -1
  20. data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
  21. data/lib/doorkeeper/oauth/authorization_request.rb +18 -23
  22. data/lib/doorkeeper/oauth/client.rb +27 -0
  23. data/lib/doorkeeper/oauth/client/credentials.rb +21 -0
  24. data/lib/doorkeeper/oauth/client/methods.rb +18 -0
  25. data/lib/doorkeeper/oauth/client_credentials/creator.rb +29 -0
  26. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +35 -0
  27. data/lib/doorkeeper/oauth/client_credentials/response.rb +42 -0
  28. data/lib/doorkeeper/oauth/client_credentials/validation.rb +33 -0
  29. data/lib/doorkeeper/oauth/client_credentials_request.rb +46 -0
  30. data/lib/doorkeeper/oauth/error.rb +9 -0
  31. data/lib/doorkeeper/oauth/error_response.rb +30 -0
  32. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -2
  33. data/lib/doorkeeper/oauth/password_access_token_request.rb +130 -0
  34. data/lib/doorkeeper/oauth/scopes.rb +60 -0
  35. data/lib/doorkeeper/version.rb +1 -1
  36. data/lib/generators/doorkeeper/templates/initializer.rb +10 -5
  37. data/lib/generators/doorkeeper/templates/migration.rb +1 -1
  38. data/script/run_all +11 -0
  39. data/spec/controllers/authorizations_controller_spec.rb +3 -3
  40. data/spec/controllers/protected_resources_controller_spec.rb +7 -0
  41. data/spec/controllers/tokens_controller_spec.rb +1 -1
  42. data/spec/dummy/app/controllers/home_controller.rb +1 -1
  43. data/spec/dummy/app/models/user.rb +9 -0
  44. data/spec/dummy/config/application.rb +2 -0
  45. data/spec/dummy/config/initializers/doorkeeper.rb +12 -5
  46. data/spec/dummy/config/locales/doorkeeper.en.yml +5 -0
  47. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +5 -0
  48. data/spec/dummy/db/migrate/{20111206151426_create_doorkeeper_tables.rb → 20120524202412_create_doorkeeper_tables.rb} +10 -1
  49. data/spec/dummy/db/schema.rb +15 -6
  50. data/spec/lib/config_spec.rb +29 -13
  51. data/spec/lib/models/scopes_spec.rb +32 -0
  52. data/spec/lib/oauth/access_token_request_spec.rb +15 -29
  53. data/spec/lib/oauth/authorization_request_spec.rb +22 -72
  54. data/spec/lib/oauth/client/credentials_spec.rb +47 -0
  55. data/spec/lib/oauth/client/methods_spec.rb +54 -0
  56. data/spec/lib/oauth/client_credentials/creator_spec.rb +47 -0
  57. data/spec/lib/oauth/client_credentials/issuer_spec.rb +57 -0
  58. data/spec/lib/oauth/client_credentials/response_spec.rb +58 -0
  59. data/spec/lib/oauth/client_credentials/validation_spec.rb +29 -0
  60. data/spec/lib/oauth/client_credentials_integration_spec.rb +27 -0
  61. data/spec/lib/oauth/client_credentials_request_spec.rb +60 -0
  62. data/spec/lib/oauth/client_spec.rb +42 -0
  63. data/spec/lib/oauth/error_response_spec.rb +40 -0
  64. data/spec/lib/oauth/error_spec.rb +19 -0
  65. data/spec/lib/oauth/helpers/scope_checker_spec.rb +15 -10
  66. data/spec/lib/oauth/password_access_token_request_spec.rb +152 -0
  67. data/spec/lib/oauth/scopes_spec.rb +115 -0
  68. data/spec/models/doorkeeper/access_grant_spec.rb +0 -15
  69. data/spec/models/doorkeeper/access_token_spec.rb +11 -4
  70. data/spec/requests/applications/authorized_applications_spec.rb +2 -2
  71. data/spec/requests/endpoints/authorization_spec.rb +2 -2
  72. data/spec/requests/endpoints/token_spec.rb +7 -0
  73. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -1
  74. data/spec/requests/flows/authorization_code_spec.rb +8 -2
  75. data/spec/requests/flows/client_credentials_spec.rb +56 -0
  76. data/spec/requests/flows/password_spec.rb +52 -0
  77. data/spec/requests/flows/skip_authorization_spec.rb +2 -2
  78. data/spec/requests/protected_resources/private_api_spec.rb +9 -2
  79. data/spec/spec_helper_integration.rb +3 -0
  80. data/spec/support/helpers/authorization_request_helper.rb +7 -5
  81. data/spec/support/helpers/model_helper.rb +3 -3
  82. data/spec/support/helpers/request_spec_helper.rb +1 -1
  83. data/spec/support/helpers/url_helper.rb +12 -0
  84. metadata +65 -30
  85. data/lib/doorkeeper/config/scope.rb +0 -11
  86. data/lib/doorkeeper/config/scopes.rb +0 -61
  87. data/lib/doorkeeper/config/scopes_builder.rb +0 -18
  88. data/spec/dummy/config/initializers/inflections.rb +0 -10
  89. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  90. data/spec/lib/config/scope_spec.rb +0 -45
  91. data/spec/lib/config/scopes_builder_spec.rb +0 -27
  92. data/spec/lib/config/scopes_spec.rb +0 -180
@@ -0,0 +1,46 @@
1
+ require 'doorkeeper/oauth/error'
2
+ require 'doorkeeper/oauth/error_response'
3
+ require 'doorkeeper/oauth/scopes'
4
+ require 'doorkeeper/oauth/client_credentials/creator'
5
+ require 'doorkeeper/oauth/client_credentials/issuer'
6
+ require 'doorkeeper/oauth/client_credentials/response'
7
+ require 'doorkeeper/oauth/client_credentials/validation'
8
+
9
+ module Doorkeeper
10
+ module OAuth
11
+ class ClientCredentialsRequest
12
+ attr_accessor :issuer, :server, :client, :original_scopes, :scopes
13
+ attr_reader :response
14
+ alias :authorization :response # Remove this when API is consistent
15
+ alias :error_response :response
16
+
17
+ delegate :error, :to => :issuer
18
+
19
+ def issuer
20
+ @issuer ||= Issuer.new(server, Validation.new(server, self))
21
+ end
22
+
23
+ def initialize(server, client, parameters = {})
24
+ @client, @server = client, server
25
+ @response = nil
26
+ @original_scopes = parameters[:scope]
27
+ end
28
+
29
+ def authorize
30
+ @response = if issuer.create(client, scopes)
31
+ Response.new(issuer.token)
32
+ else
33
+ ErrorResponse.from_request(self)
34
+ end
35
+ end
36
+
37
+ def scopes
38
+ @scopes ||= if @original_scopes.present?
39
+ Doorkeeper::OAuth::Scopes.from_string(@original_scopes)
40
+ else
41
+ server.default_scopes
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,9 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ class Error < Struct.new(:name, :state)
4
+ def description
5
+ I18n.translate name, :scope => [:doorkeeper, :errors, :messages]
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ class ErrorResponse
4
+ include ActiveModel::Serializers::JSON
5
+
6
+ self.include_root_in_json = false
7
+
8
+ def self.from_request(request)
9
+ state = request.state if request.respond_to?(:state)
10
+ new(:name => request.error, :state => state)
11
+ end
12
+
13
+ delegate :name, :description, :state, :to => :@error
14
+ alias :error :name
15
+ alias :error_description :description
16
+
17
+ def initialize(attributes = {})
18
+ @error = Doorkeeper::OAuth::Error.new(*attributes.values_at(:name, :state))
19
+ end
20
+
21
+ def attributes
22
+ { 'error' => name, 'error_description' => description, 'state' => state }.reject { |k, v| v.blank? }
23
+ end
24
+
25
+ def status
26
+ :unauthorized
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,13 +4,13 @@ module Doorkeeper
4
4
  module ScopeChecker
5
5
  def self.matches?(current_scopes, scopes)
6
6
  return false if current_scopes.nil? || scopes.nil?
7
- current_scopes.map(&:to_s).sort == scopes.split(" ").sort
7
+ current_scopes == scopes
8
8
  end
9
9
 
10
10
  def self.valid?(scope, server_scopes)
11
11
  scope.present? &&
12
12
  scope !~ /[\n|\r|\t]/ &&
13
- server_scopes.all_included?(scope)
13
+ server_scopes.has_scopes?(Doorkeeper::OAuth::Scopes.from_string(scope))
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,130 @@
1
+ # coding: utf-8
2
+
3
+ # TODO: refactor to DRY up, this is very similar to AccessTokenRequest
4
+ module Doorkeeper::OAuth
5
+ class PasswordAccessTokenRequest
6
+ include Doorkeeper::Validations
7
+
8
+ ATTRIBUTES = [
9
+ :grant_type,
10
+ :username,
11
+ :password,
12
+ :scope,
13
+ :refresh_token
14
+ ]
15
+
16
+ validate :attributes, :error => :invalid_request
17
+ validate :grant_type, :error => :unsupported_grant_type
18
+ validate :client, :error => :invalid_client
19
+ validate :resource_owner, :error => :invalid_resource_owner
20
+ validate :scope, :error => :invalid_scope
21
+
22
+ attr_accessor *ATTRIBUTES
23
+ attr_accessor :resource_owner, :client
24
+
25
+ def initialize(client, owner, attributes = {})
26
+ ATTRIBUTES.each { |attr| instance_variable_set("@#{attr}", attributes[attr]) }
27
+ @resource_owner = owner
28
+ @client = client
29
+ validate
30
+ end
31
+
32
+ def authorize
33
+ if valid?
34
+ find_or_create_access_token
35
+ end
36
+ end
37
+
38
+ def authorization
39
+ auth = {
40
+ 'access_token' => access_token.token,
41
+ 'token_type' => access_token.token_type,
42
+ 'expires_in' => access_token.expires_in,
43
+ }
44
+ auth.merge!({'refresh_token' => access_token.refresh_token}) if refresh_token_enabled?
45
+ auth
46
+ end
47
+
48
+ def valid?
49
+ self.error.nil?
50
+ end
51
+
52
+ def access_token
53
+ @access_token
54
+ end
55
+
56
+ def token_type
57
+ "bearer"
58
+ end
59
+
60
+ def error_response
61
+ Doorkeeper::OAuth::ErrorResponse.from_request(self)
62
+ end
63
+
64
+ def scopes
65
+ @scopes ||= if scope.present?
66
+ Doorkeeper::OAuth::Scopes.from_string(scope)
67
+ else
68
+ Doorkeeper.configuration.default_scopes
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def find_or_create_access_token
75
+ if access_token
76
+ access_token.expired? ? revoke_and_create_access_token : access_token
77
+ else
78
+ create_access_token
79
+ end
80
+ end
81
+
82
+ def revoke_and_create_access_token
83
+ access_token.revoke
84
+ create_access_token
85
+ end
86
+
87
+ def revoke_base_token
88
+ base_token.revoke
89
+ end
90
+
91
+ def create_access_token
92
+ @access_token = Doorkeeper::AccessToken.create!({
93
+ :application_id => client.id,
94
+ :resource_owner_id => resource_owner.id,
95
+ :scopes => scopes.to_s,
96
+ :expires_in => configuration.access_token_expires_in,
97
+ :use_refresh_token => refresh_token_enabled?
98
+ })
99
+ end
100
+
101
+ def validate_attributes
102
+ grant_type.present?
103
+ end
104
+
105
+ def refresh_token_enabled?
106
+ configuration.refresh_token_enabled?
107
+ end
108
+
109
+ def validate_client
110
+ !!client
111
+ end
112
+
113
+ def validate_scope
114
+ return true unless scope.present?
115
+ ScopeChecker.valid?(scope, configuration.scopes)
116
+ end
117
+
118
+ def validate_grant_type
119
+ grant_type == 'password'
120
+ end
121
+
122
+ def validate_resource_owner
123
+ !!resource_owner
124
+ end
125
+
126
+ def configuration
127
+ Doorkeeper.configuration
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,60 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ class Scopes
4
+ include Enumerable
5
+ include Comparable
6
+
7
+ def self.from_string(string)
8
+ string ||= ""
9
+ new.tap do |scope|
10
+ scope.add *string.split
11
+ end
12
+ end
13
+
14
+ def self.from_array(array)
15
+ new.tap do |scope|
16
+ scope.add *array
17
+ end
18
+ end
19
+
20
+ delegate :each, :to => :@scopes
21
+
22
+ def initialize
23
+ @scopes = []
24
+ end
25
+
26
+ def exists?(scope)
27
+ @scopes.include? scope.to_sym
28
+ end
29
+
30
+ def add(*scopes)
31
+ @scopes.push *scopes.map(&:to_sym)
32
+ @scopes.uniq!
33
+ end
34
+
35
+ def all
36
+ @scopes
37
+ end
38
+
39
+ def to_s
40
+ @scopes.join(" ")
41
+ end
42
+
43
+ def has_scopes?(scopes)
44
+ scopes.all? { |s| exists?(s) }
45
+ end
46
+
47
+ def +(other)
48
+ if other.is_a? Scopes
49
+ self.class.from_array(self.all + other.all)
50
+ else
51
+ super(other)
52
+ end
53
+ end
54
+
55
+ def <=>(other)
56
+ self.map(&:to_s).sort <=> other.map(&:to_s).sort
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,3 +1,3 @@
1
1
  module Doorkeeper
2
- VERSION = "0.3.4"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -21,7 +21,8 @@ Doorkeeper.configure do
21
21
  # Admin.find_by_id(session[:admin_id]) || redirect_to(routes.new_admin_session_url)
22
22
  # end
23
23
 
24
- # Access token expiration time (default 2 hours)
24
+ # Access token expiration time (default 2 hours).
25
+ # If you want to disable expiration, set this to nil.
25
26
  # access_token_expires_in 2.hours
26
27
 
27
28
  # Issue access tokens with refresh token (disabled by default)
@@ -29,8 +30,12 @@ Doorkeeper.configure do
29
30
 
30
31
  # Define access token scopes for your provider
31
32
  # For more information go to https://github.com/applicake/doorkeeper/wiki/Using-Scopes
32
- # authorization_scopes do
33
- # scope :public, :default => true, :description => "The public one"
34
- # scope :write, :description => "Updating information"
35
- # end
33
+ # default_scopes :public
34
+ # optional_scopes :write, :update
35
+
36
+ # Change the way client credentials are retrieved from the request object.
37
+ # By default it retrieves first from `HTTP_AUTHORIZATION` header and
38
+ # fallsback to `:client_id` and `:client_secret` from `params` object
39
+ # Check out the wiki for mor information on customization
40
+ # client_credentials :from_basic, :from_params
36
41
  end
@@ -24,7 +24,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration
24
24
  add_index :oauth_access_grants, :token, :unique => true
25
25
 
26
26
  create_table :oauth_access_tokens do |t|
27
- t.integer :resource_owner_id, :null => false
27
+ t.integer :resource_owner_id
28
28
  t.integer :application_id, :null => false
29
29
  t.string :token, :null => false
30
30
  t.string :refresh_token
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ RBENV_VERSION=1.8.7-p352 bundle install --quiet
5
+ RBENV_VERSION=1.8.7-p352 bundle exec rake
6
+
7
+ RBENV_VERSION=1.9.2-p290 bundle install --quiet
8
+ RBENV_VERSION=1.9.2-p290 bundle exec rake
9
+
10
+ RBENV_VERSION=1.9.3-p0 bundle install --quiet
11
+ RBENV_VERSION=1.9.3-p0 bundle exec rake
@@ -13,7 +13,7 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
13
13
  end
14
14
 
15
15
  let(:client) { FactoryGirl.create :application }
16
- let(:user) { User.create! }
16
+ let(:user) { User.create!(:name => "Joe", :password => "sekret") }
17
17
 
18
18
  before do
19
19
  controller.stub :current_resource_owner => user
@@ -55,7 +55,7 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
55
55
 
56
56
  describe "POST #create with errors" do
57
57
  before do
58
- scope_exists :public
58
+ default_scopes_exist :public
59
59
  post :create, :client_id => client.uid, :response_type => "token", :scope => "invalid", :redirect_uri => client.redirect_uri, :use_route => :doorkeeper
60
60
  end
61
61
 
@@ -100,7 +100,7 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
100
100
 
101
101
  describe "GET #new with errors" do
102
102
  before do
103
- scope_exists :public
103
+ default_scopes_exist :public
104
104
  get :new, :client_id => client.uid, :response_type => "token", :scope => "invalid", :redirect_uri => client.redirect_uri, :use_route => :doorkeeper
105
105
  end
106
106
 
@@ -99,6 +99,13 @@ describe "Doorkeeper_for helper" do
99
99
  request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64("foo:bar")}"
100
100
  get :index
101
101
  end
102
+
103
+ it "doesn't change Authorization header value" do
104
+ Doorkeeper::AccessToken.should_receive(:find_by_token).exactly(2).times
105
+ request.env["HTTP_AUTHORIZATION"] = "Bearer #{token_string}"
106
+ get :index
107
+ get :index
108
+ end
102
109
  end
103
110
 
104
111
  context "defined for all actions" do
@@ -26,7 +26,7 @@ describe Doorkeeper::TokensController do
26
26
  end
27
27
 
28
28
  it "returns the error response" do
29
- token.should_receive(:error_response)
29
+ token.stub(:error_response => stub(:to_json => [], :status => :unauthorized))
30
30
  post :create, :use_route => :doorkeeper
31
31
  response.status.should == 401
32
32
  end
@@ -4,7 +4,7 @@ class HomeController < ApplicationController
4
4
 
5
5
  def sign_in
6
6
  session[:user_id] = if Rails.env.development?
7
- User.first || User.create!
7
+ User.first || User.create!(:name => "Joe", :password => "sekret")
8
8
  else
9
9
  User.first
10
10
  end
@@ -1,2 +1,11 @@
1
1
  class User < ActiveRecord::Base
2
+ has_secure_password
3
+ validates_presence_of :password, :on => :create
4
+
5
+ attr_accessible :name, :password
6
+
7
+ def self.authenticate!(name, password)
8
+ owner = User.find_by_name(name)
9
+ owner.authenticate(password) if owner
10
+ end
2
11
  end
@@ -27,6 +27,8 @@ module Dummy
27
27
  # Activate observers that should always be running.
28
28
  # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
29
29
 
30
+ config.active_record.whitelist_attributes = true
31
+
30
32
  # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
31
33
  # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
32
34
  # config.time_zone = 'Central Time (US & Canada)'
@@ -21,14 +21,21 @@ Doorkeeper.configure do
21
21
  # Admin.find_by_id(session[:admin_id]) || redirect_to(routes.new_admin_session_url)
22
22
  # end
23
23
 
24
- authorization_scopes do
25
- scope :public, :default => true, :description => "The public one"
26
- scope :write, :description => "Updating information"
27
- end
28
-
29
24
  # Access token expiration time (default 2 hours)
25
+ # If you want to disable expiration, set this to nil.
30
26
  # access_token_expires_in 2.hours
31
27
 
32
28
  # Issue access tokens with refresh token (disabled by default)
33
29
  use_refresh_token
30
+
31
+ # Define access token scopes for your provider
32
+ # For more information go to https://github.com/applicake/doorkeeper/wiki/Using-Scopes
33
+ default_scopes :public
34
+ optional_scopes :write, :update
35
+
36
+ # Change the way client credentials are retrieved from the request object.
37
+ # By default it retrieves first from `HTTP_AUTHORIZATION` header and
38
+ # fallsback to `:client_id` and `:client_secret` from `params` object
39
+ # Check out the wiki for mor information on customization
40
+ # client_credentials :from_basic, :from_params
34
41
  end