simple_oauth2 0.0.0

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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +25 -0
  3. data/.coveralls.yml +1 -0
  4. data/.gitignore +26 -0
  5. data/.hound.yml +4 -0
  6. data/.rubocop.yml +5 -0
  7. data/.rubocop_todo.yml +12 -0
  8. data/.travis.yml +31 -0
  9. data/Gemfile +18 -0
  10. data/LICENSE +21 -0
  11. data/README.md +11 -0
  12. data/Rakefile +11 -0
  13. data/gemfiles/nobrainer.rb +15 -0
  14. data/lib/simple_oauth2/configuration/class_accessors.rb +36 -0
  15. data/lib/simple_oauth2/configuration/constants.rb +36 -0
  16. data/lib/simple_oauth2/configuration.rb +169 -0
  17. data/lib/simple_oauth2/generators/authorization.rb +64 -0
  18. data/lib/simple_oauth2/generators/base.rb +31 -0
  19. data/lib/simple_oauth2/generators/token.rb +71 -0
  20. data/lib/simple_oauth2/helpers.rb +40 -0
  21. data/lib/simple_oauth2/mixins/nobrainer/access_grant.rb +62 -0
  22. data/lib/simple_oauth2/mixins/nobrainer/access_token.rb +98 -0
  23. data/lib/simple_oauth2/mixins/nobrainer/client.rb +43 -0
  24. data/lib/simple_oauth2/resource/bearer.rb +20 -0
  25. data/lib/simple_oauth2/responses.rb +62 -0
  26. data/lib/simple_oauth2/scopes.rb +59 -0
  27. data/lib/simple_oauth2/strategies/authorization_code.rb +22 -0
  28. data/lib/simple_oauth2/strategies/base.rb +61 -0
  29. data/lib/simple_oauth2/strategies/client_credentials.rb +21 -0
  30. data/lib/simple_oauth2/strategies/code.rb +25 -0
  31. data/lib/simple_oauth2/strategies/password.rb +21 -0
  32. data/lib/simple_oauth2/strategies/refresh_token.rb +53 -0
  33. data/lib/simple_oauth2/strategies/token.rb +24 -0
  34. data/lib/simple_oauth2/uniq_token.rb +20 -0
  35. data/lib/simple_oauth2/version.rb +26 -0
  36. data/lib/simple_oauth2.rb +62 -0
  37. data/logo.png +0 -0
  38. data/simple_oauth2.gemspec +22 -0
  39. data/spec/configuration/config_spec.rb +181 -0
  40. data/spec/configuration/version_spec.rb +11 -0
  41. data/spec/dummy/endpoints/authorization.rb +15 -0
  42. data/spec/dummy/endpoints/custom_authorization.rb +21 -0
  43. data/spec/dummy/endpoints/custom_token.rb +21 -0
  44. data/spec/dummy/endpoints/status.rb +51 -0
  45. data/spec/dummy/endpoints/token.rb +22 -0
  46. data/spec/dummy/orm/nobrainer/app/config/db.rb +8 -0
  47. data/spec/dummy/orm/nobrainer/app/models/access_grant.rb +3 -0
  48. data/spec/dummy/orm/nobrainer/app/models/access_token.rb +3 -0
  49. data/spec/dummy/orm/nobrainer/app/models/client.rb +3 -0
  50. data/spec/dummy/orm/nobrainer/app/models/user.rb +11 -0
  51. data/spec/dummy/orm/nobrainer/app/twitter.rb +51 -0
  52. data/spec/dummy/orm/nobrainer/config.ru +37 -0
  53. data/spec/dummy/simple_oauth2_config.rb +7 -0
  54. data/spec/requests/flows/authorization_code_spec.rb +177 -0
  55. data/spec/requests/flows/client_credentials_spec.rb +163 -0
  56. data/spec/requests/flows/code_spec.rb +98 -0
  57. data/spec/requests/flows/password_spec.rb +183 -0
  58. data/spec/requests/flows/refresh_token_spec.rb +282 -0
  59. data/spec/requests/flows/token_spec.rb +113 -0
  60. data/spec/requests/protected_resources_spec.rb +65 -0
  61. data/spec/requests/revoke_token_spec.rb +90 -0
  62. data/spec/spec_helper.rb +51 -0
  63. data/spec/support/helper.rb +11 -0
  64. metadata +125 -0
@@ -0,0 +1,62 @@
1
+ require 'rack/oauth2'
2
+
3
+ require 'simple_oauth2/configuration/class_accessors'
4
+ require 'simple_oauth2/configuration/constants'
5
+ require 'simple_oauth2/configuration'
6
+ require 'simple_oauth2/scopes'
7
+ require 'simple_oauth2/uniq_token'
8
+ require 'simple_oauth2/resource/bearer'
9
+
10
+ # Mixins
11
+ if defined?(NoBrainer::Document)
12
+ require 'simple_oauth2/mixins/nobrainer/access_token'
13
+ require 'simple_oauth2/mixins/nobrainer/access_grant'
14
+ require 'simple_oauth2/mixins/nobrainer/client'
15
+ end
16
+
17
+ # Authorization Grants aka Flows (Strategies)
18
+ require 'simple_oauth2/strategies/base'
19
+ require 'simple_oauth2/strategies/password'
20
+ require 'simple_oauth2/strategies/authorization_code'
21
+ require 'simple_oauth2/strategies/refresh_token'
22
+ require 'simple_oauth2/strategies/code'
23
+ require 'simple_oauth2/strategies/token'
24
+ require 'simple_oauth2/strategies/client_credentials'
25
+
26
+ # Generators
27
+ require 'simple_oauth2/generators/base'
28
+ require 'simple_oauth2/generators/token'
29
+ require 'simple_oauth2/generators/authorization'
30
+
31
+ # Helpers
32
+ require 'simple_oauth2/helpers'
33
+
34
+ # Responses
35
+ require 'simple_oauth2/responses'
36
+
37
+ # Simple namespace for the gem
38
+ module Simple
39
+ # Main Simple::OAuth2 module
40
+ module OAuth2
41
+ class << self
42
+ # Simple::OAuth2 configuration
43
+ #
44
+ # @return [Simple::OAuth2::Configuration] configuration object with default values
45
+ #
46
+ def config
47
+ @config ||= Simple::OAuth2::Configuration.new
48
+ end
49
+
50
+ # Configures Simple::OAuth2.
51
+ # Yields Simple::OAuth2::Configuration instance to the block
52
+ def configure
53
+ yield config if block_given?
54
+ end
55
+
56
+ # Simple::OAuth2 default middleware
57
+ def middleware
58
+ Simple::OAuth2::Resource::Bearer
59
+ end
60
+ end
61
+ end
62
+ end
data/logo.png ADDED
Binary file
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
2
+
3
+ require 'simple_oauth2/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'simple_oauth2'
7
+ s.version = Simple::OAuth2.gem_version
8
+ s.date = '2017-01-03'
9
+ s.summary = 'OAuth2 authorization'
10
+ s.description = 'A flexible OAuth2 server authorization'
11
+ s.authors = ['Volodimir Partytskyi']
12
+ s.email = 'volodimir.partytskyi@gmail.com'
13
+ s.homepage = 'https://github.com/simple-oauth2/simple_oauth2'
14
+ s.license = 'MIT'
15
+
16
+ s.require_paths = %w(lib)
17
+ s.files = `git ls-files`.split($RS)
18
+
19
+ s.required_ruby_version = '>= 2.2.2'
20
+
21
+ s.add_runtime_dependency 'rack-oauth2', '~> 1.3.0', '>= 1.3.0'
22
+ end
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+
3
+ describe Simple::OAuth2::Configuration do
4
+ let(:config) { described_class.new }
5
+
6
+ class CustomClient
7
+ def self.authenticate(_key, _secret = nil)
8
+ 'Test'
9
+ end
10
+ end
11
+
12
+ class CustomAccessToken
13
+ def self.create_for(_client, _resource_owner, _scopes = nil); end
14
+
15
+ def self.authenticate(_token, _type = nil)
16
+ 'Test'
17
+ end
18
+
19
+ def client; end
20
+
21
+ def resource_owner; end
22
+
23
+ def expired?; end
24
+
25
+ def revoked?; end
26
+
27
+ def revoke!(_revoked_at = Time.now); end
28
+
29
+ def to_bearer_token; end
30
+ end
31
+
32
+ class CustomResourceOwner
33
+ def self.oauth_authenticate(_client, _username, _password)
34
+ 'Test'
35
+ end
36
+ end
37
+
38
+ context 'default config' do
39
+ it { expect(config.access_token_lifetime).to eq(7200) }
40
+ it { expect(config.authorization_code_lifetime).to eq(1800) }
41
+
42
+ it { expect(config.realm).to eq(Simple::OAuth2::Configuration::DEFAULT_REALM) }
43
+ it { expect(config.allowed_grant_types).to eq(%w(password authorization_code refresh_token client_credentials)) }
44
+
45
+ it { expect(config.issue_refresh_token).to be_falsey }
46
+ it { expect(config.on_refresh).to eq(:nothing) }
47
+
48
+ it { expect(config.scopes_validator_class_name).to eq(Simple::OAuth2::Scopes.name) }
49
+
50
+ it { expect { config.resource_owner_authenticator.call('test') }.to raise_error(RuntimeError) }
51
+ end
52
+
53
+ context 'custom config' do
54
+ class CustomScopesValidator
55
+ def self.valid?(_access_token, _scopes)
56
+ false
57
+ end
58
+ end
59
+
60
+ class CustomTokenGenerator
61
+ def self.generate(options = {})
62
+ if options[:custom]
63
+ 'custom_token'
64
+ else
65
+ 'default_token'
66
+ end
67
+ end
68
+ end
69
+
70
+ before do
71
+ config.access_token_class_name = 'CustomAccessToken'
72
+ config.resource_owner_class_name = 'CustomResourceOwner'
73
+ config.client_class_name = 'CustomClient'
74
+ config.access_grant_class_name = 'Object'
75
+ config.scopes_validator_class_name = 'CustomScopesValidator'
76
+ end
77
+
78
+ after do
79
+ config.access_token_class_name = 'AccessToken'
80
+ config.resource_owner_class_name = 'ResourceOwner'
81
+ config.client_class_name = 'Client'
82
+ config.access_grant_class_name = 'AccessGrant'
83
+ config.scopes_validator_class_name = 'ScopesValidator'
84
+ end
85
+
86
+ context 'works with custom token generator' do
87
+ before do
88
+ Simple::OAuth2.configure do |c|
89
+ c.token_generator_class_name = 'CustomTokenGenerator'
90
+ end
91
+ end
92
+
93
+ after do
94
+ Simple::OAuth2.configure do |c|
95
+ c.token_generator_class_name = Simple::OAuth2::UniqToken.name
96
+ end
97
+ end
98
+
99
+ it { expect(Simple::OAuth2.config.token_generator.generate).to eq('default_token') }
100
+ it { expect(Simple::OAuth2.config.token_generator.generate(custom: true)).to eq('custom_token') }
101
+ end
102
+
103
+ context 'invokes custom scopes validator' do
104
+ it { expect(config.scopes_validator.valid?(nil, nil)).to be_falsey }
105
+ end
106
+
107
+ context 'works with custom Access Token class' do
108
+ it { expect(config.access_token_class.authenticate('')).to eq('Test') }
109
+ end
110
+
111
+ context 'works with custom Client class' do
112
+ it { expect(config.client_class.authenticate('')).to eq('Test') }
113
+ end
114
+
115
+ context 'works with custom Resource Owner class' do
116
+ it { expect(config.resource_owner_class.oauth_authenticate('', '', '')).to eq('Test') }
117
+ end
118
+
119
+ context 'works with custom token authenticator' do
120
+ let(:request) { AccessToken.create }
121
+
122
+ before do
123
+ Simple::OAuth2.configure do |c|
124
+ c.token_authenticator do
125
+ raise ArgumentError, 'Test'
126
+ end
127
+ end
128
+ end
129
+
130
+ after do
131
+ Simple::OAuth2.configure do |c|
132
+ c.token_authenticator = config.default_token_authenticator
133
+ end
134
+ end
135
+
136
+ it { expect { config.token_authenticator.call }.to raise_error(ArgumentError) }
137
+ end
138
+
139
+ context 'works with custom on_refresh callback' do
140
+ let(:token) { AccessToken.create }
141
+
142
+ before do
143
+ Simple::OAuth2.configure do |c|
144
+ c.on_refresh do |access_token|
145
+ access_token.update(scopes: 'test')
146
+ end
147
+ end
148
+ end
149
+
150
+ after(:all) do
151
+ Simple::OAuth2.configure do |c|
152
+ c.on_refresh = :nothing
153
+ end
154
+ end
155
+
156
+ it do
157
+ expect { Simple::OAuth2::Strategies::RefreshToken.send(:run_callback_on_refresh_token, token) }
158
+ .to change { token.scopes }.to('test')
159
+ end
160
+ end
161
+
162
+ context 'raises an error with invalid on_refresh callback' do
163
+ before do
164
+ Simple::OAuth2.configure do |c|
165
+ c.on_refresh = 'invalid'
166
+ end
167
+ end
168
+
169
+ after(:all) do
170
+ Simple::OAuth2.configure do |c|
171
+ c.on_refresh = :nothing
172
+ end
173
+ end
174
+
175
+ it do
176
+ expect { Simple::OAuth2::Strategies::RefreshToken.send(:run_callback_on_refresh_token, nil) }
177
+ .to raise_error(ArgumentError)
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Simlpe::OAuth2 Version' do
4
+ context 'has a version string' do
5
+ it { expect(Simple::OAuth2::VERSION::STRING).to be_present }
6
+ end
7
+
8
+ context 'returns version as an instance of Gem::Version' do
9
+ it { expect(Simple::OAuth2.gem_version).to be_an_instance_of(Gem::Version) }
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Twitter
2
+ module Endpoints
3
+ module Authorization
4
+ def call(env)
5
+ response = Simple::OAuth2::Generators::Authorization.generate_for(env)
6
+
7
+ status = response.status
8
+ headers = response.headers
9
+ body = JSON.generate(response.body)
10
+
11
+ [status, headers, [body]]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module Twitter
2
+ module Endpoints
3
+ module CustomAuthorization
4
+ def request
5
+ @request
6
+ end
7
+
8
+ def call(env)
9
+ @request = Rack::Request.new(env)
10
+
11
+ response = Simple::OAuth2::Generators::Authorization.generate_for(env, &:unsupported_response_type!)
12
+
13
+ status = response.status
14
+ headers = response.headers
15
+ body = JSON.generate(response.body)
16
+
17
+ [status, headers, [body]]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Twitter
2
+ module Endpoints
3
+ module CustomToken
4
+ def request
5
+ @request
6
+ end
7
+
8
+ def call(env)
9
+ @request = Rack::Request.new(env)
10
+
11
+ response = Simple::OAuth2::Generators::Token.generate_for(env, &:unsupported_grant_type!)
12
+
13
+ status = response.status
14
+ headers = response.headers
15
+ body = JSON.generate(response.body)
16
+
17
+ [status, headers, [body]]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ module Twitter
2
+ module Endpoints
3
+ module StatusSingleScope
4
+ def request
5
+ @request
6
+ end
7
+
8
+ def call(env)
9
+ @request = Rack::Request.new(env)
10
+ @current_resource_owner = nil # Refresh cache instance variable
11
+ @current_access_token = nil # Refresh cache instance variable
12
+
13
+ access_token_required! :read
14
+ body = JSON.dump(value: 'Access read', current_user_name: current_resource_owner.username)
15
+ [200, { 'content-type' => 'application/json' }, [body]]
16
+ end
17
+ end
18
+
19
+ module StatusMultipleScopes
20
+ def request
21
+ @request
22
+ end
23
+
24
+ def call(env)
25
+ @request = Rack::Request.new(env)
26
+ @current_resource_owner = nil # Refresh cache instance variable
27
+ @current_access_token = nil # Refresh cache instance variable
28
+
29
+ access_token_required! :read, :write
30
+ body = JSON.dump(value: 'Access read, write', current_user_name: current_resource_owner.username)
31
+ [200, { 'content-type' => 'application/json' }, [body]]
32
+ end
33
+ end
34
+
35
+ module Status
36
+ def request
37
+ @request
38
+ end
39
+
40
+ def call(env)
41
+ @request = Rack::Request.new(env)
42
+ @current_resource_owner = nil # Refresh cache instance variable
43
+ @current_access_token = nil # Refresh cache instance variable
44
+
45
+ access_token_required!
46
+ body = JSON.dump(value: 'Access', current_user_name: current_resource_owner.username)
47
+ [200, { 'content-type' => 'application/json' }, [body]]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,22 @@
1
+ module Twitter
2
+ module Endpoints
3
+ module Token
4
+ def call(env)
5
+ response = Simple::OAuth2::Generators::Token.generate_for(env)
6
+
7
+ status = response.status
8
+ headers = response.headers
9
+ body = JSON.generate(response.body)
10
+
11
+ [status, headers, [body]]
12
+ end
13
+ end
14
+
15
+ module RevokeToken
16
+ def call(env)
17
+ params = Rack::Request.new(env).params
18
+ Simple::OAuth2::Generators::Token.revoke(params['token'], env)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ NoBrainer::Document::PrimaryKey.__send__(:remove_const, :DEFAULT_PK_NAME)
2
+ NoBrainer::Document::PrimaryKey.__send__(:const_set, :DEFAULT_PK_NAME, :_id_)
3
+
4
+ NoBrainer.configure do |config|
5
+ config.reset!
6
+ config.rethinkdb_url = "rethinkdb://localhost/#{"#{ENV['ORM']}_#{ENV['RAILS_ENV']}"}"
7
+ config.environment = ENV['RAILS_ENV']
8
+ end
@@ -0,0 +1,3 @@
1
+ class AccessGrant
2
+ include Simple::OAuth2::NoBrainer::AccessGrant
3
+ end
@@ -0,0 +1,3 @@
1
+ class AccessToken
2
+ include Simple::OAuth2::NoBrainer::AccessToken
3
+ end
@@ -0,0 +1,3 @@
1
+ class Client
2
+ include Simple::OAuth2::NoBrainer::Client
3
+ end
@@ -0,0 +1,11 @@
1
+ class User
2
+ include NoBrainer::Document
3
+
4
+ field :username, type: String, index: true
5
+ field :encrypted_password, type: String
6
+
7
+ def self.oauth_authenticate(_client, username, password)
8
+ user = where(username: username.to_s).first
9
+ user if user && user.encrypted_password == password
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path('../../../../../../lib/simple_oauth2', __FILE__)
2
+
3
+ require_relative 'models/access_token'
4
+ require_relative 'models/access_grant'
5
+ require_relative 'models/client'
6
+ require_relative 'models/user'
7
+
8
+ require_relative '../../../endpoints/authorization'
9
+ require_relative '../../../endpoints/custom_authorization'
10
+ require_relative '../../../endpoints/token'
11
+ require_relative '../../../endpoints/custom_token'
12
+ require_relative '../../../endpoints/status'
13
+
14
+ load File.expand_path('../config/db.rb', __FILE__)
15
+ load File.expand_path('../../../../simple_oauth2_config.rb', __FILE__)
16
+
17
+ include Simple::OAuth2::Helpers
18
+
19
+ module Twitter
20
+ class Token
21
+ extend Twitter::Endpoints::Token
22
+ end
23
+
24
+ class RevokeToken
25
+ extend Twitter::Endpoints::RevokeToken
26
+ end
27
+
28
+ class CustomToken
29
+ extend Twitter::Endpoints::CustomToken
30
+ end
31
+
32
+ class Authorization
33
+ extend Twitter::Endpoints::Authorization
34
+ end
35
+
36
+ class CustomAuthorization
37
+ extend Twitter::Endpoints::CustomAuthorization
38
+ end
39
+
40
+ class Status
41
+ extend Twitter::Endpoints::Status
42
+ end
43
+
44
+ class StatusSingleScope
45
+ extend Twitter::Endpoints::StatusSingleScope
46
+ end
47
+
48
+ class StatusMultipleScopes
49
+ extend Twitter::Endpoints::StatusMultipleScopes
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ require 'app/twitter'
4
+
5
+ use Simple::OAuth2.middleware
6
+
7
+ map '/oauth/token' do
8
+ run Twitter::Token
9
+ end
10
+
11
+ map '/oauth/revoke_token' do
12
+ run Twitter::RevokeToken
13
+ end
14
+
15
+ map '/oauth/authorization' do
16
+ run Twitter::Authorization
17
+ end
18
+
19
+ map '/oauth/custom_token' do
20
+ run Twitter::CustomToken
21
+ end
22
+
23
+ map '/oauth/custom_authorization' do
24
+ run Twitter::CustomAuthorization
25
+ end
26
+
27
+ map '/api/v1/status' do
28
+ run Twitter::Status
29
+ end
30
+
31
+ map '/api/v1/status/single_scope' do
32
+ run Twitter::StatusSingleScope
33
+ end
34
+
35
+ map '/api/v1/status/multiple_scopes' do
36
+ run Twitter::StatusMultipleScopes
37
+ end
@@ -0,0 +1,7 @@
1
+ Simple::OAuth2.configure do |config|
2
+ config.resource_owner_authenticator do |_request|
3
+ User.first
4
+ end
5
+
6
+ config.realm = 'Custom Realm'
7
+ end