stormpath-sdk 0.4.0 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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