stormpath-sdk 0.4.0 → 1.0.0.beta

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 (89) hide show
  1. data/.gitignore +6 -0
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +27 -0
  5. data/CHANGES.md +21 -1
  6. data/Gemfile +1 -2
  7. data/README.md +457 -11
  8. data/Rakefile +15 -1
  9. data/lib/stormpath-sdk.rb +52 -33
  10. data/lib/stormpath-sdk/{resource/group_list.rb → api_key.rb} +5 -9
  11. data/lib/stormpath-sdk/auth/authentication_result.rb +3 -13
  12. data/lib/stormpath-sdk/auth/basic_authenticator.rb +5 -11
  13. data/lib/stormpath-sdk/auth/basic_login_attempt.rb +6 -8
  14. data/lib/stormpath-sdk/auth/username_password_request.rb +2 -5
  15. data/lib/stormpath-sdk/cache/cache.rb +54 -0
  16. data/lib/stormpath-sdk/cache/cache_entry.rb +33 -0
  17. data/lib/stormpath-sdk/cache/cache_manager.rb +22 -0
  18. data/lib/stormpath-sdk/cache/cache_stats.rb +35 -0
  19. data/lib/stormpath-sdk/cache/memory_store.rb +29 -0
  20. data/lib/stormpath-sdk/cache/redis_store.rb +32 -0
  21. data/lib/stormpath-sdk/client.rb +111 -0
  22. data/lib/stormpath-sdk/data_store.rb +241 -0
  23. data/lib/stormpath-sdk/{client/api_key.rb → error.rb} +16 -10
  24. data/lib/stormpath-sdk/{util → ext}/hash.rb +1 -2
  25. data/lib/stormpath-sdk/http/authc/sauthc1_signer.rb +8 -4
  26. data/lib/stormpath-sdk/http/http_client_request_executor.rb +8 -7
  27. data/lib/stormpath-sdk/http/request.rb +4 -8
  28. data/lib/stormpath-sdk/{util/request_utils.rb → http/utils.rb} +17 -38
  29. data/lib/stormpath-sdk/resource/account.rb +12 -108
  30. data/lib/stormpath-sdk/resource/application.rb +35 -171
  31. data/lib/stormpath-sdk/resource/associations.rb +97 -0
  32. data/lib/stormpath-sdk/resource/base.rb +256 -0
  33. data/lib/stormpath-sdk/resource/collection.rb +94 -0
  34. data/lib/stormpath-sdk/resource/directory.rb +11 -68
  35. data/lib/stormpath-sdk/resource/email_verification_token.rb +3 -9
  36. data/lib/stormpath-sdk/resource/error.rb +4 -38
  37. data/lib/stormpath-sdk/resource/expansion.rb +28 -0
  38. data/lib/stormpath-sdk/resource/group.rb +8 -66
  39. data/lib/stormpath-sdk/resource/group_membership.rb +4 -55
  40. data/lib/stormpath-sdk/resource/{application_list.rb → instance.rb} +7 -13
  41. data/lib/stormpath-sdk/resource/password_reset_token.rb +5 -23
  42. data/lib/stormpath-sdk/resource/status.rb +22 -28
  43. data/lib/stormpath-sdk/resource/tenant.rb +5 -52
  44. data/lib/stormpath-sdk/resource/utils.rb +43 -13
  45. data/lib/stormpath-sdk/util/assert.rb +5 -15
  46. data/lib/stormpath-sdk/version.rb +3 -3
  47. data/spec/api_key_spec.rb +19 -0
  48. data/spec/auth/basic_authenticator_spec.rb +25 -0
  49. data/spec/auth/sauthc1_signer_spec.rb +42 -0
  50. data/spec/cache/cache_entry_spec.rb +157 -0
  51. data/spec/cache/cache_spec.rb +89 -0
  52. data/spec/cache/cache_stats_spec.rb +106 -0
  53. data/spec/client_spec.rb +538 -0
  54. data/spec/data_store_spec.rb +130 -0
  55. data/spec/resource/account_spec.rb +74 -0
  56. data/spec/resource/application_spec.rb +148 -0
  57. data/spec/resource/base_spec.rb +114 -0
  58. data/spec/resource/collection_spec.rb +169 -0
  59. data/spec/resource/directory_spec.rb +30 -0
  60. data/spec/resource/expansion_spec.rb +100 -0
  61. data/spec/resource/group_spec.rb +49 -0
  62. data/spec/spec_helper.rb +135 -0
  63. data/spec/support/resource_factory.rb +48 -0
  64. data/spec/support/resource_matchers.rb +27 -0
  65. data/spec/support/test_cache_stores.rb +9 -0
  66. data/spec/support/test_request_executor.rb +11 -0
  67. data/stormpath-sdk.gemspec +14 -4
  68. data/support/api.rb +55 -0
  69. metadata +214 -44
  70. data/lib/stormpath-sdk/client/client.rb +0 -38
  71. data/lib/stormpath-sdk/client/client_application.rb +0 -38
  72. data/lib/stormpath-sdk/client/client_application_builder.rb +0 -351
  73. data/lib/stormpath-sdk/client/client_builder.rb +0 -305
  74. data/lib/stormpath-sdk/ds/data_store.rb +0 -210
  75. data/lib/stormpath-sdk/ds/resource_factory.rb +0 -37
  76. data/lib/stormpath-sdk/resource/account_list.rb +0 -32
  77. data/lib/stormpath-sdk/resource/collection_resource.rb +0 -91
  78. data/lib/stormpath-sdk/resource/directory_list.rb +0 -30
  79. data/lib/stormpath-sdk/resource/group_membership_list.rb +0 -32
  80. data/lib/stormpath-sdk/resource/instance_resource.rb +0 -28
  81. data/lib/stormpath-sdk/resource/resource.rb +0 -327
  82. data/lib/stormpath-sdk/resource/resource_error.rb +0 -47
  83. data/test/client/client.yml +0 -16
  84. data/test/client/client_application_builder_spec.rb +0 -114
  85. data/test/client/client_builder_spec.rb +0 -176
  86. data/test/client/read_spec.rb +0 -254
  87. data/test/client/write_spec.rb +0 -420
  88. data/test/resource/resource_spec.rb +0 -41
  89. data/test/resource/test_resource.rb +0 -28
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stormpath::DataStore do
4
+ let(:factory) { Stormpath::Test::ResourceFactory.new }
5
+ let(:request_executor) { Stormpath::Test::TestRequestExecutor.new }
6
+ let(:store) { Stormpath::Cache::RedisStore }
7
+ let(:data_store) { Stormpath::DataStore.new request_executor, {store: store}, nil, nil }
8
+ let(:application_cache) { data_store.cache_manager.get_cache 'applications' }
9
+ let(:tenant_cache) { data_store.cache_manager.get_cache 'tenants' }
10
+ let(:group_cache) { data_store.cache_manager.get_cache 'groups' }
11
+
12
+ after do
13
+ application_cache.clear
14
+ end
15
+
16
+ describe '.region_for' do
17
+ let(:region) { data_store.send(:region_for, 'https://api.stormpath.com/v1/directories/4NykYrYH0OBiOOVOg8LXQ5') }
18
+ it 'pulls resource name from href' do
19
+ expect(region).to eq('directories')
20
+ end
21
+ end
22
+
23
+ describe '#delete' do
24
+ before do
25
+ resource = factory.resource 'application', 1, %w(tenant groups)
26
+ href = resource['href']
27
+ request_executor.response = MultiJson.dump resource
28
+ application = data_store.get_resource href, Stormpath::Resource::Application
29
+ expect(application_cache.size).to eq(1)
30
+ data_store.delete application
31
+ end
32
+
33
+ it 'removes the resource from the cache' do
34
+ expect(application_cache.size).to be(0)
35
+ end
36
+ end
37
+
38
+ describe '#get_resource' do
39
+ context 'shallow resource' do
40
+ before do
41
+ resource = factory.resource 'application', 1, %w(tenant groups)
42
+ href = resource['href']
43
+ request_executor.response = MultiJson.dump resource
44
+ data_store.get_resource href, Stormpath::Resource::Application
45
+ @cached = application_cache.get href
46
+ end
47
+
48
+ it 'caches a shallow resource' do
49
+ expect(@cached).to be
50
+ expect(@cached).to be_resource
51
+ expect(@cached['tenant']).to be_link
52
+ expect(@cached['groups']).to be_link_collection
53
+ end
54
+
55
+ it 'caches no associations' do
56
+ expect(tenant_cache.size).to eq(0)
57
+ expect(group_cache.size).to eq(0)
58
+ end
59
+
60
+ it 'misses the cache on the get' do
61
+ expect(application_cache.stats.hits).to eq(1) # this hit is when we grab @cached
62
+ expect(application_cache.stats.misses).to eq(1)
63
+ end
64
+
65
+ context 'retrieved twice' do
66
+ before do
67
+ data_store.get_resource @cached['href'], Stormpath::Resource::Application
68
+ end
69
+
70
+ it 'hits the cache on the get' do
71
+ expect(application_cache.stats.hits).to eq(2)
72
+ expect(application_cache.stats.misses).to eq(1)
73
+ end
74
+ end
75
+ end
76
+
77
+ context 'deep resource' do
78
+ before do
79
+ resource = factory.resource 'application', 2, %w(tenant groups)
80
+ href = resource['href']
81
+ request_executor.response = MultiJson.dump resource
82
+ data_store.get_resource href, Stormpath::Resource::Application
83
+ @cached = application_cache.get href
84
+ end
85
+
86
+ it 'caches a shallow resource' do
87
+ expect(@cached).to be
88
+ expect(@cached).to be_resource
89
+ expect(@cached['tenant']).to be_link
90
+ expect(@cached['groups']).to be_link_collection
91
+ end
92
+
93
+ it 'caches shallow associations' do
94
+ expect(tenant_cache.size).to eq(1)
95
+ expect(group_cache.size).to eq(2)
96
+ end
97
+ end
98
+
99
+ context 'shallow collection' do
100
+ before do
101
+ resource = factory.collection 'tenant', 'application', 1, %w(tenant groups)
102
+ href = resource['href']
103
+ request_executor.response = MultiJson.dump resource
104
+ data_store.get_resource href, Stormpath::Resource::Application
105
+ end
106
+
107
+ it 'caches collection resources only' do
108
+ expect(application_cache.size).to eq(2)
109
+ expect(tenant_cache.size).to eq(0)
110
+ expect(group_cache.size).to eq(0)
111
+ end
112
+ end
113
+
114
+ context 'deep collection' do
115
+ before do
116
+ resource = factory.collection 'tenant', 'application', 2, %w(tenant groups)
117
+ href = resource['href']
118
+ request_executor.response = MultiJson.dump resource
119
+ data_store.get_resource href, Stormpath::Resource::Application
120
+ end
121
+
122
+ it 'caches collection resources and associations' do
123
+ expect(application_cache.size).to eq(2)
124
+ expect(tenant_cache.size).to eq(2)
125
+ expect(group_cache.size).to eq(4)
126
+ end
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stormpath::Resource::Account, :vcr do
4
+ describe "#add_group" do
5
+ context "given a group" do
6
+ let(:directory) do
7
+ test_api_client.directories.create name: 'testDirectory'
8
+ end
9
+
10
+ let(:group) do
11
+ directory.groups.create name: 'testGroup'
12
+ end
13
+
14
+ let(:account) do
15
+ directory.accounts.create email: 'test@example.com',
16
+ givenName: 'Ruby SDK',
17
+ password: 'P@$$w0rd',
18
+ surname: 'SDK',
19
+ username: 'rubysdk'
20
+ end
21
+
22
+ let(:reloaded_account) do
23
+ test_api_client.accounts.get account.href
24
+ end
25
+
26
+ before do
27
+ account.add_group group
28
+ end
29
+
30
+ after do
31
+ account.delete if account
32
+ group.delete if group
33
+ directory.delete if directory
34
+ end
35
+
36
+ it 'adds the group to the account' do
37
+ group_added = false
38
+ reloaded_account.groups.each do |g|
39
+ group_added = true if g.href == group.href
40
+ end
41
+ expect(group_added).to be_true
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#save' do
47
+ context 'when property values have changed' do
48
+ let(:account) do
49
+ test_directory.accounts.create build_account
50
+ end
51
+ let(:account_uri) do
52
+ account.href
53
+ end
54
+ let(:new_surname) do
55
+ "NewSurname"
56
+ end
57
+ let(:reloaded_account) { test_api_client.accounts.get account_uri }
58
+
59
+ before do
60
+ account = test_api_client.accounts.get account_uri
61
+ account.surname = new_surname
62
+ account.save
63
+ end
64
+
65
+ after do
66
+ account.delete if account
67
+ end
68
+
69
+ it 'saves changes to the account' do
70
+ expect(reloaded_account.surname).to eq(new_surname)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,148 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stormpath::Resource::Application, :vcr do
4
+ let(:application) { test_application }
5
+ let(:directory) { test_directory }
6
+
7
+ describe '.load' do
8
+ let(:url) do
9
+ uri = URI(application.href)
10
+ credentialed_uri = URI::HTTPS.new(
11
+ uri.scheme, "#{test_api_key_id}:#{test_api_key_secret}", uri.host,
12
+ uri.port, uri.registry, uri.path, uri.query, uri.opaque, uri.fragment
13
+ )
14
+ credentialed_uri.to_s
15
+ end
16
+
17
+ it "raises a LoadError with an invalid url" do
18
+ expect {
19
+ Stormpath::Resource::Application.load 'this is an invalid url'
20
+ }.to raise_error(Stormpath::Resource::Application::LoadError)
21
+ end
22
+
23
+ it "instantiates client and application objects from a composite URL" do
24
+ loaded_application = Stormpath::Resource::Application.load(url)
25
+ expect(loaded_application).to eq(application)
26
+ end
27
+ end
28
+
29
+ describe '#authenticate_account' do
30
+ let(:account) do
31
+ directory.accounts.create build_account(password: 'P@$$w0rd')
32
+ end
33
+
34
+ let(:login_request) do
35
+ Stormpath::Authentication::UsernamePasswordRequest.new account.username, password
36
+ end
37
+
38
+ let(:authentication_result) do
39
+ application.authenticate_account login_request
40
+ end
41
+
42
+ after do
43
+ account.delete if account
44
+ end
45
+
46
+ context 'given a valid username and password' do
47
+ let(:password) {'P@$$w0rd' }
48
+
49
+ it 'returns an authentication result' do
50
+ expect(authentication_result).to be
51
+ expect(authentication_result.account).to be
52
+ expect(authentication_result.account).to be_kind_of Stormpath::Resource::Account
53
+ expect(authentication_result.account.email).to eq(account.email)
54
+ end
55
+ end
56
+
57
+ context 'given an invalid username and password' do
58
+ let(:password) { 'b@dP@$$w0rd' }
59
+
60
+ it 'raises an error' do
61
+ expect { authentication_result }.to raise_error Stormpath::Error
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '#send_password_reset_email' do
67
+ context 'given an email' do
68
+ context 'of an exisiting account on the application' do
69
+ let(:account) { directory.accounts.create build_account }
70
+
71
+ let(:sent_to_account) { application.send_password_reset_email account.email }
72
+
73
+ after do
74
+ account.delete if account
75
+ end
76
+
77
+ it 'sends a password reset request of the account' do
78
+ expect(sent_to_account).to be
79
+ expect(sent_to_account).to be_kind_of Stormpath::Resource::Account
80
+ expect(sent_to_account.email).to eq(account.email)
81
+ end
82
+ end
83
+
84
+ context 'of a non exisitng account' do
85
+ it 'raises an exception' do
86
+ expect do
87
+ application.send_password_reset_email "test@example.com"
88
+ end.to raise_error Stormpath::Error
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#verify_password_reset_token' do
95
+ let(:account) do
96
+ directory.accounts.create({
97
+ email: 'test@example.com',
98
+ given_name: 'Ruby SDK',
99
+ password: 'P@$$w0rd',
100
+ surname: 'SDK',
101
+ username: 'rubysdk'
102
+ })
103
+ end
104
+
105
+ let(:password_reset_token) do
106
+ application.password_reset_tokens.create(email: account.email).token
107
+ end
108
+
109
+ let(:reset_password_account) do
110
+ application.verify_password_reset_token password_reset_token
111
+ end
112
+
113
+ after do
114
+ account.delete if account
115
+ end
116
+
117
+ it 'retrieves the account with the reset password' do
118
+ expect(reset_password_account).to be
119
+ expect(reset_password_account.email).to eq(account.email)
120
+ end
121
+
122
+ context 'and if the password is changed' do
123
+ let(:new_password) { 'N3wP@$$w0rd' }
124
+
125
+ let(:login_request) do
126
+ Stormpath::Authentication::UsernamePasswordRequest.new account.username, new_password
127
+ end
128
+
129
+ let(:expected_email) { 'test2@example.com' }
130
+
131
+ let(:authentication_result) do
132
+ application.authenticate_account login_request
133
+ end
134
+
135
+ before do
136
+ reset_password_account.password = new_password
137
+ reset_password_account.save
138
+ end
139
+
140
+ it 'can be successfully authenticated' do
141
+ expect(authentication_result).to be
142
+ expect(authentication_result.account).to be
143
+ expect(authentication_result.account).to be_kind_of Stormpath::Resource::Account
144
+ expect(authentication_result.account.email).to eq(account.email)
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stormpath::Resource::Base do
4
+ describe '.prop_accessor' do
5
+ context 'given property names' do
6
+ class TestResource < Stormpath::Resource::Base
7
+ prop_accessor :username, :given_name
8
+ end
9
+
10
+ let(:resource) do
11
+ TestResource.new({
12
+ 'username' => 'bar',
13
+ 'givenName' => 'foo'
14
+ }, nil)
15
+ end
16
+
17
+ it 'generates a getter method for each property' do
18
+ expect(resource.username).to eq('bar')
19
+ expect(resource.given_name).to eq('foo')
20
+ end
21
+
22
+ it 'generates a setter for each property' do
23
+ resource.username = 'foo'
24
+ resource.given_name = 'bar'
25
+ expect(resource.properties).to include('username' => 'foo')
26
+ expect(resource.properties).to include('givenName' => 'bar')
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '.non_printable' do
32
+ context 'given property names' do
33
+ class TestResource < Stormpath::Resource::Base
34
+ prop_non_printable :password
35
+ end
36
+
37
+ let(:resource) do
38
+ TestResource.new({
39
+ 'username' => 'bar',
40
+ 'password' => 'P@$$w0rd'
41
+ }, nil)
42
+ end
43
+
44
+ it 'marks that property as not being printable' do
45
+ expect(resource.inspect).to include('username')
46
+ expect(resource.inspect).to_not include('password')
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '.get_property' do
52
+ context 'given the name of a dirty property' do
53
+ class TestResource < Stormpath::Resource::Base
54
+ prop_accessor :username
55
+ end
56
+
57
+ let(:resource) do
58
+ TestResource.new('http://foo.com/test/123', nil).tap do |resource|
59
+ resource.username = 'foo'
60
+ end
61
+ end
62
+
63
+ it 'does NOT attempt to materialize the entire resource' do
64
+ expect(resource.username).to eq('foo')
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '#==' do
70
+ class TestResource < Stormpath::Resource::Base; end
71
+
72
+ context 'compared against an object of the same class' do
73
+ let(:resource) do
74
+ TestResource.new('http://foo.com/test/123')
75
+ end
76
+
77
+ context 'href matches' do
78
+ let(:other) do
79
+ TestResource.new('http://foo.com/test/123')
80
+ end
81
+
82
+ it 'passes' do
83
+ expect(resource).to eq(other)
84
+ end
85
+ end
86
+
87
+ context 'href does not match' do
88
+ let(:other) do
89
+ TestResource.new('http://foo.com/test/456')
90
+ end
91
+
92
+ it 'fails' do
93
+ expect(resource).to_not eq(other)
94
+ end
95
+ end
96
+ end
97
+
98
+ context 'compared against an object of another class' do
99
+ class NotAResource; end
100
+
101
+ let(:resource) do
102
+ TestResource.new('http://foo.com/test/123')
103
+ end
104
+
105
+ let(:other) do
106
+ NotAResource.new
107
+ end
108
+
109
+ it 'fails' do
110
+ expect(resource).to_not eq(other)
111
+ end
112
+ end
113
+ end
114
+ end