grape_oauth2 0.1.1

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +18 -0
  5. data/.travis.yml +42 -0
  6. data/Gemfile +23 -0
  7. data/README.md +820 -0
  8. data/Rakefile +11 -0
  9. data/gemfiles/active_record.rb +25 -0
  10. data/gemfiles/mongoid.rb +14 -0
  11. data/gemfiles/sequel.rb +24 -0
  12. data/grape_oauth2.gemspec +27 -0
  13. data/grape_oauth2.png +0 -0
  14. data/lib/grape_oauth2.rb +129 -0
  15. data/lib/grape_oauth2/configuration.rb +143 -0
  16. data/lib/grape_oauth2/configuration/class_accessors.rb +36 -0
  17. data/lib/grape_oauth2/configuration/validation.rb +71 -0
  18. data/lib/grape_oauth2/endpoints/authorize.rb +34 -0
  19. data/lib/grape_oauth2/endpoints/token.rb +72 -0
  20. data/lib/grape_oauth2/gem_version.rb +24 -0
  21. data/lib/grape_oauth2/generators/authorization.rb +44 -0
  22. data/lib/grape_oauth2/generators/base.rb +26 -0
  23. data/lib/grape_oauth2/generators/token.rb +62 -0
  24. data/lib/grape_oauth2/helpers/access_token_helpers.rb +54 -0
  25. data/lib/grape_oauth2/helpers/oauth_params.rb +41 -0
  26. data/lib/grape_oauth2/mixins/active_record/access_grant.rb +47 -0
  27. data/lib/grape_oauth2/mixins/active_record/access_token.rb +75 -0
  28. data/lib/grape_oauth2/mixins/active_record/client.rb +35 -0
  29. data/lib/grape_oauth2/mixins/mongoid/access_grant.rb +58 -0
  30. data/lib/grape_oauth2/mixins/mongoid/access_token.rb +88 -0
  31. data/lib/grape_oauth2/mixins/mongoid/client.rb +41 -0
  32. data/lib/grape_oauth2/mixins/sequel/access_grant.rb +68 -0
  33. data/lib/grape_oauth2/mixins/sequel/access_token.rb +86 -0
  34. data/lib/grape_oauth2/mixins/sequel/client.rb +46 -0
  35. data/lib/grape_oauth2/responses/authorization.rb +10 -0
  36. data/lib/grape_oauth2/responses/base.rb +56 -0
  37. data/lib/grape_oauth2/responses/token.rb +10 -0
  38. data/lib/grape_oauth2/scopes.rb +74 -0
  39. data/lib/grape_oauth2/strategies/authorization_code.rb +38 -0
  40. data/lib/grape_oauth2/strategies/base.rb +47 -0
  41. data/lib/grape_oauth2/strategies/client_credentials.rb +20 -0
  42. data/lib/grape_oauth2/strategies/password.rb +22 -0
  43. data/lib/grape_oauth2/strategies/refresh_token.rb +47 -0
  44. data/lib/grape_oauth2/unique_token.rb +20 -0
  45. data/lib/grape_oauth2/version.rb +14 -0
  46. data/spec/configuration/config_spec.rb +231 -0
  47. data/spec/configuration/version_spec.rb +12 -0
  48. data/spec/dummy/endpoints/custom_authorization.rb +25 -0
  49. data/spec/dummy/endpoints/custom_token.rb +35 -0
  50. data/spec/dummy/endpoints/status.rb +25 -0
  51. data/spec/dummy/grape_oauth2_config.rb +11 -0
  52. data/spec/dummy/orm/active_record/app/config/db.rb +7 -0
  53. data/spec/dummy/orm/active_record/app/models/access_code.rb +3 -0
  54. data/spec/dummy/orm/active_record/app/models/access_token.rb +3 -0
  55. data/spec/dummy/orm/active_record/app/models/application.rb +3 -0
  56. data/spec/dummy/orm/active_record/app/models/application_record.rb +3 -0
  57. data/spec/dummy/orm/active_record/app/models/user.rb +10 -0
  58. data/spec/dummy/orm/active_record/app/twitter.rb +36 -0
  59. data/spec/dummy/orm/active_record/config.ru +7 -0
  60. data/spec/dummy/orm/active_record/db/schema.rb +53 -0
  61. data/spec/dummy/orm/mongoid/app/config/db.rb +6 -0
  62. data/spec/dummy/orm/mongoid/app/config/mongoid.yml +21 -0
  63. data/spec/dummy/orm/mongoid/app/models/access_code.rb +3 -0
  64. data/spec/dummy/orm/mongoid/app/models/access_token.rb +3 -0
  65. data/spec/dummy/orm/mongoid/app/models/application.rb +3 -0
  66. data/spec/dummy/orm/mongoid/app/models/user.rb +11 -0
  67. data/spec/dummy/orm/mongoid/app/twitter.rb +34 -0
  68. data/spec/dummy/orm/mongoid/config.ru +5 -0
  69. data/spec/dummy/orm/sequel/app/config/db.rb +1 -0
  70. data/spec/dummy/orm/sequel/app/models/access_code.rb +4 -0
  71. data/spec/dummy/orm/sequel/app/models/access_token.rb +4 -0
  72. data/spec/dummy/orm/sequel/app/models/application.rb +4 -0
  73. data/spec/dummy/orm/sequel/app/models/application_record.rb +2 -0
  74. data/spec/dummy/orm/sequel/app/models/user.rb +11 -0
  75. data/spec/dummy/orm/sequel/app/twitter.rb +47 -0
  76. data/spec/dummy/orm/sequel/config.ru +5 -0
  77. data/spec/dummy/orm/sequel/db/schema.rb +50 -0
  78. data/spec/lib/scopes_spec.rb +50 -0
  79. data/spec/mixins/active_record/access_token_spec.rb +185 -0
  80. data/spec/mixins/active_record/client_spec.rb +95 -0
  81. data/spec/mixins/mongoid/access_token_spec.rb +185 -0
  82. data/spec/mixins/mongoid/client_spec.rb +95 -0
  83. data/spec/mixins/sequel/access_token_spec.rb +185 -0
  84. data/spec/mixins/sequel/client_spec.rb +96 -0
  85. data/spec/requests/flows/authorization_code_spec.rb +67 -0
  86. data/spec/requests/flows/client_credentials_spec.rb +101 -0
  87. data/spec/requests/flows/password_spec.rb +210 -0
  88. data/spec/requests/flows/refresh_token_spec.rb +222 -0
  89. data/spec/requests/flows/revoke_token_spec.rb +103 -0
  90. data/spec/requests/protected_resources_spec.rb +64 -0
  91. data/spec/spec_helper.rb +60 -0
  92. data/spec/support/api_helper.rb +11 -0
  93. metadata +257 -0
@@ -0,0 +1,35 @@
1
+ module Grape
2
+ module OAuth2
3
+ module ActiveRecord
4
+ # Grape::OAuth2 Client role mixin for ActiveRecord.
5
+ # Includes all the required API, associations, validations and callbacks.
6
+ module Client
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ has_many :access_tokens, class_name: Grape::OAuth2.config.access_token_class_name, foreign_key: :client_id
11
+
12
+ validates :key, :secret, presence: true
13
+ validates :key, uniqueness: true
14
+
15
+ before_validation :generate_keys, on: :create
16
+
17
+ def self.authenticate(key, secret = nil)
18
+ if secret.nil?
19
+ find_by(key: key)
20
+ else
21
+ find_by(key: key, secret: secret)
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def generate_keys
28
+ self.key = Grape::OAuth2::UniqueToken.generate if key.blank?
29
+ self.secret = Grape::OAuth2::UniqueToken.generate if secret.blank?
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,58 @@
1
+ module Grape
2
+ module OAuth2
3
+ module Mongoid
4
+ # Grape::OAuth2 Authorization Grant role mixin for Mongoid ORM.
5
+ # Includes all the required API, associations, validations and callbacks.
6
+ module AccessGrant
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ include ::Mongoid::Document
11
+ include ::Mongoid::Timestamps
12
+
13
+ field :resource_owner_id, type: BSON::ObjectId
14
+ field :client_id, type: BSON::ObjectId
15
+
16
+ field :token, type: String
17
+ field :scopes, type: String
18
+ field :redirect_uri, type: String
19
+
20
+ field :expires_at, type: DateTime
21
+
22
+ belongs_to :client, class_name: Grape::OAuth2.config.client_class_name,
23
+ foreign_key: :client_id
24
+
25
+ belongs_to :resource_owner, class_name: Grape::OAuth2.config.resource_owner_class_name,
26
+ foreign_key: :resource_owner_id, optional: true # required!
27
+
28
+ before_validation :generate_token, on: :create
29
+ before_validation :setup_expiration, on: :create
30
+
31
+ index({ token: 1 }, unique: true)
32
+ index({ refresh_token: 1 }, unique: true, sparse: true)
33
+
34
+ class << self
35
+ def create_for(client, resource_owner, redirect_uri, scopes = nil)
36
+ create(
37
+ client_id: client.id,
38
+ resource_owner_id: resource_owner && resource_owner.id,
39
+ redirect_uri: redirect_uri,
40
+ scopes: scopes.to_s
41
+ )
42
+ end
43
+ end
44
+
45
+ protected
46
+
47
+ def generate_token
48
+ self.token = Grape::OAuth2.config.token_generator.generate(attributes)
49
+ end
50
+
51
+ def setup_expiration
52
+ self.expires_at = Time.now.utc + Grape::OAuth2.config.authorization_code_lifetime if expires_at.nil?
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,88 @@
1
+ module Grape
2
+ module OAuth2
3
+ module Mongoid
4
+ # Grape::OAuth2 Access Token role mixin for Mongoid ORM.
5
+ # Includes all the required API, associations, validations and callbacks.
6
+ module AccessToken
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ include ::Mongoid::Document
11
+ include ::Mongoid::Timestamps
12
+
13
+ field :resource_owner_id, type: BSON::ObjectId
14
+ field :client_id, type: BSON::ObjectId
15
+
16
+ belongs_to :client, class_name: 'Application', foreign_key: :client_id, optional: true
17
+ belongs_to :resource_owner, class_name: 'User', foreign_key: :resource_owner_id, optional: true
18
+
19
+ field :token, type: String
20
+ field :refresh_token, type: String
21
+ field :scopes, type: String
22
+
23
+ field :expires_at, type: DateTime
24
+ field :revoked_at, type: DateTime
25
+
26
+ index({ token: 1 }, unique: true)
27
+ index({ refresh_token: 1 }, unique: true, sparse: true)
28
+
29
+ before_validation :setup_expiration, on: :create
30
+ before_validation :generate_tokens, on: :create
31
+
32
+ validates :token, presence: true, uniqueness: true
33
+
34
+ class << self
35
+ def create_for(client, resource_owner, scopes = nil)
36
+ create(
37
+ client: client,
38
+ resource_owner: resource_owner,
39
+ scopes: scopes.to_s
40
+ )
41
+ end
42
+
43
+ def authenticate(token, type: :access_token)
44
+ if type && type.to_sym == :refresh_token
45
+ find_by(refresh_token: token.to_s)
46
+ else
47
+ find_by(token: token.to_s)
48
+ end
49
+ end
50
+ end
51
+
52
+ def expired?
53
+ !expires_at.nil? && Time.now.utc > expires_at
54
+ end
55
+
56
+ def revoked?
57
+ !revoked_at.nil? && revoked_at <= Time.now.utc
58
+ end
59
+
60
+ def revoke!(revoked_at = Time.now)
61
+ update_attribute :revoked_at, revoked_at.utc
62
+ end
63
+
64
+ def to_bearer_token
65
+ {
66
+ access_token: token,
67
+ expires_in: expires_at && Grape::OAuth2.config.access_token_lifetime.to_i,
68
+ refresh_token: refresh_token,
69
+ scope: scopes
70
+ }
71
+ end
72
+
73
+ protected
74
+
75
+ def generate_tokens
76
+ self.token = Grape::OAuth2.config.token_generator.generate(attributes) if token.blank?
77
+ self.refresh_token = Grape::OAuth2::UniqueToken.generate if Grape::OAuth2.config.issue_refresh_token
78
+ end
79
+
80
+ def setup_expiration
81
+ expires_in = Grape::OAuth2.config.access_token_lifetime
82
+ self.expires_at = Time.now + expires_in if expires_at.nil? && !expires_in.nil?
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,41 @@
1
+ module Grape
2
+ module OAuth2
3
+ module Mongoid
4
+ # Grape::OAuth2 Client role mixin for Mongoid ORM.
5
+ # Includes all the required API, associations, validations and callbacks.
6
+ module Client
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ include ::Mongoid::Document
11
+ include ::Mongoid::Timestamps
12
+
13
+ field :name, type: String
14
+ field :key, type: String
15
+ field :secret, type: String
16
+ field :redirect_uri, type: String
17
+
18
+ before_validation :generate_keys, on: :create
19
+
20
+ validates :key, :secret, presence: true
21
+ validates :key, uniqueness: true
22
+
23
+ def self.authenticate(key, secret = nil)
24
+ if secret.nil?
25
+ Application.find_by(key: key)
26
+ else
27
+ Application.find_by(key: key, secret: secret)
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ def generate_keys
34
+ self.key = Grape::OAuth2::UniqueToken.generate if key.blank?
35
+ self.secret = Grape::OAuth2::UniqueToken.generate if secret.blank?
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,68 @@
1
+ module Grape
2
+ module OAuth2
3
+ module Sequel
4
+ # Grape::OAuth2 Authorization Grant role mixin for Sequel toolkit.
5
+ # Includes all the required API, associations, validations and callbacks.
6
+ module AccessGrant
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ plugin :validation_helpers
11
+ plugin :timestamps
12
+
13
+ many_to_one :client, class: Grape::OAuth2.config.client_class_name, key: :client_id
14
+ many_to_one :resource_owner, class: Grape::OAuth2.config.resource_owner_class_name, key: :resource_owner_id
15
+
16
+ def before_validation
17
+ if new?
18
+ generate_token
19
+ setup_expiration
20
+ end
21
+
22
+ super
23
+ end
24
+
25
+ class << self
26
+ def create_for(client, resource_owner, redirect_uri, scopes = nil)
27
+ create(
28
+ client_id: client.id,
29
+ resource_owner_id: resource_owner && resource_owner.id,
30
+ redirect_uri: redirect_uri,
31
+ scopes: scopes.to_s
32
+ )
33
+ end
34
+ end
35
+
36
+ def validate
37
+ super
38
+ validates_presence [:token, :client_id]
39
+ validates_unique [:token]
40
+ end
41
+
42
+ def expired?
43
+ expires_at && Time.now.utc > expires_at
44
+ end
45
+
46
+ def revoked?
47
+ revoked_at && revoked_at <= Time.now.utc
48
+ end
49
+
50
+ def revoke!(revoked_at = Time.now)
51
+ set(revoked_at: revoked_at.utc)
52
+ save(columns: [:revoked_at], validate: false)
53
+ end
54
+
55
+ protected
56
+
57
+ def generate_token
58
+ self.token = Grape::OAuth2.config.token_generator.generate(values)
59
+ end
60
+
61
+ def setup_expiration
62
+ self.expires_at = Time.now.utc + Grape::OAuth2.config.authorization_code_lifetime if expires_at.nil?
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,86 @@
1
+ module Grape
2
+ module OAuth2
3
+ module Sequel
4
+ # Grape::OAuth2 Access Token role mixin for Sequel toolkit.
5
+ # Includes all the required API, associations, validations and callbacks.
6
+ module AccessToken
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ plugin :validation_helpers
11
+ plugin :timestamps
12
+
13
+ many_to_one :client, class: Grape::OAuth2.config.client_class_name, key: :client_id
14
+ many_to_one :resource_owner, class: Grape::OAuth2.config.resource_owner_class_name, key: :resource_owner_id
15
+
16
+ def before_validation
17
+ if new?
18
+ setup_expiration
19
+ generate_tokens
20
+ end
21
+
22
+ super
23
+ end
24
+
25
+ def validate
26
+ super
27
+ validates_presence :token
28
+ validates_unique :token
29
+ end
30
+
31
+ class << self
32
+ def create_for(client, resource_owner, scopes = nil)
33
+ create(
34
+ client: client,
35
+ resource_owner: resource_owner,
36
+ scopes: scopes.to_s
37
+ )
38
+ end
39
+
40
+ def authenticate(token, type: :access_token)
41
+ if type && type.to_sym == :refresh_token
42
+ first(refresh_token: token.to_s)
43
+ else
44
+ first(token: token.to_s)
45
+ end
46
+ end
47
+ end
48
+
49
+ def expired?
50
+ !expires_at.nil? && Time.now.utc > expires_at.utc
51
+ end
52
+
53
+ def revoked?
54
+ !revoked_at.nil? && revoked_at <= Time.now.utc
55
+ end
56
+
57
+ def revoke!(revoked_at = Time.now)
58
+ set(revoked_at: revoked_at.utc)
59
+ save(columns: [:revoked_at], validate: false)
60
+ end
61
+
62
+ def to_bearer_token
63
+ {
64
+ access_token: token,
65
+ expires_in: expires_at && Grape::OAuth2.config.access_token_lifetime.to_i,
66
+ refresh_token: refresh_token,
67
+ scope: scopes
68
+ }
69
+ end
70
+
71
+ protected
72
+
73
+ def generate_tokens
74
+ self.token = Grape::OAuth2.config.token_generator.generate(values) if token.blank?
75
+ self.refresh_token = Grape::OAuth2::UniqueToken.generate if Grape::OAuth2.config.issue_refresh_token
76
+ end
77
+
78
+ def setup_expiration
79
+ expires_in = Grape::OAuth2.config.access_token_lifetime
80
+ self.expires_at = Time.now + expires_in if expires_at.nil? && !expires_in.nil?
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,46 @@
1
+ module Grape
2
+ module OAuth2
3
+ module Sequel
4
+ # Grape::OAuth2 Client role mixin for Sequel toolkit.
5
+ # Includes all the required API, associations, validations and callbacks.
6
+ module Client
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ plugin :validation_helpers
11
+ plugin :timestamps
12
+
13
+ set_allowed_columns :name, :redirect_uri
14
+
15
+ one_to_many :access_tokens, class: Grape::OAuth2.config.access_token_class_name, key: :client_id
16
+
17
+ def before_validation
18
+ generate_keys if new?
19
+ super
20
+ end
21
+
22
+ def validate
23
+ super
24
+ validates_presence [:key, :secret]
25
+ validates_unique :key
26
+ end
27
+
28
+ def self.authenticate(key, secret = nil)
29
+ if secret.nil?
30
+ find(key: key)
31
+ else
32
+ find(key: key, secret: secret)
33
+ end
34
+ end
35
+
36
+ protected
37
+
38
+ def generate_keys
39
+ self.key = Grape::OAuth2::UniqueToken.generate if key.blank?
40
+ self.secret = Grape::OAuth2::UniqueToken.generate if secret.blank?
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ module Grape
2
+ module OAuth2
3
+ # Grape::OAuth2 responses namespace.
4
+ module Responses
5
+ # Authorization response.
6
+ class Authorization < Base
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,56 @@
1
+ module Grape
2
+ module OAuth2
3
+ # Grape::OAuth2 responses namespace.
4
+ module Responses
5
+ # Base class for Grape::OAuth2 endpoints responses.
6
+ # Processes raw Rack Responses and contains helper methods.
7
+ class Base
8
+ # Raw Rack::Response to process
9
+ #
10
+ # @return [Array] Rack response
11
+ #
12
+ # @example
13
+ # response = Grape::OAuth2::Responses::Base.new([200, {}, Rack::BodyProxy.new('Test')])
14
+ # response.rack_response
15
+ #
16
+ # #=> [200, {}, Rack::BodyProxy.new('Test')]
17
+ #
18
+ attr_reader :rack_response
19
+
20
+ # OAuth2 response class.
21
+ #
22
+ # @param rack_response [Array]
23
+ # raw Rack::Response object
24
+ #
25
+ def initialize(rack_response)
26
+ # Rack Body:
27
+ # [Status Code, Headers, Body]
28
+ @rack_response = rack_response
29
+ end
30
+
31
+ # Response status
32
+ def status
33
+ @rack_response[0]
34
+ end
35
+
36
+ # Response headers
37
+ def headers
38
+ @rack_response[1]
39
+ end
40
+
41
+ # Raw Rack body
42
+ def raw_body
43
+ @rack_response[2].body
44
+ end
45
+
46
+ # JSON-parsed body
47
+ def body
48
+ response_body = raw_body.first
49
+ return {} if response_body.nil? || response_body.empty?
50
+
51
+ JSON.parse(response_body)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end