minty 1.0.0 → 1.1.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 (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 -30
  47. data/lib/minty/api/v2.rb +0 -8
  48. data/lib/minty/client.rb +0 -7
  49. data/lib/minty/exception.rb +0 -50
  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