stormpath-sdk 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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