minty 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -16
  3. data/Gemfile.lock +28 -208
  4. data/README.md +58 -57
  5. data/Rakefile +4 -27
  6. data/docs/DefaultApi.md +217 -0
  7. data/docs/RedirectUrl.md +18 -0
  8. data/git_push.sh +57 -0
  9. data/lib/minty/api/default_api.rb +210 -0
  10. data/lib/minty/api_client.rb +388 -0
  11. data/lib/minty/api_error.rb +53 -0
  12. data/lib/minty/configuration.rb +275 -0
  13. data/lib/minty/models/redirect_url.rb +216 -0
  14. data/lib/minty/version.rb +9 -3
  15. data/lib/minty.rb +33 -7
  16. data/minty.gemspec +19 -32
  17. data/pkg/minty-1.1.0.gem +0 -0
  18. data/publish_rubygem.sh +1 -1
  19. data/spec/api/default_api_spec.rb +65 -0
  20. data/spec/api_client_spec.rb +222 -0
  21. data/spec/configuration_spec.rb +38 -0
  22. data/spec/models/redirect_url_spec.rb +28 -0
  23. data/spec/spec_helper.rb +95 -63
  24. metadata +37 -292
  25. data/.bundle/config +0 -4
  26. data/.devcontainer/Dockerfile +0 -19
  27. data/.devcontainer/devcontainer.json +0 -37
  28. data/.env.example +0 -2
  29. data/.gemrelease +0 -2
  30. data/.github/PULL_REQUEST_TEMPLATE.md +0 -33
  31. data/.github/dependabot.yml +0 -10
  32. data/.github/stale.yml +0 -20
  33. data/.gitignore +0 -18
  34. data/.rspec +0 -3
  35. data/.rubocop.yml +0 -9
  36. data/CODE_OF_CONDUCT.md +0 -3
  37. data/DEPLOYMENT.md +0 -61
  38. data/DEVELOPMENT.md +0 -35
  39. data/EXAMPLES.md +0 -195
  40. data/Guardfile +0 -39
  41. data/LICENSE +0 -21
  42. data/Makefile +0 -5
  43. data/RUBYGEM.md +0 -9
  44. data/codecov.yml +0 -22
  45. data/lib/minty/algorithm.rb +0 -7
  46. data/lib/minty/api/authentication_endpoints.rb +0 -55
  47. data/lib/minty/api/v2.rb +0 -8
  48. data/lib/minty/client.rb +0 -7
  49. data/lib/minty/exception.rb +0 -58
  50. data/lib/minty/mixins/api_token_struct.rb +0 -4
  51. data/lib/minty/mixins/headers.rb +0 -19
  52. data/lib/minty/mixins/httpproxy.rb +0 -125
  53. data/lib/minty/mixins/initializer.rb +0 -38
  54. data/lib/minty/mixins/validation.rb +0 -113
  55. data/lib/minty/mixins.rb +0 -23
  56. data/lib/minty_client.rb +0 -4
  57. data/spec/integration/lib/minty/api/api_authentication_spec.rb +0 -122
  58. data/spec/integration/lib/minty/minty_client_spec.rb +0 -92
  59. data/spec/lib/minty/client_spec.rb +0 -223
  60. data/spec/lib/minty/mixins/httpproxy_spec.rb +0 -658
  61. data/spec/lib/minty/mixins/initializer_spec.rb +0 -121
  62. data/spec/lib/minty/mixins/token_management_spec.rb +0 -129
  63. data/spec/lib/minty/mixins/validation_spec.rb +0 -559
  64. data/spec/support/credentials.rb +0 -14
  65. data/spec/support/dummy_class.rb +0 -20
  66. data/spec/support/dummy_class_for_proxy.rb +0 -6
  67. data/spec/support/dummy_class_for_restclient.rb +0 -4
  68. data/spec/support/dummy_class_for_tokens.rb +0 -18
  69. data/spec/support/import_users.json +0 -13
  70. data/spec/support/stub_response.rb +0 -3
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'zache'
4
-
5
- class Zache
6
- def last(key)
7
- @hash[key][:value] if @hash.key?(key)
8
- end
9
- end
10
-
11
- module Minty
12
- module Mixins
13
- module Validation
14
- class JWTAlgorithm
15
- private_class_method :new
16
-
17
- def name
18
- raise 'Must be overriden by the subclasses'
19
- end
20
- end
21
-
22
- module Algorithm
23
- class HS256 < JWTAlgorithm
24
- class << self
25
- private :new
26
-
27
- def secret(secret)
28
- new secret
29
- end
30
- end
31
-
32
- attr_accessor :secret
33
-
34
- def initialize(secret)
35
- raise Minty::InvalidParameter, 'Must supply a valid secret' if secret.to_s.empty?
36
-
37
- @secret = secret
38
- end
39
-
40
- def name
41
- 'HS256'
42
- end
43
- end
44
-
45
- class RS256 < JWTAlgorithm
46
- include Minty::Mixins::HTTPProxy
47
-
48
- @@cache = Zache.new.freeze
49
-
50
- class << self
51
- private :new
52
-
53
- def jwks_url(url, lifetime: 10 * 60)
54
- new url, lifetime
55
- end
56
-
57
- def remove_jwks
58
- @@cache.remove_by { true }
59
- end
60
- end
61
-
62
- def initialize(jwks_url, lifetime)
63
- raise Minty::InvalidParameter, 'Must supply a valid jwks_url' if jwks_url.to_s.empty?
64
-
65
- unless lifetime.is_a?(Integer) && lifetime >= 0
66
- raise Minty::InvalidParameter,
67
- 'Must supply a valid lifetime'
68
- end
69
-
70
- @lifetime = lifetime
71
- @jwks_url = jwks_url
72
- @did_fetch_jwks = false
73
- end
74
-
75
- def name
76
- 'RS256'
77
- end
78
-
79
- def jwks(force: false)
80
- result = fetch_jwks if force
81
-
82
- if result
83
- @@cache.put(@jwks_url, result, lifetime: @lifetime)
84
- return result
85
- end
86
-
87
- previous_value = @@cache.last(@jwks_url)
88
-
89
- @@cache.get(@jwks_url, lifetime: @lifetime, dirty: true) do
90
- new_value = fetch_jwks
91
-
92
- raise Minty::InvalidIdToken, 'Could not fetch the JWK set' unless new_value || previous_value
93
-
94
- new_value || previous_value
95
- end
96
- end
97
-
98
- def fetched_jwks?
99
- @did_fetch_jwks
100
- end
101
-
102
- private
103
-
104
- def fetch_jwks
105
- result = request_with_retry(:get, @jwks_url, {}, {})
106
- @did_fetch_jwks = result.is_a?(Hash) && result.key?('keys')
107
- result if @did_fetch_jwks
108
- end
109
- end
110
- end
111
- end
112
- end
113
- end
data/lib/minty/mixins.rb DELETED
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'base64'
4
- require 'rest-client'
5
- require 'uri'
6
-
7
- require 'minty/mixins/api_token_struct'
8
- require 'minty/mixins/headers'
9
- require 'minty/mixins/httpproxy'
10
- require 'minty/mixins/initializer'
11
- require 'minty/mixins/validation'
12
-
13
- require 'minty/api/authentication_endpoints'
14
- require 'minty/api/v2'
15
-
16
- module Minty
17
- # Collecting dependencies here
18
- module Mixins
19
- include Minty::Mixins::Headers
20
- include Minty::Mixins::HTTPProxy
21
- include Minty::Mixins::Initializer
22
- end
23
- end
data/lib/minty_client.rb DELETED
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class MintyClient < Minty::Client
4
- end
@@ -1,122 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- describe Minty::Api::AuthenticationEndpoints do
5
- attr_reader :client, :test_user_email, :test_user_pwd, :test_user
6
-
7
- before(:all) do
8
- @client = MintyClient.new(v2_creds)
9
-
10
- @test_user_email = "#{entity_suffix}-username-1@minty.page"
11
- @test_user_pwd = '23kejn2jk3en2jke2jk3be2jk3ber'
12
-
13
- VCR.use_cassette('Minty_Api_AuthenticationEndpoints/create_test_user') do
14
- @test_user ||= @client.signup(
15
- test_user_email,
16
- test_user_pwd
17
- )
18
- end
19
- end
20
-
21
- after(:all) do
22
- VCR.use_cassette('Minty_Api_AuthenticationEndpoints/delete_test_user') do
23
- @client.delete_user("minty|#{test_user['_id']}")
24
- end
25
- end
26
-
27
- describe '.signup', vcr: true do
28
- it 'should signup a new user' do
29
- expect(test_user).to(include('_id', 'email'))
30
- end
31
-
32
- it 'should return the correct email address' do
33
- expect(test_user['email']).to eq test_user_email
34
- end
35
- end
36
-
37
- describe '.change_password', vcr: true do
38
- it 'should trigger a password reset' do
39
- expect(
40
- @client.change_password(test_user_email, '')
41
- ).to(include("We've just sent you an email to reset your password."))
42
- end
43
- end
44
-
45
- describe '.saml_metadata', vcr: true do
46
- it 'should retrieve SAML metadata' do
47
- expect(@client.saml_metadata).to(include('<EntityDescriptor'))
48
- end
49
- end
50
-
51
- describe '.wsfed_metadata', vcr: true do
52
- it 'should retrieve WSFED metadata' do
53
- expect(@client.wsfed_metadata).to(include('<EntityDescriptor'))
54
- end
55
- end
56
-
57
- describe '.userinfo', vcr: true do
58
- it 'should fail as not authorized' do
59
- expect do
60
- @client.userinfo('invalid_token')
61
- end.to raise_error Minty::Unauthorized
62
- end
63
-
64
- it 'should return the userinfo' do
65
- tokens = @client.login_with_resource_owner(test_user_email, test_user_pwd)
66
- expect(@client.userinfo(tokens['access_token'])).to(
67
- include('email' => test_user_email)
68
- )
69
- end
70
- end
71
-
72
- describe '.login_with_resource_owner', vcr: true do
73
- it 'should fail with an incorrect email' do
74
- expect do
75
- @client.login_with_resource_owner(
76
- "#{test_user['email']}_invalid",
77
- test_user_pwd
78
- )
79
- end.to raise_error Minty::AccessDenied
80
- end
81
-
82
- it 'should fail with an incorrect password' do
83
- expect do
84
- @client.login_with_resource_owner(
85
- test_user['email'],
86
- "#{test_user_pwd}_invalid"
87
- )
88
- end.to raise_error Minty::AccessDenied
89
- end
90
-
91
- it 'should login successfully with a default scope' do
92
- expect(
93
- @client.login_with_resource_owner(
94
- test_user['email'],
95
- test_user_pwd
96
- ).token
97
- ).to_not be_empty
98
- end
99
-
100
- it 'should fail with an invalid audience' do
101
- expect do
102
- @client.login_with_resource_owner(
103
- test_user['email'],
104
- test_user_pwd,
105
- scope: 'test:scope',
106
- audience: 'https://brucke.club/invalid/api/v1/'
107
- )
108
- end.to raise_error Minty::BadRequest
109
- end
110
-
111
- it 'should login successfully with a custom audience' do
112
- expect(
113
- @client.login_with_resource_owner(
114
- test_user['email'],
115
- test_user_pwd,
116
- scope: 'test:scope',
117
- audience: 'https://brucke.club/custom/api/v1/'
118
- ).token
119
- ).to_not be_empty
120
- end
121
- end
122
- end
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- describe Minty::Client do
5
- shared_examples 'invalid credentials' do |credentials, error|
6
- it "raise an error with credentials #{credentials}" do
7
- if error.nil?
8
- expect { MintyClient.new(credentials) }.to raise_error
9
- else
10
- expect { MintyClient.new(credentials) }.to raise_error(error)
11
- end
12
- end
13
- end
14
-
15
- it_should_behave_like 'invalid credentials', {
16
- namespace: 'samples.minty.page'
17
- }, Minty::InvalidCredentials
18
-
19
- it_should_behave_like 'invalid credentials', {
20
- namespace: 'samples.minty.page', client_id: 'client_id'
21
- }, Minty::InvalidCredentials
22
-
23
- it_should_behave_like 'invalid credentials', {
24
- namespace: 'samples.minty.page', client_secret: 'secret'
25
- }, Minty::InvalidCredentials
26
-
27
- it_should_behave_like 'invalid credentials', {
28
- namespace: 'samples.minty.page', api_version: 2
29
- }, Minty::InvalidCredentials
30
-
31
- it_should_behave_like 'invalid credentials', {}, Minty::InvalidApiNamespace
32
-
33
- it_should_behave_like 'invalid credentials', {
34
- api_version: 2
35
- }, Minty::InvalidApiNamespace
36
-
37
- it_should_behave_like 'invalid credentials', {
38
- api_version: 1
39
- }, Minty::InvalidApiNamespace
40
-
41
- it_should_behave_like 'invalid credentials', {
42
- client_id: 'client_id', client_secret: 'secret'
43
- }, Minty::InvalidApiNamespace
44
-
45
- it_should_behave_like 'invalid credentials', {
46
- api_version: 2, token: 'token'
47
- }, Minty::InvalidApiNamespace
48
-
49
- let(:v2_credentials) { { domain: 'test.minty.page' } }
50
-
51
- shared_examples 'valid credentials' do
52
- it { expect { MintyClient.new(credentials) }.to_not raise_error }
53
- end
54
-
55
- it_should_behave_like 'valid credentials' do
56
- let(:credentials) { v2_credentials.merge(token: 'TEST_API_TOKEN') }
57
- end
58
-
59
- it_should_behave_like 'valid credentials' do
60
- let(:credentials) { v2_credentials.merge(access_token: 'TEST_API_TOKEN') }
61
- end
62
-
63
- context 'client headers' do
64
- let(:client) { Minty::Client.new(v2_credentials.merge(access_token: 'abc123', domain: 'myhost.minty.page')) }
65
- let(:headers) { client.headers }
66
- let(:telemetry) { JSON.parse(Base64.urlsafe_decode64(headers['Minty-Client'])) }
67
-
68
- it 'has the correct headers present' do
69
- expect(headers.keys.sort).to eql(%w[Minty-Client Authorization Content-Type])
70
- end
71
-
72
- it 'uses the correct access token' do
73
- expect(headers['Authorization']).to eql 'Bearer abc123'
74
- end
75
-
76
- it 'is always json' do
77
- expect(headers['Content-Type']).to eql 'application/json'
78
- end
79
-
80
- it 'should include the correct name in telemetry data' do
81
- expect(telemetry['name']).to eq('ruby')
82
- end
83
-
84
- it 'should include the correct version in telemetry data' do
85
- expect(telemetry['version']).to eq(Minty::VERSION)
86
- end
87
-
88
- it 'should include the correct env in telemetry data' do
89
- expect(telemetry['env']['ruby']).to eq(RUBY_VERSION)
90
- end
91
- end
92
- end
@@ -1,223 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Minty::Client do
6
- shared_examples_for 'v2 API client' do
7
- it { should be_a Minty::Api::V2 }
8
- it { should be_a Minty::Api::V2::Blacklists }
9
- it { should be_a Minty::Api::V2::Clients }
10
- it { should be_a Minty::Api::V2::ClientGrants }
11
- it { should be_a Minty::Api::V2::Connections }
12
- it { should be_a Minty::Api::V2::DeviceCredentials }
13
- it { should be_a Minty::Api::V2::Emails }
14
- it { should be_a Minty::Api::V2::Jobs }
15
- it { should be_a Minty::Api::V2::Logs }
16
- it { should be_a Minty::Api::V2::ResourceServers }
17
- it { should be_a Minty::Api::V2::Rules }
18
- it { should be_a Minty::Api::V2::Stats }
19
- it { should be_a Minty::Api::V2::Tenants }
20
- it { should be_a Minty::Api::V2::Tickets }
21
- it { should be_a Minty::Api::V2::UserBlocks }
22
- it { should be_a Minty::Api::V2::Users }
23
- it { should be_a Minty::Api::V2::UsersByEmail }
24
- end
25
-
26
- shared_examples_for 'Authentication API client' do
27
- it { should be_a Minty::Api::AuthenticationEndpoints }
28
- end
29
-
30
- let(:domain) { 'samples.minty.page' }
31
- let(:client_id) { '__test_client_id__' }
32
- let(:client_secret) { '__test_client_secret__' }
33
- let(:access_token) { '__test_access_token__' }
34
- let(:organization) { '__test_organization__' }
35
-
36
- describe 'V2 client with token' do
37
- before :each do
38
- expect_any_instance_of(Minty::Api::AuthenticationEndpoints)
39
- .not_to receive(:obtain_access_token)
40
- end
41
-
42
- context 'and namespace' do
43
- let(:subject) do
44
- Minty::Client.new(
45
- access_token: access_token,
46
- namespace: domain
47
- )
48
- end
49
- it_should_behave_like 'v2 API client'
50
- it_should_behave_like 'Authentication API client'
51
- end
52
-
53
- context 'and domain' do
54
- let(:subject) do
55
- Minty::Client.new(
56
- access_token: access_token,
57
- domain: domain
58
- )
59
- end
60
- it_should_behave_like 'v2 API client'
61
- it_should_behave_like 'Authentication API client'
62
- end
63
-
64
- context 'and version' do
65
- let(:subject) do
66
- Minty::Client.new(
67
- api_version: 2,
68
- access_token: access_token,
69
- domain: domain
70
- )
71
- end
72
- it_should_behave_like 'v2 API client'
73
- it_should_behave_like 'Authentication API client'
74
- end
75
-
76
- context 'with token' do
77
- let(:subject) do
78
- Minty::Client.new(
79
- api_version: 2,
80
- token: access_token,
81
- domain: domain
82
- )
83
- end
84
- it_should_behave_like 'v2 API client'
85
- it_should_behave_like 'Authentication API client'
86
- end
87
-
88
- context 'with token and client_secret' do
89
- let(:subject) do
90
- Minty::Client.new(
91
- token: access_token,
92
- domain: domain,
93
- client_secret: client_secret
94
- )
95
- end
96
- it_should_behave_like 'v2 API client'
97
- it_should_behave_like 'Authentication API client'
98
- end
99
- end
100
-
101
- describe 'V2 client without token' do
102
- context 'should try to get an API token' do
103
- before do
104
- stub_api_token
105
- end
106
-
107
- let(:subject) do
108
- Minty::Client.new(
109
- domain: domain,
110
- client_id: client_id,
111
- client_secret: client_secret
112
- )
113
- end
114
- it_should_behave_like 'v2 API client'
115
- it_should_behave_like 'Authentication API client'
116
- end
117
-
118
- context 'when try to get an API tokenwith api_identifier' do
119
- let(:api_identifier) { 'https://samples.api_identifier/api/v2/' }
120
-
121
- before do
122
- stub_api_token_with_api_identifier
123
- end
124
-
125
- let(:subject) do
126
- Minty::Client.new(
127
- domain: domain,
128
- client_id: client_id,
129
- client_secret: client_secret,
130
- api_identifier: api_identifier
131
- )
132
- end
133
-
134
- it_should_behave_like 'v2 API client'
135
- it_should_behave_like 'Authentication API client'
136
- end
137
-
138
- context 'when try to get an API token with organization' do
139
- before do
140
- stub_api_token_with_organization
141
- end
142
-
143
- let(:subject) do
144
- Minty::Client.new(
145
- domain: domain,
146
- client_id: client_id,
147
- client_secret: client_secret,
148
- organization: organization
149
- )
150
- end
151
-
152
- it_should_behave_like 'v2 API client'
153
- it_should_behave_like 'Authentication API client'
154
- end
155
-
156
- context 'should fail if' do
157
- it 'does not have a client_secret' do
158
- expect do
159
- Minty::Client.new(
160
- domain: domain,
161
- client_id: client_id
162
- )
163
- end.to raise_error('Must supply a valid API token')
164
- end
165
- end
166
- end
167
-
168
- def stub_api_token
169
- stub_request(:post, "https://#{domain}/oauth/token")
170
- .with(
171
- body: hash_including(
172
- {
173
- grant_type: 'client_credentials',
174
- client_id: client_id,
175
- client_secret: client_secret,
176
- audience: "https://#{domain}/api/v2/"
177
- }
178
- )
179
- )
180
- .to_return(
181
- headers: { 'Content-Type' => 'application/json' },
182
- body: '{"access_token":"__test_access_token__"}',
183
- status: 200
184
- )
185
- end
186
-
187
- def stub_api_token_with_api_identifier
188
- stub_request(:post, "https://#{domain}/oauth/token")
189
- .with(
190
- body: hash_including(
191
- {
192
- grant_type: 'client_credentials',
193
- client_id: client_id,
194
- client_secret: client_secret,
195
- audience: api_identifier
196
- }
197
- )
198
- )
199
- .to_return(
200
- headers: { 'Content-Type' => 'application/json' },
201
- body: '{"access_token":"__test_access_token__"}',
202
- status: 200
203
- )
204
- end
205
-
206
- def stub_api_token_with_organization
207
- stub_request(:post, "https://#{domain}/oauth/token")
208
- .with(
209
- body: hash_including(
210
- {
211
- grant_type: 'client_credentials',
212
- client_id: client_id,
213
- client_secret: client_secret
214
- }
215
- )
216
- )
217
- .to_return(
218
- headers: { 'Content-Type' => 'application/json' },
219
- body: '{"access_token":"__test_access_token__"}',
220
- status: 200
221
- )
222
- end
223
- end