idnio 2.3.2b
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
- 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\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
|