stormpath-sdk 1.0.0.beta.5 → 1.0.0.beta.6

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e9e44a0599b007cd69c76fd3ab6be4ed95685f5e
4
+ data.tar.gz: 6a627f9090aa67d81df6d94aa659a3c8697207cd
5
+ SHA512:
6
+ metadata.gz: 25e4af1d59e94e4c460f0a59d27a3bcad4d09516e8430a28e76ee7248d8077bfd1c2383a847a4eb266a8b9ace48073e74968296c9cf906ba0cfdfffdff55ea96
7
+ data.tar.gz: e538bee906bf915d706ccfb99d32a2dfd86c4bbf1c0dd51485690001964ebbc0dda8f4d9e5f94bc2e3959f5f0e11dfc27eedc4dd991d8fd6cf6a0ee43dc61462
data/.gitignore CHANGED
@@ -11,4 +11,6 @@ spec/fixtures/vcr_cassettes
11
11
  Guardfile
12
12
  tmp/*
13
13
  *.gem
14
- .bundle/
14
+ .bundle/
15
+ dump.rdb
16
+ .ruby-version
data/.travis.yml CHANGED
@@ -26,5 +26,8 @@ env:
26
26
  yvaGCsPr2h6rwtiMb8QHA4tMqHsoay0e9s7jyBtn6amgUaLsV2vdhQTWDuIU
27
27
  cksOE3PMIovYl9ANLb6KrhDWt7ue/fOxEALh0a5rAu50C/tNvgo=
28
28
 
29
+ language: ruby
30
+ rvm:
31
+ - 1.9.3
29
32
  services:
30
33
  - redis-server
data/CHANGES.md CHANGED
@@ -1,6 +1,16 @@
1
1
  stormpath-sdk-ruby Changelog
2
2
  ============================
3
3
 
4
+ Version 1.0.0.beta.6
5
+ --------------------
6
+
7
+ Released on July 7, 2014
8
+
9
+ - Fixed custom data deletion issue.
10
+ - Added resource pagination functionality.
11
+ - Fixed issue when searching by email.
12
+
13
+
4
14
  Version 1.0.0.beta.5
5
15
  --------------------
6
16
 
data/README.md CHANGED
@@ -495,8 +495,8 @@ The following environment variables need will then need to be set:
495
495
 
496
496
  ### Running
497
497
 
498
- Once properly configured, the tests can be run as the default
499
- <code>Rake<code> task:
498
+ Once properly configured, start the redis server with <code>redis-server</code> and the tests can be run as the default
499
+ <code>Rake</code> task:
500
500
 
501
501
  ```sh
502
502
  $ rake
data/lib/stormpath-sdk.rb CHANGED
@@ -6,6 +6,7 @@ require "open-uri"
6
6
  require "uri"
7
7
  require "uuidtools"
8
8
  require "yaml"
9
+ require 'active_support'
9
10
  require "active_support/core_ext"
10
11
  require 'active_support/core_ext/module/delegation'
11
12
  require 'active_support/core_ext/kernel/singleton_class'
@@ -55,18 +56,24 @@ module Stormpath
55
56
  autoload :CacheStats, 'stormpath-sdk/cache/cache_stats'
56
57
  autoload :MemoryStore, 'stormpath-sdk/cache/memory_store'
57
58
  autoload :RedisStore, 'stormpath-sdk/cache/redis_store'
59
+ autoload :DisabledCacheStore, 'stormpath-sdk/cache/disabled_cache_store'
58
60
  end
59
61
 
60
62
  module Authentication
63
+ autoload :UsernamePasswordRequest, "stormpath-sdk/auth/username_password_request"
64
+ autoload :BasicLoginAttempt, "stormpath-sdk/auth/basic_login_attempt"
65
+ autoload :AuthenticationResult, "stormpath-sdk/auth/authentication_result"
66
+ autoload :BasicAuthenticator, "stormpath-sdk/auth/basic_authenticator"
61
67
  end
62
- end
63
68
 
64
- require "stormpath-sdk/auth/username_password_request"
65
- require 'stormpath-sdk/http/utils'
66
- require "stormpath-sdk/http/request"
67
- require "stormpath-sdk/http/response"
68
- require "stormpath-sdk/http/authc/sauthc1_signer"
69
- require "stormpath-sdk/http/http_client_request_executor"
70
- require "stormpath-sdk/auth/basic_login_attempt"
71
- require "stormpath-sdk/auth/authentication_result"
72
- require "stormpath-sdk/auth/basic_authenticator"
69
+ module Http
70
+ autoload :Utils, "stormpath-sdk/http/utils"
71
+ autoload :Request, "stormpath-sdk/http/request"
72
+ autoload :Response, "stormpath-sdk/http/response"
73
+ autoload :HttpClientRequestExecutor, "stormpath-sdk/http/http_client_request_executor"
74
+
75
+ module Authc
76
+ autoload :Sauthc1Signer, "stormpath-sdk/http/authc/sauthc1_signer"
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,26 @@
1
+ module Stormpath
2
+ module Cache
3
+ class DisabledCacheStore
4
+ def initialize(opts = nil)
5
+ end
6
+
7
+ def get(key)
8
+ end
9
+
10
+ def put(key, entry)
11
+ entry
12
+ end
13
+
14
+ def delete(key)
15
+ end
16
+
17
+ def clear
18
+ {}
19
+ end
20
+
21
+ def size
22
+ 0
23
+ end
24
+ end
25
+ end
26
+ end
@@ -16,9 +16,9 @@
16
16
  require 'java_properties'
17
17
 
18
18
  module Stormpath
19
-
20
19
  class Client
21
20
  include Stormpath::Util::Assert
21
+ include Stormpath::Resource::Associations
22
22
 
23
23
  attr_reader :data_store, :application
24
24
 
@@ -38,9 +38,8 @@ module Stormpath
38
38
  options[:api_key_secret_property_name]
39
39
  end
40
40
 
41
- assert_not_nil api_key, "No API key has been provided. Please " +
42
- "pass an 'api_key' or 'api_key_file_location' to the " +
43
- "Stormpath::Client constructor."
41
+ assert_not_nil api_key, "No API key has been provided. Please pass an 'api_key' or " +
42
+ "'api_key_file_location' to the Stormpath::Client constructor."
44
43
 
45
44
  request_executor = Stormpath::Http::HttpClientRequestExecutor.new(api_key, proxy: options[:proxy])
46
45
  @data_store = Stormpath::DataStore.new(request_executor, cache_opts, self, base_url)
@@ -58,18 +57,13 @@ module Stormpath
58
57
  @data_source.cache_stats
59
58
  end
60
59
 
61
- include Stormpath::Resource::Associations
62
-
63
60
  has_many :tenants, href: '/tenants', can: :get
64
61
  has_many :applications, href: '/applications', can: [:get, :create], delegate: true
65
62
  has_many :directories, href: '/directories', can: [:get, :create], delegate: true
66
- has_many(:accounts, href: '/accounts', can: :get) do
63
+ has_many :accounts, href: '/accounts', can: :get do
67
64
  def verify_email_token(token)
68
65
  token_href = "#{href}/emailVerificationTokens/#{token}"
69
- token = Stormpath::Resource::EmailVerificationToken.new(
70
- token_href,
71
- client
72
- )
66
+ token = Stormpath::Resource::EmailVerificationToken.new token_href, client
73
67
  data_store.save token, Stormpath::Resource::Account
74
68
  end
75
69
  end
@@ -77,38 +71,32 @@ module Stormpath
77
71
  has_many :group_memberships, href: '/groupMemberships', can: [:get, :create]
78
72
  has_many :account_store_mappings, href: '/accountStoreMappings', can: [:get, :create]
79
73
 
80
-
81
74
  private
82
75
 
83
- def load_api_key_file(api_key_file_location, id_property_name, secret_property_name)
84
- begin
85
- api_key_properties = JavaProperties::Properties.new api_key_file_location
86
- rescue
87
- raise ArgumentError,
88
- "No API Key file could be found or loaded from '" +
89
- api_key_file_location +
90
- "'."
91
- end
76
+ def load_api_key_file api_key_file_location, id_property_name, secret_property_name
77
+ begin
78
+ api_key_properties = JavaProperties::Properties.new api_key_file_location
79
+ rescue
80
+ raise ArgumentError, "No API Key file could be found or loaded from '#{api_key_file_location}'."
81
+ end
92
82
 
93
- id_property_name ||= 'apiKey.id'
94
- secret_property_name ||= 'apiKey.secret'
83
+ id_property_name ||= 'apiKey.id'
84
+ secret_property_name ||= 'apiKey.secret'
95
85
 
96
- api_key_id = api_key_properties[id_property_name]
97
- assert_not_nil api_key_id,
98
- "No API id in properties. Please provide a 'apiKey.id' property in '" +
99
- api_key_file_location +
100
- "' or pass in an 'api_key_id_property_name' to the Stormpath::Client " +
101
- "constructor to specify an alternative property."
86
+ api_key_id = api_key_properties[id_property_name]
87
+ assert_not_nil api_key_id, api_key_warning_message(:id, api_key_file_location)
102
88
 
103
- api_key_secret = api_key_properties[secret_property_name]
104
- assert_not_nil api_key_secret,
105
- "No API secret in properties. Please provide a 'apiKey.secret' property in '" +
106
- api_key_file_location +
107
- "' or pass in an 'api_key_secret_property_name' to the Stormpath::Client " +
108
- "constructor to specify an alternative property."
89
+ api_key_secret = api_key_properties[secret_property_name]
90
+ assert_not_nil api_key_secret, api_key_warning_message(:secret, api_key_file_location)
109
91
 
110
- ApiKey.new api_key_id, api_key_secret
111
- end
92
+ ApiKey.new api_key_id, api_key_secret
93
+ end
94
+
95
+ def api_key_warning_message id_or_secret, api_key_file_location
96
+ "No API #{id_or_secret} in properties. Please provide a 'apiKey.#{id_or_secret}' property " +
97
+ "in '#{api_key_file_location}' or pass in an 'api_key_#{id_or_secret}_property_name' " +
98
+ "to the Stormpath::Client constructor to specify an alternative property."
99
+ end
112
100
 
113
101
  end
114
102
  end
@@ -19,6 +19,7 @@ class Stormpath::DataStore
19
19
 
20
20
  DEFAULT_SERVER_HOST = "api.stormpath.com"
21
21
  DEFAULT_API_VERSION = 1
22
+ DEFAULT_BASE_URL = "https://" + DEFAULT_SERVER_HOST + "/v" + DEFAULT_API_VERSION.to_s
22
23
  HREF_PROP_NAME = Stormpath::Resource::Base::HREF_PROP_NAME
23
24
 
24
25
  CACHE_REGIONS = %w( applications directories accounts groups groupMemberships accountMemberships tenants customData )
@@ -29,7 +30,7 @@ class Stormpath::DataStore
29
30
  assert_not_nil request_executor, "RequestExecutor cannot be null."
30
31
 
31
32
  @client = client
32
- @base_url = get_base_url(base_url)
33
+ @base_url = base_url || DEFAULT_BASE_URL
33
34
  @request_executor = request_executor
34
35
  initialize_cache cache_opts
35
36
  end
@@ -68,7 +69,6 @@ class Stormpath::DataStore
68
69
  def save(resource, clazz = nil)
69
70
  assert_not_nil resource, "resource argument cannot be null."
70
71
  assert_kind_of Stormpath::Resource::Base, resource, "resource argument must be instance of Stormpath::Resource::Base"
71
-
72
72
  href = resource.href
73
73
  assert_not_nil href, "href or resource.href cannot be null."
74
74
  assert_true href.length > 0, "save may only be called on objects that have already been persisted (i.e. they have an existing href)."
@@ -100,12 +100,7 @@ class Stormpath::DataStore
100
100
  end
101
101
 
102
102
  def qualify(href)
103
- if needs_to_be_fully_qualified(href)
104
- slash_added = href.start_with?('/') ? '' : '/'
105
- @base_url + slash_added + href
106
- else
107
- href
108
- end
103
+ needs_to_be_fully_qualified(href) ? @base_url + href : href
109
104
  end
110
105
 
111
106
  def execute_request(http_method, href, body=nil, query=nil)
@@ -121,37 +116,51 @@ class Stormpath::DataStore
121
116
 
122
117
  if response.error?
123
118
  error = Stormpath::Resource::Error.new result
124
- #puts "Error with request: #{http_method.upcase}: #{href}"
125
119
  raise Stormpath::Error.new error
126
120
  end
127
121
 
128
122
  if http_method == 'delete'
129
- cache = cache_for href
130
- cache.delete href if cache
123
+ clear_cache_on_delete(href)
131
124
  return nil
132
125
  end
133
126
 
134
- if result['href']
127
+ if result[HREF_PROP_NAME]
135
128
  cache_walk result
136
129
  else
137
130
  result
138
131
  end
139
132
  end
140
133
 
134
+ def clear_cache_on_delete href
135
+ if href =~ custom_data_delete_field_url_regex
136
+ href = href.split('/')[0..-2].join('/')
137
+ end
138
+ clear_cache href
139
+ end
140
+
141
+ def custom_data_delete_field_url_regex
142
+ /#{@base_url}\/(accounts|groups)\/\w+\/customData\/\w+[\/]{0,1}$/
143
+ end
144
+
145
+ def clear_cache(href)
146
+ cache = cache_for href
147
+ cache.delete href if cache
148
+ end
149
+
141
150
  def cache_walk(resource)
142
- assert_not_nil resource['href'], "resource must have 'href' property"
151
+ assert_not_nil resource[HREF_PROP_NAME], "resource must have 'href' property"
143
152
  items = resource['items']
144
153
 
145
154
  if items # collection resource
146
155
  resource['items'] = items.map do |item|
147
156
  cache_walk item
148
- { 'href' => item['href'] }
157
+ { HREF_PROP_NAME => item[HREF_PROP_NAME] }
149
158
  end
150
159
  else # single resource
151
160
  resource.each do |attr, value|
152
- if value.is_a? Hash and value['href']
161
+ if value.is_a? Hash and value[HREF_PROP_NAME]
153
162
  walked = cache_walk value
154
- resource[attr] = { 'href' => value['href'] } if value["href"]
163
+ resource[attr] = { HREF_PROP_NAME => value[HREF_PROP_NAME] }
155
164
  resource[attr]['items'] = walked['items'] if walked['items']
156
165
  end
157
166
  end
@@ -161,8 +170,8 @@ class Stormpath::DataStore
161
170
  end
162
171
 
163
172
  def cache(resource)
164
- cache = cache_for resource['href']
165
- cache.put resource['href'], resource if cache
173
+ cache = cache_for resource[HREF_PROP_NAME]
174
+ cache.put resource[HREF_PROP_NAME], resource if cache
166
175
  end
167
176
 
168
177
  def cache_for(href)
@@ -170,7 +179,7 @@ class Stormpath::DataStore
170
179
  end
171
180
 
172
181
  def region_for(href)
173
- return nil unless href
182
+ return nil if href.nil?
174
183
  if href.include? "/customData"
175
184
  region = href.split('/')[-1]
176
185
  else
@@ -183,7 +192,7 @@ class Stormpath::DataStore
183
192
  request.http_headers.store 'Accept', 'application/json'
184
193
  request.http_headers.store 'User-Agent', 'Stormpath-RubySDK/' + Stormpath::VERSION
185
194
 
186
- if !request.body.nil? and request.body.length > 0
195
+ if request.body and request.body.length > 0
187
196
  request.http_headers.store 'Content-Type', 'application/json'
188
197
  end
189
198
  end
@@ -195,19 +204,46 @@ class Stormpath::DataStore
195
204
 
196
205
  q_href = qualify href
197
206
 
198
- response = execute_request('post', q_href, MultiJson.dump(to_hash(resource)))
207
+ clear_cache_on_save(resource)
208
+
209
+ response = execute_request 'post', q_href, MultiJson.dump(to_hash(resource))
199
210
 
200
211
  instantiate return_type, response.to_hash
201
212
  end
202
213
 
203
- def get_base_url(base_url)
204
- base_url || "https://" + DEFAULT_SERVER_HOST + "/v" + DEFAULT_API_VERSION.to_s
214
+ def clear_cache_on_save(resource)
215
+ if resource.is_a? Stormpath::Resource::CustomDataStorage
216
+ clear_custom_data_cache_on_custom_data_storage_save(resource)
217
+ elsif resource.is_a? Stormpath::Resource::AccountStoreMapping
218
+ clear_application_cache_on_account_store_save(resource)
219
+ end
220
+ end
221
+
222
+
223
+ def clear_custom_data_cache_on_custom_data_storage_save resource
224
+ if resource.dirty_properties.has_key? "customData" and resource.new? == false
225
+ cached_href = resource.href + "/customData"
226
+ clear_cache cached_href
227
+ end
228
+ end
229
+
230
+ def clear_application_cache_on_account_store_save resource
231
+ if resource.new?
232
+ if resource.default_account_store? == true || resource.default_group_store? == true
233
+ clear_cache resource.application.href
234
+ end
235
+ else
236
+ if resource.dirty_properties["isDefaultAccountStore"] != nil || resource.dirty_properties["isDefaultGroupStore"] != nil
237
+ clear_cache resource.application.href
238
+ end
239
+ end
205
240
  end
206
241
 
207
242
  def to_hash(resource)
208
243
  Hash.new.tap do |properties|
209
244
  resource.get_dirty_property_names.each do |name|
210
- property = resource.get_property name
245
+ ignore_camelcasing = resource_not_custom_data(resource, name) ? false : true
246
+ property = resource.get_property name, ignore_camelcasing: ignore_camelcasing
211
247
 
212
248
  # Special use case is with Custom Data, it's hashes should not be simplified
213
249
  if property.kind_of?(Hash) and resource_not_custom_data resource, name
@@ -14,13 +14,9 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  module Stormpath
17
-
18
17
  module Http
19
-
20
18
  module Authc
21
-
22
19
  class Sauthc1Signer
23
-
24
20
  include OpenSSL
25
21
  include UUIDTools
26
22
  include Stormpath::Http::Utils
@@ -45,7 +41,6 @@ module Stormpath
45
41
  end
46
42
 
47
43
  def sign_request request, api_key
48
-
49
44
  request.http_headers.delete(Sauthc1Signer::AUTHORIZATION_HEADER)
50
45
  request.http_headers.delete(Sauthc1Signer::STORMPATH_DATE_HEADER)
51
46
 
@@ -60,8 +55,8 @@ module Stormpath
60
55
  # SAuthc1 requires that we sign the Host header so we
61
56
  # have to have it in the request by the time we sign.
62
57
  host_header = uri.host
63
- if !default_port?(uri)
64
58
 
59
+ if !default_port?(uri)
65
60
  host_header << ":" << uri.port.to_s
66
61
  end
67
62
 
@@ -112,128 +107,96 @@ module Stormpath
112
107
 
113
108
 
114
109
  def to_hex data
115
-
116
110
  result = ''
117
- data.each_byte { |val|
118
111
 
112
+ data.each_byte do |val|
119
113
  hex = val.to_s(16)
120
114
 
121
115
  if hex.length == 1
122
-
123
116
  result << '0'
124
-
125
117
  elsif hex.length == 8
126
-
127
118
  hex = hex[0..6]
128
119
  end
129
120
 
130
121
  result << hex
131
-
132
- }
133
-
122
+ end
134
123
  result
135
-
136
124
  end
137
125
 
138
126
  protected
139
127
 
140
- def canonicalize_query_string request
141
- request.to_s_query_string true
142
- end
143
-
144
- def hash_text text
145
- Digest.digest DEFAULT_ALGORITHM, to_utf8(text)
146
- end
147
-
148
- def sign data, key, algorithm
149
-
150
- digest_data = to_utf8 data
151
-
152
- digest = Digest::Digest.new(algorithm)
153
-
154
- HMAC.digest(digest, key, digest_data)
155
-
156
- end
157
-
158
- def to_utf8 str
159
- #we ask for multi line UTF-8 text
160
- str.scan(/./mu).join
161
- end
162
-
163
- def get_request_payload request
164
- get_request_payload_without_query_params request
165
- end
166
-
167
- def get_request_payload_without_query_params request
168
-
169
- result = ''
170
-
171
- if !request.body.nil?
172
- result = request.body
128
+ def canonicalize_query_string request
129
+ request.to_s_query_string true
173
130
  end
174
131
 
175
- result
176
-
177
- end
178
-
179
- private
180
-
181
- def create_name_value_pair name, value
182
- name + '=' + value
183
- end
184
-
185
- def canonicalize_resource_path resource_path
186
-
187
- if resource_path.nil? or resource_path.empty?
188
- '/'
189
- else
190
- encode_url resource_path, true, true
132
+ def hash_text text
133
+ Digest.digest DEFAULT_ALGORITHM, to_utf8(text)
191
134
  end
192
- end
193
135
 
194
-
195
- def canonicalize_headers request
196
-
197
- sorted_headers = request.http_headers.keys.sort!
198
-
199
- result = ''
200
-
201
- sorted_headers.each do |header|
202
-
203
- result << header.downcase << ':' << request.http_headers[header].to_s
204
-
205
- result << NL
136
+ def sign data, key, algorithm
137
+ digest_data = to_utf8 data
138
+ digest = Digest.new(algorithm)
139
+ HMAC.digest(digest, key, digest_data)
206
140
  end
207
141
 
208
- result
142
+ def to_utf8 str
143
+ #we ask for multi line UTF-8 text
144
+ str.scan(/./mu).join
145
+ end
209
146
 
210
- end
147
+ def get_request_payload request
148
+ get_request_payload_without_query_params request
149
+ end
211
150
 
212
- def get_signed_headers request
151
+ def get_request_payload_without_query_params request
152
+ result = ''
153
+ if !request.body.nil?
154
+ result = request.body
155
+ end
156
+ result
157
+ end
213
158
 
214
- sorted_headers = request.http_headers.keys.sort!
159
+ private
215
160
 
216
- result = ''
217
- sorted_headers.each do |header|
161
+ def create_name_value_pair name, value
162
+ name + '=' + value
163
+ end
218
164
 
219
- if !result.empty?
220
- result << ';' << header
165
+ def canonicalize_resource_path resource_path
166
+ if resource_path.nil? or resource_path.empty?
167
+ '/'
221
168
  else
222
- result << header
169
+ encode_url resource_path, true, true
223
170
  end
224
-
225
-
226
171
  end
227
172
 
228
- result.downcase
229
-
230
- end
231
173
 
174
+ def canonicalize_headers request
175
+ sorted_headers = request.http_headers.keys.sort!
176
+ result = ''
232
177
 
233
- end
178
+ sorted_headers.each do |header|
179
+ result << header.downcase << ':' << request.http_headers[header].to_s
180
+ result << NL
181
+ end
182
+ result
183
+ end
234
184
 
235
- end
185
+ def get_signed_headers request
186
+ sorted_headers = request.http_headers.keys.sort!
187
+ result = ''
188
+ sorted_headers.each do |header|
189
+ if !result.empty?
190
+ result << ';' << header
191
+ else
192
+ result << header
193
+ end
194
+ end
195
+ result.downcase
196
+ end
236
197
 
237
- end
238
198
 
239
- end
199
+ end#Sauthc1Signer
200
+ end#Authc
201
+ end#Http
202
+ end#Stormpath