stormpath-sdk 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +21 -0
  3. data/CHANGES.md +11 -0
  4. data/README.md +23 -25
  5. data/lib/stormpath-sdk.rb +11 -2
  6. data/lib/stormpath-sdk/api_key.rb +0 -1
  7. data/lib/stormpath-sdk/auth/basic_authenticator.rb +7 -7
  8. data/lib/stormpath-sdk/auth/basic_login_attempt.rb +7 -11
  9. data/lib/stormpath-sdk/auth/create_factor.rb +1 -1
  10. data/lib/stormpath-sdk/auth/register_service_provider.rb +41 -0
  11. data/lib/stormpath-sdk/auth/username_password_request.rb +3 -5
  12. data/lib/stormpath-sdk/cache/cache.rb +3 -3
  13. data/lib/stormpath-sdk/cache/cache_entry.rb +2 -2
  14. data/lib/stormpath-sdk/cache/cache_manager.rb +3 -4
  15. data/lib/stormpath-sdk/cache/cache_stats.rb +1 -3
  16. data/lib/stormpath-sdk/cache/disabled_cache_store.rb +5 -8
  17. data/lib/stormpath-sdk/cache/memory_store.rb +1 -1
  18. data/lib/stormpath-sdk/cache/redis_store.rb +4 -4
  19. data/lib/stormpath-sdk/client.rb +35 -33
  20. data/lib/stormpath-sdk/data_store.rb +278 -257
  21. data/lib/stormpath-sdk/error.rb +18 -7
  22. data/lib/stormpath-sdk/http/authc/sauthc1_signer.rb +76 -82
  23. data/lib/stormpath-sdk/http/http_client_request_executor.rb +10 -8
  24. data/lib/stormpath-sdk/http/response.rb +5 -7
  25. data/lib/stormpath-sdk/id_site/id_site_result.rb +5 -6
  26. data/lib/stormpath-sdk/oauth/access_token_authentication_result.rb +5 -9
  27. data/lib/stormpath-sdk/oauth/authenticator.rb +2 -2
  28. data/lib/stormpath-sdk/oauth/error.rb +4 -4
  29. data/lib/stormpath-sdk/oauth/id_site_grant_request.rb +1 -1
  30. data/lib/stormpath-sdk/oauth/password_grant_request.rb +1 -1
  31. data/lib/stormpath-sdk/oauth/refresh_grant_request.rb +2 -2
  32. data/lib/stormpath-sdk/oauth/stormpath_grant_request.rb +2 -2
  33. data/lib/stormpath-sdk/provider/account_access.rb +0 -2
  34. data/lib/stormpath-sdk/provider/account_result.rb +1 -2
  35. data/lib/stormpath-sdk/provider/facebook/facebook_provider.rb +6 -2
  36. data/lib/stormpath-sdk/provider/facebook/facebook_provider_data.rb +7 -3
  37. data/lib/stormpath-sdk/provider/github/github_provider.rb +6 -2
  38. data/lib/stormpath-sdk/provider/github/github_provider_data.rb +6 -2
  39. data/lib/stormpath-sdk/provider/google/google_provider.rb +7 -3
  40. data/lib/stormpath-sdk/provider/google/google_provider_data.rb +6 -2
  41. data/lib/stormpath-sdk/provider/linkedin/linkedin_provider.rb +6 -2
  42. data/lib/stormpath-sdk/provider/linkedin/linkedin_provider_data.rb +6 -2
  43. data/lib/stormpath-sdk/provider/provider.rb +8 -4
  44. data/lib/stormpath-sdk/provider/provider_data.rb +6 -2
  45. data/lib/stormpath-sdk/provider/saml/saml_provider.rb +10 -4
  46. data/lib/stormpath-sdk/provider/saml/saml_provider_data.rb +6 -3
  47. data/lib/stormpath-sdk/provider/stormpath/stormpath_provider.rb +6 -2
  48. data/lib/stormpath-sdk/provider/stormpath/stormpath_provider_data.rb +6 -2
  49. data/lib/stormpath-sdk/provider/twitter/twitter_provider.rb +6 -2
  50. data/lib/stormpath-sdk/provider/twitter/twitter_provider_data.rb +6 -2
  51. data/lib/stormpath-sdk/resource/account.rb +46 -40
  52. data/lib/stormpath-sdk/resource/account_link.rb +9 -5
  53. data/lib/stormpath-sdk/resource/account_linking_policy.rb +8 -4
  54. data/lib/stormpath-sdk/resource/account_membership.rb +1 -1
  55. data/lib/stormpath-sdk/resource/account_overrides.rb +20 -16
  56. data/lib/stormpath-sdk/resource/account_store.rb +15 -11
  57. data/lib/stormpath-sdk/resource/account_store_mapping.rb +14 -13
  58. data/lib/stormpath-sdk/resource/application.rb +147 -136
  59. data/lib/stormpath-sdk/resource/application_web_config.rb +11 -7
  60. data/lib/stormpath-sdk/resource/associations.rb +36 -43
  61. data/lib/stormpath-sdk/resource/attribute_statement_mapping_rules.rb +8 -0
  62. data/lib/stormpath-sdk/resource/base.rb +201 -200
  63. data/lib/stormpath-sdk/resource/challenge.rb +12 -8
  64. data/lib/stormpath-sdk/resource/collection.rb +77 -76
  65. data/lib/stormpath-sdk/resource/custom_data.rb +60 -61
  66. data/lib/stormpath-sdk/resource/custom_data_hash_methods.rb +28 -25
  67. data/lib/stormpath-sdk/resource/custom_data_storage.rb +18 -16
  68. data/lib/stormpath-sdk/resource/directory.rb +37 -60
  69. data/lib/stormpath-sdk/resource/email_verification_token.rb +7 -3
  70. data/lib/stormpath-sdk/resource/error.rb +8 -4
  71. data/lib/stormpath-sdk/resource/expansion.rb +22 -20
  72. data/lib/stormpath-sdk/resource/factor.rb +12 -8
  73. data/lib/stormpath-sdk/resource/field.rb +8 -4
  74. data/lib/stormpath-sdk/resource/group.rb +21 -16
  75. data/lib/stormpath-sdk/resource/group_membership.rb +7 -5
  76. data/lib/stormpath-sdk/resource/instance.rb +10 -6
  77. data/lib/stormpath-sdk/resource/linked_account.rb +7 -3
  78. data/lib/stormpath-sdk/resource/oauth_policy.rb +7 -3
  79. data/lib/stormpath-sdk/resource/organization.rb +14 -10
  80. data/lib/stormpath-sdk/resource/organization_account_store_mapping.rb +8 -4
  81. data/lib/stormpath-sdk/resource/password_reset_token.rb +9 -5
  82. data/lib/stormpath-sdk/resource/phone.rb +8 -4
  83. data/lib/stormpath-sdk/resource/registered_saml_service_provider.rb +8 -0
  84. data/lib/stormpath-sdk/resource/saml_identity_provider.rb +14 -0
  85. data/lib/stormpath-sdk/resource/saml_identity_provider_metadata.rb +9 -0
  86. data/lib/stormpath-sdk/resource/saml_policy.rb +10 -0
  87. data/lib/stormpath-sdk/resource/saml_service_provider.rb +7 -0
  88. data/lib/stormpath-sdk/{provider/saml/saml_mapping_rules.rb → resource/saml_service_provider_metadata.rb} +6 -5
  89. data/lib/stormpath-sdk/resource/saml_service_provider_registration.rb +11 -0
  90. data/lib/stormpath-sdk/resource/schema.rb +8 -4
  91. data/lib/stormpath-sdk/resource/tenant.rb +11 -8
  92. data/lib/stormpath-sdk/resource/user_info_mapping_rules.rb +7 -3
  93. data/lib/stormpath-sdk/resource/utils.rb +7 -10
  94. data/lib/stormpath-sdk/resource/verification_email.rb +7 -3
  95. data/lib/stormpath-sdk/resource/x_509_certificate.rb +7 -0
  96. data/lib/stormpath-sdk/util/assert.rb +1 -3
  97. data/lib/stormpath-sdk/version.rb +2 -2
  98. data/spec/auth/basic_authenticator_spec.rb +28 -24
  99. data/spec/auth/register_service_provider_spec.rb +68 -0
  100. data/spec/auth/sauthc1_signer_spec.rb +8 -4
  101. data/spec/cache/cache_entry_spec.rb +28 -29
  102. data/spec/cache/cache_spec.rb +9 -9
  103. data/spec/cache/cache_stats_spec.rb +1 -1
  104. data/spec/client_spec.rb +63 -63
  105. data/spec/data_store_spec.rb +23 -14
  106. data/spec/oauth/access_token_authentication_result_spec.rb +8 -2
  107. data/spec/provider/account_resolver_spec.rb +6 -4
  108. data/spec/provider/provider_spec.rb +6 -6
  109. data/spec/resource/account_creation_policy_spec.rb +1 -1
  110. data/spec/resource/account_link_spec.rb +7 -15
  111. data/spec/resource/account_spec.rb +17 -17
  112. data/spec/resource/account_store_mapping_spec.rb +16 -22
  113. data/spec/resource/account_store_spec.rb +3 -3
  114. data/spec/resource/application_spec.rb +324 -330
  115. data/spec/resource/base_spec.rb +7 -31
  116. data/spec/resource/collection_spec.rb +63 -114
  117. data/spec/resource/custom_data_spec.rb +1 -1
  118. data/spec/resource/directory_spec.rb +91 -87
  119. data/spec/resource/expansion_spec.rb +10 -10
  120. data/spec/resource/factor_spec.rb +1 -1
  121. data/spec/resource/group_spec.rb +1 -1
  122. data/spec/resource/linked_account_spec.rb +7 -7
  123. data/spec/resource/organization_spec.rb +12 -11
  124. data/spec/resource/phone_spec.rb +1 -1
  125. data/spec/resource/registered_saml_service_provider_spec.rb +35 -0
  126. data/spec/resource/saml_identity_provider_metadata_spec.rb +27 -0
  127. data/spec/resource/saml_identity_provider_spec.rb +94 -0
  128. data/spec/resource/saml_policy_spec.rb +27 -0
  129. data/spec/resource/saml_service_provider_registration_spec.rb +58 -0
  130. data/spec/resource/saml_service_provider_spec.rb +19 -0
  131. data/spec/resource/status_spec.rb +4 -3
  132. data/spec/resource/tenant_spec.rb +4 -6
  133. data/spec/spec_helper.rb +1 -1
  134. data/spec/support/custom_data_save_period.rb +4 -0
  135. data/spec/support/custom_data_storage_behavior.rb +7 -8
  136. data/spec/support/mocked_provider_accounts.rb +101 -101
  137. data/spec/support/mocked_saml_responses.rb +130 -0
  138. data/spec/support/resource_factory.rb +4 -4
  139. data/spec/support/resource_helpers.rb +10 -4
  140. data/spec/support/resource_matchers.rb +4 -4
  141. data/spec/support/test_request_executor.rb +2 -2
  142. metadata +21 -8
  143. data/lib/stormpath-sdk/provider/saml/saml_provider_metadata.rb +0 -19
  144. data/spec/fixtures/response/create_saml_directory.json +0 -26
  145. data/spec/fixtures/response/create_saml_directory_mapping_rules.json +0 -12
  146. data/spec/fixtures/response/get_saml_directory_provider.json +0 -16
  147. data/spec/fixtures/response/get_saml_directory_provider_metadata.json +0 -12
@@ -1,13 +1,12 @@
1
1
  module Stormpath
2
2
  module Cache
3
3
  class CacheManager
4
-
5
- def initialize(opts = nil)
4
+ def initialize(_opts = nil)
6
5
  @caches = {}
7
6
  end
8
7
 
9
8
  def create_cache(region, opts)
10
- @caches[region] = Cache.new opts
9
+ @caches[region] = Cache.new(opts)
11
10
  end
12
11
 
13
12
  def get_cache(region)
@@ -15,7 +14,7 @@ module Stormpath
15
14
  end
16
15
 
17
16
  def stats
18
- Hash[ @caches.map { |region, cache| [region, cache.stats] } ]
17
+ Hash[@caches.map { |region, cache| [region, cache.stats] }]
19
18
  end
20
19
  end
21
20
  end
@@ -22,9 +22,7 @@ module Stormpath
22
22
  end
23
23
 
24
24
  def delete
25
- if @size > 0
26
- @size -= 1
27
- end
25
+ @size -= 1 if @size > 0
28
26
  end
29
27
 
30
28
  def summary
@@ -1,18 +1,15 @@
1
1
  module Stormpath
2
2
  module Cache
3
3
  class DisabledCacheStore
4
- def initialize(opts = nil)
5
- end
4
+ def initialize(opts = nil); end
6
5
 
7
- def get(key)
8
- end
6
+ def get(key); end
9
7
 
10
- def put(key, entry)
8
+ def put(_key, entry)
11
9
  entry
12
10
  end
13
11
 
14
- def delete(key)
15
- end
12
+ def delete(key); end
16
13
 
17
14
  def clear
18
15
  {}
@@ -23,4 +20,4 @@ module Stormpath
23
20
  end
24
21
  end
25
22
  end
26
- end
23
+ end
@@ -14,7 +14,7 @@ module Stormpath
14
14
  end
15
15
 
16
16
  def delete(key)
17
- @store.delete key
17
+ @store.delete(key)
18
18
  end
19
19
 
20
20
  def clear
@@ -4,20 +4,20 @@ module Stormpath
4
4
  module Cache
5
5
  class RedisStore
6
6
  def initialize(opts = {})
7
- @redis = Redis.new opts
7
+ @redis = Redis.new(opts)
8
8
  end
9
9
 
10
10
  def get(key)
11
- entry = @redis.get key
11
+ entry = @redis.get(key)
12
12
  entry && Stormpath::Cache::CacheEntry.from_h(MultiJson.load(entry))
13
13
  end
14
14
 
15
15
  def put(key, entry)
16
- @redis.set key, MultiJson.dump(entry.to_h)
16
+ @redis.set(key, MultiJson.dump(entry.to_h))
17
17
  end
18
18
 
19
19
  def delete(key)
20
- @redis.del key
20
+ @redis.del(key)
21
21
  end
22
22
 
23
23
  def clear
@@ -28,7 +28,7 @@ module Stormpath
28
28
 
29
29
  api_key = ApiKey(options)
30
30
 
31
- assert_not_nil api_key, "No API key has been provided. Please pass an 'api_key' or " +
31
+ assert_not_nil api_key, "No API key has been provided. Please pass an 'api_key' or " \
32
32
  "'api_key_file_location' to the Stormpath::Client constructor."
33
33
 
34
34
  request_executor = Stormpath::Http::HttpClientRequestExecutor.new(proxy: options[:proxy])
@@ -36,7 +36,7 @@ module Stormpath
36
36
  end
37
37
 
38
38
  def tenant(expansion = nil)
39
- tenants.get 'current', expansion
39
+ tenants.get('current', expansion)
40
40
  end
41
41
 
42
42
  def client
@@ -49,7 +49,7 @@ module Stormpath
49
49
  has_many :accounts, href: '/accounts', can: :get do
50
50
  def verify_email_token(token)
51
51
  token_href = "#{href}/emailVerificationTokens/#{token}"
52
- token = Stormpath::Resource::EmailVerificationToken.new token_href, client
52
+ token = Stormpath::Resource::EmailVerificationToken.new(token_href, client)
53
53
  data_store.save token, Stormpath::Resource::Account
54
54
  end
55
55
  end
@@ -61,46 +61,48 @@ module Stormpath
61
61
  has_many :access_tokens, href: '/accessTokens', can: [:get]
62
62
  has_many :refresh_tokens, href: '/refreshTokens', can: [:get]
63
63
  has_many :account_links, href: '/accountLinks', can: :create
64
+ has_many :registered_saml_service_providers, href: '/registeredSamlServiceProviders', can: :create
64
65
 
65
66
  private
66
67
 
67
- def ApiKey(options={})
68
- if api_key = options[:api_key]
69
- case api_key
70
- when ApiKey then api_key
71
- when Hash then ApiKey.new api_key[:id], api_key[:secret]
72
- end
73
- elsif options[:api_key_file_location]
74
- load_api_key_file(options[:api_key_file_location],
75
- options[:api_key_id_property_name],
76
- options[:api_key_secret_property_name])
68
+ def ApiKey(options = {})
69
+ if api_key = options[:api_key]
70
+ case api_key
71
+ when ApiKey then api_key
72
+ when Hash then ApiKey.new(api_key[:id], api_key[:secret])
77
73
  end
74
+ elsif options[:api_key_file_location]
75
+ load_api_key_file(
76
+ options[:api_key_file_location],
77
+ options[:api_key_id_property_name],
78
+ options[:api_key_secret_property_name]
79
+ )
78
80
  end
81
+ end
79
82
 
80
- def load_api_key_file api_key_file_location, id_property_name, secret_property_name
81
- begin
82
- api_key_properties = JavaProperties::Properties.new api_key_file_location
83
- rescue
84
- raise ArgumentError, "No API Key file could be found or loaded from '#{api_key_file_location}'."
85
- end
86
-
87
- id_property_name ||= 'apiKey.id'
88
- secret_property_name ||= 'apiKey.secret'
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, "No API Key file could be found or loaded from '#{api_key_file_location}'."
88
+ end
89
89
 
90
- api_key_id = api_key_properties[id_property_name]
91
- assert_not_nil api_key_id, api_key_warning_message(:id, api_key_file_location)
90
+ id_property_name ||= 'apiKey.id'
91
+ secret_property_name ||= 'apiKey.secret'
92
92
 
93
- api_key_secret = api_key_properties[secret_property_name]
94
- assert_not_nil api_key_secret, api_key_warning_message(:secret, api_key_file_location)
93
+ api_key_id = api_key_properties[id_property_name]
94
+ assert_not_nil api_key_id, api_key_warning_message(:id, api_key_file_location)
95
95
 
96
- ApiKey.new api_key_id, api_key_secret
97
- end
96
+ api_key_secret = api_key_properties[secret_property_name]
97
+ assert_not_nil api_key_secret, api_key_warning_message(:secret, api_key_file_location)
98
98
 
99
- def api_key_warning_message id_or_secret, api_key_file_location
100
- "No API #{id_or_secret} in properties. Please provide a 'apiKey.#{id_or_secret}' property " +
101
- "in '#{api_key_file_location}' or pass in an 'api_key_#{id_or_secret}_property_name' " +
102
- "to the Stormpath::Client constructor to specify an alternative property."
103
- end
99
+ ApiKey.new(api_key_id, api_key_secret)
100
+ end
104
101
 
102
+ def api_key_warning_message(id_or_secret, api_key_file_location)
103
+ "No API #{id_or_secret} in properties. Please provide a 'apiKey.#{id_or_secret}' property " \
104
+ "in '#{api_key_file_location}' or pass in an 'api_key_#{id_or_secret}_property_name' " \
105
+ 'to the Stormpath::Client constructor to specify an alternative property.'
106
+ end
105
107
  end
106
108
  end
@@ -13,346 +13,367 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- class Stormpath::DataStore
17
- include Stormpath::Http
18
- include Stormpath::Util::Assert
16
+ module Stormpath
17
+ class DataStore
18
+ include Stormpath::Http
19
+ include Stormpath::Util::Assert
19
20
 
20
- DEFAULT_SERVER_HOST = "api.stormpath.com"
21
- DEFAULT_API_VERSION = 1
22
- DEFAULT_BASE_URL = "https://" + DEFAULT_SERVER_HOST + "/v" + DEFAULT_API_VERSION.to_s
23
- HREF_PROP_NAME = Stormpath::Resource::Base::HREF_PROP_NAME
21
+ DEFAULT_SERVER_HOST = 'api.stormpath.com'.freeze
22
+ DEFAULT_API_VERSION = 1
23
+ DEFAULT_BASE_URL = 'https://' + DEFAULT_SERVER_HOST + '/v' + DEFAULT_API_VERSION.to_s
24
+ HREF_PROP_NAME = Stormpath::Resource::Base::HREF_PROP_NAME
24
25
 
25
- CACHE_REGIONS = %w(applications directories accounts groups groupMemberships accountMemberships tenants customData provider providerData)
26
+ CACHE_REGIONS = %w(applications directories accounts groups groupMemberships accountMemberships
27
+ tenants customData provider providerData).freeze
26
28
 
27
- attr_reader :client, :request_executor, :cache_manager, :api_key, :base_url
29
+ attr_reader :client, :request_executor, :cache_manager, :api_key, :base_url
28
30
 
29
- def initialize(request_executor, api_key, cache_opts, client, base_url = nil)
30
- assert_not_nil request_executor, "RequestExecutor cannot be null."
31
+ def initialize(request_executor, api_key, cache_opts, client, base_url = nil)
32
+ assert_not_nil request_executor, 'RequestExecutor cannot be null.'
31
33
 
32
- @client = client
33
- @base_url = base_url || DEFAULT_BASE_URL
34
- @request_executor = request_executor
35
- @api_key = api_key
36
- initialize_cache cache_opts
37
- end
34
+ @client = client
35
+ @base_url = base_url || DEFAULT_BASE_URL
36
+ @request_executor = request_executor
37
+ @api_key = api_key
38
+ initialize_cache(cache_opts)
39
+ end
38
40
 
39
- def initialize_cache(cache_opts)
40
- @cache_manager = Stormpath::Cache::CacheManager.new
41
- regions_opts = cache_opts[:regions] || {}
42
- CACHE_REGIONS.each do |region|
43
- region_opts = regions_opts[region.to_sym] || {}
44
- region_opts[:store] ||= cache_opts[:store]
45
- region_opts[:store_opts] ||= cache_opts[:store_opts]
46
- @cache_manager.create_cache region, region_opts
41
+ def initialize_cache(cache_opts)
42
+ @cache_manager = Stormpath::Cache::CacheManager.new
43
+ regions_opts = cache_opts[:regions] || {}
44
+ CACHE_REGIONS.each do |region|
45
+ region_opts = regions_opts[region.to_sym] || {}
46
+ region_opts[:store] ||= cache_opts[:store]
47
+ region_opts[:store_opts] ||= cache_opts[:store_opts]
48
+ @cache_manager.create_cache(region, region_opts)
49
+ end
47
50
  end
48
- end
49
51
 
50
- def instantiate(clazz, properties = {})
51
- clazz.new properties, client
52
- end
52
+ def instantiate(clazz, properties = {})
53
+ clazz.new(properties, client)
54
+ end
53
55
 
54
- def get_resource(href, clazz, query=nil)
55
- q_href = qualify href
56
+ def get_resource(href, clazz, query = nil)
57
+ q_href = qualify href
56
58
 
57
- data = execute_request('get', q_href, nil, query)
59
+ data = execute_request('get', q_href, nil, query)
58
60
 
59
- clazz = clazz.call(data) if clazz.respond_to? :call
61
+ clazz = clazz.call(data) if clazz.respond_to? :call
60
62
 
61
- instantiate clazz, data.to_hash
62
- end
63
+ instantiate(clazz, data.to_hash)
64
+ end
63
65
 
64
- def create(parent_href, resource, return_type, options = {})
65
- #TODO assuming there is no ? in url
66
- parent_href = "#{parent_href}?#{URI.encode_www_form(options)}" unless options.empty?
66
+ def create(parent_href, resource, return_type, options = {})
67
+ # TODO: assuming there is no ? in url
68
+ parent_href = "#{parent_href}?#{URI.encode_www_form(options)}" unless options.empty?
67
69
 
68
- save_resource(parent_href, resource, return_type).tap do |returned_resource|
69
- if resource.kind_of? return_type
70
- resource.set_properties returned_resource.properties
70
+ save_resource(parent_href, resource, return_type).tap do |returned_resource|
71
+ if resource.is_a?(return_type)
72
+ resource.set_properties(returned_resource.properties)
73
+ end
71
74
  end
72
75
  end
73
- end
74
-
75
- def save(resource, clazz = nil)
76
- assert_not_nil resource, "resource argument cannot be null."
77
- assert_kind_of Stormpath::Resource::Base, resource, "resource argument must be instance of Stormpath::Resource::Base"
78
- href = resource.href
79
- assert_not_nil href, "href or resource.href cannot be null."
80
- assert_true href.length > 0, "save may only be called on objects that have already been persisted (i.e. they have an existing href)."
81
76
 
82
- href = qualify(href)
83
-
84
- clazz ||= resource.class
77
+ def save(resource, clazz = nil)
78
+ assert_not_nil(resource, 'resource argument cannot be null.')
79
+ assert_kind_of(
80
+ Stormpath::Resource::Base,
81
+ resource,
82
+ 'resource argument must be instance of Stormpath::Resource::Base'
83
+ )
84
+ href = resource.href
85
+ assert_not_nil(href, 'href or resource.href cannot be null.')
86
+ assert_true(
87
+ !href.empty?,
88
+ 'save may only be called on objects that have already been persisted'\
89
+ ' (i.e. they have an existing href).'
90
+ )
91
+
92
+ href = qualify(href)
93
+
94
+ clazz ||= resource.class
95
+
96
+ save_resource(href, resource, clazz).tap do |return_value|
97
+ resource.set_properties(return_value)
98
+ end
99
+ end
85
100
 
86
- save_resource(href, resource, clazz).tap do |return_value|
87
- resource.set_properties return_value
101
+ def delete(resource, property_name = nil)
102
+ assert_not_nil(resource, 'resource argument cannot be null.')
103
+ assert_kind_of(
104
+ Stormpath::Resource::Base,
105
+ resource,
106
+ 'resource argument must be instance of Stormpath::Resource::Base'
107
+ )
108
+
109
+ href = resource.href
110
+ href += "/#{property_name}" if property_name
111
+ href = qualify(href)
112
+
113
+ execute_request('delete', href)
114
+ clear_cache_on_delete(href)
115
+ nil
88
116
  end
89
- end
90
117
 
91
- def delete(resource, property_name = nil)
92
- assert_not_nil resource, "resource argument cannot be null."
93
- assert_kind_of Stormpath::Resource::Base, resource, "resource argument must be instance of Stormpath::Resource::Base"
118
+ def execute_raw_request(href, body, klass)
119
+ request = Request.new('POST', href, nil, {}, body.to_json, @api_key)
120
+ apply_default_request_headers(request)
121
+ response = @request_executor.execute_request(request)
122
+ result = !response.body.empty? ? MultiJson.load(response.body) : ''
94
123
 
95
- href = resource.href
96
- href += "/#{property_name}" if property_name
97
- href = qualify(href)
124
+ if response.error?
125
+ error = Stormpath::Resource::Error.new(result)
126
+ raise Stormpath::Error, error
127
+ end
98
128
 
99
- execute_request('delete', href)
100
- clear_cache_on_delete(href)
101
- return nil
102
- end
129
+ cache_walk(result)
130
+ instantiate(klass, result)
131
+ end
103
132
 
104
- def execute_raw_request(href, body, klass)
105
- request = Request.new('POST', href, nil, {}, body.to_json, @api_key)
106
- apply_default_request_headers(request)
107
- response = @request_executor.execute_request(request)
108
- result = response.body.length > 0 ? MultiJson.load(response.body) : ''
133
+ private
109
134
 
110
- if response.error?
111
- error = Stormpath::Resource::Error.new result
112
- raise Stormpath::Error.new(error)
135
+ def needs_to_be_fully_qualified?(href)
136
+ !href.downcase.start_with?('http')
113
137
  end
114
138
 
115
- cache_walk(result)
116
- instantiate(klass, result)
117
- end
139
+ def qualify(href)
140
+ needs_to_be_fully_qualified?(href) ? @base_url + href : href
141
+ end
118
142
 
119
- private
143
+ def execute_request(http_method, href, resource = nil, query = nil)
144
+ if http_method == 'get' && (cache = cache_for href)
145
+ cached_result = cache.get(href)
146
+ return cached_result if cached_result
147
+ end
120
148
 
121
- def needs_to_be_fully_qualified?(href)
122
- !href.downcase.start_with? 'http'
123
- end
149
+ body = extract_body_from_resource(resource)
124
150
 
125
- def qualify(href)
126
- needs_to_be_fully_qualified?(href) ? @base_url + href : href
127
- end
151
+ request = Request.new(http_method, href, query, {}, body, @api_key)
128
152
 
129
- def execute_request(http_method, href, resource=nil, query=nil)
130
- if http_method == 'get' && (cache = cache_for href)
131
- cached_result = cache.get href
132
- return cached_result if cached_result
133
- end
153
+ if resource.try(:form_data?)
154
+ apply_form_data_request_headers(request)
155
+ else
156
+ apply_default_request_headers(request)
157
+ end
134
158
 
135
- body = extract_body_from_resource(resource)
159
+ response = @request_executor.execute_request(request)
136
160
 
137
- request = Request.new(http_method, href, query, Hash.new, body, @api_key)
161
+ result = !response.body.empty? ? MultiJson.load(response.body) : ''
138
162
 
139
- if resource.try(:form_data?)
140
- apply_form_data_request_headers request
141
- else
142
- apply_default_request_headers request
143
- end
163
+ if response.error?
164
+ error = Stormpath::Resource::Error.new(result)
165
+ raise Stormpath::Error, error
166
+ end
144
167
 
145
- response = @request_executor.execute_request request
168
+ if resource.is_a?(Stormpath::Provider::AccountAccess)
169
+ is_new_account = response.http_status == 201
170
+ result = { is_new_account: is_new_account, account: result }
171
+ end
146
172
 
147
- result = response.body.length > 0 ? MultiJson.load(response.body) : ''
173
+ return if http_method == 'delete'
148
174
 
149
- if response.error?
150
- error = Stormpath::Resource::Error.new result
151
- raise Stormpath::Error.new error
175
+ if result[HREF_PROP_NAME] && !resource_is_saml_mapping_rules?(resource) && !user_info_mapping_rules?(resource)
176
+ cache_walk result
177
+ else
178
+ result
179
+ end
152
180
  end
153
181
 
154
- if resource.is_a? Stormpath::Provider::AccountAccess
155
- is_new_account = response.http_status == 201
156
- result = {is_new_account: is_new_account, account: result }
182
+ def clear_cache_on_delete(href)
183
+ if href =~ custom_data_delete_field_url_regex
184
+ href = href.split('/')[0..-2].join('/')
185
+ end
186
+ clear_cache(href)
157
187
  end
158
188
 
159
- return if http_method == 'delete'
160
-
161
- if result[HREF_PROP_NAME] && !resource_is_saml_mapping_rules?(resource) && !user_info_mapping_rules?(resource)
162
- cache_walk result
163
- else
164
- result
189
+ def custom_data_delete_field_url_regex
190
+ /#{@base_url}\/(accounts|groups)\/\w+\/customData\/\w+[\/]{0,1}$/
165
191
  end
166
- end
167
192
 
168
- def clear_cache_on_delete href
169
- if href =~ custom_data_delete_field_url_regex
170
- href = href.split('/')[0..-2].join('/')
193
+ def clear_cache(href)
194
+ cache = cache_for(href)
195
+ cache.delete(href) if cache
171
196
  end
172
- clear_cache href
173
- end
174
197
 
175
- def custom_data_delete_field_url_regex
176
- /#{@base_url}\/(accounts|groups)\/\w+\/customData\/\w+[\/]{0,1}$/
177
- end
178
-
179
- def clear_cache(href)
180
- cache = cache_for href
181
- cache.delete href if cache
182
- end
198
+ def cache_walk(resource)
199
+ assert_not_nil(resource[HREF_PROP_NAME], "resource must have 'href' property")
200
+ items = resource['items']
183
201
 
184
- def cache_walk(resource)
185
- assert_not_nil resource[HREF_PROP_NAME], "resource must have 'href' property"
186
- items = resource['items']
187
-
188
- if items # collection resource
189
- resource['items'] = items.map do |item|
190
- cache_walk item
191
- { HREF_PROP_NAME => item[HREF_PROP_NAME] }
192
- end
193
- else # single resource
194
- resource.each do |attr, value|
195
- if value.is_a? Hash and value[HREF_PROP_NAME]
196
- walked = cache_walk value
202
+ if items # collection resource
203
+ resource['items'] = items.map do |item|
204
+ cache_walk(item)
205
+ { HREF_PROP_NAME => item[HREF_PROP_NAME] }
206
+ end
207
+ else # single resource
208
+ resource.each do |attr, value|
209
+ next unless value.is_a?(Hash) && value[HREF_PROP_NAME]
210
+ walked = cache_walk(value)
197
211
  resource[attr] = { HREF_PROP_NAME => value[HREF_PROP_NAME] }
198
212
  resource[attr]['items'] = walked['items'] if walked['items']
199
213
  end
214
+ cache(resource) if resource.length > 1
200
215
  end
201
- cache resource if resource.length > 1
216
+ resource
202
217
  end
203
- resource
204
- end
205
-
206
- def cache(resource)
207
- cache = cache_for resource[HREF_PROP_NAME]
208
- cache.put resource[HREF_PROP_NAME], resource if cache
209
- end
210
-
211
- def cache_for(href)
212
- @cache_manager.get_cache(region_for href)
213
- end
214
218
 
215
- def region_for(href)
216
- return nil if href.nil?
217
- if href.include? "/customData"
218
- region = href.split('/')[-1]
219
- else
220
- region = href.split('/')[-2]
219
+ def cache(resource)
220
+ cache = cache_for(resource[HREF_PROP_NAME])
221
+ cache.put(resource[HREF_PROP_NAME], resource) if cache
221
222
  end
222
- CACHE_REGIONS.include?(region) ? region : nil
223
- end
224
-
225
- def apply_default_request_headers(request)
226
- request.http_headers.store 'Accept', 'application/json'
227
- apply_default_user_agent(request)
228
223
 
229
- if request.body and request.body.length > 0
230
- request.http_headers.store 'Content-Type', 'application/json'
224
+ def cache_for(href)
225
+ @cache_manager.get_cache(region_for(href))
231
226
  end
232
- end
233
227
 
234
- def apply_form_data_request_headers(request)
235
- request.http_headers.store 'Content-Type', 'application/x-www-form-urlencoded'
236
- apply_default_user_agent(request)
237
- end
238
-
239
- def apply_default_user_agent(request)
240
- request.http_headers.store 'User-Agent', 'stormpath-sdk-ruby/' + Stormpath::VERSION +
241
- " ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" +
242
- " " + Gem::Platform.local.os.to_s + "/" + Gem::Platform.local.version.to_s
243
- end
244
-
245
- def save_resource(href, resource, return_type)
246
- assert_not_nil resource, "resource argument cannot be null."
247
- assert_not_nil return_type, "returnType class cannot be null."
248
- assert_kind_of Stormpath::Resource::Base, resource, "resource argument must be instance of Stormpath::Resource::Base"
249
-
250
- q_href = qualify href
251
-
252
- clear_cache_on_save(resource)
228
+ def region_for(href)
229
+ return nil if href.nil?
230
+ region = if href.include?('/customData')
231
+ href.split('/')[-1]
232
+ else
233
+ href.split('/')[-2]
234
+ end
235
+ CACHE_REGIONS.include?(region) ? region : nil
236
+ end
253
237
 
254
- response = execute_request 'post', q_href, resource
238
+ def apply_default_request_headers(request)
239
+ request.http_headers.store('Accept', 'application/json')
240
+ apply_default_user_agent(request)
255
241
 
256
- instantiate return_type, parse_response(response)
257
- end
242
+ if request.body && !request.body.empty?
243
+ request.http_headers.store('Content-Type', 'application/json')
244
+ end
245
+ end
258
246
 
259
- def parse_response(response)
260
- return {} if response.is_a? String and response.blank?
261
- response.to_hash
262
- end
247
+ def apply_form_data_request_headers(request)
248
+ request.http_headers.store('Content-Type', 'application/x-www-form-urlencoded')
249
+ apply_default_user_agent(request)
250
+ end
263
251
 
264
- def clear_cache_on_save(resource)
265
- if resource.is_a? Stormpath::Resource::CustomDataStorage
266
- clear_custom_data_cache_on_custom_data_storage_save(resource)
267
- elsif resource.is_a? Stormpath::Resource::AccountStoreMapping
268
- clear_application_cache_on_account_store_save(resource)
252
+ def apply_default_user_agent(request)
253
+ request.http_headers.store(
254
+ 'User-Agent', 'stormpath-sdk-ruby/' + Stormpath::VERSION +
255
+ " ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}" \
256
+ ' ' + Gem::Platform.local.os.to_s + '/' + Gem::Platform.local.version.to_s
257
+ )
269
258
  end
270
- end
271
259
 
260
+ def save_resource(href, resource, return_type)
261
+ assert_not_nil(resource, 'resource argument cannot be null.')
262
+ assert_not_nil(return_type, 'returnType class cannot be null.')
263
+ assert_kind_of(
264
+ Stormpath::Resource::Base,
265
+ resource,
266
+ 'resource argument must be instance of Stormpath::Resource::Base'
267
+ )
268
+
269
+ q_href = qualify(href)
270
+ clear_cache_on_save(resource)
271
+ response = execute_request('post', q_href, resource)
272
+ instantiate(return_type, parse_response(response))
273
+ end
272
274
 
273
- def clear_custom_data_cache_on_custom_data_storage_save resource
274
- if resource.dirty_properties.has_key? "customData" and resource.new? == false
275
- cached_href = resource.href + "/customData"
276
- clear_cache cached_href
275
+ def parse_response(response)
276
+ return {} if response.is_a?(String) && response.blank?
277
+ response.to_hash
277
278
  end
278
- end
279
279
 
280
- def clear_application_cache_on_account_store_save resource
281
- if resource.new?
282
- if resource.default_account_store? == true || resource.default_group_store? == true
283
- clear_cache resource.application.href
284
- end
285
- else
286
- if resource.dirty_properties["isDefaultAccountStore"] != nil || resource.dirty_properties["isDefaultGroupStore"] != nil
287
- clear_cache resource.application.href
280
+ def clear_cache_on_save(resource)
281
+ if resource.is_a?(Stormpath::Resource::CustomDataStorage)
282
+ clear_custom_data_cache_on_custom_data_storage_save(resource)
283
+ elsif resource.is_a?(Stormpath::Resource::AccountStoreMapping)
284
+ clear_application_cache_on_account_store_save(resource)
288
285
  end
289
286
  end
290
- end
291
287
 
292
- def extract_body_from_resource(resource)
293
- return if resource.nil?
294
- form_data = resource.try(:form_data?)
288
+ def clear_custom_data_cache_on_custom_data_storage_save(resource)
289
+ if resource.dirty_properties.key?('customData') && (resource.new? == false)
290
+ cached_href = resource.href + '/customData'
291
+ clear_cache(cached_href)
292
+ end
293
+ end
295
294
 
296
- if form_data
297
- form_request_parse(resource)
298
- else
299
- MultiJson.dump(to_hash(resource))
295
+ def clear_application_cache_on_account_store_save(resource)
296
+ if resource.new?
297
+ if resource.default_account_store? == true || resource.default_group_store? == true
298
+ clear_cache(resource.application.href)
299
+ end
300
+ else
301
+ if !resource.dirty_properties['isDefaultAccountStore'].nil? || !resource.dirty_properties['isDefaultGroupStore'].nil?
302
+ clear_cache(resource.application.href)
303
+ end
304
+ end
300
305
  end
301
- end
302
306
 
303
- def form_request_parse(resource)
304
- URI.encode_www_form(resource.form_properties.to_a)
305
- end
307
+ def extract_body_from_resource(resource)
308
+ return if resource.nil?
309
+ form_data = resource.try(:form_data?)
306
310
 
307
- def to_hash(resource)
308
- {}.tap do |properties|
309
- resource.get_dirty_property_names.each do |name|
310
- ignore_camelcasing = resource_is_custom_data(resource, name)
311
- property = resource.get_property name, ignore_camelcasing: ignore_camelcasing
311
+ if form_data
312
+ form_request_parse(resource)
313
+ else
314
+ MultiJson.dump(to_hash(resource))
315
+ end
316
+ end
312
317
 
313
- # Special use cases are with Custom Data, Provider and ProviderData, their hashes should not be simplified
314
- # As of the implementation for MFA, Phone resource is added too
315
- if property.is_a?(Hash) && !resource_nested_submittable(resource, name) && name != 'items' && name != 'phone'
316
- property = to_simple_reference name, property
317
- end
318
+ def form_request_parse(resource)
319
+ URI.encode_www_form(resource.form_properties.to_a)
320
+ end
318
321
 
319
- if name == 'items' && resource_is_saml_mapping_rules?(resource)
320
- property = property.map { |item| item.transform_keys { |key| key.to_s.camelize(:lower).to_sym } }
322
+ def to_hash(resource)
323
+ {}.tap do |properties|
324
+ resource.get_dirty_property_names.each do |name|
325
+ ignore_camelcasing = resource_is_custom_data(resource, name)
326
+ property = resource.get_property name, ignore_camelcasing: ignore_camelcasing
327
+
328
+ # Special use cases are with Custom Data, Provider and ProviderData, their hashes should not be simplified
329
+ # As of the implementation for MFA, Phone resource is added too
330
+ if property.is_a?(Hash) && !resource_nested_submittable(resource, name) && name != 'items' && name != 'phone'
331
+ property = to_simple_reference(name, property)
332
+ end
333
+
334
+ if name == 'items' && resource_is_saml_mapping_rules?(resource)
335
+ property = property.map do |item|
336
+ item.transform_keys { |key| key.to_s.camelize(:lower).to_sym }
337
+ end
338
+ end
339
+
340
+ properties.store(name, property)
321
341
  end
322
-
323
- properties.store name, property
324
342
  end
325
343
  end
326
- end
327
344
 
328
- def to_simple_reference(property_name, hash)
329
- assert_true hash.key?(HREF_PROP_NAME), "Nested resource '#{property_name}' must have an 'href' property."
345
+ def to_simple_reference(property_name, hash)
346
+ assert_true(
347
+ hash.key?(HREF_PROP_NAME),
348
+ "Nested resource '#{property_name}' must have an 'href' property."
349
+ )
330
350
 
331
- href = hash[HREF_PROP_NAME]
351
+ href = hash[HREF_PROP_NAME]
332
352
 
333
- { HREF_PROP_NAME => href }
334
- end
353
+ { HREF_PROP_NAME => href }
354
+ end
335
355
 
336
- def resource_nested_submittable(resource, name)
337
- ['provider', 'providerData', 'accountStore'].include?(name) ||
338
- resource_is_custom_data(resource, name) ||
339
- resource_is_application_web_config(resource, name)
340
- end
356
+ def resource_nested_submittable(resource, name)
357
+ ['provider', 'providerData', 'accountStore'].include?(name) ||
358
+ resource_is_custom_data(resource, name) ||
359
+ resource_is_application_web_config(resource, name)
360
+ end
341
361
 
342
- def resource_is_custom_data(resource, name)
343
- resource.is_a?(Stormpath::Resource::CustomData) || name == 'customData'
344
- end
362
+ def resource_is_custom_data(resource, name)
363
+ resource.is_a?(Stormpath::Resource::CustomData) || name == 'customData'
364
+ end
345
365
 
346
- def resource_is_application_web_config(resource, name)
347
- resource.is_a?(Stormpath::Resource::ApplicationWebConfig) &&
348
- Stormpath::Resource::ApplicationWebConfig::ENDPOINTS.include?(name.underscore.to_sym)
349
- end
366
+ def resource_is_application_web_config(resource, name)
367
+ resource.is_a?(Stormpath::Resource::ApplicationWebConfig) &&
368
+ Stormpath::Resource::ApplicationWebConfig::ENDPOINTS.include?(name.underscore.to_sym)
369
+ end
350
370
 
351
- def user_info_mapping_rules?(resource)
352
- resource.is_a?(Stormpath::Resource::UserInfoMappingRules)
353
- end
371
+ def resource_is_saml_mapping_rules?(resource)
372
+ resource.is_a?(Stormpath::Resource::AttributeStatementMappingRules)
373
+ end
354
374
 
355
- def resource_is_saml_mapping_rules?(resource)
356
- resource.is_a?(Stormpath::Provider::SamlMappingRules)
375
+ def user_info_mapping_rules?(resource)
376
+ resource.is_a?(Stormpath::Resource::UserInfoMappingRules)
377
+ end
357
378
  end
358
379
  end