sp-seutils 0.0.1

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 (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/sp-seutils.rb +2347 -0
  3. metadata +43 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fbad290d1c8944202370f5dc03228c9952a3c664bd61b8f061e6e0121cc75ea6
4
+ data.tar.gz: a81cb77c92d5bb2009e5c5d40053a61b5aed034b72eaa2d4ae8e58def4f64be6
5
+ SHA512:
6
+ metadata.gz: f79bcd35c7dc7ffe8bbc3e5a95094d6407aa8530c273a4dca360ed9bddbe714510495168dd569019dc5ab29860e5c3f69623249e35c3bb317de4c9197c455e71
7
+ data.tar.gz: 4b53eca404537d0ffb8c34bd294b6b1fada511f5f45869703b4026bc0da03e24afb0021e824531f1f63cdc81a0e6795c337c6d5102e68da3b2782fbcdf911451
@@ -0,0 +1,2347 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # idn-utils.rb
4
+ #
5
+ # Utilities for IdentityNow management
6
+
7
+ class SeUtils
8
+
9
+ require 'net/http'
10
+ require 'net/https'
11
+ require 'uri'
12
+ require 'rest-client'
13
+ # uncomment the following if you want detailed logging of REST calls
14
+ # RestClient.log = 'stdout'
15
+ RestClient.log = '/dev/null'
16
+ require 'digest'
17
+ require 'json'
18
+ require 'base64'
19
+
20
+ def self.cc_url(url)
21
+ base_url = url.split(".identitynow.com").first+".api.identitynow.com"
22
+ api = "/cc/api"+url.split("/api").last
23
+ api = url.split("/api").last if api.split("/").include? "beta"
24
+ api = url.split("/api").last if api.split("/").include? "v2"
25
+ api = url.split("/api").last if api.split("/").include? "v3"
26
+ return base_url+api
27
+ end
28
+
29
+ #-----------------------------------------------------------------------------#
30
+ # API GET UTILS
31
+ # Methods to assist in running API GET calls
32
+ #-----------------------------------------------------------------------------#
33
+
34
+ #-----------------------------------------------------------------------------#
35
+ # run an API GET method, pass in the api
36
+ # org needs to be a full url like 'https://myorg.identitynow.com'
37
+ # api_user and api_key are not the same as user/password
38
+ # returns JSON
39
+ #
40
+ def self.api_get(org,api,user,password,client_id,client_secret,token=nil)
41
+ api = "api/"+api
42
+ url = cc_url(URI.join(org, api).to_s)
43
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
44
+ rest_result = nil
45
+ do_retry = true
46
+ loop do
47
+ break unless do_retry
48
+ begin
49
+ response = RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |r_response, request, result, &block|
50
+ rest_result = handle_rest_response(r_response, request, result, &block)
51
+ do_retry = false
52
+ end
53
+ do_retry = response.nil?
54
+ puts "Received nil response, rertrying..." if response.nil?
55
+ rescue RestClient::Exceptions::OpenTimeout => e
56
+ e.response
57
+ puts "Timed out. Retrying..."
58
+ do_retry = true
59
+ rescue RestClient::Exceptions::ReadTimeout => e
60
+ e.response
61
+ puts "Timed out. Retrying..."
62
+ do_retry = true
63
+ rescue SocketError => e
64
+ puts "SocketError. Retrying..."
65
+ do_retry = true
66
+ rescue RestClient::InternalServerError => e
67
+ puts "RestClient::InternalServerError Retrying..."
68
+ do_retry = true
69
+ rescue RestClient::GatewayTimeout => e
70
+ puts "RestClient::GatewayTimeout Retrying..."
71
+ do_retry = true
72
+ end
73
+ sleep 5 if do_retry # going to retry
74
+ end
75
+ return rest_result
76
+ end
77
+
78
+ #-----------------------------------------------------------------------------#
79
+ # run an API GET method, pass in the api
80
+ # org needs to be a full url like 'https://myorg.identitynow.com'
81
+ # returns JSON
82
+ #
83
+ def self.api_get_new(org,api,client_id,client_secret,token=nil)
84
+ api = "api/"+api
85
+ url = cc_url(URI.join(org, api).to_s)
86
+ token = token || idn_oauth_2(org,client_id,client_secret)
87
+ rest_result = nil
88
+ do_retry = true
89
+ loop do
90
+ break unless do_retry
91
+ begin
92
+ response = RestClient.get(url, {
93
+ 'Authorization'=>"Bearer #{token}",
94
+ "Content-Type"=>"application/json",
95
+ "Accept"=>'*/*',
96
+ "Host"=>URI.parse(org).host,
97
+ "Accept-Encoding" =>'gzip, deflate',
98
+ "Connection" => 'keep-alive',
99
+ "cache-control" => 'no-cache'
100
+ } ) do |r_response, request, result, &block|
101
+ rest_result = handle_rest_response(r_response, request, result, &block)
102
+ do_retry = false
103
+ end
104
+ do_retry = response.nil?
105
+ puts "Received nil response, rertrying..." if response.nil?
106
+ rescue RestClient::Exceptions::OpenTimeout => e
107
+ e.response
108
+ puts "Timed out. Retrying..."
109
+ do_retry = true
110
+ rescue RestClient::Exceptions::ReadTimeout => e
111
+ e.response
112
+ puts "Timed out. Retrying..."
113
+ do_retry = true
114
+ rescue SocketError => e
115
+ puts "SocketError. Retrying..."
116
+ do_retry = true
117
+ rescue RestClient::InternalServerError => e
118
+ puts "RestClient::InternalServerError Retrying..."
119
+ do_retry = true
120
+ rescue RestClient::GatewayTimeout => e
121
+ puts "RestClient::GatewayTimeout Retrying..."
122
+ do_retry = true
123
+ end
124
+ sleep 5 if do_retry # going to retry
125
+ end
126
+ return rest_result
127
+ end
128
+
129
+
130
+ #-----------------------------------------------------------------------------#
131
+ # Get a list of apps
132
+ # returns JSON
133
+ # filter can select which apps to return
134
+ # filter=available is all apps that the logged-in user can add
135
+ # filter=org is the org-wide list
136
+ # filter=added is the ones on the launchpad for the logged-in user
137
+ #
138
+ def self.api_app_list(org,user,password,client_id,client_secret,filter='org',token=nil)
139
+ api = "api/app/list"
140
+ url = cc_url(URI.join(org, api).to_s+"?filter=#{filter}")
141
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
142
+ rest_result = nil
143
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
144
+ rest_result = handle_rest_response(response, request, result, &block)
145
+ end
146
+ return rest_result
147
+ end
148
+
149
+ #-----------------------------------------------------------------------------#
150
+ # Get a list of requestable apps
151
+ # returns JSON
152
+ #
153
+ def self.api_requestable_apps(org,user,password,client_id,client_secret,token=nil)
154
+ api = "api/v2/identity/apps"
155
+ url = URI.join(org, api).to_s
156
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
157
+ rest_result = nil
158
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
159
+ rest_result = handle_rest_response(response, request, result, &block)
160
+ end
161
+ return rest_result
162
+ end
163
+
164
+ #-----------------------------------------------------------------------------#
165
+ # Get an identity object
166
+ # returns JSON
167
+ #
168
+ def self.api_identity(org,user,password,client_id,client_secret,id,token=nil)
169
+ api = "api/v2/identity/#{id}"
170
+ url = URI.join(org, api).to_s
171
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
172
+ rest_result = nil
173
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
174
+ rest_result = handle_rest_response(response, request, result, &block)
175
+ end
176
+ return rest_result
177
+ end
178
+
179
+ #-----------------------------------------------------------------------------#
180
+ # Get a list of identities
181
+ # returns JSON
182
+ # include alias (string) or id (integer) as arg to return only the identity for that one user
183
+ #
184
+ def self.api_identities(org,user,password,client_id,client_secret,arg=nil,token=nil)
185
+ api = "api/v2/identities/"
186
+ # have to paginate in chunks of 250
187
+ rest_result = []
188
+ i = 0
189
+ loop do
190
+ url = URI.join(org, api).to_s+arg.to_s+"?limit=250&offset="+i.to_s
191
+ # puts "executing API GET #{url}"
192
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
193
+ page = nil
194
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
195
+ page = handle_rest_response(response, request, result, &block)
196
+ end
197
+ i += JSON.parse(page).length
198
+ rest_result += JSON.parse(page)
199
+ break if JSON.parse(page).length != 250
200
+ end
201
+ return JSON.generate(rest_result)
202
+ end
203
+
204
+ #-----------------------------------------------------------------------------#
205
+ # Get a list of access profiles
206
+ # returns JSON
207
+ #
208
+ def self.api_accessprofile_list(org,user,password,client_id,client_secret,token=nil)
209
+ api = URI.escape("api/v2/access-profiles")
210
+ rest_result = []
211
+ i = 0
212
+ loop do
213
+ url = URI.join(org, api).to_s+"?limit=250&offset="+i.to_s
214
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
215
+ page = nil
216
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
217
+ page = handle_rest_response(response, request, result, &block)
218
+ end
219
+ i += JSON.parse(page).length
220
+ rest_result += JSON.parse(page)
221
+ break if JSON.parse(page).length != 250
222
+ end
223
+ return rest_result
224
+ end
225
+
226
+ #-----------------------------------------------------------------------------#
227
+ # Get an access profile by id
228
+ # returns JSON
229
+ #
230
+ def self.api_accessprofile_get(org,user,password,client_id,client_secret,id,token=nil)
231
+ api = URI.escape("api/v2/access-profiles/#{id}")
232
+ url = URI.join(org, api).to_s
233
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
234
+ rest_result = nil
235
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
236
+ rest_result = handle_rest_response(response, request, result, &block)
237
+ end
238
+ return rest_result
239
+ end
240
+
241
+ #-----------------------------------------------------------------------------#
242
+ # Get governance groups
243
+ # returns JSON
244
+ #
245
+ def self.api_workgroups_get(org,user,password,client_id,client_secret,id=nil,token=nil)
246
+ api = URI.escape("api/v2/workgroups/#{id}") # hopefully no more than 250 governance groups
247
+ url = URI.join(org, api).to_s
248
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
249
+ rest_result = nil
250
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
251
+ rest_result = handle_rest_response(response, request, result, &block)
252
+ end
253
+ return rest_result
254
+ end
255
+
256
+ #-----------------------------------------------------------------------------#
257
+ # Get an app's config
258
+ # Returns the JSON config data from an app so you can import it elsewhere
259
+ # Exactly the same output as app/update POST
260
+ #
261
+ def self.api_app_get(org,user,password,client_id,client_secret,id,token=nil)
262
+ api = "api/app/get/"+id.to_s
263
+ url = URI.join(org, api).to_s
264
+ return if id.nil? or id == ""
265
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
266
+ redirected_url = nil
267
+ rest_result = nil
268
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
269
+ rest_result = handle_rest_response(response, request, result, &block)
270
+ end
271
+ return rest_result
272
+ end
273
+
274
+ #-----------------------------------------------------------------------------#
275
+ # List all identities
276
+ # returns JSON object wth a total, and then an array of "items"
277
+ #
278
+ def self.api_identity_list(org,user,password,client_id,client_secret,token=nil)
279
+ api = "api/user/list"
280
+ url = cc_url(URI.join(org, api).to_s)
281
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
282
+ redirected_url = nil
283
+ rest_result = nil
284
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
285
+ rest_result = handle_rest_response(response, request, result, &block)
286
+ end
287
+ return rest_result
288
+ end
289
+
290
+ #-----------------------------------------------------------------------------#
291
+ # Get an identity
292
+ # Returns the JSON object for an identity cube
293
+ #
294
+ def self.api_user_details(org,user,password,client_id,client_secret,id,token=nil)
295
+ api = "api/user/details/"+id.to_s
296
+ url = cc_url(URI.join(org, api).to_s)
297
+ return if id.nil? or id == ""
298
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
299
+ redirected_url = nil
300
+ rest_result = nil
301
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
302
+ rest_result = handle_rest_response(response, request, result, &block)
303
+ end
304
+ return rest_result
305
+ end
306
+
307
+ #-----------------------------------------------------------------------------#
308
+ # Get uncorrelated accounts from a source
309
+ #
310
+ #
311
+ def self.api_source_getuncorrelatedaccounts(org,user,password,client_id,client_secret,source_id,token=nil)
312
+ api = "api/source/getUncorrelatedAccounts?id="+source_id
313
+ url = cc_url(URI.join(org, api).to_s)
314
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
315
+ redirected_url = nil
316
+ rest_result = nil
317
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
318
+ rest_result = handle_rest_response(response, request, result, &block)
319
+ end
320
+ return rest_result
321
+ end
322
+
323
+ #-----------------------------------------------------------------------------#
324
+ # Get a list of all connectors
325
+ #
326
+ def self.api_connector_list(org,user,pass,client_id,client_secret,token)
327
+ api = "api/connector/list"
328
+ url = cc_url(URI.join(org, api).to_s)
329
+ redirected_url = nil
330
+ rest_result = nil
331
+ RestClient.get(url, {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
332
+ rest_result = handle_rest_response(response,request,result,&block)
333
+ end
334
+ return rest_result
335
+ end
336
+
337
+ #-----------------------------------------------------------------------------#
338
+ # List sources (beta)
339
+ #
340
+ def self.api_source_get(org,client_id,client_secret,user,pass,source_ext_id,token=nil)
341
+ # if source_ext_id is nil, it will return a list
342
+ api = "/beta/sources/"
343
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
344
+ url = URI.join(base_url, api).to_s
345
+ url = URI.join(base_url, api).to_s+"/"+source_ext_id.to_s unless source_ext_id.nil?
346
+ token = token || idn_oauth_3(org,user,pass,client_id,client_secret)
347
+ rest_result = nil
348
+ redirected_url = nil
349
+ response = RestClient::Request.new({
350
+ method: :get,
351
+ url: url,
352
+ timeout: nil,
353
+ headers: {'Authorization' => "Bearer #{token}",
354
+ "Accept"=>"*/*" ,
355
+ 'Content-Type' => 'application/json',
356
+ 'cache-control' => 'no-cache'
357
+ }
358
+ }).execute do |response, request, result, &block|
359
+ rest_result = handle_rest_response(response, request, result, &block)
360
+ end
361
+ return rest_result
362
+ end
363
+
364
+ #-----------------------------------------------------------------------------#
365
+ # Create a source (beta)
366
+ #
367
+ def self.api_source_post(org,client_id,client_secret,user,pass,args,token=nil)
368
+ return nil if args.nil?
369
+ ["id", "created", "modified","accountCorrelationConfig","schemas","passwordPolicies",
370
+ "backUpAttributes", "managerCorrelationRule"].each{|i| args.delete i}
371
+ ["cloudExternalId", "healthCheckTimeout", "cloudCacheUpdate","acctAggregationEnd",
372
+ "acctAggregationStart", "hasFullAggregationCompleted", "source-config", "user[]",
373
+ "cloudDisplayName", "md5"].each{|i| args["connectorAttributes"].delete i}
374
+ body = JSON.generate(args)
375
+ api = "/beta/sources/"
376
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
377
+ url = URI.join(base_url, api).to_s
378
+ token = token || idn_oauth_3(org,user,pass,client_id,client_secret)
379
+ rest_result = nil
380
+ redirected_url = nil
381
+ response = RestClient::Request.new({
382
+ method: :post,
383
+ url: url,
384
+ payload: body,
385
+ timeout: nil,
386
+ headers: {'Authorization' => "Bearer #{token}",
387
+ "Accept"=>"*/*" ,
388
+ 'Content-Type' => 'application/json',
389
+ 'cache-control' => 'no-cache'
390
+ }
391
+ }).execute do |response, request, result, &block|
392
+ rest_result = handle_rest_response(response, request, result, &block)
393
+ end
394
+ return rest_result
395
+ end
396
+
397
+ #-----------------------------------------------------------------------------#
398
+ # Update a source (beta)
399
+ #
400
+ def self.api_source_put(org,client_id,client_secret,user,pass,source_ext_id,args,token=nil)
401
+ return "{}" if args.nil?
402
+ return "{}" unless args.is_a? Hash
403
+ # have to actually go get the object bundle first, because if we pass in incomplete list,
404
+ # it tends to delete stuff...
405
+ source_json = JSON.parse(api_source_get(org,client_id,client_secret,user,pass,source_ext_id,token))
406
+ source_json.each do |k,v|
407
+ if v.is_a? Hash
408
+ v.each do |l,w|
409
+ source_json[k][l] = "true" if w.is_a? TrueClass
410
+ source_json[k][l] = "false" if w.is_a? FalseClass
411
+ end
412
+ end
413
+ source_json[k] = "true" if v.is_a? TrueClass
414
+ source_json[k] = "false" if v.is_a? FalseClass
415
+ end
416
+ source_json["connectorAttributes"]["cloudExternalId"] = source_json["connectorAttributes"]["cloudExternalId"].to_s
417
+
418
+ # shouldn't be updating these things... we'll keep the ones from the GET
419
+ ["type","connector","connectorClass","created", "modified"].each{|i| args.delete i}
420
+ ["cloudExternalId", "healthCheckTimeout", "cloudCacheUpdate","acctAggregationEnd",
421
+ "acctAggregationStart", "hasFullAggregationCompleted", "user[]",
422
+ "cloudDisplayName", "md5"].each{|i| args["connectorAttributes"].delete i} if args.keys.include? "connectorAttributes"
423
+
424
+ args.each do |k,v|
425
+ if v.is_a? Hash
426
+ args[k].each{|l,w| source_json[k][l] = w}
427
+ else
428
+ source_json[k] = v
429
+ end
430
+ end
431
+
432
+ body = JSON.generate(source_json)
433
+ api = "/beta/sources/"
434
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
435
+ url = URI.join(base_url, api, source_ext_id).to_s
436
+ token = token || idn_oauth_3(org,user,pass,client_id,client_secret)
437
+ rest_result = nil
438
+ redirected_url = nil
439
+ response = RestClient::Request.new({
440
+ method: :put,
441
+ url: url,
442
+ payload: body,
443
+ timeout: nil,
444
+ headers: {'Authorization' => "Bearer #{token}",
445
+ "Accept"=>"*/*" ,
446
+ 'Content-Type' => 'application/json',
447
+ 'cache-control' => 'no-cache'
448
+ }
449
+ }).execute do |response, request, result, &block|
450
+ rest_result = handle_rest_response(response, request, result, &block)
451
+ end
452
+ return rest_result
453
+ end
454
+
455
+ #-----------------------------------------------------------------------------#
456
+ # Patch a source (beta)
457
+ # args is a an array of hashes, must be in specific format:
458
+ # [{:op=>"replace", :path=>"/connectorAttributes/password",:value=>"MyPassword!"}]
459
+ #
460
+ def self.api_source_patch(org,client_id,client_secret,user,pass,source_ext_id,args,token=nil)
461
+ return nil if args.nil?
462
+ args.each do |arg|
463
+ arg.keys.each{|k|arg.delete k unless [:op,:path,:value].include? k.to_sym}
464
+ arg[:op] = "replace"
465
+ end
466
+ body = JSON.generate(args)
467
+ api = "/beta/sources/"
468
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
469
+ url = URI.join(base_url, api, source_ext_id).to_s
470
+ token = token || idn_oauth_3(org,client_id,client_secret,user,pass)
471
+ rest_result = nil
472
+ redirected_url = nil
473
+ response = RestClient::Request.new({
474
+ method: :patch,
475
+ url: url,
476
+ payload: body,
477
+ timeout: nil,
478
+ headers: {'Authorization' => "Bearer #{token}",
479
+ "Accept"=>"*/*" ,
480
+ 'Content-Type' => 'application/json-patch+json',
481
+ 'cache-control' => 'no-cache'
482
+ }
483
+ }).execute do |response, request, result, &block|
484
+ rest_result = handle_rest_response(response, request, result, &block)
485
+ end
486
+ return rest_result
487
+ end
488
+
489
+
490
+ #-----------------------------------------------------------------------------#
491
+ # Get accounts from a source
492
+ #
493
+ def self.api_source_getaccounts(org,user,password,client_id,client_secret,source_id,token=nil)
494
+ api = "api/source/getAccounts"
495
+
496
+ body = JSON.generate({:id=>source_id})
497
+ url = cc_url(URI.join(org, api).to_s)
498
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
499
+
500
+ rest_result = nil
501
+ redirected_url = nil
502
+ response = RestClient::Request.new({
503
+ method: :get,
504
+ url: url,
505
+ payload: body,
506
+ timeout: nil,
507
+ headers: {'Authorization' => "Bearer #{token}",
508
+ "Accept"=>"*/*" ,
509
+ 'Cache-Control' => 'no-cache',
510
+ 'Content-Type' => 'application/json',
511
+ 'cache-control' => 'no-cache'
512
+ }
513
+ }).execute do |response, request, result, &block|
514
+ rest_result = handle_rest_response(response, request, result, &block)
515
+ end
516
+ return rest_result
517
+ end
518
+
519
+ #-----------------------------------------------------------------------------#
520
+ # Get accounts CSV file
521
+ #
522
+ #
523
+ def self.api_source_getaccountscsv(org,user,password,client_id,client_secret,source_id,token=nil)
524
+ puts "api/source/getAccountsCsv is deprecated. Using runAccountsExportReport"
525
+ csv = api_source_export_accounts(org,user,password,client_id,client_secret,source_id,token=nil)
526
+ return csv
527
+ end
528
+
529
+ def self.identity_map(org,user,pass,client_id,client_secret,token)
530
+ token = token || idn_oauth_2(org,client_id,client_secret)
531
+ users = {}
532
+ i = 0
533
+ nil_users = 0
534
+ loop do
535
+ page = JSON.parse(api_get(org, "user/list?start=#{i}&limit=250",user,pass,client_id,client_secret,token))["items"]
536
+ i += page.length
537
+ page.each do |u|
538
+ users[u["name"].split(" ").join(".")] = u["externalId"] unless u.nil?
539
+ @idn_users << u unless u.nil?
540
+ nil_users+=1 if u.nil?
541
+ end
542
+ break if page.length != 250
543
+ end
544
+ puts "There were #{nil_users} nil users!" unless nil_users == 0
545
+ return users
546
+ end
547
+
548
+ def self.source_map(org,user,pass,client_id,client_secret,token)
549
+ token = token || idn_oauth_3(org,user,pass,client_id,client_secret)
550
+ sources = {}
551
+ result = JSON.parse(api_get_new(org,"source/list",client_id,client_secret,token))
552
+ result.each{|s| sources[s["name"]] = s["externalId"]}
553
+
554
+ t_entitlements = []
555
+ entitlements = {}
556
+ sources.each do |source_name,ext_id|
557
+ entitlement_bundle = []
558
+ api = "api/entitlement/list"
559
+ t_entitlements << Thread.new do
560
+ page_size = 250
561
+ offset = 0
562
+ rest_result = nil
563
+ loop do
564
+ args = "?CISApplicationId=#{ext_id}&offset=#{offset}&limit=#{page_size}"
565
+ url = URI.join(org, api+args).to_s # might have to paginate using args above
566
+
567
+ redirected_url = nil
568
+ response = RestClient::Request.new({
569
+ method: :get,
570
+ url: url,
571
+ timeout: nil,
572
+ headers: {'Authorization' => "Bearer #{token}",
573
+ "Accept"=>"*/*" ,
574
+ 'Cache-Control' => 'no-cache',
575
+ 'Content-Type' => 'application/json',
576
+ 'cache-control' => 'no-cache'
577
+ }
578
+ }).execute do |response, request, result, &block|
579
+ rest_result = handle_rest_response(response, request, result, &block)
580
+ end
581
+ puts "result = "+JSON.parse(rest_result).inspect if @debug
582
+ entitlement_bundle += JSON.parse(rest_result)["items"]
583
+ break if JSON.parse(rest_result).length < page_size
584
+ offset += page_size
585
+ end
586
+ t_ent_items = []
587
+ entitlement_bundle.each do |ent|
588
+ t_ent_items << Thread.new{ entitlements[ent["displayableName"]] = ent["id"]}
589
+ end
590
+ t_ent_items.each(&:join)
591
+ end
592
+ end
593
+ t_entitlements.each(&:join)
594
+ return {"applications" => sources, "entitlements" => entitlements}
595
+ end
596
+
597
+
598
+ def self.entitlement_map(org,user,pass,client_id,client_secret,token)
599
+ token = token || idn_oauth(org,user,pass,client_id,client_secret)
600
+ entitlements = {}
601
+ sources = {}
602
+ result = JSON.parse(api_get(org, "source/list",user,pass,client_id,client_secret,token))
603
+ result.each{|s| sources[s["name"]] = s["externalId"]}
604
+ result.each{|s| entitlements[s["name"]] = {"entitlements"=>{}}}
605
+
606
+ t_entitlements = []
607
+ sources.each do |source_name,ext_id|
608
+ t_entitlements << Thread.new do
609
+ result = api_get(org, "entitlement/list/?CISApplicationId=#{ext_id}",user,pass,client_id,client_secret,token)
610
+ entitlement_bundle = JSON.parse(result)["items"]
611
+ t_ent_items = []
612
+ entitlement_bundle.each do |ent|
613
+ t_ent_items << Thread.new{ entitlements[source_name]["entitlements"][ent["displayableName"]] = ent["id"]}
614
+ end
615
+ t_ent_items.each(&:join)
616
+ end
617
+ end
618
+ t_entitlements.each(&:join)
619
+ return entitlements
620
+ end
621
+
622
+ #-----------------------------------------------------------------------------#
623
+ # API POST UTILS
624
+ # Methods to assist in running API POST calls
625
+ #-----------------------------------------------------------------------------#
626
+
627
+ #-----------------------------------------------------------------------------#
628
+ # Run the specified api as POST
629
+ # doesn't work in most cases because IdN's POST methods almost always require
630
+ # specialized or unique data configuration
631
+ #
632
+ def self.api_post(org,api,user,password,client_id,client_secret,data=nil,token=nil)
633
+ api = "api/"+api
634
+ url = cc_url(URI.join(org, api).to_s)
635
+ rest_result = nil
636
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
637
+ redirected_url = nil
638
+ RestClient.post(url, data,
639
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck', "Accept"=>"*/*"
640
+ }) do |response, request, result, &block|
641
+ rest_result = handle_rest_response(response, request, result, &block)
642
+ end
643
+ return rest_result
644
+ end
645
+
646
+ def self.get_recommendations(org,client_id,client_secret,token,data)
647
+ iai_base_url = org.split(".identitynow.com").first+".api.identitynow.com"
648
+ api = "beta/recommendations/request"
649
+
650
+ body = JSON.generate(data)
651
+ url = URI.join(iai_base_url, api).to_s
652
+ token = token || idn_oauth_2(org,client_id,client_secret)
653
+
654
+ rest_result = nil
655
+ redirected_url = nil
656
+ response = RestClient::Request.new({
657
+ method: :post,
658
+ url: url,
659
+ payload: body,
660
+ timeout: nil,
661
+ headers: {'Authorization' => "Bearer #{token}",
662
+ "Accept"=>"*/*" ,
663
+ 'Cache-Control' => 'no-cache',
664
+ 'Content-Type' => 'application/json',
665
+ 'cache-control' => 'no-cache'
666
+ }
667
+ }).execute do |response, request, result, &block|
668
+ rest_result = handle_rest_response(response, request, result, &block)
669
+ end
670
+ return rest_result
671
+ end
672
+
673
+ #-----------------------------------------------------------------------------#
674
+ # Search for identities
675
+ #
676
+ def self.api_search_identities(org,client_id,client_secret,user,pass,query,token=nil)
677
+
678
+ body = nil
679
+ if query.is_a? Array # we passed in a list of identities
680
+ n = []
681
+ query.each{|display_name| n << "attributes.displayName:"+display_name}
682
+ body = JSON.generate({"query"=> {"query"=>n.join(" OR "), "includeNested"=>false}})
683
+ elsif query.is_a? Hash
684
+ # assume the query is correctly formatted, dunno why we have a double query key, so add it
685
+ query = JSON.parse(query) unless query.is_a? Hash
686
+ body = JSON.generate({"query"=>query}) if query.keys.include? "query"
687
+ body = JSON.generate({"query"=>{"query"=>query}}) unless query.keys.include? "query"
688
+ end
689
+
690
+
691
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
692
+ api = "beta/search/identities"
693
+
694
+ token = token || idn_oauth_3(org,user,pass,client_id,client_secret)
695
+
696
+ result = []
697
+ page_size = 50
698
+ offset = 0
699
+
700
+ loop do
701
+ rest_result = []
702
+ args = "?offset=#{offset}&limit=#{page_size}"
703
+ url = URI.join(base_url, api+args).to_s # might have to paginate using args above
704
+
705
+ redirected_url = nil
706
+ response = RestClient::Request.new({
707
+ method: :post,
708
+ url: url,
709
+ payload: body,
710
+ timeout: nil,
711
+ headers: {'Authorization' => "Bearer #{token}",
712
+ "Accept"=>"*/*" ,
713
+ 'Cache-Control' => 'no-cache',
714
+ 'Content-Type' => 'application/json',
715
+ 'cache-control' => 'no-cache'
716
+ }
717
+ }).execute do |response, request, result, &block|
718
+ rest_result = handle_rest_response(response, request, result, &block)
719
+ end
720
+ puts "result = "+JSON.parse(rest_result).inspect if @debug
721
+ result += JSON.parse(rest_result)
722
+ break if JSON.parse(rest_result).length < page_size
723
+ offset += page_size
724
+ end
725
+ return JSON.generate(result)
726
+ end
727
+
728
+ #-----------------------------------------------------------------------------#
729
+ # Get details of a single identity
730
+ #
731
+ def self.api_search_identities_id(org,client_id,client_secret,user,pass,id,token=nil)
732
+
733
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
734
+ api = "beta/search"
735
+
736
+ token = token || idn_oauth_3(org,client_id,client_secret,user,pass)
737
+
738
+ url = URI.join(base_url, api).to_s + "/identities/" + id.to_s
739
+ rest_result = nil
740
+ redirected_url = nil
741
+ response = RestClient::Request.new({
742
+ method: :get,
743
+ url: url,
744
+ timeout: nil,
745
+ headers: {'Authorization' => "Bearer #{token}",
746
+ "Accept"=>"*/*" ,
747
+ 'Cache-Control' => 'no-cache',
748
+ 'Content-Type' => 'application/json',
749
+ 'cache-control' => 'no-cache',
750
+ "Host"=>URI.parse(org).host,
751
+ "Accept-Encoding" =>'gzip, deflate'
752
+ }
753
+ }).execute do |response, request, result, &block|
754
+ rest_result = handle_rest_response(response, request, result, &block)
755
+ end
756
+ return rest_result
757
+ end
758
+
759
+
760
+ #-----------------------------------------------------------------------------#
761
+ # Create a VA cluster
762
+ #
763
+ def self.api_cluster_create(org,user,password,client_id,client_secret,cluster_name,token=nil)
764
+
765
+ url = URI.join(org, "api/cluster/create").to_s
766
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
767
+ redirected_url = nil
768
+ rest_result = nil
769
+ RestClient.post(url, {:name => cluster_name},
770
+ {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
771
+ rest_result = handle_rest_response(response,request,result,&block)
772
+ end
773
+ return rest_result
774
+ end
775
+
776
+ #-----------------------------------------------------------------------------#
777
+ # Create a direct-connect source on the target system
778
+ #
779
+ def self.api_source_create_direct(org,user,password,client_id,client_secret,args,token=nil)
780
+ api = "api/source/create"
781
+ args.each{|k,v|args[k.to_sym] = v}
782
+ url = cc_url(URI.join(org, api).to_s)
783
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
784
+ redirected_url = nil
785
+ rest_result = nil
786
+ RestClient.post(url, {:name => args[:name],
787
+ :description => args[:description],
788
+ :applicationTemplate => args[:type],
789
+ :serviceDefinitionName => args[:type],
790
+ :sourceType => args[:source_type],
791
+ :serviceType => 'app' },
792
+ {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
793
+ rest_result = handle_rest_response(response,request,result,&block)
794
+ end
795
+ return rest_result
796
+ end
797
+
798
+ #-----------------------------------------------------------------------------#
799
+ # Clone an entitlement
800
+ # You have to do this whenever you create an App Access Profile
801
+ # args are a hash of {:ids => <array of entitlement IDs>, :CISApplicationId => <Source External ID>
802
+ #
803
+ def self.api_entitlement_clone(org,user,password,client_id,client_secret,args,token=nil)
804
+ # this call uses "ids" NOT as a comma-separated list, but as a list of "ids=<id>"
805
+ ids = []
806
+ args[:ids].each{|id|ids << "&ids="+id}
807
+ args.delete :ids
808
+ data = hash_to_args(args)+ids.join
809
+ api = "api/entitlement/clone"
810
+ url = URI.join(org, api).to_s
811
+ rest_result = nil
812
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
813
+ redirected_url = nil
814
+ args =
815
+ RestClient.post(url, data,
816
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck', "Accept"=>"*/*"
817
+ }) do |response, request, result, &block|
818
+ rest_result = handle_rest_response(response, request, result, &block)
819
+ end
820
+ sleep 2
821
+ return rest_result
822
+ end
823
+
824
+ #-----------------------------------------------------------------------------#
825
+ # Create an access profile on the source
826
+ # the "data" is a hash that has required elements:
827
+ # :name :sourceId :entitlements :description
828
+ # :entitlements is an array of the entitlement IDs
829
+ #
830
+ def self.api_accessprofile_create(org,user,password,client_id,client_secret,data,token=nil)
831
+ api = "api/v2/access-profiles/"
832
+ if data[:entitlements].nil? or data[:entitlements].length == 0
833
+ puts "Can't create Access Profile '#{data[:name]}' without entitlements"
834
+ return
835
+ end
836
+ org = org.split('.').first
837
+ data[:entitlements] = Array(data[:entitlements].reject(&:empty?))
838
+ data[:description] = data[:name] if data[:description].nil?
839
+ id = data[:id]
840
+ data.delete(:id)
841
+ payload = JSON.generate(data)
842
+ url = URI.join("#{org}.identitynow.com", api).to_s
843
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
844
+ redirected_url = nil
845
+ rest_result = nil
846
+ do_retry = true
847
+ loop do
848
+ break unless do_retry
849
+ begin
850
+ response = RestClient.post(url, payload,
851
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
852
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'application/json',
853
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
854
+ 'Connection' => 'keep-alive' }) do |r_response, request, result, &block|
855
+ rest_result = handle_rest_response(r_response,request,result,&block)
856
+ do_retry = false
857
+ end
858
+ do_retry = response.nil?
859
+ puts "Received nil response, rertrying..." if response.nil?
860
+ rescue RestClient::Exceptions::OpenTimeout => e
861
+ e.response
862
+ puts "Timed out. Retrying..."
863
+ do_retry = true
864
+ rescue RestClient::Exceptions::ReadTimeout => e
865
+ e.response
866
+ puts "Timed out. Retrying..."
867
+ do_retry = true
868
+ rescue SocketError => e
869
+ puts "SocketError. Retrying..."
870
+ do_retry = true
871
+ rescue RestClient::InternalServerError => e
872
+ puts "RestClient::InternalServerError Retrying..."
873
+ do_retry = true
874
+ rescue RestClient::GatewayTimeout => e
875
+ puts "RestClient::GatewayTimeout Retrying..."
876
+ do_retry = true
877
+ end
878
+ sleep 5 if do_retry
879
+ end
880
+ sleep 2
881
+ return rest_result
882
+ end
883
+
884
+ def self.api_accessprofile_bulkdelete(org,user,password,client_id,client_secret,data,token=nil)
885
+ api = "api/v2/access-profiles/bulk-delete/"
886
+ payload = JSON.generate(data)
887
+ url = URI.join(org,api).to_s
888
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
889
+ redirected_url = nil
890
+ rest_result = nil
891
+ RestClient.post(url, payload,
892
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
893
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'application/json',
894
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
895
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
896
+ rest_result = handle_rest_response(response,request,result,&block)
897
+ end
898
+ puts "result = "+JSON.parse(rest_result).inspect if @debug
899
+ sleep 2
900
+ return rest_result
901
+ end
902
+
903
+ #-----------------------------------------------------------------------------#
904
+ # Create an access profile on an app
905
+ # the "data" is a hash that has required elements:
906
+ # :name :sourceId :entitlements :description
907
+ # :entitlements is an array of the entitlement IDs
908
+ #
909
+ def self.api_app_createaccessprofile(org,user,password,client_id,client_secret,data,token=nil)
910
+ puts "Don't use this method api_app_createaccessprofile, creating on source instead with api_accessprofile_create"
911
+ return api_accessprofile_create(org, user, password, api_user ,api_key, data, token)
912
+ end
913
+
914
+ #-----------------------------------------------------------------------------#
915
+ # Update an access profile
916
+ # the "data" is a hash that has required elements:
917
+ # :name :sourceId :entitlements :description :id
918
+ # :entitlements is an array of the entitlement IDs for the AP, and :id is the
919
+ # id of the target access profile to update
920
+ #
921
+ def self.api_accessprofile_update(org,user,password,client_id,client_secret,data,token=nil)
922
+ api = "api/v2/access-profiles/"
923
+ org = org.split('.').first
924
+ data[:entitlements] = Array(data[:entitlements].reject(&:empty?))
925
+ data[:description] = data[:name] if data[:description].nil?
926
+ id = data[:id]
927
+ data.delete(:id)
928
+ json_data = JSON.generate data
929
+ payload = json_data
930
+ url = URI.join("#{org}.identitynow.com", api, id).to_s
931
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
932
+ redirected_url = nil
933
+ rest_result = nil
934
+ RestClient.patch(url, payload,
935
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
936
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'application/json',
937
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
938
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
939
+ rest_result = handle_rest_response(response,request,result,&block)
940
+ end
941
+ sleep 2
942
+ return rest_result
943
+ end
944
+
945
+ #-----------------------------------------------------------------------------#
946
+ # Post an access request
947
+ #
948
+ # data is a hash including:
949
+ # :user_id - id of the user who is making the access request
950
+ # :app_id - id of the application being requested
951
+ # :ap_id - id of the access profile being requested
952
+
953
+ def self.api_accessrequest_make(org,user,password,client_id,client_secret,data,basic_auth,token=nil)
954
+ api = "/api/v2/users/#{data[:user_id]}/launchers?appId=#{data[:app_id]}&accessProfileId=#{data[:ap_id]}"
955
+ org = org.split('.').first
956
+ url = URI.join("#{org}.identitynow.com", api).to_s
957
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
958
+ redirected_url = nil
959
+ rest_result = nil
960
+ RestClient.post(url, nil, headers = {'Authorization' => "Basic #{basic_auth}", 'X-CSRF-Token' => 'nocheck',
961
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'application/json',
962
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
963
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
964
+ rest_result = handle_rest_response(response,request,result,&block)
965
+ end
966
+ sleep 2
967
+ return rest_result
968
+ end
969
+
970
+ #-----------------------------------------------------------------------------#
971
+ # Access Request approvals
972
+ #
973
+ # data is a hash including:
974
+ # :request_id - id of access request to approve (or nil)
975
+ # :action - approve or reject (or nil)
976
+ # data is REQUIRED but can be nil
977
+ # basic auth probably won't work except for getting a list of all approvals
978
+
979
+ # /api/v2/approvals/<id>/approve (or reject)
980
+
981
+ def self.api_accessrequest_approval(org,user,password,client_id,client_secret,data,basic_auth,token=nil)
982
+ if data.nil?
983
+ api = "/api/v2/approvals/"
984
+ else
985
+ api = "/api/v2/approvals/#{data[:request_id]}/#{data[:action]}"
986
+ end
987
+ org = org.split('.').first
988
+
989
+ url = URI.join("#{org}.identitynow.com", api).to_s
990
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
991
+ redirected_url = nil
992
+ rest_result = nil
993
+ RestClient.post(url, nil, headers = {'Authorization' => "Basic #{basic_auth}", 'X-CSRF-Token' => 'nocheck',
994
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'application/json',
995
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
996
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
997
+ rest_result = handle_rest_response(response,request,result,&block)
998
+ end
999
+ sleep 2
1000
+ return rest_result
1001
+ end
1002
+
1003
+ #-----------------------------------------------------------------------------#
1004
+ # Create a custom connector
1005
+ #
1006
+ def self.api_connector_create(org,user,pass,client_id,client_secret,args,token)
1007
+ api = "api/connector/create"
1008
+ url = URI.join(org, api).to_s
1009
+ redirected_url = nil
1010
+ rest_result = nil
1011
+ RestClient.post(url, args,
1012
+ {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1013
+ rest_result = handle_rest_response(response,request,result,&block)
1014
+ end
1015
+ return rest_result
1016
+ end
1017
+
1018
+ #-----------------------------------------------------------------------------#
1019
+ # Discover Schema
1020
+ #
1021
+ def self.api_source_discoverschema(org,user,password,client_id,client_secret,id,token=nil)
1022
+ api = "/cc/api/source/discoverSchema"
1023
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
1024
+ token = token || idn_oauth_3(org,client_id,client_secret,user,pass)
1025
+ url = URI.join(base_url,api).to_s+"/"+id.to_s
1026
+ redirected_url = nil
1027
+ rest_result = nil
1028
+ response = RestClient::Request.new({
1029
+ method: :post,
1030
+ url: url,
1031
+ path: api+"/"+id.to_s,
1032
+ timeout: nil,
1033
+ headers: {'Authorization' => "Bearer #{token}",
1034
+ "Accept"=>"*/*" ,
1035
+ 'Cache-Control' => 'no-cache'
1036
+ }
1037
+ }).execute do |response, request, result, &block|
1038
+ rest_result = handle_rest_response(response, request, result, &block)
1039
+ end
1040
+ return rest_result
1041
+ end
1042
+
1043
+ #-----------------------------------------------------------------------------#
1044
+ # Set schema (beta)
1045
+ #
1046
+ def self.set_schema(org,user,pass,clent_id,client_secret,source_id,schema,token=nil)
1047
+ return nil if schema.nil?
1048
+ # first figure out if a schema already exists of this type, because it's PUT vs. POST
1049
+
1050
+ api = "/beta/sources/#{source_id}/schemas/"
1051
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
1052
+ url = URI.join(base_url, api).to_s
1053
+ token = token || idn_oauth_3(org,user,pass,client_id,client_secret)
1054
+ rest_result = nil
1055
+ redirected_url = nil
1056
+ response = RestClient::Request.new({
1057
+ method: :get,
1058
+ url: url,
1059
+ timeout: nil,
1060
+ headers: {'Authorization' => "Bearer #{token}",
1061
+ "Accept"=>"*/*" ,
1062
+ 'Content-Type' => 'application/json',
1063
+ 'cache-control' => 'no-cache'
1064
+ }
1065
+ }).execute do |response, request, result, &block|
1066
+ rest_result = handle_rest_response(response, request, result, &block)
1067
+ end
1068
+
1069
+ existing_schemas = JSON.parse(rest_result)
1070
+ found_existing = false
1071
+ existing_schema_id = nil
1072
+ existing_schemas.each do |s|
1073
+ if s["nativeObjectType"] == schema["nativeObjectType"]
1074
+ schema["id"] = s["id"]
1075
+ existing_schema_id = s["id"]
1076
+ found_existing = true
1077
+ end
1078
+ end
1079
+ body = JSON.generate(schema)
1080
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
1081
+
1082
+ if !found_existing
1083
+ api = "/beta/sources/#{source_id}/schemas/"
1084
+ url = URI.join(base_url, api).to_s
1085
+ token = token || idn_oauth_3(org,user,pass,client_id,client_secret)
1086
+ rest_result = nil
1087
+ redirected_url = nil
1088
+ response = RestClient::Request.new({
1089
+ method: :post,
1090
+ url: url,
1091
+ payload: body,
1092
+ timeout: nil,
1093
+ headers: {'Authorization' => "Bearer #{token}",
1094
+ "Accept"=>"*/*" ,
1095
+ 'Content-Type' => 'application/json',
1096
+ 'cache-control' => 'no-cache'
1097
+ }
1098
+ }).execute do |response, request, result, &block|
1099
+ rest_result = handle_rest_response(response, request, result, &block)
1100
+ end
1101
+ else
1102
+ api = "/beta/sources/#{source_id}/schemas/#{existing_schema_id}"
1103
+ url = URI.join(base_url, api).to_s
1104
+ token = token || idn_oauth_3(org,user,pass,client_id,client_secret)
1105
+ rest_result = nil
1106
+ redirected_url = nil
1107
+ response = RestClient::Request.new({
1108
+ method: :put,
1109
+ url: url,
1110
+ payload: body,
1111
+ timeout: nil,
1112
+ headers: {'Authorization' => "Bearer #{token}",
1113
+ "Accept"=>"*/*" ,
1114
+ 'Content-Type' => 'application/json',
1115
+ 'cache-control' => 'no-cache'
1116
+ }
1117
+ }).execute do |response, request, result, &block|
1118
+ rest_result = handle_rest_response(response, request, result, &block)
1119
+ end
1120
+ end
1121
+ return rest_result
1122
+ end
1123
+
1124
+ #-----------------------------------------------------------------------------#
1125
+ # Import source xml file
1126
+ #
1127
+ def self.api_source_import(org,user,password,client_id,client_secret,id,filename,token=nil)
1128
+ api = "/cc/api/source/import"
1129
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
1130
+ token = token || idn_oauth_3(org,client_id,client_secret,user,pass)
1131
+ url = URI.join(base_url,api).to_s+"/"+id.to_s
1132
+ redirected_url = nil
1133
+ rest_result = nil
1134
+ response = RestClient::Request.new({
1135
+ method: :post,
1136
+ url: url,
1137
+ path: api+"/"+id.to_s,
1138
+ :payload => { :file => File.new(filename, 'rb') },
1139
+ timeout: nil,
1140
+ headers: {'Authorization' => "Bearer #{token}",
1141
+ "Accept"=>"*/*" ,
1142
+ 'cache-control' => 'no-cache'
1143
+ }
1144
+ }).execute do |response, request, result, &block|
1145
+ rest_result = handle_rest_response(response, request, result, &block)
1146
+ end
1147
+ return rest_result
1148
+ end
1149
+
1150
+ #-----------------------------------------------------------------------------#
1151
+ # Export connector zip file
1152
+ #
1153
+ def self.api_connector_export(org,user,pass,client_id,client_secret,connector_id,token)
1154
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1155
+ api = "api/connector/export/"+connector_id.to_s
1156
+ url = URI.join(org, api).to_s
1157
+ rest_result = nil
1158
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
1159
+ rest_result = handle_rest_response(response, request, result, &block)
1160
+ end
1161
+ zip_data = rest_result
1162
+
1163
+ zip_filename = "#{connector_id}.zip"
1164
+ puts "Writing connector file: "+zip_filename
1165
+ pwd = Dir.pwd
1166
+ out_file = File.open(File.join(pwd, zip_filename), 'w')
1167
+ out_file.write(zip_data)
1168
+ out_file.close
1169
+
1170
+ return zip_data
1171
+ end
1172
+
1173
+ #-----------------------------------------------------------------------------#
1174
+ # Import connector zip file
1175
+ #
1176
+ def self.api_connector_import(org,user,password,client_id,client_secret,id,filename,token=nil)
1177
+ api = "api/connector/import/#{id.to_s}"
1178
+ url = URI.join(org, api).to_s
1179
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1180
+ redirected_url = nil
1181
+ rest_result = nil
1182
+ RestClient.post(url, {:file => File.new(filename)},
1183
+ {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1184
+ rest_result = handle_rest_response(response,request,result,&block)
1185
+ end
1186
+ return rest_result
1187
+ end
1188
+
1189
+ #-----------------------------------------------------------------------------#
1190
+ # Export source flat file
1191
+ #
1192
+ def self.api_source_export_accounts(org,user,password,client_id,client_secret,source_id,token=nil)
1193
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1194
+
1195
+ json_source = JSON.parse(api_get(org, "source/get/#{source_id}", user, password, api_user, api_key, token))
1196
+ # if json_source["sourceType"] == "DELIMITED_FILE"
1197
+ # source_csv = api_get(org, "source/exportAccountFeed/?id=#{source_id}", user, password, api_user, api_key, @token)
1198
+ # return source_csv
1199
+ # end
1200
+
1201
+ # we will have to
1202
+ # 1. runAccountsExportReport
1203
+ # 2. get the task id
1204
+ # 3. poll until the task is finished
1205
+ # 4. download the file
1206
+ # 5. unzip the file
1207
+ # 6. read the unzipped file
1208
+ # 7. delete the fields
1209
+ # 8. return the csv
1210
+
1211
+ t = Time.now.to_i
1212
+
1213
+ args = {:sourceId => source_id, :reportName => "Accounts Report"}
1214
+
1215
+ api = "api/source/runAccountsExportReport/"+source_id.to_s
1216
+ url = URI.join(org, api).to_s
1217
+ # puts "executing API POST #{url}"
1218
+ redirected_url = nil
1219
+ rest_result = nil
1220
+ RestClient.post(url, args, {'Authorization' => "Bearer #{token}",
1221
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1222
+ rest_result = handle_rest_response(response,request,result,&block)
1223
+ end
1224
+ puts "result = "+JSON.parse(rest_result).inspect if @debug
1225
+
1226
+ require "shellwords"
1227
+ task = JSON.parse(rest_result)
1228
+ zip_filename = Shellwords.escape(task["name"]+".zip")
1229
+ zip_filename = "accounts.zip"
1230
+
1231
+ api = "api/taskResult/get/?id="+task["id"]
1232
+ loop do
1233
+ sleep 2
1234
+ result = JSON.parse(api_get(org,api,user,password,client_id,client_secret,@token))
1235
+ break if result["progress"] == "Completed"
1236
+ end
1237
+
1238
+ api = "api/report/get/"+task["id"].to_s+"?format=csv&name=source_"+source_id.to_s+"_accounts"
1239
+ url = URI.join(org, api).to_s
1240
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
1241
+ rest_result = handle_rest_response(response, request, result, &block)
1242
+ end
1243
+ zip_data = rest_result
1244
+
1245
+ pwd = Dir.pwd
1246
+ tmp_dir = File.join(pwd,"_zipfile_temp_#{t}")
1247
+ Dir.mkdir tmp_dir unless File.exist? tmp_dir
1248
+
1249
+ out_file = File.open(File.join(tmp_dir, zip_filename), 'w')
1250
+
1251
+ out_file.write(zip_data)
1252
+ out_file.close
1253
+
1254
+ require 'zip'
1255
+ file = File.join(tmp_dir, zip_filename)
1256
+
1257
+ csv_filename = ""
1258
+
1259
+ Zip::File.open(file) do |zip_file|
1260
+ zip_file.each do |f|
1261
+ fpath = File.join(tmp_dir, f.name)
1262
+ result = zip_file.extract(f, fpath)
1263
+ csv_filename = f.name
1264
+ end
1265
+ end
1266
+
1267
+ accounts_csv = File.read(File.join(tmp_dir, csv_filename))
1268
+ puts "Exported accounts flat file: "+csv_filename
1269
+
1270
+ require 'fileutils'
1271
+ FileUtils.rm_r tmp_dir
1272
+
1273
+ return accounts_csv
1274
+ end
1275
+
1276
+
1277
+ #-----------------------------------------------------------------------------#
1278
+ # Import source flat file
1279
+ #
1280
+ def self.api_source_loadaccounts(org,user,password,client_id,client_secret,id,filename,token=nil)
1281
+ api = "api/source/loadAccounts/"
1282
+ url = URI.join(org, api, id).to_s
1283
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1284
+ redirected_url = nil
1285
+ rest_result = nil
1286
+ RestClient.post(url, {:file => File.new(filename)},
1287
+ {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1288
+ rest_result = handle_rest_response(response,request,result,&block)
1289
+ end
1290
+ return rest_result
1291
+ end
1292
+
1293
+ #-----------------------------------------------------------------------------#
1294
+ # Update the config of a source from the output of api/sources/list for that
1295
+ # source. config_json is the config data. Deletes keys that can't be updated.
1296
+ # I found this list by trial and error and it's almost definitely not complete.
1297
+ # -JK
1298
+ #
1299
+ def self.api_service_update(org,user,password,client_id,client_secret,id,config_json,token=nil)
1300
+ puts "calling api/source/update instead of [requested] api/service/update."
1301
+ api = "api/source/update/"
1302
+ url = URI.join(org, api, id).to_s
1303
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1304
+ data = config_json.first
1305
+ # have to delete some stuff from the data that will not import on a target source.
1306
+ # this list will have to be constantly updated when each new source type is encountered
1307
+ # because our APIs are totally not user-friendly
1308
+ a = ["id", "version", "lastUpdated", "appCount", "userCount", "sourceConnected",
1309
+ "connectedApps", "connector_secret", "connector_kid", "health", "externalId",
1310
+ "passwordPolicy", "passwordPolicyName", "passwordSyncGroup",
1311
+ "connectorCluster",
1312
+ "connectorClusterGmtOffset", "authenticationCookie", "certificate","metadata","entityID",
1313
+ "forceNameID", "hideAcsUrl", "hideAttributeMap", "hideEntityID", "acsUrl", "applicationTemplate",
1314
+ "encryptAssertion", "connector_password", "connector_user", "attributeMap",
1315
+ "domainName", "enableSAML", "nameIdFormat", "none_supported", "passwordReplay_supported",
1316
+ "proxy_supported", "samlBinding", "saml_supported", "wsfed_supported"]
1317
+ # for whatever reason, icon is not getting set
1318
+ # eventually need to do passwordPolicy and passwordSyncGroup
1319
+ # attributeMap should be put back eventually, it's a bug due to switching from OpenDJ
1320
+ a.each{|i|data.delete i}
1321
+ args = hash_to_args data # it comes in as an array with a hash first
1322
+ redirected_url = nil
1323
+ rest_result = nil
1324
+ RestClient.post(url, args,
1325
+ {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1326
+ rest_result = handle_rest_response(response,request,result,&block)
1327
+ end
1328
+ puts "result = "+JSON.parse(rest_result).inspect if @debug
1329
+ return rest_result
1330
+ end
1331
+
1332
+ #-----------------------------------------------------------------------------#
1333
+ # Update properties of a source
1334
+ #
1335
+ def self.api_source_update(org,user,password,client_id,client_secret,args,token=nil)
1336
+ api = "api/source/update/"
1337
+
1338
+ url = URI.join(org, api, args[:id].to_s).to_s
1339
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1340
+ redirected_url = nil
1341
+ rest_result = nil
1342
+ do_retry = true
1343
+ loop do
1344
+ break unless do_retry
1345
+ RestClient.post(url, args,
1346
+ {'Authorization' => "Bearer #{token}",'usage' => 'create', 'content_type' => 'text/plain',
1347
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1348
+ rest_result = handle_rest_response(response,request,result,&block)
1349
+ end
1350
+ do_retry = ([503,504].include? rest_result.code.to_i)
1351
+ sleep 5 if do_retry # going to retry
1352
+ puts "Retrying source update due temporary error." if do_retry
1353
+ end
1354
+ return rest_result
1355
+ end
1356
+
1357
+ #-----------------------------------------------------------------------------#
1358
+ # Update the so-called "Create Profile" (account profile) on the specified
1359
+ # source
1360
+ #
1361
+ def self.api_accountprofile_bulkupdate(org,user,password,client_id,client_secret,id,account_profile_json,token=nil)
1362
+
1363
+ api = "api/accountProfile/bulkUpdate/"
1364
+ url = cc_url(URI.join(org, api, id).to_s).to_s
1365
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1366
+ payload = JSON.generate(JSON.parse(account_profile_json)).to_s if account_profile_json.is_a? String
1367
+ payload = JSON.generate(account_profile_json).to_s if account_profile_json.is_a? Hash
1368
+ redirected_url = nil
1369
+ rest_result = nil
1370
+ RestClient.post(url, payload,
1371
+ {'Authorization' => "Bearer #{token}",'usage' => 'create',
1372
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate',
1373
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Cache-Control' => 'no-cache',
1374
+ 'Connection' => 'keep-alive', 'Content-Type' => 'text/plain;charset=UTF-8',
1375
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1376
+ rest_result = handle_rest_response(response,request,result,&block)
1377
+ end
1378
+ return rest_result
1379
+ end
1380
+
1381
+ #-----------------------------------------------------------------------------#
1382
+ # Update the so-called "Create Profile" (account profile) on the specified
1383
+ # source
1384
+ #
1385
+ def self.api_accountprofile_update(org,user,password,client_id,client_secret,id,account_profile_json,token=nil)
1386
+ return nil if account_profile_json.nil?
1387
+ api = "api/accountProfile/update/"
1388
+ url = cc_url(URI.join(org, api, id).to_s)+"?usage=Create"
1389
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1390
+ payload = JSON.generate(account_profile_json) # if account_profile_json.is_a? Hash
1391
+ redirected_url = nil
1392
+ rest_result = nil
1393
+ RestClient.post(url, payload,
1394
+ {'Authorization' => "Bearer #{token}",
1395
+ 'Accept' => '*/*',
1396
+ "Content-Type" => "application/json",
1397
+ 'Cache-Control' => 'no-cache',
1398
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1399
+ rest_result = handle_rest_response(response,request,result,&block)
1400
+ end
1401
+ return rest_result
1402
+ end
1403
+
1404
+ #-----------------------------------------------------------------------------#
1405
+ # Reset a source
1406
+ #
1407
+ def self.api_source_reset(org,user,password,client_id,client_secret,id,query,token=nil)
1408
+ api = "api/source/reset/"
1409
+ url = cc_url(URI.join(org, api, id, query).to_s).to_s
1410
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1411
+ redirected_url = nil
1412
+ rest_result = nil
1413
+ RestClient.post(url, nil,
1414
+ {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1415
+ rest_result = handle_rest_response(response,request,result,&block)
1416
+ end
1417
+ return rest_result
1418
+ end
1419
+
1420
+ #-----------------------------------------------------------------------------#
1421
+ # Import entitlement CSV file
1422
+ #
1423
+ def self.api_source_import_entitlementscsv(org,user,password,client_id,client_secret,id,filename,token=nil)
1424
+ api = "api/source/importEntitlementsCsv/"
1425
+ url = URI.join(org, api, id).to_s
1426
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1427
+ redirected_url = nil
1428
+ rest_result = nil
1429
+ RestClient.post(url, {:file => File.new(filename)},
1430
+ {'Authorization' => "Bearer #{token}",'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1431
+ rest_result = handle_rest_response(response,request,result,&block)
1432
+ end
1433
+ return rest_result
1434
+ end
1435
+
1436
+ #-----------------------------------------------------------------------------#
1437
+ # Aggregate a source with optimization disabled
1438
+ #
1439
+ def self.api_aggregate_source(org,user,password,client_id,client_secret,id,token=nil)
1440
+ api = "api/source/loadAccounts/"
1441
+ url = cc_url(URI.join(org, api, id).to_s+"?disableOptimization=true")
1442
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1443
+ rest_result = nil
1444
+ redirected_url = nil
1445
+ response = RestClient::Request.new({
1446
+ method: :post,
1447
+ url: url,
1448
+ payload: nil,
1449
+ timeout: nil,
1450
+ headers: {'Authorization' => "Bearer #{token}",
1451
+ "Accept"=>"*/*" ,
1452
+ 'Cache-Control' => 'no-cache',
1453
+ 'Content-Type' => 'application/json',
1454
+ 'cache-control' => 'no-cache'
1455
+ }
1456
+ }).execute do |response, request, result, &block|
1457
+ rest_result = handle_rest_response(response, request, result, &block)
1458
+ end
1459
+ return rest_result
1460
+ end
1461
+
1462
+
1463
+ #-----------------------------------------------------------------------------#
1464
+ # Create an app
1465
+ # args needs to be a hash including :name and :description
1466
+ #
1467
+ def self.api_app_create(org,user,password,client_id,client_secret,args,token=nil)
1468
+ api = "api/app/create"
1469
+ url = URI.join(org, api).to_s
1470
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1471
+ redirected_url = nil
1472
+ rest_result = nil
1473
+ RestClient.post(url, {:name => args[:name],
1474
+ :description => args[:description]},
1475
+ {'Authorization' => "Bearer #{token}",'usage' => 'create', 'content_type' => 'text/plain',
1476
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1477
+ rest_result = handle_rest_response(response,request,result,&block)
1478
+ end
1479
+ return rest_result
1480
+ end
1481
+
1482
+ #-----------------------------------------------------------------------------#
1483
+ # Update an app
1484
+ # Also returns the JSON config data from an app so you can import it elsewhere
1485
+ # Use this to set most of the infiormation for an app.
1486
+ #
1487
+ def self.api_app_update(org,user,password,client_id,client_secret,args,token=nil)
1488
+ api = "api/app/update/"
1489
+ id = args[:id]
1490
+ url = URI.join(org, api, id.to_s).to_s
1491
+ token_time = Time.now.to_i
1492
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1493
+ token_monitor = Thread.new do
1494
+ loop do
1495
+ sleep 60
1496
+ if Time.now.to_i - token_time > 300 # the token may expire
1497
+ token = idn_oauth(org,user,password,client_id,client_secret)
1498
+ end
1499
+ end
1500
+ end
1501
+ redirected_url = nil
1502
+ rest_result = nil
1503
+ app_name = args[:name]
1504
+
1505
+ # if they only passed in the id, they probably wanted api/app/get
1506
+ app_json = api_app_get(org,user,password,client_id,client_secret,args[:id],token)
1507
+ current = JSON.parse(app_json) unless app_json.nil?
1508
+ return JSON.generate(current) if args.keys == [:id]
1509
+ # Updating an app can't be done all at once. The UI does it in multiple steps,
1510
+ # and the API seems to be unable to handle it any other way.
1511
+ # Going to have to partition the dataset and perform multiple REST calls
1512
+
1513
+ # Remove unnecessary stuff...
1514
+ [:appId, :serviceId, :serviceAppId, :health, :dateCreated, :lastUpdated,
1515
+ :applicationTemplate, :externalId, :launcherCount, :enabledSamlTimestamp,
1516
+ :app_accounts, :appAccounts, :authenticationCookie,
1517
+ :scriptName, :appProfiles, :accountServiceExternalId,
1518
+ :passwordServiceName, :accountServiceName,
1519
+ :passwordServiceUseForPasswordManagement,
1520
+ :privateApp, :status, :usageAnalytics,:supportsEntitlementAggregation,
1521
+ :defaultAccessProfile, :accountServicePolicyName].each{|i|args.delete i}
1522
+
1523
+
1524
+ # TODO: translate/fix this. actually the burden should be on the source.
1525
+ args.delete :accountServicePolicies
1526
+
1527
+ args.each{|k,v| args.delete k if v.nil?}
1528
+ args.each{|k,v| args.delete k if v == ""}
1529
+
1530
+ compare = {}
1531
+
1532
+ # fix the usageCertText so that it will parse correctly
1533
+ args[:usageCertText] = args[:usageCertText].gsub('&', 'and') unless args[:usageCertText].nil?
1534
+
1535
+ args[:service] = current["service"] if args[:service].nil?
1536
+ args.delete :accountServiceId if args[:accountServiceId].to_s == "-1"
1537
+
1538
+ equal = []
1539
+ unequal_keys = [:id, :controlType, :accountServiceId]
1540
+
1541
+ wait_for_identity_refresh(org,user,password,client_id,client_secret,token)
1542
+ rest_result = nil
1543
+ source = nil
1544
+ if args.keys.include? :accountServiceId
1545
+ source = {:id => args[:id], :accountServiceId => args[:accountServiceId].to_s,
1546
+ :accountServiceMatchAllAccounts => args[:accountServiceMatchAllAccounts],
1547
+ :provisionRequestEnabled => args[:provisionRequestEnabled],
1548
+ :app_accounts => args[:app_accounts],
1549
+ :appCenterEnabled => args[:appCenterEnabled],
1550
+ :controlType => args[:controlType] }
1551
+ source[:accountServiceMatchAllAccounts] = false if args[:accountServiceMatchAllAccounts].nil?
1552
+ source.each{|k,v| source.delete k if v.nil?}
1553
+ source.each{|k,v| source.delete k if v == ""}
1554
+ end
1555
+
1556
+ owner = nil
1557
+ if args.keys.include? :owner
1558
+ unless (args[:owner] == []) || (args[:owner].nil?)
1559
+ owner = { :id=>args[:id], :ownerId=>args[:owner]["id"]}
1560
+ end
1561
+ end
1562
+
1563
+ access_profiles = nil
1564
+ if args.keys.include? :accessProfileIds
1565
+ unless args[:accessProfileIds].nil?
1566
+ access_profiles = { :id=>args[:id], :accessProfileIds => args[:accessProfileIds]}
1567
+ end
1568
+ args.delete :accessProfileIds
1569
+ end
1570
+
1571
+ [source, owner, args].each do |rest_data|
1572
+ next if rest_data.nil?
1573
+ RestClient.post(url, rest_data,
1574
+ {'Authorization' => "Bearer #{token}",'usage' => 'create', 'content_type' => 'application/json',
1575
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1576
+ rest_result = handle_rest_response(response,request,result,&block)
1577
+ end
1578
+ puts "result = "+JSON.parse(rest_result).inspect if @debug
1579
+ wait_for_identity_refresh(org,user,password,client_id,client_secret,token)
1580
+ end
1581
+
1582
+ unless access_profiles.nil?
1583
+ # RestClient has a bug that results in an array in the data field being posted wrong, so we can't use it to set access profiles
1584
+ # Using curl instead. hackish and non-portable. But nothing else works.
1585
+ headers = "-H \'Authorization: Bearer #{token}\' -H \'usage: create\' -H \'Content-Type: application/json\' -H \'X-CSRF-Token: nocheck\'"
1586
+ curl_payload = JSON.generate(access_profiles)
1587
+ puts "Due to an issue with RestClient on passing arrays in JSON data, using curl..."
1588
+ cmd = "curl -X POST #{headers} -d \'#{curl_payload}\' #{url}"
1589
+ puts "\n"+cmd+"\n\n"
1590
+ result = `#{cmd}`+"\n\n"
1591
+ puts result
1592
+ puts "\n\nUnable to find Access Profiles matching id(s): #{access_pofiles[:accessProfileIds].join(",")}
1593
+ for source ID: #{source[:accountServiceId]}" if result.split.include? "\"NotFoundException:" # no error handling for curl!
1594
+ wait_for_identity_refresh(org,user,password,client_id,client_secret,token)
1595
+ end
1596
+ # Get the current state of the app to return
1597
+ result = JSON.parse(api_app_get(org,user,password,client_id,client_secret,args[:id],token))
1598
+ return result
1599
+ end
1600
+
1601
+ def self.source_name_id_extid(org, user, pass, api_user, api_key)
1602
+ sources_list = JSON.parse(api_get(org, "source/list", user, pass, api_user, api_key, @token))
1603
+ name_id_extid = {}
1604
+ sources_list.each do |i|
1605
+ next if i["name"] == "IdN Admins" # skip this source, which is flat file and unique to each IdN org
1606
+ id = i["id"].to_i
1607
+ ext_id = i["externalId"]
1608
+ name = i["name"]
1609
+ name_id_extid[name] = {:id => id, :ext_id => ext_id}
1610
+ end
1611
+ return name_id_extid
1612
+ end
1613
+
1614
+ def self.entitlement_id_transform(org, user, pass, api_user, api_key)
1615
+ entitlement_maps = {}
1616
+ source_dir_contents = Dir.entries(@sources_dir)[2..-1]
1617
+ sources = {}
1618
+ entitlement_maps = {}
1619
+ # get the names of all of the sources
1620
+ a = []
1621
+ source_dir_contents.each{|f| a << f.split(File.extname(f)).first.split('_')[2]}
1622
+ a.each{|i|sources[i] = {}}
1623
+
1624
+ source_dir_contents.each do |f|
1625
+ f.chomp!
1626
+ f_base = f.split(File.extname(f)).first
1627
+ type = f_base.split('_')[1]
1628
+ name = f_base.split('_')[2]
1629
+ entitlement_maps[name] = File.join(@sources_dir, f) if type == "ENTITLEMENTMAP"
1630
+ end
1631
+ all_entitlement_map = {}
1632
+ entitlement_maps.each do |name, f|
1633
+ j = JSON.parse(File.read(entitlement_maps[name]))
1634
+ j.each do |ent|
1635
+ all_entitlement_map[ent["id"]] = { :source_id => ent["applicationName"].split("[source-").last[0..-2],
1636
+ :source_name => name,
1637
+ :name =>ent["displayName"] }
1638
+ all_entitlement_map[ent["id"]][:name] = ent["displayableName"] if ent["displayName"].nil? # could be a bug?
1639
+ end
1640
+ end
1641
+ sources_list = JSON.parse(api_get(org, "source/list", user, pass, api_user, api_key, @token))
1642
+ ext_ids = {}
1643
+ sources_list.each do |i|
1644
+ next if i["name"] == "IdN Admins" # skip this source, which is flat file and unique to each IdN org
1645
+ id = i["id"].to_i
1646
+ ext_id = i["externalId"]
1647
+ ext_ids[id] = ext_id
1648
+ end
1649
+ target_entitlements = []
1650
+ threads = []
1651
+ ext_ids.each do |id, ext_id|
1652
+ threads << Thread.new do
1653
+ app_entitlements = []
1654
+ result = api_get(org, "entitlement/list/?CISApplicationId=#{ext_id}", user, pass, api_user, api_key, @token)
1655
+ entitlement_bundle = JSON.parse(result)["items"]
1656
+ entitlement_bundle.each do |ent|
1657
+ entitlement = {}
1658
+ entitlement["applicationName"] = ent["applicationName"]
1659
+ entitlement["displayName"] = ent["displayName"]
1660
+ entitlement["displayableName"] = ent["displayableName"]
1661
+ entitlement["id"] = ent["id"]
1662
+ target_entitlements << entitlement
1663
+ end
1664
+ end
1665
+ end
1666
+ threads.each(&:join)
1667
+ id_transform = {}
1668
+ # old => new
1669
+ all_entitlement_map.each do |id, h|
1670
+ new_id = nil
1671
+ target_entitlements.each do |ent|
1672
+ next if ent["id"].nil? # we can't use it anyway
1673
+ target_source_name = ent["applicationName"].split(" [source-").first
1674
+ next unless target_source_name == h[:source_name]
1675
+ target_name = ent["displayName"]
1676
+ target_name = ent["displayableName"] if ent["displayName"].nil? # I think this is a bug... in the API code. -JK
1677
+ new_id = ent["id"] if target_name == h[:name]
1678
+ end
1679
+ id_transform[id] = new_id
1680
+ end
1681
+ return id_transform
1682
+ end
1683
+
1684
+ #-----------------------------------------------------------------------------#
1685
+ # Import an app
1686
+ # args is a hash, :id and :xml which is the filename
1687
+ # You cannot import a pre-installed app!
1688
+ #
1689
+ def self.api_app_import(org,user,password,client_id,client_secret,args,token=nil)
1690
+ # required: "id","xml"
1691
+ api = "api/app/import/"
1692
+ url = URI.join(org, api, args[:id].to_s).to_s
1693
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1694
+ redirected_url = nil
1695
+ rest_result = nil
1696
+ filename = args[:xml]
1697
+ RestClient.post(url, { :xml => args[:xml]},
1698
+ {'Authorization' => "Bearer #{token}",'usage' => 'create', 'content_type' => 'multipart/form-data',
1699
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1700
+ rest_result = handle_rest_response(response,request,result,&block)
1701
+ end
1702
+ return rest_result
1703
+ end
1704
+
1705
+ #-----------------------------------------------------------------------------#
1706
+ # Import an custom icon
1707
+ # args is a hash, :id and :image which is the filename of the png file
1708
+ # file has to be a 80x80 png preferably
1709
+ # if you are downloading it from the link you get from /api/source/get then it's the "70.png" in the specified location
1710
+ #
1711
+ def self.api_source_upload_custom_icon(org,user,password,client_id,client_secret,args,token=nil)
1712
+ # required: "id","xml"
1713
+ api = "api/source/uploadCustomIcon/"
1714
+ url = URI.join(org, api, args[:id].to_s).to_s
1715
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1716
+ redirected_url = nil
1717
+ rest_result = nil
1718
+ filename = args[:image]
1719
+ RestClient.post(url, { :image => args[:image]},
1720
+ {'Authorization' => "Bearer #{token}",'usage' => 'create', 'content_type' => 'text/plain',
1721
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1722
+ rest_result = handle_rest_response(response,request,result,&block)
1723
+ end
1724
+ return rest_result
1725
+ end
1726
+
1727
+ #-----------------------------------------------------------------------------#
1728
+ # Import an custom connector jar file
1729
+ # args is a hash, "id" and "file" which is the filename of the jar file
1730
+ #
1731
+ def self.api_source_upload_connector_file(org,client_id,client_secret,user,pass,args,token=nil)
1732
+ api = "/cc/api/source/uploadConnectorFile"
1733
+ base_url = org.split(".identitynow.com").first+".api.identitynow.com"
1734
+ token = token || idn_oauth_3(org,client_id,client_secret,user,pass)
1735
+ args.each{|k,v|args[k.to_s] = v} #inconsistently using symbols and strings
1736
+ url = URI.join(base_url,api).to_s+"/"+args["id"].to_s
1737
+ redirected_url = nil
1738
+ rest_result = nil
1739
+ filename = args["file"]
1740
+ response = RestClient::Request.new({
1741
+ method: :post,
1742
+ url: url,
1743
+ path: api+"/"+args["id"].to_s,
1744
+ :payload => {
1745
+ :multipart => true,
1746
+ :file => File.new(filename, 'rb')
1747
+ },
1748
+ timeout: nil,
1749
+ headers: {'Authorization' => "Bearer #{token}",
1750
+ "Accept"=>"*/*" ,
1751
+ 'Cache-Control' => 'no-cache'
1752
+ }
1753
+ }).execute do |response, request, result, &block|
1754
+ rest_result = handle_rest_response(response, request, result, &block)
1755
+ end
1756
+ return rest_result
1757
+ end
1758
+
1759
+
1760
+ #-----------------------------------------------------------------------------#
1761
+ # Create an Identity Profile
1762
+ #
1763
+ def self.api_profile_create(org,user,password,client_id,client_secret,args,token=nil)
1764
+ api = "api/profile/create"
1765
+ url = URI.join(org, api).to_s
1766
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1767
+ redirected_url = nil
1768
+ rest_result = nil
1769
+ payload = {:name => args[:name], :sourceId => args[:source_id]}
1770
+ RestClient.post(url, payload,
1771
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
1772
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'text/plain;charset=UTF-8',
1773
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
1774
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
1775
+ rest_result = handle_rest_response(response,request,result,&block)
1776
+ end
1777
+ return rest_result
1778
+ end
1779
+
1780
+ #-----------------------------------------------------------------------------#
1781
+ # Create an Identity Attribute
1782
+ #
1783
+ def self.api_identityattribute_create(org,user,password,client_id,client_secret,args,token=nil)
1784
+ # args is going to be a hash we have to JSON
1785
+ api = "api/identityAttribute/create"
1786
+ url = URI.join(org, api).to_s
1787
+ ["attributeSyncTargets", "extendedNumber", "targets"].each{|i| args.delete i}
1788
+ payload = JSON.generate(args)
1789
+
1790
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1791
+ redirected_url = nil
1792
+ rest_result = nil
1793
+ RestClient.post(url, payload,
1794
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
1795
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'text/plain;charset=UTF-8',
1796
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
1797
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
1798
+ rest_result = handle_rest_response(response,request,result,&block)
1799
+ end
1800
+ return rest_result
1801
+ end
1802
+
1803
+ #-----------------------------------------------------------------------------#
1804
+ # Update an Identity Profile
1805
+ # TODO:
1806
+ # This probably has lots of dependencies that are unresolved
1807
+ # even if it did work, which it doesn't as of 10/12/15 -JK
1808
+ def self.api_profile_update(org,user,password,client_id,client_secret,id,data_hash,token=nil)
1809
+ api = "api/profile/update/"
1810
+ url = URI.join(org, api, id.to_s).to_s
1811
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1812
+ redirected_url = nil
1813
+ rest_result = nil
1814
+ payload = JSON.generate(data_hash)
1815
+ RestClient.post(url, payload,
1816
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
1817
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'text/plain;charset=UTF-8',
1818
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
1819
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
1820
+ rest_result = handle_rest_response(response,request,result,&block)
1821
+ end
1822
+ return rest_result
1823
+ end
1824
+
1825
+ #-----------------------------------------------------------------------------#
1826
+ # Create a Transform (seaspray)
1827
+ #
1828
+ def self.api_transform_create(org,user,password,client_id,client_secret,args,token=nil)
1829
+ return if ["ISO3166 Country Format", "Remove Diacritical Marks"].include? args[:id]
1830
+ api = "api/transform/create"
1831
+ url = URI.join(org, api).to_s
1832
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1833
+ redirected_url = nil
1834
+ rest_result = nil
1835
+ payload = JSON.generate(args) # should be a hash
1836
+ RestClient.post(url, payload,
1837
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
1838
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'text/plain;charset=UTF-8',
1839
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
1840
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
1841
+ rest_result = handle_rest_response(response,request,result,&block)
1842
+ end
1843
+ return rest_result
1844
+ end
1845
+
1846
+ #-----------------------------------------------------------------------------#
1847
+ # Update a transform
1848
+ #
1849
+ def self.api_transform_update(org,user,password,client_id,client_secret,args,token=nil)
1850
+ return if ["ISO3166 Country Format", "Remove Diacritical Marks"].include? args[:id]
1851
+ api = "api/transform/update"
1852
+ url = URI.join(org, api).to_s
1853
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1854
+ redirected_url = nil
1855
+ rest_result = nil
1856
+ payload = JSON.generate(args)
1857
+ RestClient.post(url, payload,
1858
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
1859
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'text/plain;charset=UTF-8',
1860
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
1861
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
1862
+ rest_result = handle_rest_response(response,request,result,&block)
1863
+ end
1864
+ return rest_result
1865
+ end
1866
+
1867
+ #-----------------------------------------------------------------------------#
1868
+ # Create a governance group (workgroup)
1869
+ #
1870
+ def self.api_workgroup_create(org,user,password,client_id,client_secret,args,token=nil)
1871
+ # args is a hash
1872
+ ["connectionCount", "created", "id", "memberCount", "modified"].each{|k| args.delete k}
1873
+ api = "api/v2/workgroups"
1874
+ url = URI.join(org, api).to_s
1875
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1876
+ redirected_url = nil
1877
+ rest_result = nil
1878
+ payload = JSON.generate(args).to_s
1879
+ RestClient.post(url, payload,
1880
+ {'Authorization' => "Bearer #{token}",'usage' => 'create', 'Content-Type' => 'application/json',
1881
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1882
+ rest_result = handle_rest_response(response,request,result,&block)
1883
+ end
1884
+ return rest_result
1885
+ end
1886
+
1887
+ #-----------------------------------------------------------------------------#
1888
+ # Create a role
1889
+ #
1890
+ def self.api_role_create(org,user,password,client_id,client_secret,name,token=nil)
1891
+ api = "api/role/create"
1892
+ url = URI.join(org, api).to_s
1893
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1894
+ redirected_url = nil
1895
+ rest_result = nil
1896
+ RestClient.post(url, {:name => name},
1897
+ {'Authorization' => "Bearer #{token}",'usage' => 'create', 'Content-Type' => 'application/x-www-form-urlencoded',
1898
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
1899
+ rest_result = handle_rest_response(response,request,result,&block)
1900
+ end
1901
+ return rest_result
1902
+ end
1903
+
1904
+ #-----------------------------------------------------------------------------#
1905
+ # Update a role
1906
+ #
1907
+ def self.api_role_update(org,user,password,client_id,client_secret,id,args,token=nil)
1908
+ api = "api/role/update/"
1909
+ url = URI.join(org, api, id).to_s
1910
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1911
+ redirected_url = nil
1912
+ rest_result = nil
1913
+ args.delete :name
1914
+ args["selector"].each{|k,v| args["selector"].delete k if v.nil?}
1915
+ payload = JSON.generate(args).to_s
1916
+ RestClient.post(url, payload,
1917
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
1918
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'text/plain;charset=UTF-8',
1919
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
1920
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
1921
+ rest_result = handle_rest_response(response,request,result,&block)
1922
+ end
1923
+ return rest_result
1924
+ end
1925
+
1926
+
1927
+ #-----------------------------------------------------------------------------#
1928
+ # Create a Password Policy
1929
+ #
1930
+ def self.api_password_policy_create(org,user,password,client_id,client_secret,args,token=nil)
1931
+ api = "api/passwordPolicy/create/"
1932
+ url = URI.join(org, api).to_s
1933
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1934
+ redirected_url = nil
1935
+ rest_result = nil
1936
+ args.delete :id
1937
+ args.delete
1938
+ payload = JSON.generate(args).to_s
1939
+ RestClient.post(url, payload,
1940
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
1941
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'text/plain;charset=UTF-8',
1942
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
1943
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
1944
+ rest_result = handle_rest_response(response,request,result,&block)
1945
+ end
1946
+ return rest_result
1947
+ end
1948
+
1949
+ #-----------------------------------------------------------------------------#
1950
+ # Update a Password Policy
1951
+ #
1952
+ def self.api_password_policy_set(org,user,password,client_id,client_secret,id,args,token=nil)
1953
+ api = "api/passwordPolicy/set"
1954
+ url = URI.join(org, api, id).to_s
1955
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1956
+ redirected_url = nil
1957
+ rest_result = nil
1958
+ args.delete :id
1959
+ # :connectedServices may refer to sources that are not set up ... should process these out of the
1960
+ # args before making the call
1961
+ payload = JSON.generate(args).to_s
1962
+ RestClient.post(url, payload,
1963
+ {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck',
1964
+ 'Accept-Language' => 'en-US,en;q=0.8', 'Content-Type' => 'text/plain;charset=UTF-8',
1965
+ 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, deflate', 'Cache-Control' => 'no-cache',
1966
+ 'Connection' => 'keep-alive' }) do |response, request, result, &block|
1967
+ rest_result = handle_rest_response(response,request,result,&block)
1968
+ end
1969
+ return rest_result
1970
+ end
1971
+
1972
+
1973
+ #-----------------------------------------------------------------------------#
1974
+ # Certification API UTILS
1975
+ #-----------------------------------------------------------------------------#
1976
+
1977
+ #-----------------------------------------------------------------------------#
1978
+ # List all certification campaigns
1979
+ #
1980
+ def self.api_campaign_list(org,user,password,client_id,client_secret,token=nil)
1981
+ api = "api/campaign/list"
1982
+ url = URI.join(org, api).to_s
1983
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
1984
+ redirected_url = nil
1985
+ rest_result = nil
1986
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
1987
+ rest_result = handle_rest_response(response, request, result, &block)
1988
+ end
1989
+ return rest_result
1990
+ end
1991
+
1992
+ #-----------------------------------------------------------------------------#
1993
+ # Create a certification campaigns
1994
+ # args requires data that doesn't come easily or at all out of the get/list methods
1995
+ #
1996
+ def self.api_campaign_create(org,user,password,client_id,client_secret,args,token=nil)
1997
+
1998
+ new_args = "?"
1999
+ args.each do |k,v|
2000
+ new_args = new_args+k.to_s+"="+v.to_s+"&" unless k == :sourceIds # sourceIds will be an array
2001
+ if k == :sourceIds # must be converted to multiple keys with each one value
2002
+ s = ""
2003
+ args[k].each {|id| s = s+"sourceIds="+id.to_s+"&"}
2004
+ new_args = new_args+s
2005
+ end
2006
+ end
2007
+
2008
+ new_args.chop!
2009
+
2010
+ args = URI.escape(new_args)
2011
+
2012
+ api = "api/campaign/create"
2013
+ url = URI.join(org, api, args).to_s
2014
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
2015
+ redirected_url = nil
2016
+ rest_result = nil
2017
+
2018
+ RestClient.post(url, "", {'Authorization' => "Bearer #{token}",
2019
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
2020
+ rest_result = handle_rest_response(response,request,result,&block)
2021
+ end
2022
+ return rest_result
2023
+ end
2024
+
2025
+ #-----------------------------------------------------------------------------#
2026
+ # List all certifications
2027
+ #
2028
+ def self.api_certification_list(org,user,password,client_id,client_secret,token=nil)
2029
+ api = "api/certification/list"
2030
+ url = URI.join(org, api).to_s
2031
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
2032
+ redirected_url = nil
2033
+ rest_result = nil
2034
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
2035
+ rest_result = handle_rest_response(response, request, result, &block)
2036
+ end
2037
+ return rest_result
2038
+ end
2039
+
2040
+
2041
+ #-----------------------------------------------------------------------------#
2042
+ # Get a certification by id
2043
+ #
2044
+ def self.api_certification_get(org,user,password,client_id,client_secret,id,token=nil)
2045
+ api = "api/certification/getCertification"
2046
+ url = URI.join(org, api).to_s + "?certificationId=#{id}"
2047
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
2048
+ redirected_url = nil
2049
+ rest_result = nil
2050
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
2051
+ rest_result = handle_rest_response(response, request, result, &block)
2052
+ end
2053
+ return rest_result
2054
+ end
2055
+
2056
+ #-----------------------------------------------------------------------------#
2057
+ # Complete an assigned task
2058
+ #
2059
+ def self.api_task_complete(org,user,password,client_id,client_secret,id,token=nil)
2060
+ api = "api/task/complete/"+id.to_s
2061
+
2062
+ # args = {:id => id}
2063
+ url = URI.join(org, api).to_s
2064
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
2065
+ redirected_url = nil
2066
+ rest_result = nil
2067
+
2068
+ RestClient.post(url, nil, {'Authorization' => "Bearer #{token}",
2069
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
2070
+ rest_result = handle_rest_response(response,request,result,&block)
2071
+ end
2072
+ return rest_result
2073
+ end
2074
+
2075
+
2076
+ #-----------------------------------------------------------------------------#
2077
+ # API ACCOUNT STUFF
2078
+ # CRUD accounts
2079
+ #-----------------------------------------------------------------------------#
2080
+
2081
+ #-----------------------------------------------------------------------------#
2082
+ # Get a list of accounts on a source
2083
+ # returns JSON
2084
+ # include alias (string) or id (integer) as arg to return only the identity for that one user
2085
+ #
2086
+ def self.api_accounts_get(org,user,password,client_id,client_secret,source_id,token=nil)
2087
+ api = "api/v2/accounts/"
2088
+ url = URI.join(org, api).to_s+"?sourceId="+source_id.to_s
2089
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
2090
+ rest_result = nil
2091
+ RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
2092
+ rest_result = handle_rest_response(response, request, result, &block)
2093
+ end
2094
+ return rest_result
2095
+ end
2096
+
2097
+ #-----------------------------------------------------------------------------#
2098
+ # Create an account on a FF source
2099
+ # returns JSON
2100
+ # include source id (integer) and the body as a hash of attributes
2101
+ #
2102
+ def self.api_accounts_post(org,user,password,client_id,client_secret,source_id,args,token=nil)
2103
+ api = "api/v2/accounts/"
2104
+ url = URI.join(org, api).to_s+"?sourceId="+source_id.to_s
2105
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
2106
+ rest_result = nil
2107
+ RestClient.post(url, args, {'Authorization' => "Bearer #{token}",
2108
+ 'X-CSRF-Token' => 'nocheck' }) do |response, request, result, &block|
2109
+ rest_result = handle_rest_response(response,request,result,&block)
2110
+ end
2111
+ return rest_result
2112
+ end
2113
+
2114
+ #-----------------------------------------------------------------------------#
2115
+ # API MISC UTILS
2116
+ # Methods to assist in running API calls besides POST and GET
2117
+ #-----------------------------------------------------------------------------#
2118
+
2119
+ def self.get_api_key(org,user,password,client_id,client_secret)
2120
+ api = "api/client/create?type=CLI"
2121
+ url = URI.join(org, api).to_s
2122
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
2123
+ rest_result = nil
2124
+ RestClient.post(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |response, request, result, &block|
2125
+ rest_result = handle_rest_response(response, request, result, &block)
2126
+ end
2127
+ return rest_result
2128
+ end
2129
+
2130
+ def self.hash_password(user,password)
2131
+ # generates a password hash for oauth token authentication
2132
+ user_hash = Digest::SHA256.hexdigest(user)
2133
+ password_hash = Digest::SHA256.hexdigest([password,user_hash].join)
2134
+ return password_hash
2135
+ end
2136
+
2137
+ def self.idn_oauth(org,user,password,client_id,client_secret)
2138
+ return idn_oauth_3(org,user,password,client_id,client_secret)
2139
+ end
2140
+
2141
+ def self.idn_oauth_2(org,client_id,client_secret)
2142
+ org = org.split(".identitynow.com").first+".api.identitynow.com"
2143
+ api = "/oauth/token"
2144
+
2145
+ args = "?grant_type=client_credentials&client_id=#{client_id}&client_secret=#{client_secret}"
2146
+ url = URI.join(org, api, args).to_s
2147
+ rest_result = nil
2148
+ RestClient.post(url, nil, {'X-CSRF-Token'=>'nocheck'}) do |response, request, result, &block|
2149
+ rest_result = handle_rest_response(response,request,result,&block)
2150
+ end
2151
+ return token
2152
+ end
2153
+
2154
+ def self.idn_oauth_3(org,user,password,client_id,client_secret)
2155
+ user = user.downcase
2156
+ pass_hash = hash_password(user,password)
2157
+ org = org.split(".identitynow.com").first+".api.identitynow.com"
2158
+ api = "/oauth/token"
2159
+ resource = RestClient::Resource.new(org, client_id, client_secret)
2160
+ args = "?grant_type=password&client_id=#{client_id}&client_secret=#{client_secret}&username=#{user}&password=#{pass_hash}"
2161
+ url = URI.join(org, api, args).to_s
2162
+ rest_result = nil
2163
+ RestClient.post(url, nil, {'X-CSRF-Token'=>'nocheck'}) do |response, request, result, &block|
2164
+ rest_result = handle_rest_response(response,request,result,&block)
2165
+ end
2166
+ token = JSON.parse(rest_result)["access_token"] unless rest_result.nil?
2167
+ return token
2168
+ end
2169
+
2170
+ #-----------------------------------------------------------------------------#
2171
+ # Get status of pending identity updates
2172
+ # this is what displays the "blue bar"
2173
+ #
2174
+ def self.identity_update_pending(org,user,password,client_id,client_secret,token=nil)
2175
+ api = "api/org/getPendingIdentityTasks"
2176
+ url = URI.join(org, api).to_s
2177
+ token = token || idn_oauth(org,user,password,client_id,client_secret)
2178
+ redirected_url = nil
2179
+ rest_result = nil
2180
+
2181
+ do_retry = true
2182
+ loop do
2183
+ break unless do_retry
2184
+ begin
2185
+ response = RestClient.get(url, {'Authorization' => "Bearer #{token}", 'X-CSRF-Token' => 'nocheck' } ) do |r_response, request, result, &block|
2186
+ rest_result = handle_rest_response(r_response, request, result, &block)
2187
+ do_retry = false
2188
+ end
2189
+ do_retry = response.nil?
2190
+ puts "Received nil response, rertrying..." if response.nil?
2191
+ rescue RestClient::Exceptions::OpenTimeout => e
2192
+ e.response
2193
+ puts "Timed out. Retrying..."
2194
+ do_retry = true
2195
+ rescue RestClient::Exceptions::ReadTimeout => e
2196
+ e.response
2197
+ puts "Timed out. Retrying..."
2198
+ do_retry = true
2199
+ rescue SocketError => e
2200
+ puts "SocketError. Retrying..."
2201
+ do_retry = true
2202
+ rescue RestClient::InternalServerError => e
2203
+ puts "RestClient::InternalServerError Retrying..."
2204
+ do_retry = true
2205
+ end
2206
+ end
2207
+ return rest_result
2208
+ end
2209
+
2210
+
2211
+ #-----------------------------------------------------------------------------#
2212
+ # Wait until identity updates are completed
2213
+ # this is what displays the "blue bar"
2214
+ #
2215
+ def self.wait_for_identity_refresh(org,user,password,client_id,client_secret,token=nil)
2216
+ puts "Waiting for Identity refresh to complete."
2217
+ token_time = Time.now.to_i
2218
+ token = token || idn_oauth_3(org,user,password,client_id,client_secret)
2219
+ token_monitor = Thread.new do
2220
+ loop do
2221
+ sleep 60
2222
+ if Time.now.to_i - token_time > 300 # the token may expire
2223
+ token = idn_oauth_3(org,user,password,client_id,client_secret)
2224
+ token_time = Time.now.to_i
2225
+ end
2226
+ end
2227
+ end
2228
+ t = 0
2229
+ loop do
2230
+ result = JSON.parse(identity_update_pending(org,user,password,client_id,client_secret,token))
2231
+ break if result == []
2232
+ break if !result.first["completed"].nil?
2233
+ print "\r"+t.to_s+" seconds..."
2234
+ sleep 10
2235
+ t += 10
2236
+ # break if t == 18000 # if we are waiting 300 minutes, there's a problem
2237
+ end
2238
+ puts "\n"
2239
+ puts "result = "+JSON.parse(result).inspect if @debug
2240
+ puts "Waited #{t} seconds for Identity refresh. Resuming."
2241
+ end
2242
+
2243
+ #-----------------------------------------------------------------------------#
2244
+ # wait until a job is complete
2245
+ #
2246
+ def self.wait_for_job(org,user,password,client_id,client_secret,job_id,token=nil)
2247
+ t = 0
2248
+ loop do
2249
+ sleep 1
2250
+ t += 1
2251
+ result = JSON.parse(api_get("/api/job/getStatus/#{job_id}", org, user, password, api_user, api_key, token))
2252
+ break if result.values.include? "Success"
2253
+ break if t == 3600 # if we are waiting 60 minutes, there's might be a problem
2254
+ end
2255
+ puts "result = "+JSON.parse(result).inspect if @debug
2256
+ puts "\n\nTimed out waiting for job #{job_id} to finish.\n\n" if t >= 3600
2257
+ puts "\n\nWaited #{t} seconds for job #{job_id} to finish.\n\n" if t < 3600
2258
+ end
2259
+
2260
+ def self.handle_rest_response(response,request,result,&block)
2261
+ if response.code < 400
2262
+ # puts "Success:\n"+request.url.inspect+"\n\n"
2263
+ response.return!
2264
+ elsif response.code == 400
2265
+ puts "Bad Request:\n"+[request.url.inspect, request.args.inspect, request.raw_response.inspect, response.body].join("\n")
2266
+ response.return!
2267
+ elsif response.code == 403
2268
+ puts "Unauthorized:\n"+[request.url.inspect, request.args.inspect, request.raw_response.inspect, response.body].join("\n")
2269
+ response.return!
2270
+ elsif response.code == 404
2271
+ puts "Resource Not Found:\n"+[request.url.inspect, request.args.inspect, request.raw_response.inspect, response.body].join("\n")
2272
+ response.return!
2273
+ elsif response.code == 500
2274
+ puts "IdentityNow Internal Server Error:\n"+[request.url.inspect, request.args.inspect, request.raw_response.inspect, response.body].join("\n")
2275
+ response.return!
2276
+ elsif response.code == 504
2277
+ puts "Gateway Timeout:\n"+[request.url.inspect, request.args.inspect, request.raw_response.inspect, response.body].join("\n")
2278
+ response.return!
2279
+ else
2280
+ puts "Error:\n"+[request.url.inspect, request.args.inspect, request.raw_response.inspect, response.body].join("\n")
2281
+ response.return!
2282
+ end
2283
+ end
2284
+
2285
+ def self.hash_to_args(hash)
2286
+ # convert a hash to URL args
2287
+ args = "?"
2288
+ hash.each do |k,v|
2289
+ args = args+k.to_s+"="+v.to_s+"&"
2290
+ end
2291
+ args.chop!
2292
+ return URI.escape(args)
2293
+ end
2294
+
2295
+ def self.unescape(s)
2296
+ YAML.load(%Q(---\n"#{s}"\n))
2297
+ end
2298
+
2299
+ def self.data_binary(data)
2300
+ # data had better be string from JSON.generate
2301
+ s = JSON.pretty_generate(JSON.parse(data))
2302
+ return ['--data-binary $\'', s, '\''].join
2303
+ end
2304
+
2305
+ def self.csv_to_json(csv)
2306
+ # convert CSV file to JSON
2307
+ require 'csv'
2308
+ lines = csv.split("\n")
2309
+ a = []
2310
+ keys = []
2311
+ lines.first.split(",").each{|k|keys << k.to_sym}
2312
+ lines[1..-1].each do |row|
2313
+ h = {}
2314
+ fields = row.parse_csv
2315
+ keys.each_with_index{|k,i| h[k] = fields[i]}
2316
+ a << h
2317
+ end
2318
+ result = JSON.generate(a)
2319
+ return result
2320
+ end
2321
+
2322
+ def self.rest_value_array(a)
2323
+ return a if (a == []) || (a.nil?)
2324
+ s = ["["]
2325
+ a.each{ |i| s << i+','}
2326
+ s.last.chop!
2327
+ s << "]"
2328
+ a = s.join
2329
+ return a
2330
+ end
2331
+
2332
+ def self.extract_zip(file)
2333
+ require 'zip'
2334
+ destination = file.split('.').first
2335
+ FileUtils.mkdir_p(destination)
2336
+ output_file = ""
2337
+ Zip::File.open(file) do |zip_file|
2338
+ zip_file.each do |f|
2339
+ fpath = File.join(destination, f.name)
2340
+ result = zip_file.extract(f, fpath)
2341
+ output_file = result.keys.first
2342
+ end
2343
+ end
2344
+ return output_file
2345
+ end
2346
+
2347
+ end #class