idnio 2.3.2b
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/idnio.rb +295 -0
- data/lib/idnio/crypto.rb +34 -0
- data/lib/idnio/idnapi.rb +158 -0
- data/lib/idnio/markdown.rb +345 -0
- data/lib/idnio/program.rb +153 -0
- data/lib/idnio/timer.rb +57 -0
- data/lib/idnio/version.rb +4 -0
- data/lib/objects/access-profiles.rb +107 -0
- data/lib/objects/access-request-config.rb +90 -0
- data/lib/objects/account-profiles.rb +167 -0
- data/lib/objects/account-schemas.rb +341 -0
- data/lib/objects/applications.rb +145 -0
- data/lib/objects/attribute-sync-config.rb +122 -0
- data/lib/objects/branding.rb +49 -0
- data/lib/objects/campaign-filters.rb +61 -0
- data/lib/objects/connectors.rb +291 -0
- data/lib/objects/email-templates.rb +226 -0
- data/lib/objects/identity-attributes.rb +136 -0
- data/lib/objects/identity-profiles.rb +206 -0
- data/lib/objects/integrations.rb +149 -0
- data/lib/objects/lifecycle-states.rb +86 -0
- data/lib/objects/password-policies.rb +107 -0
- data/lib/objects/password-sync-groups.rb +100 -0
- data/lib/objects/public-identities-config.rb +78 -0
- data/lib/objects/reference-resolver.rb +137 -0
- data/lib/objects/roles.rb +117 -0
- data/lib/objects/rules.rb +198 -0
- data/lib/objects/sources.rb +217 -0
- data/lib/objects/system-settings.rb +185 -0
- data/lib/objects/transforms.rb +157 -0
- metadata +124 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,345 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
#
|
3
|
+
# Markdown Writing Utility
|
4
|
+
#
|
5
|
+
module Markdown
|
6
|
+
|
7
|
+
@@buffer = ""
|
8
|
+
@@file = nil
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
# private_class_method :new
|
13
|
+
|
14
|
+
#def self.open( file )
|
15
|
+
# open( file )
|
16
|
+
#end
|
17
|
+
|
18
|
+
def self.open( file )
|
19
|
+
@@file = file
|
20
|
+
if File.exist?( @@file )
|
21
|
+
FileUtils.rm_f( @@file )
|
22
|
+
end
|
23
|
+
|
24
|
+
FileUtils.mkdir_p File.dirname( file )
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.toc
|
28
|
+
text = "\n[TOC]\n"
|
29
|
+
@@buffer << text
|
30
|
+
return text
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.strong( text )
|
34
|
+
text = "**#{text}**"
|
35
|
+
@@buffer << text
|
36
|
+
return text
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.h1( text )
|
40
|
+
text = "\n# #{text}\n"
|
41
|
+
@@buffer << text
|
42
|
+
return text
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.h2( text )
|
46
|
+
text = "\n## #{text}\n"
|
47
|
+
@@buffer << text
|
48
|
+
return text
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.h3( text )
|
52
|
+
text = "\n### #{text}\n"
|
53
|
+
@@buffer << text
|
54
|
+
return text
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.h4( text )
|
58
|
+
text = "\n#### #{text}\n"
|
59
|
+
@@buffer << text
|
60
|
+
return text
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.h5( text )
|
64
|
+
text = "\n##### #{text}\n"
|
65
|
+
@@buffer << text
|
66
|
+
return text
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.link( link, label )
|
70
|
+
text = "[#{label}](#{link})"
|
71
|
+
@@buffer << text
|
72
|
+
return text
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.image( file, alt_text )
|
76
|
+
text = "\n![#{alt_text}](#{file})\n"
|
77
|
+
@@buffer << text
|
78
|
+
return text
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.code( text )
|
82
|
+
text = "\n~~~\n#{text}\n~~~\n"
|
83
|
+
@@buffer << text
|
84
|
+
return text
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.json( text )
|
88
|
+
text = "\n~~~json\n#{text}\n~~~\n"
|
89
|
+
@@buffer << text
|
90
|
+
return text
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.xml( text )
|
94
|
+
text = "\n~~~xml\n#{text}\n~~~\n"
|
95
|
+
@@buffer << text
|
96
|
+
return text
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.text( text )
|
100
|
+
text = "#{text}"
|
101
|
+
@@buffer << text
|
102
|
+
return text
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.line( text )
|
106
|
+
text = "\n***\n"
|
107
|
+
@@buffer << text
|
108
|
+
return text
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.ul( list )
|
112
|
+
text = ""
|
113
|
+
list.each do |item|
|
114
|
+
text << "- #{item}\n"
|
115
|
+
end
|
116
|
+
@@buffer << text
|
117
|
+
return text
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.ul2( list )
|
121
|
+
text = ""
|
122
|
+
list.each do |item|
|
123
|
+
text << "\t- #{item}\n"
|
124
|
+
end
|
125
|
+
@@buffer << text
|
126
|
+
return text
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.ol( list )
|
130
|
+
text = ""
|
131
|
+
list.each_with_index do |item, index|
|
132
|
+
output << "#{index + 1}. #{item}\n"
|
133
|
+
end
|
134
|
+
@@buffer << text
|
135
|
+
return text
|
136
|
+
end
|
137
|
+
|
138
|
+
# Generate a Markdown table.
|
139
|
+
# labels and data are one and two-dimensional arrays, respectively.
|
140
|
+
# All input must have a to_s method.
|
141
|
+
# Pass align: 'l' for left alignment or 'r' for right alignment. Anything
|
142
|
+
# else will result in cells being centered. Pass an array of align values
|
143
|
+
# to specify alignment per column.
|
144
|
+
# If is_rows is true, then each sub-array of data represents a row.
|
145
|
+
# Conversely, if is_rows is false, each sub-array of data represents a column.
|
146
|
+
# Empty cells can be given with nil or an empty string.
|
147
|
+
def self.make_table(labels, data, align: '', is_rows: false)
|
148
|
+
validate(labels, data, align, is_rows)
|
149
|
+
|
150
|
+
# Deep copy the arguments so we don't mutate the originals.
|
151
|
+
labels = Marshal.load(Marshal.dump(labels))
|
152
|
+
data = Marshal.load(Marshal.dump(data))
|
153
|
+
|
154
|
+
# Remove any breaking Markdown characters.
|
155
|
+
labels.map! {|label| sanitize(label)}
|
156
|
+
data.map! {|datum| datum.map {|cell| sanitize(cell)}}
|
157
|
+
|
158
|
+
# Convert align to something that other methods won't need to validate.
|
159
|
+
align.class == String && align = [align] * labels.length
|
160
|
+
align.map! {|a| a =~ /[lr]/i ? a.downcase : 'c'}
|
161
|
+
|
162
|
+
# Generate the column labels and alignment line.
|
163
|
+
header_line = labels.join('|')
|
164
|
+
alignment_line = parse_alignment(align, labels.length)
|
165
|
+
|
166
|
+
# Pad the data arrays so that it can be transposed if necessary.
|
167
|
+
max_len = data.map(&:length).max
|
168
|
+
data.map! {|datum| fill(datum, max_len)}
|
169
|
+
|
170
|
+
# Generate the table rows.
|
171
|
+
rows = (is_rows ? data : data.transpose).map {|row| row.join('|')}
|
172
|
+
|
173
|
+
text = [header_line, alignment_line, rows.join("\n")].join("\n")
|
174
|
+
@@buffer << text
|
175
|
+
return text
|
176
|
+
end
|
177
|
+
|
178
|
+
# Convert a Markdown table into human-readable form.
|
179
|
+
# def self.plain_text( md_table )
|
180
|
+
# md_table !~ // && raise('Invalid input')
|
181
|
+
#
|
182
|
+
# # Split the table into lines to get the labels, rows, and alignments.
|
183
|
+
# lines = md_table.split("\n")
|
184
|
+
# alignments = lines[1].split('|')
|
185
|
+
# # labels or rows might have some empty values but alignments
|
186
|
+
# # is guaranteed to be of the right width.
|
187
|
+
# table_width = alignments.length
|
188
|
+
# # '|||'.split('|') == [], so we need to manually add trailing empty cells.
|
189
|
+
# # Leading empty cells are taken care of automatically.
|
190
|
+
# labels = fill(lines[0].split('|'), table_width)
|
191
|
+
# rows = lines[2..-1].map {|line| fill(line.split('|'), table_width)}
|
192
|
+
#
|
193
|
+
# # Get the width for each column.
|
194
|
+
# cols = rows.transpose
|
195
|
+
# widths = cols.each_index.map {|i| column_width(cols[i].push(labels[i]))}
|
196
|
+
#
|
197
|
+
# # Align the labels and cells.
|
198
|
+
# labels = labels.each_index.map { |i|
|
199
|
+
# aligned_cell(unsanitize(labels[i]), widths[i], alignments[i])
|
200
|
+
# }
|
201
|
+
# rows.map! { |row|
|
202
|
+
# row.each_index.map { |i|
|
203
|
+
# aligned_cell(unsanitize(row[i]), widths[i], alignments[i])
|
204
|
+
# }
|
205
|
+
# }
|
206
|
+
#
|
207
|
+
# border = "\n|" + widths.map {|w| '=' * w}.join('|') + "|\n"
|
208
|
+
# return (
|
209
|
+
# border + [
|
210
|
+
# '|' + labels.join('|') + '|',
|
211
|
+
# rows.map {|row| '|' + row.join('|') + '|'}.join(border.tr('=', '-'))
|
212
|
+
# ].join(border) + border
|
213
|
+
# ).strip
|
214
|
+
#
|
215
|
+
# end
|
216
|
+
|
217
|
+
# Sanity checks for make_table.
|
218
|
+
private_class_method def self.validate(labels, data, align, is_rows)
|
219
|
+
if labels.class != Array
|
220
|
+
raise('labels must be an array')
|
221
|
+
end
|
222
|
+
if data.class != Array || data.any? {|datum| datum.class != Array}
|
223
|
+
raise('data must be a two-dimensional array')
|
224
|
+
end
|
225
|
+
if labels.empty?
|
226
|
+
raise('No column labels given')
|
227
|
+
end
|
228
|
+
if data.all? {|datum| datum.empty?}
|
229
|
+
raise('No cells given')
|
230
|
+
end
|
231
|
+
if labels.any? {|label| !label.respond_to?(:to_s)}
|
232
|
+
raise('One or more column labels cannot be made into a string')
|
233
|
+
end
|
234
|
+
if data.any? {|datum| datum.any? {|cell| !cell.respond_to?(:to_s)}}
|
235
|
+
raise('One or more cells cannot be made into a string')
|
236
|
+
end
|
237
|
+
if ![String, Array].include?(align.class)
|
238
|
+
raise('align must be a string or array')
|
239
|
+
end
|
240
|
+
if align.class == Array && align.any? {|val| val.class != String}
|
241
|
+
raise('One or more align values is not a string')
|
242
|
+
end
|
243
|
+
if !is_rows && data.length > labels.length
|
244
|
+
raise('Too many data columns given')
|
245
|
+
end
|
246
|
+
if is_rows && data.any? {|row| row.length > labels.length}
|
247
|
+
raise('One or more rows has too many cells')
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Convert some input to a string and replace any '|' characters with
|
252
|
+
# a non-breaking equivalent,
|
253
|
+
private_class_method def self.sanitize(input)
|
254
|
+
bar = '|' # Non-breaking HTML vertical bar.
|
255
|
+
return input.to_s.gsub('|', bar)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Replace non-breaking HTML characters with their plaintext counterparts.
|
259
|
+
private_class_method def self.unsanitize(input)
|
260
|
+
return input.gsub(/( )|(|)/, ' ' => ' ', '|' => '|')
|
261
|
+
end
|
262
|
+
|
263
|
+
# Generate the alignment line from a string or array.
|
264
|
+
# align must be a string or array of strings.
|
265
|
+
# n: number of labels in the table to be created.
|
266
|
+
private_class_method def self.parse_alignment(align, n)
|
267
|
+
align_map = {'l' => ':-', 'c' => ':-:', 'r' => '-:'}
|
268
|
+
alignments = align.map {|a| align_map[a]}
|
269
|
+
# If not enough values were given, center the remaining columns.
|
270
|
+
alignments.length < n && alignments += [':-:'] * (n - alignments.length)
|
271
|
+
return alignments.join('|')
|
272
|
+
end
|
273
|
+
|
274
|
+
# Align some text in a cell.
|
275
|
+
private_class_method def self.aligned_cell(text, width, align)
|
276
|
+
if align =~ /:-+:/ # Center alignment.
|
277
|
+
start = (width / 2) - (text.length / 2)
|
278
|
+
elsif align =~ /-+:/ # Right alignment.
|
279
|
+
start = width - text.length - 1
|
280
|
+
else # Left alignment.
|
281
|
+
start = 1
|
282
|
+
end
|
283
|
+
return ' ' * start + text + ' ' * (width - start - text.length)
|
284
|
+
end
|
285
|
+
|
286
|
+
# Get the width for a column.
|
287
|
+
private_class_method def self.column_width(col)
|
288
|
+
# Pad each cell on either side and maintain a minimum 3 width of characters.
|
289
|
+
return [(!col.empty? ? col.map(&:length).max : 0) + 2, 3].max
|
290
|
+
end
|
291
|
+
|
292
|
+
# Add any missing empty values to a row.
|
293
|
+
private_class_method def self.fill(row, n)
|
294
|
+
row.length > n && raise('Sanity checks failed for fill')
|
295
|
+
return row.length < n ? row + ([''] * (n - row.length)) : row
|
296
|
+
end
|
297
|
+
|
298
|
+
#
|
299
|
+
# Used to get the string buffer (if needed).
|
300
|
+
#
|
301
|
+
def self.get_buffer
|
302
|
+
return @@buffer
|
303
|
+
end
|
304
|
+
|
305
|
+
#
|
306
|
+
# Used to set the string buffer (if needed).
|
307
|
+
#
|
308
|
+
def self.set_buffer( new_buffer )
|
309
|
+
@@buffer = new_buffer
|
310
|
+
return @@buffer
|
311
|
+
end
|
312
|
+
|
313
|
+
#
|
314
|
+
# Replaces a string in the buffer.
|
315
|
+
#
|
316
|
+
def self.replace( lookups, replacement )
|
317
|
+
|
318
|
+
#
|
319
|
+
# Check to see if the lookups are an array;
|
320
|
+
# If yes, do all the replacements.
|
321
|
+
# If no, then only do the single replacement.
|
322
|
+
#
|
323
|
+
if lookups.kind_of?(Array)
|
324
|
+
lookups.each do |lookup|
|
325
|
+
@@buffer.gsub!( lookup, replacement )
|
326
|
+
end
|
327
|
+
else
|
328
|
+
@@buffer.gsub!( lookups, replacement )
|
329
|
+
end
|
330
|
+
|
331
|
+
#
|
332
|
+
# Return the string buffer
|
333
|
+
#
|
334
|
+
return @@buffer
|
335
|
+
end
|
336
|
+
|
337
|
+
#
|
338
|
+
# Write the string out to file; and clear the string buffer.
|
339
|
+
#
|
340
|
+
def self.write
|
341
|
+
File.open( @@file, 'a+') { |file| file.write( @@buffer ) }
|
342
|
+
@@buffer = ""
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'idnio/timer'
|
2
|
+
require "json"
|
3
|
+
#
|
4
|
+
# Program Utility
|
5
|
+
#
|
6
|
+
module Program
|
7
|
+
|
8
|
+
def self.setConfig(config)
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.line
|
13
|
+
$log.info "--------------------------------------------------------------"
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Utility method for printing messages.
|
18
|
+
#
|
19
|
+
def self.output( message )
|
20
|
+
Program.line
|
21
|
+
$log.info " #{message}"
|
22
|
+
Program.line
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Utility to start the program
|
27
|
+
#
|
28
|
+
def self.start( name, author, version, date )
|
29
|
+
Timer.start
|
30
|
+
|
31
|
+
$log.unknown "--------------------------------------------------------------"
|
32
|
+
$log.unknown " #{name}"
|
33
|
+
$log.unknown "--------------------------------------------------------------"
|
34
|
+
$log.unknown " Version: #{version}"
|
35
|
+
$log.unknown " Date: #{date}"
|
36
|
+
$log.unknown " Author: #{author}"
|
37
|
+
$log.unknown "--------------------------------------------------------------"
|
38
|
+
|
39
|
+
if $config.has_key?( 'tenant' )
|
40
|
+
|
41
|
+
$log.debug "Personal Access Token configuration detected. Using settings from 'tenant'."
|
42
|
+
|
43
|
+
$url = $config['tenant']['url']
|
44
|
+
$tenant = $config['tenant']['url'].clone.gsub!(/.api|https:\/\//,"")
|
45
|
+
|
46
|
+
$log.debug "Calling IdentityNow to get a JWT OAuth token..."
|
47
|
+
|
48
|
+
response = IDNAPI.post_unauth( "#{$url}/oauth/token?grant_type=client_credentials&client_id=#{$config['tenant']['client-id']}&client_secret=#{$config['tenant']['client-secret']}" )
|
49
|
+
|
50
|
+
case response
|
51
|
+
when Net::HTTPSuccess
|
52
|
+
|
53
|
+
session = JSON.parse( response.body )
|
54
|
+
|
55
|
+
if !session.nil? && session.has_key?( 'access_token' )
|
56
|
+
$token = session['access_token']
|
57
|
+
$log.debug "Session token: #{$token}"
|
58
|
+
else
|
59
|
+
$log.fatal "Error: Unable to parse session token from response. (#{response.code})"
|
60
|
+
abort
|
61
|
+
end
|
62
|
+
|
63
|
+
else
|
64
|
+
$log.fatal "Error: Unable to retreive session token. Please check 'tenant' configuration and try again. (#{response.code})"
|
65
|
+
abort
|
66
|
+
end
|
67
|
+
|
68
|
+
elsif $config.has_key?( 'session' )
|
69
|
+
|
70
|
+
$log.debug "Session configuration detected. Using settings from 'session'."
|
71
|
+
|
72
|
+
$tenant = $config['session']['baseUrl'].clone.gsub!(/.api|https:\/\//,"")
|
73
|
+
$url = $config['session']['baseUrl']
|
74
|
+
$token = $config['session']['accessToken']
|
75
|
+
|
76
|
+
$log.debug "Session token: #{$token}"
|
77
|
+
|
78
|
+
else
|
79
|
+
|
80
|
+
$log.fatal "Error: Configuration does not have details to retreive session. Please configure 'session' or 'tenant' settings."
|
81
|
+
abort
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
unless $tenant.nil?
|
86
|
+
$log.unknown " Tenant: #{$tenant}"
|
87
|
+
$log.unknown "--------------------------------------------------------------"
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Utility to end the program
|
94
|
+
#
|
95
|
+
def self.end( )
|
96
|
+
Timer.stop
|
97
|
+
output( "Process completed in #{Timer.elapsed}" )
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Utility method to write output files to a directory.
|
102
|
+
#
|
103
|
+
def self.write_file( directory, name, text )
|
104
|
+
name.gsub!(/[^0-9A-Za-z. -]/,"-")
|
105
|
+
FileUtils.mkdir_p directory
|
106
|
+
File.open( File.join( directory, name ), 'w+') { |file| file.write( text ) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.get_filenames( directory )
|
110
|
+
output = Array.new
|
111
|
+
Dir.glob("#{directory}/*.json") do |file|
|
112
|
+
#tmp = file.slice! "#{directory}/"
|
113
|
+
output.push(file)
|
114
|
+
end
|
115
|
+
return output
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.read_directory( directory )
|
119
|
+
output = Array.new
|
120
|
+
Dir.glob("#{directory}/*.json") do |file|
|
121
|
+
output.push(Program.read_file(file))
|
122
|
+
end
|
123
|
+
return output
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Utility method to read output files to a directory.
|
128
|
+
#
|
129
|
+
def self.read_file( directory, name )
|
130
|
+
return Program.read_file("#{directory}/#{name}")
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.read_file( name )
|
134
|
+
output = ""
|
135
|
+
#name.gsub!(/[^0-9A-Za-z. -]/,"-")
|
136
|
+
f = File.open( name, 'r')
|
137
|
+
f.each_line do |line|
|
138
|
+
output += line
|
139
|
+
end
|
140
|
+
f.close
|
141
|
+
return output
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.write_csv(directory, name, content)
|
145
|
+
if !File.file?("#{directory}/#{name}")
|
146
|
+
Program.write_file(directory, name, "")
|
147
|
+
end
|
148
|
+
CSV.open("#{directory}/#{name}", "ab") do |csv|
|
149
|
+
csv << content
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|