simple_crowd 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/Gemfile CHANGED
@@ -4,5 +4,12 @@ source "http://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  group :test do
7
+ gem 'factory_girl', '>= 2.0.1'
8
+ gem 'fcoury-matchy', '>= 0.4.0'
9
+ gem 'shoulda', '>= 2.0.0'
10
+ gem 'forgery', '>= 0.4.0'
11
+ gem 'webmock', '>= 1.8.0'
12
+ gem 'rr', '>= 1.0.0'
13
+
7
14
  gem 'i18n'
8
15
  end
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
- require 'rake/rdoctask'
3
+ require 'rdoc/task'
4
4
  require 'bundler'
5
5
  Bundler::GemHelper.install_tasks
6
6
 
@@ -0,0 +1,86 @@
1
+ module SimpleCrowd
2
+ module Cache
3
+
4
+ # A cache store implementation which doesn't actually store anything.
5
+ #
6
+ # Implements ActiveSupport::Cache::Store interface without depending on
7
+ # the active_support gem.
8
+ class NullStore
9
+ attr_reader :silence, :options
10
+ alias :silence? :silence
11
+
12
+ def initialize(options = nil)
13
+ end
14
+
15
+ def mute
16
+ yield
17
+ end
18
+
19
+ def silence!
20
+ end
21
+
22
+ def synchronize
23
+ yield
24
+ end
25
+
26
+ def fetch(name, options = nil)
27
+ if block_given?
28
+ yield
29
+ else
30
+ read(name, options)
31
+ end
32
+ end
33
+
34
+ def read(name, options = nil)
35
+ nil
36
+ end
37
+
38
+ def write(name, vaue, options = nil)
39
+ true
40
+ end
41
+
42
+ def delete(name, options = nil)
43
+ false
44
+ end
45
+
46
+ def exist?(name, options = nil)
47
+ false
48
+ end
49
+
50
+ def clear(options = nil)
51
+ end
52
+
53
+ def cleanup(options = nil)
54
+ end
55
+
56
+ def increment(name, amount = 1, options = nil)
57
+ end
58
+
59
+ def decrement(name, amount = 1, options = nil)
60
+ end
61
+
62
+ def delete_matched(matcher, options = nil)
63
+ end
64
+
65
+ def self.logger
66
+ nil
67
+ end
68
+
69
+ def self.logger=(logger)
70
+ end
71
+
72
+ protected
73
+
74
+ def read_entry(key, options) # :nodoc:
75
+ end
76
+
77
+ def write_entry(key, entry, options) # :nodoc:
78
+ true
79
+ end
80
+
81
+ def delete_entry(key, options) # :nodoc:
82
+ false
83
+ end
84
+ end
85
+ end
86
+ end
@@ -1,298 +1,334 @@
1
- module SimpleCrowd
2
- class Client
3
-
4
- attr_reader :options
5
-
6
- def initialize options = {}
7
- @options = SimpleCrowd.options options
8
-
9
- # TODO: Fix error handling
10
- # Errors do not contained Exception info so we'll handle the errors ourselves
11
- # Savon::Response.raise_errors = false
12
- # @client = Savon::Client.new @options[:service_url]
13
- yield(@options) if block_given?
14
- end
15
- def app_token
16
- @app_token ||= authenticate_application
17
- end
18
- attr_writer :app_token
19
-
20
- def get_cookie_info
21
- simple_soap_call :get_cookie_info
22
- end
23
-
24
- def get_granted_authorities
25
- groups = simple_soap_call :get_granted_authorities
26
- groups[:string] unless groups.nil?
27
- end
28
-
29
- def authenticate_application(name = @options[:app_name], password = @options[:app_password])
30
- response = client.authenticate_application! do |soap|
31
- prepare soap
32
- soap.body = {:in0 => {
33
- 'auth:name' => name,
34
- 'auth:credential' => {'auth:credential' => password}
35
- }.merge(no_validation_factors)}
36
- end
37
- raise CrowdError.new(response.soap_fault, response.to_hash[:fault]) if response.soap_fault?
38
- response.to_hash[:authenticate_application_response][:out][:token]
39
- end
40
-
41
- # Authenticate user by name/pass and retrieve login token
42
- # @return [String] user token
43
- def authenticate_user name, password, factors = nil
44
- if factors
45
- factors = prepare_validation_factors(factors)
46
- simple_soap_call :authenticate_principal, {'auth:application' => @options[:app_name], 'auth:name' => name,
47
- 'auth:credential' => {'auth:credential' => password},
48
- 'auth:validationFactors' => factors}
49
- else
50
- simple_soap_call :authenticate_principal_simple, name, password
51
- end
52
- end
53
-
54
- def create_user_token name
55
- simple_soap_call :create_principal_token, name, nil
56
- end
57
-
58
- # Invalidate an existing user token (log out)
59
- # NOTE: call will return true even if token is invalid
60
- # @return [Boolean] success (does not guarantee valid token)
61
- def invalidate_user_token token
62
- simple_soap_call :invalidate_principal_token, token do |res|
63
- !res.soap_fault? && res.to_hash.key?(:invalidate_principal_token_response)
64
- end
65
- end
66
-
67
- def is_valid_user_token? token, factors = nil
68
- factors = prepare_validation_factors(factors) unless factors.nil?
69
- simple_soap_call :is_valid_principal_token, token, factors
70
- end
71
-
72
- def is_cache_enabled?
73
- simple_soap_call :is_cache_enabled
74
- end
75
-
76
- def is_group_member? group, user
77
- simple_soap_call :is_group_member, group, user
78
- end
79
-
80
- def find_group_by_name name
81
- SimpleCrowd::Group.parse_from :soap, simple_soap_call(:find_group_by_name, name)
82
- end
83
-
84
- def find_all_group_names
85
- (simple_soap_call :find_all_group_names)[:string]
86
- end
87
-
88
- def update_group group, description, active
89
- simple_soap_call :update_group, group, description, active do |res|
90
- !res.soap_fault? && res.to_hash.key?(:update_group_response)
91
- end
92
- end
93
-
94
- def add_user_to_group user, group
95
- simple_soap_call :add_principal_to_group, user, group do |res|
96
- !res.soap_fault? && res.to_hash.key?(:add_principal_to_group_response)
97
- end
98
- end
99
-
100
- def remove_user_from_group user, group
101
- simple_soap_call :remove_principal_from_group, user, group do |res|
102
- !res.soap_fault? && res.to_hash.key?(:remove_principal_from_group_response)
103
- end
104
- end
105
-
106
- def reset_user_password name
107
- simple_soap_call :reset_principal_credential, name do |res|
108
- !res.soap_fault? && res.to_hash.key?(:reset_principal_credential_response)
109
- end
110
- end
111
-
112
- def find_all_user_names
113
- (simple_soap_call :find_all_principal_names)[:string]
114
- end
115
-
116
- def find_user_by_name name
117
- SimpleCrowd::User.parse_from :soap, simple_soap_call(:find_principal_by_name, name) rescue nil
118
- end
119
-
120
- def find_user_with_attributes_by_name name
121
- SimpleCrowd::User.parse_from :soap, simple_soap_call(:find_principal_with_attributes_by_name, name) rescue nil
122
- end
123
-
124
- def find_user_by_token token
125
- SimpleCrowd::User.parse_from :soap, simple_soap_call(:find_principal_by_token, token) rescue nil
126
- end
127
-
128
- def find_username_by_token token
129
- user = find_user_by_token token
130
- user && user[:username]
131
- end
132
-
133
- # Exact email match
134
- def find_user_by_email email
135
- search_users_by_email(email).find{|u| u.email.casecmp(email) == 0}
136
- end
137
-
138
- # Partial email match
139
- def search_users_by_email email
140
- search_users({'principal.email' => email})
141
- end
142
-
143
- def search_users restrictions
144
- soap_restrictions = prepare_search_restrictions restrictions
145
- users = simple_soap_call :search_principals, soap_restrictions rescue []
146
- return [] if users.nil? || users[:soap_principal].nil?
147
- users = users[:soap_principal].is_a?(Array) ? users[:soap_principal] : [users[:soap_principal]]
148
- users.map{|u| SimpleCrowd::User.parse_from :soap, u}
149
- end
150
-
151
- def add_user user, credential
152
- return if user.nil? || credential.nil?
153
- [:email, :first_name, :last_name].each do |k|
154
- user.send(:"#{k}=", "") if user.send(k).nil?
155
- end
156
- soap_user = user.map_to :soap
157
- # We don't use these attributes when creating
158
- soap_user.delete(:id)
159
- soap_user.delete(:directory_id)
160
- # Add blank attributes if missing
161
-
162
- # Declare require namespaces
163
- soap_user = soap_user.inject({}) {|hash, (k, v)| hash["int:#{k}"] = v;hash}
164
- SimpleCrowd::User.parse_from :soap, simple_soap_call(:add_principal, soap_user, {'auth:credential' => credential, 'auth:encryptedCredential' => false})
165
- end
166
-
167
- def remove_user name
168
- simple_soap_call :remove_principal, name do |res|
169
- !res.soap_fault? && res.to_hash.key?(:remove_principal_response)
170
- end
171
- end
172
-
173
- def update_user_credential user, credential, encrypted = false
174
- simple_soap_call :update_principal_credential, user,
175
- {'auth:credential' => credential, 'auth:encryptedCredential' => encrypted} do |res|
176
- !res.soap_fault? && res.to_hash.key?(:update_principal_credential_response)
177
- end
178
- end
179
-
180
- # Only supports single value attributes
181
- # TODO: Allow value arrays
182
- # @param user [String] name of user to update
183
- # @param name [String] of attribute to update
184
- # @param value [String] of attribute to update
185
- def update_user_attribute user, name, value
186
- return unless (name.is_a?(String) || name.is_a?(Symbol)) && (value.is_a?(String) || value.is_a?(Array))
187
- soap_attr = SimpleCrowd::Mappers::SoapAttributes.produce({name => value})
188
- simple_soap_call :update_principal_attribute, user, soap_attr['int:SOAPAttribute'][0] do |res|
189
- !res.soap_fault? && res.to_hash.key?(:update_principal_attribute_response)
190
- end
191
- end
192
- alias_method :add_user_attribute, :update_user_attribute
193
-
194
- # @param user [SimpleCrowd::User] dirty user to update
195
- def update_user user
196
- return unless user.dirty?
197
- # Exclude non-attribute properties (only attributes can be updated in crowd)
198
- attrs_to_update = user.dirty_attributes
199
- return if attrs_to_update.empty?
200
-
201
- attrs_to_update.each do |a|
202
- prop = SimpleCrowd::User.property_by_name a
203
- soap_prop = prop.maps[:soap].nil? ? prop : prop.maps[:soap]
204
- self.update_user_attribute user.username, soap_prop, user.send(a)
205
- end
206
- end
207
-
208
- private
209
-
210
- # Simplify the duplicated soap calls across methods
211
- # @param [Symbol] action the soap action to call
212
- # @param data the list of args to pass to the server as "in" args (in1, in2, etc.)
213
- def simple_soap_call action, *data
214
- # Take each arg and assign it to "in" keys for SOAP call starting with in1 (in0 is app token)
215
- soap_args = data.inject({}){|hash, arg| hash[:"in#{hash.length + 1}"] = arg; hash }
216
- # Ordered "in" keys ex. in1, in2, etc. for SOAP ordering
217
- in_keys = soap_args.length ? (1..soap_args.length).collect {|v| :"in#{v}" } : []
218
- # Make the SOAP call to the dynamic action
219
- response = with_app_token do
220
- client.send :"#{action}!" do |soap|
221
- prepare soap
222
- # Pass in all the args as "in" vars
223
- soap.body = {:in0 => hash_authenticated_token}.merge(soap_args).merge({:order! => [:in0, *in_keys]})
224
- end
225
- end
226
- # If a block is given then call it and pass in the response object, otherwise get the default out value
227
- block_given? ? yield(response) : response.to_hash[:"#{action}_response"][:out]
228
- end
229
-
230
- def with_app_token retries = 1, &block
231
- begin
232
- Savon::Response.raise_errors = false
233
- response = block.call
234
- raise CrowdError.new(response.soap_fault, response.to_hash[:fault]) if response.soap_fault?
235
- return response
236
- rescue CrowdError => e
237
- if retries > 0 && e.type?(:invalid_authorization_token_exception)
238
- # Clear token to force a refresh
239
- self.app_token = nil
240
- retries -= 1
241
- retry
242
- end
243
- raise
244
- ensure
245
- Savon::Response.raise_errors = true
246
- end
247
- end
248
-
249
- # Generate new client on every request (Savon bug?)
250
- def client
251
- Savon::Client.new @options[:service_url]
252
- end
253
-
254
- # Setup soap object for request
255
- def prepare soap
256
- soap.namespace = @options[:service_ns]
257
- soap.namespaces.merge! @options[:service_namespaces]
258
- end
259
-
260
- # Take Crowd SOAP attribute format and return a simple ruby hash
261
- # @param attributes the soap attributes array
262
- def process_soap_attributes attributes
263
- soap = attributes[:soap_attribute]
264
- (soap && soap.inject({}) {|hash, attr| hash[attr[:name].to_sym] = attr[:values][:string]; hash }) || {}
265
- end
266
-
267
- def map_group_hash group
268
- attributes = process_soap_attributes group[:attributes]
269
- supported_keys = attributes.keys & SimpleCrowd::Group.mapped_properties(:soap)
270
- group = group.merge attributes.inject({}) {|map, (k, v)| map[k] = v if supported_keys.include? k; map}
271
- group[:attributes] = attributes.inject({}) {|map, (k, v)| map[k] = v unless supported_keys.include? k; map}
272
- group.delete :attributes if group[:attributes].empty?
273
- SimpleCrowd::Group.new group
274
- end
275
-
276
- def prepare_validation_factors factors
277
- {'auth:validationFactor' =>
278
- factors.inject([]) {|arr, factor| arr << {'auth:name' => factor[0], 'auth:value' => factor[1]} }
279
- }
280
- end
281
-
282
- def prepare_search_restrictions restrictions
283
- {'int:searchRestriction' =>
284
- restrictions.inject([]) {|arr, restrict| arr << {'int:name' => restrict[0], 'int:value' => restrict[1]}}
285
- }
286
- end
287
-
288
- def hash_authenticated_token name = @options[:app_name], token = nil
289
- token ||= app_token
290
- {'auth:name' => name, 'auth:token' => token}
291
- end
292
-
293
- def no_validation_factors
294
- {'auth:validationFactors' => {}, :attributes! => {'auth:validationFactors' => {'xsi:nil' => true}}}
295
- end
296
-
297
- end
298
- end
1
+ module SimpleCrowd
2
+ class Client
3
+
4
+ attr_reader :options
5
+ attr_accessor :app_token, :cache_store
6
+
7
+ def initialize options = {}
8
+ @options = SimpleCrowd.options options
9
+ yield(@options) if block_given?
10
+ self.cache_store = @options.delete(:cache_store)
11
+ end
12
+
13
+ def get_cookie_info
14
+ @cookie_info ||= cache.fetch(cache_key(:cookie_info)) do
15
+ # Remove custom SOAP attributes from the strings
16
+ simple_soap_call(:get_cookie_info).inject({}) do |cookie_info, (key, val)|
17
+ cookie_info[key] = val ? val.to_s : val
18
+ cookie_info
19
+ end
20
+ end
21
+ end
22
+
23
+ def get_granted_authorities
24
+ groups = simple_soap_call :get_granted_authorities
25
+ groups[:string] unless groups.nil?
26
+ end
27
+
28
+ def authenticate_application(name = @options[:app_name], password = @options[:app_password])
29
+ response = convert_soap_errors do
30
+ client.request :authenticate_application do |soap|
31
+ prepare soap
32
+ soap.body = {:in0 => {
33
+ 'auth:name' => name,
34
+ 'auth:credential' => {'auth:credential' => password}
35
+ }.merge(no_validation_factors)}
36
+ end
37
+ end
38
+ response.to_hash[:authenticate_application_response][:out][:token].to_s
39
+ end
40
+
41
+ # Authenticate user by name/pass and retrieve login token
42
+ # @return [String] user token
43
+ def authenticate_user name, password, factors = nil
44
+ if factors
45
+ factors = prepare_validation_factors(factors)
46
+ simple_soap_call :authenticate_principal, {'auth:application' => @options[:app_name], 'auth:name' => name,
47
+ 'auth:credential' => {'auth:credential' => password},
48
+ 'auth:validationFactors' => factors}
49
+ else
50
+ simple_soap_call :authenticate_principal_simple, name, password
51
+ end
52
+ end
53
+
54
+ def create_user_token name
55
+ simple_soap_call :create_principal_token, name, nil
56
+ end
57
+
58
+ # Invalidate an existing user token (log out)
59
+ # NOTE: call will return true even if token is invalid
60
+ # @return [Boolean] success (does not guarantee valid token)
61
+ def invalidate_user_token token
62
+ simple_soap_call :invalidate_principal_token, token do |res|
63
+ !res.soap_fault? && res.to_hash.key?(:invalidate_principal_token_response)
64
+ end
65
+ end
66
+
67
+ def is_valid_user_token? token, factors = nil
68
+ factors = prepare_validation_factors(factors)
69
+ simple_soap_call :is_valid_principal_token, token, factors
70
+ end
71
+
72
+ def is_cache_enabled?
73
+ simple_soap_call :is_cache_enabled
74
+ end
75
+
76
+ def is_group_member? group, user
77
+ simple_soap_call :is_group_member, group, user
78
+ end
79
+
80
+ def find_group_by_name name
81
+ SimpleCrowd::Group.parse_from :soap, simple_soap_call(:find_group_by_name, name)
82
+ end
83
+
84
+ def find_all_group_names
85
+ (simple_soap_call :find_all_group_names)[:string]
86
+ end
87
+
88
+ def update_group group, description, active
89
+ simple_soap_call :update_group, group, description, active do |res|
90
+ !res.soap_fault? && res.to_hash.key?(:update_group_response)
91
+ end
92
+ end
93
+
94
+ def add_user_to_group user, group
95
+ simple_soap_call :add_principal_to_group, user, group do |res|
96
+ !res.soap_fault? && res.to_hash.key?(:add_principal_to_group_response)
97
+ end
98
+ end
99
+
100
+ def remove_user_from_group user, group
101
+ simple_soap_call :remove_principal_from_group, user, group do |res|
102
+ !res.soap_fault? && res.to_hash.key?(:remove_principal_from_group_response)
103
+ end
104
+ end
105
+
106
+ def reset_user_password name
107
+ simple_soap_call :reset_principal_credential, name do |res|
108
+ !res.soap_fault? && res.to_hash.key?(:reset_principal_credential_response)
109
+ end
110
+ end
111
+
112
+ def find_all_user_names
113
+ (simple_soap_call :find_all_principal_names)[:string]
114
+ end
115
+
116
+ def find_user_by_name name
117
+ SimpleCrowd::User.parse_from :soap, simple_soap_call(:find_principal_by_name, name) rescue nil
118
+ end
119
+
120
+ def find_user_with_attributes_by_name name
121
+ SimpleCrowd::User.parse_from :soap, simple_soap_call(:find_principal_with_attributes_by_name, name) rescue nil
122
+ end
123
+
124
+ def find_user_by_token token
125
+ SimpleCrowd::User.parse_from :soap, simple_soap_call(:find_principal_by_token, token) rescue nil
126
+ end
127
+
128
+ def find_username_by_token token
129
+ user = find_user_by_token token
130
+ user && user[:username]
131
+ end
132
+
133
+ # Exact email match
134
+ def find_user_by_email email
135
+ search_users_by_email(email).find{|u| u.email == email}
136
+ end
137
+
138
+ # Partial email match
139
+ def search_users_by_email email
140
+ search_users({'principal.email' => email})
141
+ end
142
+
143
+ def search_users restrictions
144
+ soap_restrictions = prepare_search_restrictions restrictions
145
+ users = simple_soap_call :search_principals, soap_restrictions rescue []
146
+ return [] if users.nil? || users[:soap_principal].nil?
147
+ users = users[:soap_principal].is_a?(Array) ? users[:soap_principal] : [users[:soap_principal]]
148
+ users.map{|u| SimpleCrowd::User.parse_from :soap, u}
149
+ end
150
+
151
+ def add_user user, credential
152
+ return if user.nil? || credential.nil?
153
+ [:email, :first_name, :last_name].each do |k|
154
+ user.send(:"#{k}=", "") if user.send(k).nil?
155
+ end
156
+ soap_user = user.map_to :soap
157
+ # We don't use these attributes when creating
158
+ soap_user.delete(:id)
159
+ soap_user.delete(:directory_id)
160
+ # Add blank attributes if missing
161
+
162
+ # Declare require namespaces
163
+ soap_user = soap_user.inject({}) {|hash, (k, v)| hash["int:#{k}"] = v;hash}
164
+ SimpleCrowd::User.parse_from :soap, simple_soap_call(:add_principal, soap_user, {'auth:credential' => credential, 'auth:encryptedCredential' => false})
165
+ end
166
+
167
+ def remove_user name
168
+ simple_soap_call :remove_principal, name do |res|
169
+ !res.soap_fault? && res.to_hash.key?(:remove_principal_response)
170
+ end
171
+ end
172
+
173
+ def update_user_credential user, credential, encrypted = false
174
+ simple_soap_call :update_principal_credential, user,
175
+ {'auth:credential' => credential, 'auth:encryptedCredential' => encrypted} do |res|
176
+ !res.soap_fault? && res.to_hash.key?(:update_principal_credential_response)
177
+ end
178
+ end
179
+
180
+ # Only supports single value attributes
181
+ # TODO: Allow value arrays
182
+ # @param user [String] name of user to update
183
+ # @param name [String] of attribute to update
184
+ # @param value [String] of attribute to update
185
+ def update_user_attribute user, name, value
186
+ return unless (name.is_a?(String) || name.is_a?(Symbol)) && (value.is_a?(String) || value.is_a?(Array))
187
+ soap_attr = SimpleCrowd::Mappers::SoapAttributes.produce({name => value})
188
+ simple_soap_call :update_principal_attribute, user, soap_attr['int:SOAPAttribute'][0] do |res|
189
+ !res.soap_fault? && res.to_hash.key?(:update_principal_attribute_response)
190
+ end
191
+ end
192
+ alias_method :add_user_attribute, :update_user_attribute
193
+
194
+ # @param user [SimpleCrowd::User] dirty user to update
195
+ def update_user user
196
+ return unless user.dirty?
197
+ # Exclude non-attribute properties (only attributes can be updated in crowd)
198
+ attrs_to_update = user.dirty_attributes
199
+ return if attrs_to_update.empty?
200
+
201
+ attrs_to_update.each do |a|
202
+ prop = SimpleCrowd::User.property_by_name a
203
+ soap_prop = prop.maps[:soap].nil? ? prop : prop.maps[:soap]
204
+ self.update_user_attribute user.username, soap_prop, user.send(a)
205
+ end
206
+ end
207
+
208
+ def app_token
209
+ @app_token ||= cache.read(cache_key(:app_token))
210
+ end
211
+
212
+ def app_token=(token)
213
+ cache.write(cache_key(:app_token), token)
214
+ @app_token = token
215
+ end
216
+
217
+ def cache_store=(store)
218
+ @cache_store = store || Cache::NullStore.new
219
+ end
220
+ alias_method :cache, :cache_store
221
+
222
+ def reset_cache
223
+ [:app_token, :cookie_info].each do |key|
224
+ cache.delete(cache_key(key))
225
+ end
226
+ end
227
+
228
+ private
229
+
230
+ # Simplify the duplicated soap calls across methods
231
+ # @param [Symbol] action the soap action to call
232
+ # @param data the list of args to pass to the server as "in" args (in1, in2, etc.)
233
+ def simple_soap_call action, *data
234
+ # Take each arg and assign it to "in" keys for SOAP call starting with in1 (in0 is app token)
235
+ soap_args = data.inject({}){|hash, arg| hash[:"in#{hash.length + 1}"] = arg; hash }
236
+ # Ordered "in" keys ex. in1, in2, etc. for SOAP ordering
237
+ in_keys = soap_args.length ? (1..soap_args.length).collect {|v| :"in#{v}" } : []
238
+ # Make the SOAP call to the dynamic action
239
+ response = client_with_app_token do |client|
240
+ convert_soap_errors do
241
+ client.request :"#{action}" do |soap|
242
+ prepare soap
243
+ # Pass in all the args as "in" vars
244
+ soap.body = {:in0 => hash_authenticated_token}.merge(soap_args).merge({:order! => [:in0, *in_keys]})
245
+ end
246
+ end
247
+ end
248
+ # If a block is given then call it and pass in the response object, otherwise get the default out value
249
+ block_given? ? yield(response) : response.to_hash[:"#{action}_response"][:out]
250
+ end
251
+
252
+ def convert_soap_errors
253
+ begin
254
+ old_raise_errors = Savon.raise_errors?
255
+ Savon.raise_errors = true
256
+ yield
257
+ rescue Savon::SOAP::Fault => fault
258
+ raise CrowdError.new(fault.to_s, fault)
259
+ rescue Savon::HTTP::Error => e
260
+ raise CrowdError.new(e.to_s)
261
+ ensure
262
+ Savon.raise_errors = old_raise_errors
263
+ end
264
+ end
265
+
266
+ def client
267
+ @client ||= Savon::Client.new do
268
+ wsdl.endpoint = options[:service_url]
269
+ wsdl.namespace = options[:service_ns]
270
+ end
271
+ end
272
+
273
+ def client_with_app_token retries = 1
274
+ self.app_token = authenticate_application unless self.app_token
275
+ begin
276
+ yield client
277
+ rescue CrowdError => e
278
+ if retries > 0 && e.type?(:invalid_authorization_token_exception)
279
+ # Refresh the app token
280
+ self.app_token = authenticate_application
281
+ retries -= 1
282
+ retry
283
+ end
284
+ raise
285
+ end
286
+ end
287
+
288
+ # Setup soap object for request
289
+ def prepare soap
290
+ soap.namespaces.merge! options[:service_namespaces]
291
+ end
292
+
293
+ # Take Crowd SOAP attribute format and return a simple ruby hash
294
+ # @param attributes the soap attributes array
295
+ def process_soap_attributes attributes
296
+ soap = attributes[:soap_attribute]
297
+ (soap && soap.inject({}) {|hash, attr| hash[attr[:name].to_sym] = attr[:values][:string]; hash }) || {}
298
+ end
299
+
300
+ def map_group_hash group
301
+ attributes = process_soap_attributes group[:attributes]
302
+ supported_keys = attributes.keys & SimpleCrowd::Group.mapped_properties(:soap)
303
+ group = group.merge attributes.inject({}) {|map, (k, v)| map[k] = v if supported_keys.include? k; map}
304
+ group[:attributes] = attributes.inject({}) {|map, (k, v)| map[k] = v unless supported_keys.include? k; map}
305
+ group.delete :attributes if group[:attributes].empty?
306
+ SimpleCrowd::Group.new group
307
+ end
308
+
309
+ def prepare_validation_factors factors
310
+ {'auth:validationFactor' =>
311
+ (factors || []).inject([]) {|arr, factor| arr << {'auth:name' => factor[0], 'auth:value' => factor[1]} }
312
+ }
313
+ end
314
+
315
+ def prepare_search_restrictions restrictions
316
+ {'int:searchRestriction' =>
317
+ restrictions.inject([]) {|arr, restrict| arr << {'int:name' => restrict[0], 'int:value' => restrict[1]}}
318
+ }
319
+ end
320
+
321
+ def hash_authenticated_token name = @options[:app_name], token = nil
322
+ token ||= app_token
323
+ {'auth:name' => name, 'auth:token' => token}
324
+ end
325
+
326
+ def no_validation_factors
327
+ {'auth:validationFactors' => {}, :attributes! => {'auth:validationFactors' => {'xsi:nil' => true}}}
328
+ end
329
+
330
+ def cache_key(key)
331
+ "#{@options[:cache_prefix]}#{key}"
332
+ end
333
+ end
334
+ end