lapis-yggdrasil 0.5.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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +18 -0
  3. data/.gitignore +151 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +1156 -0
  6. data/.travis.yml +4 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE.md +16 -0
  9. data/README.md +130 -0
  10. data/Rakefile +28 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +7 -0
  13. data/lapis-yggdrasil.gemspec +35 -0
  14. data/lib/lapis/yggdrasil.rb +18 -0
  15. data/lib/lapis/yggdrasil/agent.rb +52 -0
  16. data/lib/lapis/yggdrasil/authentication_client.rb +106 -0
  17. data/lib/lapis/yggdrasil/authentication_error.rb +29 -0
  18. data/lib/lapis/yggdrasil/client.rb +38 -0
  19. data/lib/lapis/yggdrasil/error_codes.rb +43 -0
  20. data/lib/lapis/yggdrasil/messaging.rb +23 -0
  21. data/lib/lapis/yggdrasil/messaging/authentication_request.rb +50 -0
  22. data/lib/lapis/yggdrasil/messaging/authentication_response.rb +36 -0
  23. data/lib/lapis/yggdrasil/messaging/error_response.rb +74 -0
  24. data/lib/lapis/yggdrasil/messaging/invalidate_request.rb +34 -0
  25. data/lib/lapis/yggdrasil/messaging/refresh_request.rb +47 -0
  26. data/lib/lapis/yggdrasil/messaging/refresh_response.rb +29 -0
  27. data/lib/lapis/yggdrasil/messaging/request.rb +25 -0
  28. data/lib/lapis/yggdrasil/messaging/response.rb +33 -0
  29. data/lib/lapis/yggdrasil/messaging/response_factory.rb +82 -0
  30. data/lib/lapis/yggdrasil/messaging/signout_request.rb +38 -0
  31. data/lib/lapis/yggdrasil/messaging/token_request.rb +42 -0
  32. data/lib/lapis/yggdrasil/messaging/token_response.rb +36 -0
  33. data/lib/lapis/yggdrasil/messaging/validate_request.rb +34 -0
  34. data/lib/lapis/yggdrasil/profile.rb +65 -0
  35. data/lib/lapis/yggdrasil/session.rb +60 -0
  36. data/lib/lapis/yggdrasil/session_info.rb +62 -0
  37. data/lib/lapis/yggdrasil/version.rb +5 -0
  38. data/spec/factories/agent_factory.rb +10 -0
  39. data/spec/factories/authentication_error_factory.rb +14 -0
  40. data/spec/factories/message_factory.rb +111 -0
  41. data/spec/factories/profile_factory.rb +20 -0
  42. data/spec/factories/session_info_factory.rb +11 -0
  43. data/spec/factories/uuid_factory.rb +28 -0
  44. data/spec/lapis/yggdrasil/agent_spec.rb +103 -0
  45. data/spec/lapis/yggdrasil/authentication_client_spec.rb +200 -0
  46. data/spec/lapis/yggdrasil/authentication_error_spec.rb +42 -0
  47. data/spec/lapis/yggdrasil/messaging/authentication_request_spec.rb +61 -0
  48. data/spec/lapis/yggdrasil/messaging/authentication_response_spec.rb +63 -0
  49. data/spec/lapis/yggdrasil/messaging/error_response_spec.rb +164 -0
  50. data/spec/lapis/yggdrasil/messaging/invalidate_request_spec.rb +29 -0
  51. data/spec/lapis/yggdrasil/messaging/refresh_request_spec.rb +70 -0
  52. data/spec/lapis/yggdrasil/messaging/refresh_response_spec.rb +50 -0
  53. data/spec/lapis/yggdrasil/messaging/response_factory_spec.rb +130 -0
  54. data/spec/lapis/yggdrasil/messaging/response_spec.rb +36 -0
  55. data/spec/lapis/yggdrasil/messaging/signout_request_spec.rb +29 -0
  56. data/spec/lapis/yggdrasil/messaging/validate_request_spec.rb +29 -0
  57. data/spec/lapis/yggdrasil/profile_spec.rb +108 -0
  58. data/spec/lapis/yggdrasil/session_info_spec.rb +131 -0
  59. data/spec/lapis/yggdrasil/session_spec.rb +158 -0
  60. data/spec/spec_helper.rb +89 -0
  61. metadata +269 -0
@@ -0,0 +1,42 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ RSpec.describe Lapis::Yggdrasil::AuthenticationError do
4
+
5
+ describe '#code' do
6
+ let(:code) { Lapis::Yggdrasil::ErrorCodes::NOT_FOUND }
7
+ subject(:error) { build(:error, :code => code) }
8
+ subject { error.code }
9
+
10
+ it 'is the expected value' do
11
+ is_expected.to eq code
12
+ end
13
+ end
14
+
15
+ describe '#message' do
16
+ let(:code) { Lapis::Yggdrasil::ErrorCodes::INVALID_CREDENTIALS }
17
+ let(:error) { build(:error, :code => code) }
18
+ subject { error.to_s }
19
+
20
+ it 'contains the code' do
21
+ is_expected.to match(/#{code}/i)
22
+ end
23
+
24
+ context 'with a message' do
25
+ let(:message) { 'foobarbaz' }
26
+ let(:error) { build(:error, :message => message) }
27
+
28
+ it 'contains the message' do
29
+ is_expected.to include message
30
+ end
31
+ end
32
+
33
+ context 'without a message' do
34
+ let(:error) { build(:error, :no_message) }
35
+
36
+ it 'contains the class name' do
37
+ is_expected.to include Lapis::Yggdrasil::AuthenticationError.to_s
38
+ end
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,61 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ RSpec.describe Lapis::Yggdrasil::Messaging::AuthenticationRequest do
4
+ let(:username) { 'Notch' }
5
+ let(:password) { 'qwerty' }
6
+ let(:agent) { build(:agent) }
7
+ let(:token) { build(:uuid) }
8
+ subject(:request) { Lapis::Yggdrasil::Messaging::AuthenticationRequest.new(username, password, agent, token) }
9
+
10
+ describe '#endpoint' do
11
+ subject { request.endpoint }
12
+
13
+ it 'is /authenticate' do
14
+ is_expected.to eq '/authenticate'
15
+ end
16
+ end
17
+
18
+ describe '#to_json' do
19
+ subject(:json_str) { request.to_json }
20
+ subject(:structure) { JSON.parse(json_str) }
21
+
22
+ it 'contains the username' do
23
+ is_expected.to include('username' => username)
24
+ end
25
+
26
+ it 'contains the password' do
27
+ is_expected.to include('password' => password)
28
+ end
29
+
30
+ it 'contains the agent info' do
31
+ is_expected.to include('agent')
32
+ end
33
+
34
+ context 'the agent' do
35
+ subject { structure['agent'] }
36
+
37
+ it 'contains the name' do
38
+ is_expected.to include('name' => agent.name)
39
+ end
40
+
41
+ it 'contains the version' do
42
+ is_expected.to include('version' => agent.version)
43
+ end
44
+ end
45
+
46
+ context 'with a client token' do
47
+ it 'contains the client token' do
48
+ is_expected.to include('clientToken' => token.to_s(false))
49
+ end
50
+ end
51
+
52
+ context 'without a client token' do
53
+ let(:token) { nil }
54
+
55
+ it 'does not contain a token entry' do
56
+ is_expected.not_to include('clientToken')
57
+ end
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,63 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ RSpec.describe Lapis::Yggdrasil::Messaging::AuthenticationResponse do
4
+ let(:access) { build(:uuid) }
5
+ let(:client) { build(:uuid) }
6
+ let(:profiles) { build_list(:profile, 5) }
7
+ let(:selected) { profiles.first }
8
+ let(:message) { build(:auth_message, :access => access, :client => client, :profiles => profiles, :selected => selected) }
9
+ subject(:response) { Lapis::Yggdrasil::Messaging::AuthenticationResponse.new(message) }
10
+
11
+ describe '#client_token' do
12
+ subject { response.client_token }
13
+
14
+ it 'is the expected value' do
15
+ is_expected.to eq client
16
+ end
17
+ end
18
+
19
+ describe '#access_token' do
20
+ subject { response.access_token }
21
+
22
+ it 'is the expected value' do
23
+ is_expected.to eq access
24
+ end
25
+ end
26
+
27
+ describe '#available_profiles' do
28
+ subject { response.available_profiles }
29
+
30
+ it 'has the expected profiles' do
31
+ is_expected.to contain_exactly(*profiles)
32
+ end
33
+
34
+ it 'is frozen' do
35
+ is_expected.to be_frozen
36
+ end
37
+ end
38
+
39
+ describe '#selected_profile' do
40
+ subject { response.selected_profile }
41
+
42
+ it 'is the expected value' do
43
+ is_expected.to eq selected
44
+ end
45
+ end
46
+
47
+ describe '#status' do
48
+ subject { response.status }
49
+
50
+ it 'is the expected value' do
51
+ is_expected.to eq message.status
52
+ end
53
+ end
54
+
55
+ describe '#ok?' do
56
+ subject { response.ok? }
57
+
58
+ it 'is the expected value' do
59
+ is_expected.to eq message.ok?
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,164 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ RSpec.describe Lapis::Yggdrasil::Messaging::ErrorResponse do
4
+ let(:error) { 'Not Found' }
5
+ let(:error_message) { 'The server has not found anything matching the request URI' }
6
+ let(:cause) { 'Testing' }
7
+ let(:message) { build(:error_message, :error => error, :error_message => error_message, :cause => cause) }
8
+ subject(:response) { Lapis::Yggdrasil::Messaging::ErrorResponse.new(message) }
9
+
10
+ describe '#error' do
11
+ subject { response.error }
12
+
13
+ it 'is the expected value' do
14
+ is_expected.to eq error
15
+ end
16
+ end
17
+
18
+ describe '#message' do
19
+ subject { response.message }
20
+
21
+ it 'is the expected value' do
22
+ is_expected.to eq error_message
23
+ end
24
+ end
25
+
26
+ describe '#cause' do
27
+ subject { response.cause }
28
+
29
+ it 'is the expected value' do
30
+ is_expected.to eq cause
31
+ end
32
+
33
+ context 'without a cause' do
34
+ let(:message) { build(:error_message, :no_cause) }
35
+
36
+ it 'is nil' do
37
+ is_expected.to eq nil
38
+ end
39
+ end
40
+ end
41
+
42
+ describe '#status' do
43
+ subject { response.status }
44
+
45
+ it 'is the expected value' do
46
+ is_expected.to eq message.status
47
+ end
48
+ end
49
+
50
+ describe '#ok?' do
51
+ subject { response.ok? }
52
+
53
+ it 'is the expected value' do
54
+ is_expected.to eq message.ok?
55
+ end
56
+ end
57
+
58
+ describe '#to_error' do
59
+ subject(:ex) { response.to_error }
60
+
61
+ describe '.code' do
62
+ subject { ex.code }
63
+
64
+ context 'with method not allowed' do
65
+ let(:error) { 'Method Not Allowed' }
66
+ let(:error_message) { 'The method specified in the request is not allowed for the resource identified by the request URI' }
67
+ let(:cause) { nil }
68
+
69
+ it 'is METHOD_NOT_ALLOWED' do
70
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::METHOD_NOT_ALLOWED
71
+ end
72
+ end
73
+
74
+ context 'with resource not found' do
75
+ let(:error) { 'Not Found' }
76
+ let(:error_message) { 'The server has not found anything matching the request URI' }
77
+ let(:cause) { nil }
78
+
79
+ it 'is NOT_FOUND' do
80
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::NOT_FOUND
81
+ end
82
+ end
83
+
84
+ context 'with a migrated account' do
85
+ let(:error) { 'ForbiddenOperationException' }
86
+ let(:error_message) { 'Invalid credentials. Account migrated, use e-mail as username.' }
87
+ let(:cause) { 'UserMigratedException' }
88
+
89
+ it 'is ACCOUNT_MIGRATED' do
90
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::ACCOUNT_MIGRATED
91
+ end
92
+ end
93
+
94
+ context 'with wrong credentials' do
95
+ let(:error) { 'ForbiddenOperationException' }
96
+ let(:error_message) { 'Invalid credentials. Invalid username or password.' }
97
+ let(:cause) { nil }
98
+
99
+ it 'is INVALID_CREDENTIALS' do
100
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::INVALID_CREDENTIALS
101
+ end
102
+ end
103
+
104
+ context 'with too many attempts' do
105
+ let(:error) { 'ForbiddenOperationException' }
106
+ let(:error_message) { 'Invalid credentials.' }
107
+ let(:cause) { nil }
108
+
109
+ it 'is TOO_MANY_ATTEMPTS' do
110
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::TOO_MANY_ATTEMPTS
111
+ end
112
+ end
113
+
114
+ context 'with an invalid token' do
115
+ let(:error) { 'ForbiddenOperationException' }
116
+ let(:error_message) { 'Invalid token.' }
117
+ let(:cause) { nil }
118
+
119
+ it 'is INVALID_TOKEN' do
120
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::INVALID_TOKEN
121
+ end
122
+ end
123
+
124
+ context 'with a profile' do
125
+ let(:error) { 'IllegalArgumentException' }
126
+ let(:error_message) { 'Access token already has a profile assigned.' }
127
+ let(:cause) { nil }
128
+
129
+ it 'is PROFILE_NOT_SUPPORTED' do
130
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::PROFILE_NOT_SUPPORTED
131
+ end
132
+ end
133
+
134
+ context 'with no credentials' do
135
+ let(:error) { 'IllegalArgumentException' }
136
+ let(:error_message) { 'credentials is null' }
137
+ let(:cause) { nil }
138
+
139
+ it 'is NO_CREDENTIALS' do
140
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::NO_CREDENTIALS
141
+ end
142
+ end
143
+
144
+ context 'with wrong content-type' do
145
+ let(:error) { 'Unsupported Media Type' }
146
+ let(:error_message) { 'The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method' }
147
+ let(:cause) { nil }
148
+
149
+ it 'is UNSUPPORTED_MEDIA_TYPE' do
150
+ is_expected.to eq Lapis::Yggdrasil::ErrorCodes::UNSUPPORTED_MEDIA_TYPE
151
+ end
152
+ end
153
+ end
154
+
155
+ describe '.message' do
156
+ subject { ex.message }
157
+
158
+ it 'is passed along' do
159
+ is_expected.to include error_message
160
+ end
161
+ end
162
+ end
163
+
164
+ end
@@ -0,0 +1,29 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ RSpec.describe Lapis::Yggdrasil::Messaging::InvalidateRequest do
4
+ let(:client) { build(:uuid) }
5
+ let(:access) { build(:uuid) }
6
+ subject(:request) { Lapis::Yggdrasil::Messaging::InvalidateRequest.new(client, access) }
7
+
8
+ describe '#endpoint' do
9
+ subject { request.endpoint }
10
+
11
+ it 'is /invalidate' do
12
+ is_expected.to eq '/invalidate'
13
+ end
14
+ end
15
+
16
+ describe '#to_json' do
17
+ subject(:json_str) { request.to_json }
18
+ subject(:structure) { JSON.parse(json_str) }
19
+
20
+ it 'contains the client token' do
21
+ is_expected.to include('clientToken' => client.to_s(false))
22
+ end
23
+
24
+ it 'contains the access token' do
25
+ is_expected.to include('accessToken' => access.to_s(false))
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,70 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ RSpec.describe Lapis::Yggdrasil::Messaging::RefreshRequest do
4
+ let(:access) { build(:uuid) }
5
+ let(:client) { build(:uuid) }
6
+ let(:profile) { build(:profile) }
7
+ subject(:request) { Lapis::Yggdrasil::Messaging::RefreshRequest.new(client, access, profile) }
8
+
9
+ describe '#endpoint' do
10
+ subject { request.endpoint }
11
+
12
+ it 'is /refresh' do
13
+ is_expected.to eq '/refresh'
14
+ end
15
+ end
16
+
17
+ describe '#to_json' do
18
+ subject(:json_str) { request.to_json }
19
+ subject(:structure) { JSON.parse(json_str) }
20
+
21
+ it 'contains the access token' do
22
+ is_expected.to include('accessToken' => access.to_s(false))
23
+ end
24
+
25
+ it 'contains the client token' do
26
+ is_expected.to include('clientToken' => client.to_s(false))
27
+ end
28
+
29
+ it 'contains the profile info' do
30
+ is_expected.to include('selectedProfile')
31
+ end
32
+
33
+ context 'the profile' do
34
+ subject { structure['selectedProfile'] }
35
+
36
+ it 'contains the name' do
37
+ is_expected.to include('name' => profile.name)
38
+ end
39
+
40
+ it 'contains the ID' do
41
+ is_expected.to include('id' => profile.id.to_s(false))
42
+ end
43
+
44
+ context 'with a legacy profile' do
45
+ let(:profile) { build(:profile, :legacy) }
46
+
47
+ it 'contains the legacy flag' do
48
+ is_expected.to include('legacy' => true)
49
+ end
50
+ end
51
+
52
+ context 'with a migrated profile' do
53
+ let(:profile) { build(:profile, :modern) }
54
+
55
+ it 'does not contain the legacy flag' do
56
+ is_expected.not_to include('legacy')
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'without a profile' do
62
+ let(:profile) { nil }
63
+
64
+ it 'does not contain a profile entry' do
65
+ is_expected.not_to include('selectedProfile')
66
+ end
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,50 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ RSpec.describe Lapis::Yggdrasil::Messaging::RefreshResponse do
4
+ let(:access) { build(:uuid) }
5
+ let(:client) { build(:uuid) }
6
+ let(:profile) { build(:profile) }
7
+ let(:message) { build(:refresh_message, :access => access, :client => client, :profile => profile) }
8
+ subject(:response) { Lapis::Yggdrasil::Messaging::RefreshResponse.new(message) }
9
+
10
+ describe '#client_token' do
11
+ subject { response.client_token }
12
+
13
+ it 'is the expected value' do
14
+ is_expected.to eq client
15
+ end
16
+ end
17
+
18
+ describe '#access_token' do
19
+ subject { response.access_token }
20
+
21
+ it 'is the expected value' do
22
+ is_expected.to eq access
23
+ end
24
+ end
25
+
26
+ describe '#profile' do
27
+ subject { response.profile }
28
+
29
+ it 'is the expected value' do
30
+ is_expected.to eq profile
31
+ end
32
+ end
33
+
34
+ describe '#status' do
35
+ subject { response.status }
36
+
37
+ it 'is the expected value' do
38
+ is_expected.to eq message.status
39
+ end
40
+ end
41
+
42
+ describe '#ok?' do
43
+ subject { response.ok? }
44
+
45
+ it 'is the expected value' do
46
+ is_expected.to eq message.ok?
47
+ end
48
+ end
49
+
50
+ end