doorkeeper 3.1.0 → 4.4.3
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.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.github/ISSUE_TEMPLATE.md +25 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- data/.gitignore +6 -1
- data/.hound.yml +2 -13
- data/.rubocop.yml +17 -0
- data/.travis.yml +26 -10
- data/Appraisals +18 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +5 -5
- data/NEWS.md +141 -2
- data/README.md +149 -66
- data/RELEASING.md +5 -12
- data/Rakefile +1 -1
- data/SECURITY.md +15 -0
- data/app/controllers/doorkeeper/application_controller.rb +4 -6
- data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
- data/app/controllers/doorkeeper/applications_controller.rb +18 -8
- data/app/controllers/doorkeeper/authorizations_controller.rb +1 -1
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +62 -15
- data/app/helpers/doorkeeper/dashboard_helper.rb +14 -10
- data/app/validators/redirect_uri_validator.rb +12 -2
- data/app/views/doorkeeper/applications/_delete_form.html.erb +1 -2
- data/app/views/doorkeeper/applications/_form.html.erb +13 -2
- data/app/views/doorkeeper/applications/index.html.erb +2 -0
- data/app/views/doorkeeper/applications/show.html.erb +4 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
- data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
- data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +1 -1
- data/config/locales/en.yml +12 -7
- data/doorkeeper.gemspec +16 -11
- data/gemfiles/rails_4_2.gemfile +13 -0
- data/gemfiles/rails_5_0.gemfile +12 -0
- data/gemfiles/rails_5_1.gemfile +12 -0
- data/gemfiles/rails_5_2.gemfile +12 -0
- data/gemfiles/rails_master.gemfile +14 -0
- data/lib/doorkeeper/config.rb +119 -46
- data/lib/doorkeeper/engine.rb +11 -7
- data/lib/doorkeeper/errors.rb +18 -0
- data/lib/doorkeeper/grape/helpers.rb +14 -8
- data/lib/doorkeeper/helpers/controller.rb +8 -19
- data/lib/doorkeeper/models/access_grant_mixin.rb +10 -21
- data/lib/doorkeeper/models/access_token_mixin.rb +147 -43
- data/lib/doorkeeper/models/application_mixin.rb +33 -35
- data/lib/doorkeeper/models/concerns/accessible.rb +4 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +15 -5
- data/lib/doorkeeper/models/concerns/orderable.rb +13 -0
- data/lib/doorkeeper/models/concerns/ownership.rb +6 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +37 -2
- data/lib/doorkeeper/oauth/authorization/token.rb +22 -18
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +20 -18
- data/lib/doorkeeper/oauth/authorization_code_request.rb +7 -5
- data/lib/doorkeeper/oauth/{request_concern.rb → base_request.rb} +9 -2
- data/lib/doorkeeper/oauth/base_response.rb +29 -0
- data/lib/doorkeeper/oauth/client/credentials.rb +21 -8
- data/lib/doorkeeper/oauth/client.rb +2 -3
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +3 -2
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -8
- data/lib/doorkeeper/oauth/code_response.rb +16 -16
- data/lib/doorkeeper/oauth/error.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +10 -10
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +17 -1
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -4
- data/lib/doorkeeper/oauth/password_access_token_request.rb +8 -13
- data/lib/doorkeeper/oauth/pre_authorization.rb +5 -3
- data/lib/doorkeeper/oauth/refresh_token_request.rb +23 -14
- data/lib/doorkeeper/oauth/scopes.rb +18 -8
- data/lib/doorkeeper/oauth/token.rb +20 -21
- data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
- data/lib/doorkeeper/oauth/token_request.rb +1 -2
- data/lib/doorkeeper/oauth/token_response.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -0
- data/lib/doorkeeper/orm/active_record/access_token.rb +34 -8
- data/lib/doorkeeper/orm/active_record/application.rb +48 -11
- data/lib/doorkeeper/orm/active_record.rb +17 -22
- data/lib/doorkeeper/rails/helpers.rb +6 -9
- data/lib/doorkeeper/rails/routes/mapper.rb +4 -4
- data/lib/doorkeeper/rails/routes/mapping.rb +1 -1
- data/lib/doorkeeper/rails/routes.rb +17 -11
- data/lib/doorkeeper/request/authorization_code.rb +7 -1
- data/lib/doorkeeper/request/password.rb +2 -2
- data/lib/doorkeeper/request/refresh_token.rb +1 -1
- data/lib/doorkeeper/request.rb +7 -1
- data/lib/doorkeeper/server.rb +0 -8
- data/lib/doorkeeper/validations.rb +3 -2
- data/lib/doorkeeper/version.rb +34 -1
- data/lib/doorkeeper.rb +10 -2
- data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +31 -0
- data/lib/generators/doorkeeper/application_owner_generator.rb +11 -2
- data/lib/generators/doorkeeper/migration_generator.rb +13 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +35 -0
- data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +11 -0
- data/{spec/dummy/db/migrate/20130902175349_add_owner_to_application.rb → lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +11 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +38 -6
- data/lib/generators/doorkeeper/templates/migration.rb.erb +69 -0
- data/spec/controllers/application_metal_controller.rb +10 -0
- data/spec/controllers/applications_controller_spec.rb +15 -4
- data/spec/controllers/authorizations_controller_spec.rb +74 -27
- data/spec/controllers/protected_resources_controller_spec.rb +70 -32
- data/spec/controllers/token_info_controller_spec.rb +17 -13
- data/spec/controllers/tokens_controller_spec.rb +198 -12
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -4
- data/spec/dummy/app/controllers/home_controller.rb +1 -1
- data/spec/dummy/app/controllers/metal_controller.rb +1 -1
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +3 -3
- data/spec/dummy/app/models/user.rb +0 -4
- data/spec/dummy/config/application.rb +2 -36
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/test.rb +4 -15
- data/spec/dummy/config/initializers/doorkeeper.rb +19 -3
- data/spec/dummy/config/initializers/new_framework_defaults.rb +6 -0
- data/spec/dummy/config/initializers/secret_token.rb +0 -1
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
- data/{lib/generators/doorkeeper/templates/migration.rb → spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb} +16 -4
- data/{lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb → spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb} +4 -2
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +13 -0
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_application.rb +13 -0
- data/spec/dummy/db/schema.rb +24 -22
- data/spec/factories.rb +4 -2
- data/spec/generators/application_owner_generator_spec.rb +24 -5
- data/spec/generators/migration_generator_spec.rb +24 -3
- data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
- data/spec/grape/grape_integration_spec.rb +135 -0
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +159 -14
- data/spec/lib/doorkeeper_spec.rb +135 -13
- data/spec/lib/models/expirable_spec.rb +0 -1
- data/spec/lib/models/revocable_spec.rb +27 -4
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +1 -2
- data/spec/lib/oauth/authorization_code_request_spec.rb +55 -12
- data/spec/lib/oauth/base_request_spec.rb +155 -0
- data/spec/lib/oauth/base_response_spec.rb +45 -0
- data/spec/lib/oauth/client/credentials_spec.rb +45 -2
- data/spec/lib/oauth/client_credentials/creator_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_request_spec.rb +1 -0
- data/spec/lib/oauth/code_request_spec.rb +1 -3
- data/spec/lib/oauth/code_response_spec.rb +34 -0
- data/spec/lib/oauth/error_response_spec.rb +9 -9
- data/spec/lib/oauth/error_spec.rb +1 -1
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -1
- data/spec/lib/oauth/invalid_token_response_spec.rb +36 -8
- data/spec/lib/oauth/password_access_token_request_spec.rb +14 -8
- data/spec/lib/oauth/pre_authorization_spec.rb +12 -7
- data/spec/lib/oauth/refresh_token_request_spec.rb +52 -9
- data/spec/lib/oauth/scopes_spec.rb +28 -2
- data/spec/lib/oauth/token_request_spec.rb +6 -8
- data/spec/lib/oauth/token_spec.rb +12 -5
- data/spec/lib/server_spec.rb +10 -3
- data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
- data/spec/models/doorkeeper/access_token_spec.rb +116 -48
- data/spec/models/doorkeeper/application_spec.rb +145 -29
- data/spec/requests/applications/applications_request_spec.rb +5 -5
- data/spec/requests/endpoints/authorization_spec.rb +5 -6
- data/spec/requests/endpoints/token_spec.rb +8 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +11 -1
- data/spec/requests/flows/authorization_code_spec.rb +6 -13
- data/spec/requests/flows/client_credentials_spec.rb +29 -1
- data/spec/requests/flows/implicit_grant_errors_spec.rb +2 -2
- data/spec/requests/flows/password_spec.rb +118 -15
- data/spec/requests/flows/refresh_token_spec.rb +89 -19
- data/spec/requests/flows/revoke_token_spec.rb +105 -91
- data/spec/requests/protected_resources/metal_spec.rb +1 -1
- data/spec/requests/protected_resources/private_api_spec.rb +1 -1
- data/spec/routing/custom_controller_routes_spec.rb +4 -0
- data/spec/routing/default_routes_spec.rb +5 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/spec_helper_integration.rb +22 -4
- data/spec/support/dependencies/factory_girl.rb +2 -2
- data/spec/support/helpers/access_token_request_helper.rb +1 -1
- data/spec/support/helpers/model_helper.rb +34 -7
- data/spec/support/helpers/request_spec_helper.rb +17 -5
- data/spec/support/helpers/url_helper.rb +9 -8
- data/spec/support/http_method_shim.rb +38 -0
- data/spec/support/shared/controllers_shared_context.rb +15 -10
- data/spec/support/shared/models_shared_examples.rb +5 -5
- data/spec/validators/redirect_uri_validator_spec.rb +51 -6
- data/spec/version/version_spec.rb +15 -0
- metadata +128 -46
- data/lib/doorkeeper/oauth/client/methods.rb +0 -18
- data/lib/generators/doorkeeper/application_scopes_generator.rb +0 -34
- data/lib/generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb +0 -5
- data/spec/dummy/db/migrate/20130902165751_create_doorkeeper_tables.rb +0 -41
- data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
- data/spec/lib/oauth/client/methods_spec.rb +0 -54
@@ -1,8 +1,6 @@
|
|
1
1
|
module Doorkeeper
|
2
2
|
module OAuth
|
3
|
-
class RefreshTokenRequest
|
4
|
-
include Validations
|
5
|
-
include OAuth::RequestConcern
|
3
|
+
class RefreshTokenRequest < BaseRequest
|
6
4
|
include OAuth::Helpers
|
7
5
|
|
8
6
|
validate :token_presence, error: :invalid_request
|
@@ -29,16 +27,19 @@ module Doorkeeper
|
|
29
27
|
|
30
28
|
private
|
31
29
|
|
32
|
-
attr_reader :refresh_token_parameter
|
33
|
-
|
34
30
|
def before_successful_response
|
35
31
|
refresh_token.transaction do
|
36
32
|
refresh_token.lock!
|
37
33
|
raise Errors::InvalidTokenReuse if refresh_token.revoked?
|
38
34
|
|
39
|
-
refresh_token.revoke
|
35
|
+
refresh_token.revoke unless refresh_token_revoked_on_use?
|
40
36
|
create_access_token
|
41
37
|
end
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def refresh_token_revoked_on_use?
|
42
|
+
Doorkeeper::AccessToken.refresh_token_revoked_on_use?
|
42
43
|
end
|
43
44
|
|
44
45
|
def default_scopes
|
@@ -46,21 +47,29 @@ module Doorkeeper
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def create_access_token
|
49
|
-
|
50
|
-
|
51
|
-
client
|
52
|
-
)
|
50
|
+
@access_token = AccessToken.create!(access_token_attributes)
|
51
|
+
end
|
53
52
|
|
54
|
-
|
53
|
+
def access_token_attributes
|
54
|
+
{
|
55
55
|
application_id: refresh_token.application_id,
|
56
56
|
resource_owner_id: refresh_token.resource_owner_id,
|
57
57
|
scopes: scopes.to_s,
|
58
|
-
expires_in:
|
59
|
-
use_refresh_token: true
|
58
|
+
expires_in: access_token_expires_in,
|
59
|
+
use_refresh_token: true
|
60
|
+
}.tap do |attributes|
|
61
|
+
if refresh_token_revoked_on_use?
|
62
|
+
attributes[:previous_refresh_token] = refresh_token.refresh_token
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def access_token_expires_in
|
68
|
+
Authorization::Token.access_token_expires_in(server, client)
|
60
69
|
end
|
61
70
|
|
62
71
|
def validate_token_presence
|
63
|
-
refresh_token.present? || refresh_token_parameter.present?
|
72
|
+
refresh_token.present? || @refresh_token_parameter.present?
|
64
73
|
end
|
65
74
|
|
66
75
|
def validate_token
|
@@ -45,20 +45,30 @@ module Doorkeeper
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def +(other)
|
48
|
-
|
49
|
-
self.class.from_array(self.all + other.all)
|
50
|
-
else
|
51
|
-
super(other)
|
52
|
-
end
|
48
|
+
self.class.from_array(all + to_array(other))
|
53
49
|
end
|
54
50
|
|
55
51
|
def <=>(other)
|
56
|
-
|
52
|
+
if other.respond_to?(:map)
|
53
|
+
map(&:to_s).sort <=> other.map(&:to_s).sort
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
57
57
|
end
|
58
58
|
|
59
59
|
def &(other)
|
60
|
-
|
61
|
-
|
60
|
+
self.class.from_array(all & to_array(other))
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def to_array(other)
|
66
|
+
case other
|
67
|
+
when Scopes
|
68
|
+
other.all
|
69
|
+
else
|
70
|
+
other.to_a
|
71
|
+
end
|
62
72
|
end
|
63
73
|
end
|
64
74
|
end
|
@@ -1,7 +1,23 @@
|
|
1
1
|
module Doorkeeper
|
2
2
|
module OAuth
|
3
3
|
class Token
|
4
|
-
|
4
|
+
class << self
|
5
|
+
def from_request(request, *methods)
|
6
|
+
methods.inject(nil) do |credentials, method|
|
7
|
+
method = self.method(method) if method.is_a?(Symbol)
|
8
|
+
credentials = method.call(request)
|
9
|
+
break credentials unless credentials.blank?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def authenticate(request, *methods)
|
14
|
+
if (token = from_request(request, *methods))
|
15
|
+
access_token = AccessToken.by_token(token)
|
16
|
+
access_token.revoke_previous_refresh_token! if access_token
|
17
|
+
access_token
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
5
21
|
def from_access_token_param(request)
|
6
22
|
request.parameters[:access_token]
|
7
23
|
end
|
@@ -26,12 +42,11 @@ module Doorkeeper
|
|
26
42
|
|
27
43
|
def token_from_basic_header(header, pattern)
|
28
44
|
encoded_header = token_from_header(header, pattern)
|
29
|
-
|
30
|
-
token
|
45
|
+
decode_basic_credentials_token(encoded_header)
|
31
46
|
end
|
32
47
|
|
33
|
-
def
|
34
|
-
Base64.decode64(encoded_header).split(/:/, 2)
|
48
|
+
def decode_basic_credentials_token(encoded_header)
|
49
|
+
Base64.decode64(encoded_header).split(/:/, 2).first
|
35
50
|
end
|
36
51
|
|
37
52
|
def token_from_header(header, pattern)
|
@@ -42,22 +57,6 @@ module Doorkeeper
|
|
42
57
|
header && header.match(pattern)
|
43
58
|
end
|
44
59
|
end
|
45
|
-
|
46
|
-
extend Methods
|
47
|
-
|
48
|
-
def self.from_request(request, *methods)
|
49
|
-
methods.inject(nil) do |credentials, method|
|
50
|
-
method = self.method(method) if method.is_a?(Symbol)
|
51
|
-
credentials = method.call(request)
|
52
|
-
break credentials unless credentials.blank?
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.authenticate(request, *methods)
|
57
|
-
if token = from_request(request, *methods)
|
58
|
-
AccessToken.by_token(token)
|
59
|
-
end
|
60
|
-
end
|
61
60
|
end
|
62
61
|
end
|
63
62
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Doorkeeper
|
2
|
+
module OAuth
|
3
|
+
# RFC7662 OAuth 2.0 Token Introspection
|
4
|
+
#
|
5
|
+
# @see https://tools.ietf.org/html/rfc7662
|
6
|
+
class TokenIntrospection
|
7
|
+
attr_reader :server, :token
|
8
|
+
attr_reader :error
|
9
|
+
|
10
|
+
def initialize(server, token)
|
11
|
+
@server = server
|
12
|
+
@token = token
|
13
|
+
|
14
|
+
authorize!
|
15
|
+
end
|
16
|
+
|
17
|
+
def authorized?
|
18
|
+
@error.blank?
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_json
|
22
|
+
active? ? success_response : failure_response
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# If the protected resource uses OAuth 2.0 client credentials to
|
28
|
+
# authenticate to the introspection endpoint and its credentials are
|
29
|
+
# invalid, the authorization server responds with an HTTP 401
|
30
|
+
# (Unauthorized) as described in Section 5.2 of OAuth 2.0 [RFC6749].
|
31
|
+
#
|
32
|
+
# Endpoint must first validate the authentication.
|
33
|
+
# If the authentication is invalid, the endpoint should respond with
|
34
|
+
# an HTTP 401 status code and an invalid_client response.
|
35
|
+
#
|
36
|
+
# @see https://www.oauth.com/oauth2-servers/token-introspection-endpoint/
|
37
|
+
#
|
38
|
+
def authorize!
|
39
|
+
# Requested client authorization
|
40
|
+
if server.credentials
|
41
|
+
@error = :invalid_client unless authorized_client
|
42
|
+
else
|
43
|
+
# Requested bearer token authorization
|
44
|
+
@error = :invalid_request unless authorized_token
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Client Authentication
|
49
|
+
def authorized_client
|
50
|
+
@_authorized_client ||= server.credentials && server.client
|
51
|
+
end
|
52
|
+
|
53
|
+
# Bearer Token Authentication
|
54
|
+
def authorized_token
|
55
|
+
@_authorized_token ||=
|
56
|
+
OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
|
57
|
+
end
|
58
|
+
|
59
|
+
# 2.2. Introspection Response
|
60
|
+
def success_response
|
61
|
+
{
|
62
|
+
active: true,
|
63
|
+
scope: @token.scopes_string,
|
64
|
+
client_id: @token.try(:application).try(:uid),
|
65
|
+
token_type: @token.token_type,
|
66
|
+
exp: @token.expires_at.to_i,
|
67
|
+
iat: @token.created_at.to_i
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# If the introspection call is properly authorized but the token is not
|
72
|
+
# active, does not exist on this server, or the protected resource is
|
73
|
+
# not allowed to introspect this particular token, then the
|
74
|
+
# authorization server MUST return an introspection response with the
|
75
|
+
# "active" field set to "false". Note that to avoid disclosing too
|
76
|
+
# much of the authorization server's state to a third party, the
|
77
|
+
# authorization server SHOULD NOT include any additional information
|
78
|
+
# about an inactive token, including why the token is inactive.
|
79
|
+
#
|
80
|
+
# @see https://tools.ietf.org/html/rfc7662 2.2. Introspection Response
|
81
|
+
#
|
82
|
+
def failure_response
|
83
|
+
{
|
84
|
+
active: false
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Boolean indicator of whether or not the presented token
|
89
|
+
# is currently active. The specifics of a token's "active" state
|
90
|
+
# will vary depending on the implementation of the authorization
|
91
|
+
# server and the information it keeps about its tokens, but a "true"
|
92
|
+
# value return for the "active" property will generally indicate
|
93
|
+
# that a given token has been issued by this authorization server,
|
94
|
+
# has not been revoked by the resource owner, and is within its
|
95
|
+
# given time window of validity (e.g., after its issuance time and
|
96
|
+
# before its expiration time).
|
97
|
+
#
|
98
|
+
# Any other error is considered an "inactive" token.
|
99
|
+
#
|
100
|
+
# * The token requested does not exist or is invalid
|
101
|
+
# * The token expired
|
102
|
+
# * The token was issued to a different client than is making this request
|
103
|
+
#
|
104
|
+
def active?
|
105
|
+
if authorized_client
|
106
|
+
valid_token? && authorized_for_client?
|
107
|
+
else
|
108
|
+
valid_token?
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Token can be valid only if it is not expired or revoked.
|
113
|
+
def valid_token?
|
114
|
+
@token.present? && @token.accessible?
|
115
|
+
end
|
116
|
+
|
117
|
+
# If token doesn't belong to some client, then it is public.
|
118
|
+
# Otherwise in it required for token to be connected to the same client.
|
119
|
+
def authorized_for_client?
|
120
|
+
if @token.application.present?
|
121
|
+
@token.application == authorized_client.application
|
122
|
+
else
|
123
|
+
true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module Doorkeeper
|
2
2
|
module OAuth
|
3
3
|
class TokenRequest
|
4
|
-
attr_accessor :pre_auth, :resource_owner
|
4
|
+
attr_accessor :pre_auth, :resource_owner
|
5
5
|
|
6
6
|
def initialize(pre_auth, resource_owner)
|
7
7
|
@pre_auth = pre_auth
|
8
|
-
@client = pre_auth.client
|
9
8
|
@resource_owner = resource_owner
|
10
9
|
end
|
11
10
|
|
@@ -14,7 +14,7 @@ module Doorkeeper
|
|
14
14
|
'expires_in' => token.expires_in_seconds,
|
15
15
|
'refresh_token' => token.refresh_token,
|
16
16
|
'scope' => token.scopes_string,
|
17
|
-
'created_at' => token.created_at.to_i
|
17
|
+
'created_at' => token.created_at.to_i
|
18
18
|
}.reject { |_, value| value.blank? }
|
19
19
|
end
|
20
20
|
|
@@ -3,5 +3,32 @@ module Doorkeeper
|
|
3
3
|
self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}".to_sym
|
4
4
|
|
5
5
|
include AccessGrantMixin
|
6
|
+
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
7
|
+
|
8
|
+
belongs_to_options = {
|
9
|
+
class_name: 'Doorkeeper::Application',
|
10
|
+
inverse_of: :access_grants
|
11
|
+
}
|
12
|
+
|
13
|
+
if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
|
14
|
+
belongs_to_options[:optional] = true
|
15
|
+
end
|
16
|
+
|
17
|
+
belongs_to :application, belongs_to_options
|
18
|
+
|
19
|
+
validates :resource_owner_id, :application_id, :token, :expires_in, :redirect_uri, presence: true
|
20
|
+
validates :token, uniqueness: true
|
21
|
+
|
22
|
+
before_validation :generate_token, on: :create
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Generates token value with UniqueToken class.
|
27
|
+
#
|
28
|
+
# @return [String] token value
|
29
|
+
#
|
30
|
+
def generate_token
|
31
|
+
self.token = UniqueToken.generate
|
32
|
+
end
|
6
33
|
end
|
7
34
|
end
|
@@ -3,19 +3,45 @@ module Doorkeeper
|
|
3
3
|
self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}".to_sym
|
4
4
|
|
5
5
|
include AccessTokenMixin
|
6
|
+
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
belongs_to_options = {
|
9
|
+
class_name: 'Doorkeeper::Application',
|
10
|
+
inverse_of: :access_tokens
|
11
|
+
}
|
12
|
+
|
13
|
+
if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
|
14
|
+
belongs_to_options[:optional] = true
|
10
15
|
end
|
11
|
-
private_class_method :delete_all_for
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
belongs_to :application, belongs_to_options
|
18
|
+
|
19
|
+
validates :token, presence: true, uniqueness: true
|
20
|
+
validates :refresh_token, uniqueness: true, if: :use_refresh_token?
|
21
|
+
|
22
|
+
# @attr_writer [Boolean, nil] use_refresh_token
|
23
|
+
# indicates the possibility of using refresh token
|
24
|
+
attr_writer :use_refresh_token
|
25
|
+
|
26
|
+
before_validation :generate_token, on: :create
|
27
|
+
before_validation :generate_refresh_token,
|
28
|
+
on: :create, if: :use_refresh_token?
|
29
|
+
|
30
|
+
# Searches for not revoked Access Tokens associated with the
|
31
|
+
# specific Resource Owner.
|
32
|
+
#
|
33
|
+
# @param resource_owner [ActiveRecord::Base]
|
34
|
+
# Resource Owner model instance
|
35
|
+
#
|
36
|
+
# @return [ActiveRecord::Relation]
|
37
|
+
# active Access Tokens for Resource Owner
|
38
|
+
#
|
39
|
+
def self.active_for(resource_owner)
|
40
|
+
where(resource_owner_id: resource_owner.id, revoked_at: nil)
|
15
41
|
end
|
16
42
|
|
17
|
-
def self.
|
18
|
-
'
|
43
|
+
def self.refresh_token_revoked_on_use?
|
44
|
+
column_names.include?('previous_refresh_token')
|
19
45
|
end
|
20
46
|
end
|
21
47
|
end
|
@@ -3,22 +3,59 @@ module Doorkeeper
|
|
3
3
|
self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}".to_sym
|
4
4
|
|
5
5
|
include ApplicationMixin
|
6
|
+
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
has_many :access_grants, dependent: :delete_all, class_name: 'Doorkeeper::AccessGrant'
|
9
|
+
has_many :access_tokens, dependent: :delete_all, class_name: 'Doorkeeper::AccessToken'
|
10
|
+
|
11
|
+
validates :name, :secret, :uid, presence: true
|
12
|
+
validates :uid, uniqueness: true
|
13
|
+
validates :redirect_uri, redirect_uri: true
|
14
|
+
validates :confidential, inclusion: { in: [true, false] }
|
15
|
+
|
16
|
+
before_validation :generate_uid, :generate_secret, on: :create
|
17
|
+
|
18
|
+
has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: 'AccessToken'
|
12
19
|
has_many :authorized_applications, through: :authorized_tokens, source: :application
|
13
20
|
|
14
|
-
|
15
|
-
|
21
|
+
# Returns Applications associated with active (not revoked) Access Tokens
|
22
|
+
# that are owned by the specific Resource Owner.
|
23
|
+
#
|
24
|
+
# @param resource_owner [ActiveRecord::Base]
|
25
|
+
# Resource Owner model instance
|
26
|
+
#
|
27
|
+
# @return [ActiveRecord::Relation]
|
28
|
+
# Applications authorized for the Resource Owner
|
29
|
+
#
|
30
|
+
def self.authorized_for(resource_owner)
|
31
|
+
resource_access_tokens = AccessToken.active_for(resource_owner)
|
32
|
+
where(id: resource_access_tokens.select(:application_id).distinct)
|
16
33
|
end
|
17
34
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
35
|
+
# Fallback to existing, default behaviour of assuming all apps to be
|
36
|
+
# confidential if the migration hasn't been run
|
37
|
+
def confidential
|
38
|
+
return super if self.class.supports_confidentiality?
|
39
|
+
ActiveSupport::Deprecation.warn 'You are susceptible to security bug ' \
|
40
|
+
'CVE-2018-1000211. Please follow instructions outlined in ' \
|
41
|
+
'Doorkeeper::CVE_2018_1000211_WARNING'
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
alias_method :confidential?, :confidential
|
46
|
+
|
47
|
+
def self.supports_confidentiality?
|
48
|
+
column_names.include?('confidential')
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def generate_uid
|
54
|
+
self.uid = UniqueToken.generate if uid.blank?
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_secret
|
58
|
+
self.secret = UniqueToken.generate if secret.blank?
|
22
59
|
end
|
23
60
|
end
|
24
61
|
end
|
@@ -1,38 +1,33 @@
|
|
1
|
+
require 'active_support/lazy_load_hooks'
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module Orm
|
3
5
|
module ActiveRecord
|
4
6
|
def self.initialize_models!
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
lazy_load do
|
8
|
+
require 'doorkeeper/orm/active_record/access_grant'
|
9
|
+
require 'doorkeeper/orm/active_record/access_token'
|
10
|
+
require 'doorkeeper/orm/active_record/application'
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
if Doorkeeper.configuration.active_record_options[:establish_connection]
|
13
|
+
[Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application].each do |model|
|
14
|
+
options = Doorkeeper.configuration.active_record_options[:establish_connection]
|
15
|
+
model.establish_connection(options)
|
16
|
+
end
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
21
|
def self.initialize_application_owner!
|
17
|
-
|
22
|
+
lazy_load do
|
23
|
+
require 'doorkeeper/models/concerns/ownership'
|
18
24
|
|
19
|
-
|
25
|
+
Doorkeeper::Application.send :include, Doorkeeper::Models::Ownership
|
26
|
+
end
|
20
27
|
end
|
21
28
|
|
22
|
-
def self.
|
23
|
-
|
24
|
-
::ActiveRecord::Base.connection.table_exists?(
|
25
|
-
Doorkeeper::Application.table_name
|
26
|
-
)
|
27
|
-
unless Doorkeeper::Application.new.attributes.include?("scopes")
|
28
|
-
migration_path = '../../../generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb'
|
29
|
-
puts <<-MSG.squish
|
30
|
-
[doorkeeper] Missing column: `oauth_applications.scopes`.
|
31
|
-
Create the following migration and run `rake db:migrate`.
|
32
|
-
MSG
|
33
|
-
puts File.read(File.expand_path(migration_path, __FILE__))
|
34
|
-
end
|
35
|
-
end
|
29
|
+
def self.lazy_load(&block)
|
30
|
+
ActiveSupport.on_load(:active_record, {}, &block)
|
36
31
|
end
|
37
32
|
end
|
38
33
|
end
|
@@ -1,21 +1,17 @@
|
|
1
1
|
module Doorkeeper
|
2
2
|
module Rails
|
3
3
|
module Helpers
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
4
|
def doorkeeper_authorize!(*scopes)
|
7
5
|
@_doorkeeper_scopes = scopes.presence || Doorkeeper.configuration.default_scopes
|
8
6
|
|
9
|
-
|
7
|
+
unless valid_doorkeeper_token?
|
10
8
|
doorkeeper_render_error
|
11
9
|
end
|
12
10
|
end
|
13
11
|
|
14
|
-
def doorkeeper_unauthorized_render_options(
|
15
|
-
end
|
12
|
+
def doorkeeper_unauthorized_render_options(**); end
|
16
13
|
|
17
|
-
def doorkeeper_forbidden_render_options(
|
18
|
-
end
|
14
|
+
def doorkeeper_forbidden_render_options(**); end
|
19
15
|
|
20
16
|
def valid_doorkeeper_token?
|
21
17
|
doorkeeper_token && doorkeeper_token.acceptable?(@_doorkeeper_scopes)
|
@@ -25,14 +21,15 @@ module Doorkeeper
|
|
25
21
|
|
26
22
|
def doorkeeper_render_error
|
27
23
|
error = doorkeeper_error
|
28
|
-
headers.merge!
|
24
|
+
headers.merge!(error.headers.reject { |k| k == "Content-Type" })
|
29
25
|
doorkeeper_render_error_with(error)
|
30
26
|
end
|
31
27
|
|
32
28
|
def doorkeeper_render_error_with(error)
|
33
29
|
options = doorkeeper_render_options(error) || {}
|
34
30
|
status = doorkeeper_status_for_error(
|
35
|
-
error, options.delete(:respond_not_found_when_forbidden)
|
31
|
+
error, options.delete(:respond_not_found_when_forbidden)
|
32
|
+
)
|
36
33
|
if options.blank?
|
37
34
|
head status
|
38
35
|
else
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Doorkeeper
|
2
2
|
module Rails
|
3
|
-
class Routes
|
3
|
+
class Routes # :nodoc:
|
4
4
|
class Mapper
|
5
|
-
def initialize
|
6
|
-
@mapping =
|
5
|
+
def initialize
|
6
|
+
@mapping = Mapping.new
|
7
7
|
end
|
8
8
|
|
9
9
|
def map(&block)
|
10
|
-
|
10
|
+
instance_eval(&block) if block
|
11
11
|
@mapping
|
12
12
|
end
|
13
13
|
|