doorkeeper 0.2.0 → 0.3.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 (30) hide show
  1. data/README.md +5 -1
  2. data/app/controllers/doorkeeper/application_controller.rb +11 -0
  3. data/app/controllers/doorkeeper/authorizations_controller.rb +4 -0
  4. data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -2
  5. data/app/controllers/doorkeeper/tokens_controller.rb +3 -0
  6. data/app/models/access_grant.rb +6 -20
  7. data/app/models/access_token.rb +23 -16
  8. data/app/models/application.rb +23 -5
  9. data/app/views/doorkeeper/authorizations/error.html.erb +6 -1
  10. data/app/views/doorkeeper/authorizations/new.html.erb +0 -2
  11. data/config/locales/en.yml +20 -0
  12. data/lib/doorkeeper.rb +10 -5
  13. data/lib/doorkeeper/config/scopes.rb +4 -0
  14. data/lib/doorkeeper/doorkeeper_for.rb +15 -20
  15. data/lib/doorkeeper/models/expirable.rb +18 -0
  16. data/lib/doorkeeper/models/revocable.rb +13 -0
  17. data/lib/doorkeeper/oauth/access_token_request.rb +24 -4
  18. data/lib/doorkeeper/oauth/authorization.rb +9 -0
  19. data/lib/doorkeeper/oauth/authorization/code.rb +34 -0
  20. data/lib/doorkeeper/oauth/authorization/token.rb +38 -0
  21. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +27 -0
  22. data/lib/doorkeeper/oauth/authorization_request.rb +40 -48
  23. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +18 -0
  24. data/lib/doorkeeper/oauth/helpers/unique_token.rb +16 -0
  25. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +28 -0
  26. data/lib/doorkeeper/version.rb +1 -1
  27. data/lib/generators/doorkeeper/install_generator.rb +1 -0
  28. data/lib/generators/doorkeeper/templates/migration.rb +9 -0
  29. metadata +47 -38
  30. data/lib/doorkeeper/oauth/random_string.rb +0 -15
data/README.md CHANGED
@@ -13,7 +13,7 @@ For more information about the supported features, check out the related [page i
13
13
  Put this in your Gemfile:
14
14
 
15
15
  ``` ruby
16
- gem 'doorkeeper'
16
+ gem 'doorkeeper', '~> 0.3.0'
17
17
  ```
18
18
 
19
19
  Run the installation generator with:
@@ -144,6 +144,10 @@ All supported ruby versions are [listed here](https://github.com/applicake/doork
144
144
  - Felipe Elias Philipp ([github.com/felipeelias](https://github.com/felipeelias))
145
145
  - Piotr Jakubowski ([github.com/piotrj](https://github.com/piotrj))
146
146
 
147
+ ### Contributors
148
+
149
+ Thanks to all our [awesome contributors](https://github.com/applicake/doorkeeper/contributors)!
150
+
147
151
  ### License
148
152
 
149
153
  MIT License. Copyright 2011 Applicake. [http://applicake.com](http://applicake.com)
@@ -2,6 +2,17 @@ module Doorkeeper
2
2
  class ApplicationController < ActionController::Base
3
3
  private
4
4
 
5
+ def parse_client_info_from_basic_auth
6
+ auth_header = request.env['HTTP_AUTHORIZATION']
7
+ return unless auth_header && auth_header =~ /^Basic (.*)/m
8
+ client_info = Base64.decode64($1).split(/:/, 2)
9
+ client_id = client_info[0]
10
+ client_secret = client_info[1]
11
+ return if client_id.nil? || client_secret.nil?
12
+ params[:client_id] = client_id
13
+ params[:client_secret] = client_secret
14
+ end
15
+
5
16
  def authenticate_resource_owner!
6
17
  current_resource_owner
7
18
  end
@@ -7,6 +7,8 @@ class Doorkeeper::AuthorizationsController < Doorkeeper::ApplicationController
7
7
  authorization.authorize
8
8
  redirect_to authorization.success_redirect_uri
9
9
  end
10
+ elsif authorization.redirect_on_error?
11
+ redirect_to authorization.invalid_redirect_uri
10
12
  else
11
13
  render :error
12
14
  end
@@ -15,6 +17,8 @@ class Doorkeeper::AuthorizationsController < Doorkeeper::ApplicationController
15
17
  def create
16
18
  if authorization.authorize
17
19
  redirect_to authorization.success_redirect_uri
20
+ elsif authorization.redirect_on_error?
21
+ redirect_to authorization.invalid_redirect_uri
18
22
  else
19
23
  render :error
20
24
  end
@@ -6,8 +6,7 @@ class Doorkeeper::AuthorizedApplicationsController < Doorkeeper::ApplicationCont
6
6
  end
7
7
 
8
8
  def destroy
9
- token = AccessToken.authorized_for(params[:id], current_resource_owner)
10
- token.revoke
9
+ AccessToken.revoke_all_for params[:id], current_resource_owner
11
10
  redirect_to authorized_applications_path, :notice => "Application revoked."
12
11
  end
13
12
  end
@@ -1,4 +1,7 @@
1
1
  class Doorkeeper::TokensController < Doorkeeper::ApplicationController
2
+
3
+ before_filter :parse_client_info_from_basic_auth, :only => :create
4
+
2
5
  def create
3
6
  response.headers.merge!({
4
7
  'Pragma' => 'no-cache',
@@ -1,7 +1,9 @@
1
1
  class AccessGrant < ActiveRecord::Base
2
- include Doorkeeper::OAuth::RandomString
2
+ include Doorkeeper::OAuth::Helpers
3
+ include Doorkeeper::Models::Expirable
4
+ include Doorkeeper::Models::Revocable
3
5
 
4
- set_table_name :oauth_access_grants
6
+ self.table_name = :oauth_access_grants
5
7
 
6
8
  belongs_to :application
7
9
 
@@ -9,24 +11,12 @@ class AccessGrant < ActiveRecord::Base
9
11
 
10
12
  before_validation :generate_token, :on => :create
11
13
 
12
- def expired?
13
- expires_in.present? && Time.now > expired_time
14
- end
15
-
16
14
  def accessible?
17
15
  !expired? && !revoked?
18
16
  end
19
17
 
20
- def revoke
21
- update_attribute :revoked_at, DateTime.now
22
- end
23
-
24
- def revoked?
25
- revoked_at.present?
26
- end
27
-
28
18
  def scopes
29
- self[:scopes].split(" ").map(&:to_sym)
19
+ self[:scopes].split(" ").map(&:to_sym) if self[:scopes]
30
20
  end
31
21
 
32
22
  def scopes_string
@@ -35,11 +25,7 @@ class AccessGrant < ActiveRecord::Base
35
25
 
36
26
  private
37
27
 
38
- def expired_time
39
- self.created_at + expires_in.seconds
40
- end
41
-
42
28
  def generate_token
43
- self.token = unique_random_string_for(:token)
29
+ self.token = UniqueToken.generate_for :token, self.class
44
30
  end
45
31
  end
@@ -1,7 +1,9 @@
1
1
  class AccessToken < ActiveRecord::Base
2
- include Doorkeeper::OAuth::RandomString
2
+ include Doorkeeper::OAuth::Helpers
3
+ include Doorkeeper::Models::Expirable
4
+ include Doorkeeper::Models::Revocable
3
5
 
4
- set_table_name :oauth_access_tokens
6
+ self.table_name = :oauth_access_tokens
5
7
 
6
8
  belongs_to :application
7
9
 
@@ -14,20 +16,29 @@ class AccessToken < ActiveRecord::Base
14
16
  before_validation :generate_token, :on => :create
15
17
  before_validation :generate_refresh_token, :on => :create, :if => :use_refresh_token?
16
18
 
17
- def self.authorized_for(application_id, resource_owner_id)
18
- accessible.where(:application_id => application_id, :resource_owner_id => resource_owner_id).first
19
+ def self.revoke_all_for(application_id, resource_owner)
20
+ where(:application_id => application_id,
21
+ :resource_owner_id => resource_owner.id).delete_all
19
22
  end
20
23
 
21
- def revoke
22
- update_attribute :revoked_at, DateTime.now
24
+ def self.matching_token_for(application, resource_owner_or_id, scopes)
25
+ token = last_authorized_token_for(application, resource_owner_or_id)
26
+ token if token && ScopeChecker.matches?(token.scopes, scopes)
23
27
  end
24
28
 
25
- def revoked?
26
- self.revoked_at.present?
29
+ def self.last_authorized_token_for(application, resource_owner_or_id)
30
+ resource_owner_id = resource_owner_or_id.kind_of?(ActiveRecord::Base) ? resource_owner_or_id.id : resource_owner_or_id
31
+ accessible.
32
+ where(:application_id => application.id,
33
+ :resource_owner_id => resource_owner_id).
34
+ order("created_at desc").
35
+ limit(1).
36
+ first
27
37
  end
38
+ private_class_method :last_authorized_token_for
28
39
 
29
- def expired?
30
- expires_in.present? && Time.now > expired_time
40
+ def token_type
41
+ "bearer"
31
42
  end
32
43
 
33
44
  def accessible?
@@ -49,15 +60,11 @@ class AccessToken < ActiveRecord::Base
49
60
 
50
61
  private
51
62
 
52
- def expired_time
53
- self.created_at + expires_in.seconds
54
- end
55
-
56
63
  def generate_refresh_token
57
- self.refresh_token = unique_random_string_for(:refresh_token)
64
+ self.refresh_token = UniqueToken.generate_for :refresh_token, self.class
58
65
  end
59
66
 
60
67
  def generate_token
61
- self.token = unique_random_string_for(:token)
68
+ self.token = UniqueToken.generate_for :token, self.class
62
69
  end
63
70
  end
@@ -1,7 +1,7 @@
1
1
  class Application < ActiveRecord::Base
2
- include Doorkeeper::OAuth::RandomString
2
+ include Doorkeeper::OAuth::Helpers
3
3
 
4
- set_table_name :oauth_applications
4
+ self.table_name = :oauth_applications
5
5
 
6
6
  has_many :access_grants
7
7
  has_many :authorized_tokens, :class_name => "AccessToken", :conditions => { :revoked_at => nil }
@@ -9,19 +9,37 @@ class Application < ActiveRecord::Base
9
9
 
10
10
  validates :name, :secret, :redirect_uri, :presence => true
11
11
  validates :uid, :presence => true, :uniqueness => true
12
+ validate :validate_redirect_uri
12
13
 
13
14
  before_validation :generate_uid, :generate_secret, :on => :create
14
15
 
16
+ def self.column_names_with_table
17
+ self.column_names.map { |c| "oauth_applications.#{c}" }
18
+ end
19
+
15
20
  def self.authorized_for(resource_owner)
16
- joins(:authorized_applications).where(:oauth_access_tokens => { :resource_owner_id => resource_owner.id })
21
+ joins(:authorized_applications).
22
+ where(:oauth_access_tokens => { :resource_owner_id => resource_owner.id }).
23
+ group(column_names_with_table.join(','))
24
+ end
25
+
26
+ def validate_redirect_uri
27
+ return unless redirect_uri
28
+ uri = URI.parse(redirect_uri)
29
+ errors.add(:redirect_uri, "cannot contain a fragment.") unless uri.fragment.nil?
30
+ errors.add(:redirect_uri, "must be an absolute URL.") if uri.scheme.nil? || uri.host.nil?
31
+ errors.add(:redirect_uri, "cannot contain a query parameter.") unless uri.query.nil?
32
+ rescue URI::InvalidURIError => e
33
+ errors.add(:redirect_uri, "must be a valid URI.")
17
34
  end
18
35
 
19
36
  private
37
+
20
38
  def generate_uid
21
- self.uid = unique_random_string_for(:uid)
39
+ self.uid = UniqueToken.generate_for :uid, self.class
22
40
  end
23
41
 
24
42
  def generate_secret
25
- self.secret = random_string
43
+ self.secret = UniqueToken.generate_for :secret, self.class
26
44
  end
27
45
  end
@@ -1 +1,6 @@
1
- <p>An error has occurred</p>
1
+ <div class="span16">
2
+ <h2>An error has occurred</h2>
3
+ <p>
4
+ <pre><%= t @authorization.error, :scope => [:doorkeeper, :errors, :messages] %></pre>
5
+ </p>
6
+ </div>
@@ -2,8 +2,6 @@
2
2
  <h2>Authorize <%= @authorization.client.name %> to use your account?</h2>
3
3
  </div>
4
4
 
5
-
6
-
7
5
  <div class="span16">
8
6
  <% if @authorization.scopes %>
9
7
  <p>
@@ -0,0 +1,20 @@
1
+ en:
2
+ doorkeeper:
3
+ errors:
4
+ messages:
5
+ # Common error messages
6
+ invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
7
+ invalid_redirect_uri: 'The redirect uri included is not valid.'
8
+ unauthorized_client: 'The client is not authorized to perform this request using this method.'
9
+ access_denied: 'The resource owner or authorization server denied the request.'
10
+ invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
11
+ server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
12
+ temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
13
+
14
+ # Access grant errors
15
+ unsupported_response_type: 'The authorization server does not support this response type.'
16
+
17
+ # Access token errors
18
+ invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'
19
+ invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'
20
+ unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'
data/lib/doorkeeper.rb CHANGED
@@ -6,14 +6,19 @@ module Doorkeeper
6
6
  autoload :Validations, "doorkeeper/validations"
7
7
 
8
8
  module OAuth
9
- class MismatchRedirectURI < StandardError; end
10
-
11
- autoload :RandomString, "doorkeeper/oauth/random_string"
12
9
  autoload :AuthorizationRequest, "doorkeeper/oauth/authorization_request"
13
10
  autoload :AccessTokenRequest, "doorkeeper/oauth/access_token_request"
11
+ autoload :Authorization, "doorkeeper/oauth/authorization"
12
+
13
+ module Helpers
14
+ autoload :ScopeChecker, "doorkeeper/oauth/helpers/scope_checker"
15
+ autoload :URIChecker, "doorkeeper/oauth/helpers/uri_checker"
16
+ autoload :UniqueToken, "doorkeeper/oauth/helpers/unique_token"
17
+ end
14
18
  end
15
19
 
16
- def self.setup
17
- yield self
20
+ module Models
21
+ autoload :Expirable, "doorkeeper/models/expirable"
22
+ autoload :Revocable, "doorkeeper/models/revocable"
18
23
  end
19
24
  end
@@ -21,6 +21,10 @@ module Doorkeeper
21
21
  self[scope].present?
22
22
  end
23
23
 
24
+ def all_included?(scopes_string)
25
+ scopes_string.split(" ").all? { |s| self.exists?(s) }
26
+ end
27
+
24
28
  def add (scope)
25
29
  raise IllegalElement unless valid_element?(scope)
26
30
  @scopes << scope
@@ -64,29 +64,12 @@ module Doorkeeper
64
64
  case args.first
65
65
  when :all
66
66
  AllDoorkeeperFor.new(args[1] || {})
67
- when Hash
68
- handle_hash(args.first)
69
- when nil
67
+ when Hash, nil
70
68
  raise InvalidSyntax
71
69
  else
72
70
  SelectedDoorkeeperFor.new(*args)
73
71
  end
74
72
  end
75
-
76
- def self.handle_hash(hash)
77
- if hash.has_key?(:only)
78
- warn "DEPRECATED: :only option. Put the actions you want doorkeeper to take care of after doorkeeper_for eg: doorkeeper_for :index, :new"
79
- args = [hash[:only], hash.except(:only)]
80
- return create_doorkeeper_for(*args)
81
- end
82
-
83
- if hash.has_key?(:except)
84
- warn "DEPRECATED: :except option. Use in connection with :all -> doorkeeper_for :all, :except => "
85
- return create_doorkeeper_for(:all, hash)
86
- end
87
-
88
- raise InvalidSyntax
89
- end
90
73
  end
91
74
 
92
75
  module Controller
@@ -95,7 +78,15 @@ module Doorkeeper
95
78
  doorkeeper_for = DoorkeeperForBuilder.create_doorkeeper_for(*args)
96
79
 
97
80
  before_filter doorkeeper_for.filter_options do
98
- head :unauthorized unless doorkeeper_for.validate_token(doorkeeper_token)
81
+ return if doorkeeper_for.validate_token(doorkeeper_token)
82
+ render_options = doorkeeper_unauthorized_render_options
83
+ if render_options.nil? || render_options.empty?
84
+ head :unauthorized
85
+ else
86
+ render_options[:status] = :unauthorized
87
+ render_options[:layout] = false if render_options[:layout].nil?
88
+ render render_options
89
+ end
99
90
  end
100
91
  end
101
92
  end
@@ -113,8 +104,12 @@ module Doorkeeper
113
104
  token = params[:access_token] || params[:bearer_token] || request.env['HTTP_AUTHORIZATION']
114
105
  if token
115
106
  token.gsub!(/Bearer /, '')
107
+ AccessToken.find_by_token(token)
116
108
  end
117
- AccessToken.find_by_token(token)
109
+ end
110
+
111
+ def doorkeeper_unauthorized_render_options
112
+ nil
118
113
  end
119
114
  end
120
115
  end
@@ -0,0 +1,18 @@
1
+ module Doorkeeper
2
+ module Models
3
+ module Expirable
4
+ def expired?
5
+ expires_in && Time.now > expired_time
6
+ end
7
+
8
+ def time_left
9
+ expired? ? 0 : expired_time - Time.now
10
+ end
11
+
12
+ def expired_time
13
+ created_at + expires_in.seconds
14
+ end
15
+ private :expired_time
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module Doorkeeper
2
+ module Models
3
+ module Revocable
4
+ def revoke(clock = DateTime)
5
+ update_attribute :revoked_at, clock.now
6
+ end
7
+
8
+ def revoked?
9
+ revoked_at.present?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -27,14 +27,14 @@ module Doorkeeper::OAuth
27
27
  def authorize
28
28
  if valid?
29
29
  revoke_base_token
30
- create_access_token
30
+ find_or_create_access_token
31
31
  end
32
32
  end
33
33
 
34
34
  def authorization
35
35
  auth = {
36
36
  'access_token' => access_token.token,
37
- 'token_type' => token_type,
37
+ 'token_type' => access_token.token_type,
38
38
  'expires_in' => access_token.expires_in,
39
39
  }
40
40
  auth.merge!({'refresh_token' => access_token.refresh_token}) if refresh_token_enabled?
@@ -46,7 +46,7 @@ module Doorkeeper::OAuth
46
46
  end
47
47
 
48
48
  def access_token
49
- @access_token
49
+ @access_token ||= AccessToken.matching_token_for client, base_token.resource_owner_id, base_token.scopes_string
50
50
  end
51
51
 
52
52
  def token_type
@@ -54,11 +54,27 @@ module Doorkeeper::OAuth
54
54
  end
55
55
 
56
56
  def error_response
57
- { 'error' => error.to_s }
57
+ {
58
+ 'error' => error.to_s,
59
+ 'error_description' => error_description
60
+ }
58
61
  end
59
62
 
60
63
  private
61
64
 
65
+ def find_or_create_access_token
66
+ if access_token
67
+ access_token.expired? ? revoke_and_create_access_token : access_token
68
+ else
69
+ create_access_token
70
+ end
71
+ end
72
+
73
+ def revoke_and_create_access_token
74
+ access_token.revoke
75
+ create_access_token
76
+ end
77
+
62
78
  def revoke_base_token
63
79
  base_token.revoke
64
80
  end
@@ -123,6 +139,10 @@ module Doorkeeper::OAuth
123
139
  %w(authorization_code refresh_token).include? grant_type
124
140
  end
125
141
 
142
+ def error_description
143
+ I18n.translate error, :scope => [:doorkeeper, :errors, :messages]
144
+ end
145
+
126
146
  def configuration
127
147
  Doorkeeper.configuration
128
148
  end
@@ -0,0 +1,9 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module Authorization
4
+ autoload :Code, "doorkeeper/oauth/authorization/code"
5
+ autoload :Token, "doorkeeper/oauth/authorization/token"
6
+ autoload :URIBuilder, "doorkeeper/oauth/authorization/uri_builder"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module Authorization
4
+ class Code
5
+ include URIBuilder
6
+
7
+ DEFAULT_EXPIRATION_TIME = 600
8
+
9
+ attr_accessor :authorization, :grant
10
+
11
+ def initialize(authorization)
12
+ @authorization = authorization
13
+ end
14
+
15
+ def issue_token
16
+ @grant ||= AccessGrant.create!(
17
+ :application_id => authorization.client.id,
18
+ :resource_owner_id => authorization.resource_owner.id,
19
+ :expires_in => DEFAULT_EXPIRATION_TIME,
20
+ :redirect_uri => authorization.redirect_uri,
21
+ :scopes => authorization.scope
22
+ )
23
+ end
24
+
25
+ def callback
26
+ uri_with_query(authorization.redirect_uri, {
27
+ :code => grant.token,
28
+ :state => authorization.state
29
+ })
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module Authorization
4
+ class Token
5
+ include URIBuilder
6
+
7
+ attr_accessor :authorization, :access_token
8
+
9
+ def initialize(authorization)
10
+ @authorization = authorization
11
+ end
12
+
13
+ def callback
14
+ uri_with_fragment(authorization.redirect_uri, {
15
+ :access_token => access_token.token,
16
+ :token_type => access_token.token_type,
17
+ :expires_in => access_token.time_left,
18
+ :state => authorization.state
19
+ })
20
+ end
21
+
22
+ def issue_token
23
+ @access_token ||= AccessToken.create!({
24
+ :application_id => authorization.client.id,
25
+ :resource_owner_id => authorization.resource_owner.id,
26
+ :scopes => authorization.scope,
27
+ :expires_in => configuration.access_token_expires_in,
28
+ :use_refresh_token => false
29
+ })
30
+ end
31
+
32
+ def configuration
33
+ Doorkeeper.configuration
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module Authorization
4
+ module URIBuilder
5
+ include Rack::Utils
6
+
7
+ def uri_with_query(url, parameters = {})
8
+ uri = URI.parse(url)
9
+ original_query = parse_query(uri.query)
10
+ uri.query = build_query(original_query.merge(parameters))
11
+ uri.to_s
12
+ end
13
+
14
+ def uri_with_fragment(url, parameters = {})
15
+ uri = URI.parse(url)
16
+ uri.fragment = build_query(parameters)
17
+ uri.to_s
18
+ end
19
+
20
+ def build_query(parameters = {})
21
+ parameters = parameters.reject { |k, v| v.blank? }
22
+ super parameters
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,8 +1,8 @@
1
1
  module Doorkeeper::OAuth
2
2
  class AuthorizationRequest
3
3
  include Doorkeeper::Validations
4
-
5
- DEFAULT_EXPIRATION_TIME = 600
4
+ include Doorkeeper::OAuth::Authorization::URIBuilder
5
+ include Doorkeeper::OAuth::Helpers
6
6
 
7
7
  ATTRIBUTES = [
8
8
  :response_type,
@@ -12,9 +12,9 @@ module Doorkeeper::OAuth
12
12
  :state
13
13
  ]
14
14
 
15
- validate :attributes, :error => :invalid_request
16
15
  validate :client, :error => :invalid_client
17
16
  validate :redirect_uri, :error => :invalid_redirect_uri
17
+ validate :attributes, :error => :invalid_request
18
18
  validate :response_type, :error => :unsupported_response_type
19
19
  validate :scope, :error => :invalid_scope
20
20
 
@@ -24,17 +24,18 @@ module Doorkeeper::OAuth
24
24
  def initialize(resource_owner, attributes)
25
25
  ATTRIBUTES.each { |attr| instance_variable_set("@#{attr}", attributes[attr]) }
26
26
  @resource_owner = resource_owner
27
- @grant = nil
28
27
  @scope ||= Doorkeeper.configuration.default_scope_string
29
28
  validate
30
29
  end
31
30
 
32
31
  def authorize
33
- create_authorization if valid?
32
+ return false unless valid?
33
+ @authorization = authorization_method.new(self)
34
+ @authorization.issue_token
34
35
  end
35
36
 
36
37
  def access_token_exists?
37
- access_token.present? && access_token_scope_matches?
38
+ AccessToken.matching_token_for(client, resource_owner, scope).present?
38
39
  end
39
40
 
40
41
  def deny
@@ -42,19 +43,20 @@ module Doorkeeper::OAuth
42
43
  end
43
44
 
44
45
  def success_redirect_uri
45
- build_uri do |uri|
46
- query = "code=#{token}"
47
- query << "&state=#{state}" if has_state?
48
- uri.query = query
49
- end
46
+ @authorization.callback
50
47
  end
51
48
 
52
49
  def invalid_redirect_uri
53
- build_uri do |uri|
54
- query = "error=#{error}"
55
- query << "&state=#{state}" if has_state?
56
- uri.query = query
57
- end
50
+ uri_builder = is_token_request? ? :uri_with_fragment : :uri_with_query
51
+ send(uri_builder, redirect_uri, {
52
+ :error => error,
53
+ :error_description => error_description,
54
+ :state => state
55
+ })
56
+ end
57
+
58
+ def redirect_on_error?
59
+ (error != :invalid_redirect_uri) && (error != :invalid_client)
58
60
  end
59
61
 
60
62
  def client
@@ -67,36 +69,12 @@ module Doorkeeper::OAuth
67
69
 
68
70
  private
69
71
 
70
- def create_authorization
71
- @grant = AccessGrant.create!(
72
- :application_id => client.id,
73
- :resource_owner_id => resource_owner.id,
74
- :expires_in => DEFAULT_EXPIRATION_TIME,
75
- :redirect_uri => redirect_uri,
76
- :scopes => scope
77
- )
78
- end
79
-
80
- def has_state?
81
- state.present?
82
- end
83
-
84
72
  def has_scope?
85
73
  Doorkeeper.configuration.scopes.all.present?
86
74
  end
87
75
 
88
- def token
89
- @grant.token
90
- end
91
-
92
- def build_uri
93
- uri = URI.parse(client.redirect_uri)
94
- yield uri
95
- uri.to_s
96
- end
97
-
98
76
  def validate_attributes
99
- %w(response_type client_id redirect_uri).all? { |attr| send(attr).present? }
77
+ response_type.present?
100
78
  end
101
79
 
102
80
  def validate_client
@@ -104,24 +82,38 @@ module Doorkeeper::OAuth
104
82
  end
105
83
 
106
84
  def validate_redirect_uri
107
- client.redirect_uri == redirect_uri
85
+ return false unless redirect_uri
86
+ URIChecker.valid_for_authorization?(redirect_uri, client.redirect_uri)
108
87
  end
109
88
 
110
89
  def validate_response_type
111
- response_type == "code"
90
+ is_code_request? || is_token_request?
112
91
  end
113
92
 
114
93
  def validate_scope
115
94
  return true unless has_scope?
116
- scope.present? && scope !~ /[\n|\r|\t]/ && scope.split(" ").all? { |s| Doorkeeper.configuration.scopes.exists?(s) }
95
+ ScopeChecker.valid?(scope, configuration.scopes)
96
+ end
97
+
98
+ def is_code_request?
99
+ response_type == "code"
100
+ end
101
+
102
+ def is_token_request?
103
+ response_type == "token"
104
+ end
105
+
106
+ def error_description
107
+ I18n.translate error, :scope => [:doorkeeper, :errors, :messages]
117
108
  end
118
109
 
119
- def access_token
120
- AccessToken.accessible.where(:application_id => client.id, :resource_owner_id => resource_owner.id).first
110
+ def configuration
111
+ Doorkeeper.configuration
121
112
  end
122
113
 
123
- def access_token_scope_matches?
124
- (access_token.scopes - scope.split(" ").map(&:to_sym)).empty?
114
+ def authorization_method
115
+ klass = is_code_request? ? "Code" : "Token"
116
+ "Doorkeeper::OAuth::Authorization::#{klass}".constantize
125
117
  end
126
118
  end
127
119
  end
@@ -0,0 +1,18 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module Helpers
4
+ module ScopeChecker
5
+ def self.matches?(current_scopes, scopes)
6
+ return false if current_scopes.nil? || scopes.nil?
7
+ current_scopes.map(&:to_s).sort == scopes.split(" ").sort
8
+ end
9
+
10
+ def self.valid?(scope, server_scopes)
11
+ scope.present? &&
12
+ scope !~ /[\n|\r|\t]/ &&
13
+ server_scopes.all_included?(scope)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module Helpers
4
+ module UniqueToken
5
+ def self.generate_for(attribute, klass, options = {})
6
+ generator_method = options.delete(:generator) || SecureRandom.method(:hex)
7
+ token_size = options.delete(:size) || 32
8
+ loop do
9
+ token = generator_method.call(token_size)
10
+ break token unless klass.send("find_by_#{attribute}", token)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module Helpers
4
+ module URIChecker
5
+ def self.valid?(url)
6
+ uri = as_uri(url)
7
+ uri.fragment.nil? && !uri.host.nil? && !uri.scheme.nil?
8
+ rescue URI::InvalidURIError
9
+ false
10
+ end
11
+
12
+ def self.matches?(url, client_url)
13
+ url, client_url = as_uri(url), as_uri(client_url)
14
+ url.query = nil
15
+ url == client_url
16
+ end
17
+
18
+ def self.valid_for_authorization?(url, client_url)
19
+ valid?(url) && matches?(url, client_url)
20
+ end
21
+
22
+ def self.as_uri(url)
23
+ URI.parse(url)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Doorkeeper
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -7,6 +7,7 @@ class Doorkeeper::InstallGenerator < Rails::Generators::Base
7
7
  def install
8
8
  migration_template 'migration.rb', 'db/migrate/create_doorkeeper_tables.rb'
9
9
  template "initializer.rb", "config/initializers/doorkeeper.rb"
10
+ copy_file "../../../../config/locales/en.yml", "config/locales/doorkeeper.en.yml"
10
11
  route "mount Doorkeeper::Engine => '/oauth'"
11
12
  readme "README"
12
13
  end
@@ -8,6 +8,8 @@ class CreateDoorkeeperTables < ActiveRecord::Migration
8
8
  t.timestamps
9
9
  end
10
10
 
11
+ add_index :oauth_applications, :uid, :unique => true
12
+
11
13
  create_table :oauth_access_grants do |t|
12
14
  t.integer :resource_owner_id, :null => false
13
15
  t.integer :application_id, :null => false
@@ -19,6 +21,8 @@ class CreateDoorkeeperTables < ActiveRecord::Migration
19
21
  t.string :scopes
20
22
  end
21
23
 
24
+ add_index :oauth_access_grants, :token, :unique => true
25
+
22
26
  create_table :oauth_access_tokens do |t|
23
27
  t.integer :resource_owner_id, :null => false
24
28
  t.integer :application_id, :null => false
@@ -29,5 +33,10 @@ class CreateDoorkeeperTables < ActiveRecord::Migration
29
33
  t.datetime :created_at, :null => false
30
34
  t.string :scopes
31
35
  end
36
+
37
+ add_index :oauth_access_tokens, :token, :unique => true
38
+ add_index :oauth_access_tokens, :resource_owner_id
39
+ add_index :oauth_access_tokens, :refresh_token, :unique => true
40
+
32
41
  end
33
42
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doorkeeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,96 +10,96 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-12-17 00:00:00.000000000 Z
13
+ date: 2012-02-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: rails
17
- requirement: &70330040514360 !ruby/object:Gem::Requirement
16
+ name: railties
17
+ requirement: &70362054833440 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
21
21
  - !ruby/object:Gem::Version
22
- version: 3.1.1
22
+ version: '3.1'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70330040514360
25
+ version_requirements: *70362054833440
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: sqlite3
28
- requirement: &70330040513880 !ruby/object:Gem::Requirement
28
+ requirement: &70362054832720 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
- - - ! '>='
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 1.3.5
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *70330040513880
36
+ version_requirements: *70362054832720
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rspec-rails
39
- requirement: &70330040513400 !ruby/object:Gem::Requirement
39
+ requirement: &70362054831840 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
- - - ! '>='
42
+ - - ~>
43
43
  - !ruby/object:Gem::Version
44
- version: '0'
44
+ version: 2.8.1
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *70330040513400
47
+ version_requirements: *70362054831840
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: capybara
50
- requirement: &70330040512980 !ruby/object:Gem::Requirement
50
+ requirement: &70362054831180 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
- - - ! '>='
53
+ - - ~>
54
54
  - !ruby/object:Gem::Version
55
- version: '0'
55
+ version: 1.1.2
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *70330040512980
58
+ version_requirements: *70362054831180
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: generator_spec
61
- requirement: &70330040528920 !ruby/object:Gem::Requirement
61
+ requirement: &70362054830420 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
- - - ! '>='
64
+ - - ~>
65
65
  - !ruby/object:Gem::Version
66
- version: '0'
66
+ version: 0.8.5
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *70330040528920
69
+ version_requirements: *70362054830420
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: factory_girl_rails
72
- requirement: &70330040528460 !ruby/object:Gem::Requirement
72
+ requirement: &70362054829800 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
- - - ! '>='
75
+ - - ~>
76
76
  - !ruby/object:Gem::Version
77
- version: '0'
77
+ version: 1.4.0
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *70330040528460
80
+ version_requirements: *70362054829800
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: timecop
83
- requirement: &70330040528000 !ruby/object:Gem::Requirement
83
+ requirement: &70362054829180 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
- - - ! '>='
86
+ - - ~>
87
87
  - !ruby/object:Gem::Version
88
- version: '0'
88
+ version: 0.3.5
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *70330040528000
91
+ version_requirements: *70362054829180
92
92
  - !ruby/object:Gem::Dependency
93
93
  name: database_cleaner
94
- requirement: &70330040527560 !ruby/object:Gem::Requirement
94
+ requirement: &70362054828560 !ruby/object:Gem::Requirement
95
95
  none: false
96
96
  requirements:
97
- - - ! '>='
97
+ - - ~>
98
98
  - !ruby/object:Gem::Version
99
- version: '0'
99
+ version: 0.7.1
100
100
  type: :development
101
101
  prerelease: false
102
- version_requirements: *70330040527560
102
+ version_requirements: *70362054828560
103
103
  description: Doorkeeper is an OAuth 2 provider for Rails.
104
104
  email:
105
105
  - felipe@applicake.com
@@ -140,6 +140,7 @@ files:
140
140
  - app/views/layouts/doorkeeper/application.html.erb
141
141
  - config/initializers/form_errors.rb
142
142
  - config/initializers/form_errors.rbc
143
+ - config/locales/en.yml
143
144
  - config/routes.rb
144
145
  - config/routes.rbc
145
146
  - lib/doorkeeper/config/scope.rb
@@ -151,11 +152,19 @@ files:
151
152
  - lib/doorkeeper/doorkeeper_for.rbc
152
153
  - lib/doorkeeper/engine.rb
153
154
  - lib/doorkeeper/engine.rbc
155
+ - lib/doorkeeper/models/expirable.rb
156
+ - lib/doorkeeper/models/revocable.rb
154
157
  - lib/doorkeeper/oauth/access_token_request.rb
155
158
  - lib/doorkeeper/oauth/access_token_request.rbc
159
+ - lib/doorkeeper/oauth/authorization/code.rb
160
+ - lib/doorkeeper/oauth/authorization/token.rb
161
+ - lib/doorkeeper/oauth/authorization/uri_builder.rb
162
+ - lib/doorkeeper/oauth/authorization.rb
156
163
  - lib/doorkeeper/oauth/authorization_request.rb
157
164
  - lib/doorkeeper/oauth/authorization_request.rbc
158
- - lib/doorkeeper/oauth/random_string.rb
165
+ - lib/doorkeeper/oauth/helpers/scope_checker.rb
166
+ - lib/doorkeeper/oauth/helpers/unique_token.rb
167
+ - lib/doorkeeper/oauth/helpers/uri_checker.rb
159
168
  - lib/doorkeeper/oauth/random_string.rbc
160
169
  - lib/doorkeeper/validations.rb
161
170
  - lib/doorkeeper/validations.rbc
@@ -187,7 +196,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
187
196
  version: '0'
188
197
  segments:
189
198
  - 0
190
- hash: -4506102143272219054
199
+ hash: 2535613111185415722
191
200
  required_rubygems_version: !ruby/object:Gem::Requirement
192
201
  none: false
193
202
  requirements:
@@ -196,10 +205,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
196
205
  version: '0'
197
206
  segments:
198
207
  - 0
199
- hash: -4506102143272219054
208
+ hash: 2535613111185415722
200
209
  requirements: []
201
210
  rubyforge_project:
202
- rubygems_version: 1.8.11
211
+ rubygems_version: 1.8.12
203
212
  signing_key:
204
213
  specification_version: 3
205
214
  summary: Doorkeeper is an OAuth 2 provider for Rails.
@@ -1,15 +0,0 @@
1
- module Doorkeeper::OAuth
2
- module RandomString
3
- def random_string
4
- SecureRandom.hex(32)
5
- end
6
-
7
- def unique_random_string_for(attribute)
8
- loop do
9
- token = random_string
10
- break token unless self.class.send("find_by_#{attribute}", token)
11
- end
12
- end
13
- end
14
- end
15
-