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.
- checksums.yaml +7 -0
- data/lib/sp-seutils.rb +2347 -0
- metadata +43 -0
checksums.yaml
ADDED
|
@@ -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
|
data/lib/sp-seutils.rb
ADDED
|
@@ -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
|