duo_api 1.4.0 → 1.5.0

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.
@@ -0,0 +1,832 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_client'
4
+ require_relative 'api_helpers'
5
+
6
+ class DuoApi
7
+ ##
8
+ # Duo Admin API (https://duo.com/docs/adminapi)
9
+ #
10
+ class Admin < DuoApi
11
+ ##
12
+ # Users
13
+ #
14
+ def get_users(**optional_params)
15
+ # optional_params: username, email, user_id_list, username_list
16
+ optional_params.tap do |p|
17
+ p[:user_id_list] = json_serialized_array(p[:user_id_list]) if p[:user_id_list]
18
+ p[:username_list] = json_serialized_array(p[:username_list]) if p[:username_list]
19
+ end
20
+ get_all('/admin/v1/users', optional_params)[:response]
21
+ end
22
+
23
+ def create_user(username:, **optional_params)
24
+ # optional_params: alias1, alias2, alias3, alias4, aliases, realname, email,
25
+ # enable_auto_prompt, status, notes, firstname, lastname
26
+ optional_params.tap do |p|
27
+ p[:aliases] = serialized_aliases(p[:aliases]) if p[:aliases]
28
+ end
29
+ params = optional_params.merge({ username: username })
30
+ post('/admin/v1/users', params)[:response]
31
+ end
32
+
33
+ def bulk_create_users(users:)
34
+ # Each user hash in users array requires :username and supports the following
35
+ # optional keys: realname, emaiml, status, notes, firstname, lastname
36
+ params = { users: json_serialized_array(users) }
37
+ post('/admin/v1/users/bulk_create', params)[:response]
38
+ end
39
+
40
+ def bulk_restore_users(user_id_list:)
41
+ params = { user_id_list: json_serialized_array(user_id_list) }
42
+ post('/admin/v1/users/bulk_restore', params)[:response]
43
+ end
44
+
45
+ def bulk_trash_users(user_id_list:)
46
+ params = { user_id_list: json_serialized_array(user_id_list) }
47
+ post('/admin/v1/users/bulk_send_to_trash', params)[:response]
48
+ end
49
+
50
+ def get_user(user_id:)
51
+ get("/admin/v1/users/#{user_id}")[:response]
52
+ end
53
+
54
+ def update_user(user_id:, **optional_params)
55
+ # optional_params: alias1, alias2, alias3, alias4, aliases, realname, email,
56
+ # enable_auto_prompt, status, notes, firstname, lastname,
57
+ # username
58
+ optional_params.tap do |p|
59
+ p[:aliases] = serialized_aliases(p[:aliases]) if p[:aliases]
60
+ end
61
+ post("/admin/v1/users/#{user_id}", optional_params)[:response]
62
+ end
63
+
64
+ def delete_user(user_id:)
65
+ delete("/admin/v1/users/#{user_id}")[:response]
66
+ end
67
+
68
+ def enroll_user(username:, email:, **optional_params)
69
+ # optional_params: valid_secs
70
+ params = optional_params.merge({ username: username, email: email })
71
+ post('/admin/v1/users/enroll', params)[:response]
72
+ end
73
+
74
+ def create_user_bypass_codes(user_id:, **optional_params)
75
+ # optional_params: count, codes, preserve_existing, reuse_count, valid_secs
76
+ optional_params.tap do |p|
77
+ p[:codes] = csv_serialized_array(p[:codes]) if p[:codes]
78
+ end
79
+ post("/admin/v1/users/#{user_id}/bypass_codes", optional_params)[:response]
80
+ end
81
+
82
+ def get_user_bypass_codes(user_id:)
83
+ get_all("/admin/v1/users/#{user_id}/bypass_codes")[:response]
84
+ end
85
+
86
+ def get_user_groups(user_id:)
87
+ get_all("/admin/v1/users/#{user_id}/groups")[:response]
88
+ end
89
+
90
+ def add_user_group(user_id:, group_id:)
91
+ params = { group_id: group_id }
92
+ post("/admin/v1/users/#{user_id}/groups", params)[:response]
93
+ end
94
+
95
+ def remove_user_group(user_id:, group_id:)
96
+ delete("/admin/v1/users/#{user_id}/groups/#{group_id}")[:response]
97
+ end
98
+
99
+ def get_user_phones(user_id:)
100
+ get_all("/admin/v1/users/#{user_id}/phones")[:response]
101
+ end
102
+
103
+ def add_user_phone(user_id:, phone_id:)
104
+ params = { phone_id: phone_id }
105
+ post("/admin/v1/users/#{user_id}/phones", params)[:response]
106
+ end
107
+
108
+ def remove_user_phone(user_id:, phone_id:)
109
+ delete("/admin/v1/users/#{user_id}/phones/#{phone_id}")[:response]
110
+ end
111
+
112
+ def get_user_hardware_tokens(user_id:)
113
+ get_all("/admin/v1/users/#{user_id}/tokens")[:response]
114
+ end
115
+
116
+ def add_user_hardware_token(user_id:, token_id:)
117
+ params = { token_id: token_id }
118
+ post("/admin/v1/users/#{user_id}/tokens", params)[:response]
119
+ end
120
+
121
+ def remove_user_hardware_token(user_id:, token_id:)
122
+ delete("/admin/v1/users/#{user_id}/tokens/#{token_id}")[:response]
123
+ end
124
+
125
+ def get_user_webauthn_credentials(user_id:)
126
+ get_all("/admin/v1/users/#{user_id}/webauthncredentials")[:response]
127
+ end
128
+
129
+ def get_user_desktop_authenticators(user_id:)
130
+ get_all("/admin/v1/users/#{user_id}/desktopauthenticators")[:response]
131
+ end
132
+
133
+ def sync_user(username:, directory_key:)
134
+ params = { username: username }
135
+ post("/admin/v1/users/directorysync/#{directory_key}/syncuser", params)[:response]
136
+ end
137
+
138
+ def send_verification_push(user_id:, phone_id:)
139
+ params = { phone_id: phone_id }
140
+ post("/admin/v1/users/#{user_id}/send_verification_push", params)[:response]
141
+ end
142
+
143
+ def get_verification_push_response(user_id:, push_id:)
144
+ params = { push_id: push_id }
145
+ get("/admin/v1/users/#{user_id}/verification_push_response", params)[:response]
146
+ end
147
+
148
+ ##
149
+ # Bulk Operations
150
+ #
151
+ def bulk_operations(operations:)
152
+ # Each hash in user_operations array requires :method, :path, and :body
153
+ # Each :body has the same parameter requirements as the individual operation
154
+ # Supported operations:
155
+ # Create User: POST /admin/v1/users
156
+ # Modify User: POST /admin/v1/users/[user_id]
157
+ # Delete User: DELETE /admin/v1/users/[user_id]
158
+ # Add User Group: POST /admin/v1/users/[user_id]/groups
159
+ # Remove User Group: POST /admin/v1/users/[user_id]/groups/[group_id]
160
+ operations.each{ |o| o[:body][:aliases] = serialized_aliases(o[:body][:aliases]) if o[:body][:aliases] }
161
+ params = { operations: json_serialized_array(operations) }
162
+ post('/admin/v1/bulk', params)[:response]
163
+ end
164
+
165
+ ##
166
+ # Groups
167
+ #
168
+ def get_groups(**optional_params)
169
+ # optional_params: group_id_list
170
+ get_all('/admin/v1/groups', optional_params)[:response]
171
+ end
172
+
173
+ def create_group(name:, **optional_params)
174
+ # optional_params: desc, status
175
+ params = optional_params.merge({ name: name })
176
+ post('/admin/v1/groups', params)[:response]
177
+ end
178
+
179
+ def get_group(group_id:)
180
+ get("/admin/v2/groups/#{group_id}")[:response]
181
+ end
182
+
183
+ def get_group_users(group_id:)
184
+ get_all("/admin/v2/groups/#{group_id}/users")[:response]
185
+ end
186
+
187
+ def update_group(group_id:, **optional_params)
188
+ # optional_params: desc, status, name
189
+ post("/admin/v1/groups/#{group_id}", optional_params)[:response]
190
+ end
191
+
192
+ def delete_group(group_id:)
193
+ delete("/admin/v1/groups/#{group_id}")[:response]
194
+ end
195
+
196
+ ##
197
+ # Phones
198
+ #
199
+ def get_phones(**optional_params)
200
+ # optional_params: number, extension
201
+ get_all('/admin/v1/phones', optional_params)[:response]
202
+ end
203
+
204
+ def create_phone(**optional_params)
205
+ # optional_params: number, name, extension, type, platform, predelay, postdelay
206
+ post('/admin/v1/phones', optional_params)[:response]
207
+ end
208
+
209
+ def get_phone(phone_id:)
210
+ get("/admin/v1/phones/#{phone_id}")[:response]
211
+ end
212
+
213
+ def update_phone(phone_id:, **optional_params)
214
+ # optional_params: number, name, extension, type, platform, predelay, postdelay
215
+ post("/admin/v1/phones/#{phone_id}", optional_params)[:response]
216
+ end
217
+
218
+ def delete_phone(phone_id:)
219
+ delete("/admin/v1/phones/#{phone_id}")[:response]
220
+ end
221
+
222
+ def create_activation_url(phone_id:, **optional_params)
223
+ # optional_params: valid_secs, install
224
+ post("/admin/v1/phones/#{phone_id}/activation_url", optional_params)[:response]
225
+ end
226
+
227
+ def send_sms_activation(phone_id:, **optional_params)
228
+ # optional_params: valid_secs, install, installation_msg, activation_msg
229
+ post("/admin/v1/phones/#{phone_id}/send_sms_activation", optional_params)[:response]
230
+ end
231
+
232
+ def send_sms_installation(phone_id:, **optional_params)
233
+ # optional_params: installation_msg
234
+ post("/admin/v1/phones/#{phone_id}/send_sms_installation", optional_params)[:response]
235
+ end
236
+
237
+ def send_sms_passcodes(phone_id:)
238
+ post("/admin/v1/phones/#{phone_id}/send_sms_passcodes")[:response]
239
+ end
240
+
241
+ ##
242
+ # Tokens
243
+ #
244
+ def get_tokens(**optional_params)
245
+ # optional_params: type, serial
246
+ get_all('/admin/v1/tokens', optional_params)[:response]
247
+ end
248
+
249
+ def create_token(type:, serial:, **optional_params)
250
+ # optional_params: secret, counter, private_id, aes_key
251
+ params = optional_params.merge({ type: type, serial: serial })
252
+ post('/admin/v1/tokens', params)[:response]
253
+ end
254
+
255
+ def get_token(token_id:)
256
+ get("/admin/v1/tokens/#{token_id}")[:response]
257
+ end
258
+
259
+ def resync_token(token_id:, code1:, code2:, code3:)
260
+ params = { code1: code1, code2: code2, code3: code3 }
261
+ post("/admin/v1/tokens/#{token_id}/resync", params)[:response]
262
+ end
263
+
264
+ def delete_token(token_id:)
265
+ delete("/admin/v1/tokens/#{token_id}")[:response]
266
+ end
267
+
268
+ ##
269
+ # WebAuthn Credentials
270
+ #
271
+ def get_webauthncredentials
272
+ get_all('/admin/v1/webauthncredentials')[:response]
273
+ end
274
+
275
+ def get_webauthncredential(webauthnkey:)
276
+ get("/admin/v1/webauthncredentials/#{webauthnkey}")[:response]
277
+ end
278
+
279
+ def delete_webauthncredential(webauthnkey:)
280
+ delete("/admin/v1/webauthncredentials/#{webauthnkey}")[:response]
281
+ end
282
+
283
+ ##
284
+ # Desktop Authenticators
285
+ #
286
+ def get_desktop_authenticators
287
+ get_all('/admin/v1/desktop_authenticators')[:response]
288
+ end
289
+
290
+ def get_desktop_authenticator(dakey:)
291
+ get("/admin/v1/desktop_authenticators/#{dakey}")[:response]
292
+ end
293
+
294
+ def delete_desktop_authenticator(dakey:)
295
+ delete("/admin/v1/desktop_authenticators/#{dakey}")[:response]
296
+ end
297
+
298
+ def get_shared_desktop_authenticators
299
+ get_all('/admin/v1/desktop_authenticators/shared_device_auth')[:response]
300
+ end
301
+
302
+ def get_shared_desktop_authenticator(shared_device_key:)
303
+ get("/admin/v1/desktop_authenticators/shared_device_auth/#{shared_device_key}")[:response]
304
+ end
305
+
306
+ def create_shared_desktop_authenticator(group_id_list:, trusted_endpoint_integration_id_list:,
307
+ **optional_params)
308
+ # optional_params: active, name
309
+ params = optional_params.merge({
310
+ group_id_list: group_id_list,
311
+ trusted_endpoint_integration_id_list: trusted_endpoint_integration_id_list
312
+ })
313
+ post('/admin/v1/desktop_authenticators/shared_device_auth', params)[:response]
314
+ end
315
+
316
+ def update_shared_desktop_authenticator(shared_device_key:, **optional_params)
317
+ # optional_params: active, name, group_id_list, trusted_endpoint_integration_id_list
318
+ put("/admin/v1/desktop_authenticators/shared_device_auth/#{shared_device_key}",
319
+ optional_params)[:response]
320
+ end
321
+
322
+ def delete_shared_desktop_authenticator(shared_device_key:)
323
+ delete("/admin/v1/desktop_authenticators/shared_device_auth/#{shared_device_key}")[:response]
324
+ end
325
+
326
+ ##
327
+ # Bypass Codes
328
+ #
329
+ def get_bypass_codes
330
+ get_all('/admin/v1/bypass_codes')[:response]
331
+ end
332
+
333
+ def get_bypass_code(bypass_code_id:)
334
+ get("/admin/v1/bypass_codes/#{bypass_code_id}")[:response]
335
+ end
336
+
337
+ def delete_bypass_code(bypass_code_id:)
338
+ delete("/admin/v1/bypass_codes/#{bypass_code_id}")[:response]
339
+ end
340
+
341
+ ##
342
+ # Integrations
343
+ #
344
+ def get_integrations
345
+ get_all('/admin/v3/integrations')[:response]
346
+ end
347
+
348
+ def create_integration(name:, type:, **optional_params)
349
+ # optional_params: adminapi_admins, adminapi_admins_read, adminapi_allow_to_set_permissions,
350
+ # adminapi_info, adminapi_integrations, adminapi_read_log,
351
+ # adminapi_read_resource, adminapi_settings, adminapi_write_resource,
352
+ # enroll_policy, greeting, groups_allowed, ip_whitelist,
353
+ # ip_whitelist_enroll_policy, networks_for_api_access, notes,
354
+ # trusted_device_days, self_service_allowed, sso, username_normalization_policy
355
+ #
356
+ # sso params: https://duo.com/docs/adminapi#sso-parameters
357
+ params = optional_params.merge({ name: name, type: type })
358
+ post('/admin/v3/integrations', params)[:response]
359
+ end
360
+
361
+ def get_integration(integration_key:)
362
+ get("/admin/v3/integrations/#{integration_key}")[:response]
363
+ end
364
+
365
+ def update_integration(integration_key:, **optional_params)
366
+ # optional_params: adminapi_admins, adminapi_admins_read, adminapi_allow_to_set_permissions,
367
+ # adminapi_info, adminapi_integrations, adminapi_read_log,
368
+ # adminapi_read_resource, adminapi_settings, adminapi_write_resource,
369
+ # enroll_policy, greeting, groups_allowed, ip_whitelist,
370
+ # ip_whitelist_enroll_policy, networks_for_api_access, notes,
371
+ # trusted_device_days, self_service_allowed, sso, username_normalization_policy,
372
+ # name, policy_key, prompt_v4_enabled, reset_secret_key
373
+ #
374
+ # sso params: https://duo.com/docs/adminapi#sso-parameters
375
+ post("/admin/v3/integrations/#{integration_key}", optional_params)[:response]
376
+ end
377
+
378
+ def delete_integration(integration_key:)
379
+ delete("/admin/v3/integrations/#{integration_key}")[:response]
380
+ end
381
+
382
+ def get_integration_secret_key(integration_key:)
383
+ get("/admin/v1/integrations/#{integration_key}/skey")[:response]
384
+ end
385
+
386
+ def get_oauth_integration_client_secret(integration_key:, client_id:)
387
+ get("/admin/v2/integrations/oauth_cc/#{integration_key}/client_secret/#{client_id}")[:response]
388
+ end
389
+
390
+ def reset_oauth_integration_client_secret(integration_key:, client_id:)
391
+ post("/admin/v2/integrations/oauth_cc/#{integration_key}/client_secret/#{client_id}")[:response]
392
+ end
393
+
394
+ def get_oidc_integration_client_secret(integration_key:)
395
+ get("/admin/v2/integrations/oidc/#{integration_key}/client_secret")[:response]
396
+ end
397
+
398
+ def reset_oidc_integration_client_secret(integration_key:)
399
+ post("/admin/v2/integrations/oidc/#{integration_key}/client_secret")[:response]
400
+ end
401
+
402
+ ##
403
+ # Policies
404
+ #
405
+ def get_policies_summary
406
+ get('/admin/v2/policies/summary')[:response]
407
+ end
408
+
409
+ def get_policies
410
+ get_all('/admin/v2/policies')[:response]
411
+ end
412
+
413
+ def get_global_policy
414
+ get('/admin/v2/policies/global')[:response]
415
+ end
416
+
417
+ def get_policy(policy_key:)
418
+ get("/admin/v2/policies/#{policy_key}")[:response]
419
+ end
420
+
421
+ def calculate_policy(user_id:, integration_key:)
422
+ params = { user_id: user_id, integration_key: integration_key }
423
+ get('/admin/v2/policies/calculate', params)[:response]
424
+ end
425
+
426
+ def copy_policy(policy_key:, **optional_params)
427
+ # optional_params: new_policy_names_list
428
+ params = optional_params.merge({ policy_key: policy_key })
429
+ post('/admin/v2/policies/copy', params)[:response]
430
+ end
431
+
432
+ def create_policy(policy_name:, **optional_params)
433
+ # optional_params: apply_to_apps, apply_to_groups_in_apps, sections
434
+ params = optional_params.merge({ policy_name: policy_name })
435
+ post('/admin/v2/policies', params)[:response]
436
+ end
437
+
438
+ def update_policies(policies_to_update:, policy_changes:)
439
+ # parameter formatting: https://duo.com/docs/adminapi#update-policies
440
+ params = { policies_to_update: policies_to_update, policy_changes: policy_changes }
441
+ put('/admin/v2/policies/update', params)[:response]
442
+ end
443
+
444
+ def update_policy(policy_key:, **optional_params)
445
+ # optional_params: apply_to_apps, apply_to_groups_in_apps, sections,
446
+ # policy_name, sections_to_delete
447
+ params = optional_params.merge({ policy_key: policy_key })
448
+ put("/admin/v2/policies/#{policy_key}", params)[:response]
449
+ end
450
+
451
+ def delete_policy(policy_key:)
452
+ delete("/admin/v2/policies/#{policy_key}")[:response]
453
+ end
454
+
455
+ ##
456
+ # Endpoints
457
+ #
458
+ def get_endpoints
459
+ get_all('/admin/v1/endpoints')[:response]
460
+ end
461
+
462
+ def get_endpoint(epkey:)
463
+ get("/admin/v1/endpoints/#{epkey}")[:response]
464
+ end
465
+
466
+ ##
467
+ # Registered Devices
468
+ #
469
+ def get_registered_devices
470
+ get_all('/admin/v1/registered_devices')[:response]
471
+ end
472
+
473
+ def get_registered_device(compkey:)
474
+ get("/admin/v1/registered_devices/#{compkey}")[:response]
475
+ end
476
+
477
+ def delete_registered_device(compkey:)
478
+ delete("/admin/v1/registered_devices/#{compkey}")[:response]
479
+ end
480
+
481
+ def get_blocked_registered_devices
482
+ get_all('/admin/v1/registered_devices/blocked')[:response]
483
+ end
484
+
485
+ def get_blocked_registered_device(compkey:)
486
+ get("/admin/v1/registered_devices/blocked/#{compkey}")[:response]
487
+ end
488
+
489
+ def block_registered_devices(registered_device_key_list:)
490
+ params = { registered_device_key_list: registered_device_key_list }
491
+ post('/admin/v1/registered_devices/blocked', params)[:response]
492
+ end
493
+
494
+ def block_registered_device(compkey:)
495
+ post("/admin/v1/registered_devices/blocked/#{compkey}")[:response]
496
+ end
497
+
498
+ def unblock_registered_devices(registered_device_key_list:)
499
+ params = { registered_device_key_list: registered_device_key_list }
500
+ delete('/admin/v1/registered_devices/blocked', params)[:response]
501
+ end
502
+
503
+ def unblock_registered_device(compkey:)
504
+ delete("/admin/v1/registered_devices/blocked/#{compkey}")[:response]
505
+ end
506
+
507
+ ##
508
+ # Passport
509
+ #
510
+ def get_passport_config
511
+ get('/admin/v2/passport/config')[:response]
512
+ end
513
+
514
+ def update_passport_config(disabled_groups:, enabled_groups:, enabled_status:)
515
+ params = { disabled_groups: disabled_groups, enabled_groups: enabled_groups,
516
+ enabled_status: enabled_status }
517
+ post('/admin/v2/passport/config', params)[:response]
518
+ end
519
+
520
+ ##
521
+ # Administrators
522
+ #
523
+ def get_admins
524
+ get_all('/admin/v1/admins')[:response]
525
+ end
526
+
527
+ def create_admin(email:, name:, **optional_params)
528
+ # optional_params: phone, role, restricted_by_admin_units, send_email, token_id, valid_days
529
+ params = optional_params.merge({ email: email, name: name })
530
+ post('/admin/v1/admins', params)[:response]
531
+ end
532
+
533
+ def get_admin(admin_id:)
534
+ get("/admin/v1/admins/#{admin_id}")[:response]
535
+ end
536
+
537
+ def update_admin(admin_id:, **optional_params)
538
+ # optional_params: phone, role, restricted_by_admin_units, token_id, name, status
539
+ post("/admin/v1/admins/#{admin_id}", optional_params)[:response]
540
+ end
541
+
542
+ def delete_admin(admin_id:)
543
+ delete("/admin/v1/admins/#{admin_id}")[:response]
544
+ end
545
+
546
+ def reset_admin_auth_attempts(admin_id:)
547
+ post("/admin/v1/admins/#{admin_id}/reset")[:response]
548
+ end
549
+
550
+ def clear_admin_inactivity(admin_id:)
551
+ post("/admin/v1/admins/#{admin_id}/clear_inactivity")[:response]
552
+ end
553
+
554
+ def create_existing_admin_activation_link(admin_id:)
555
+ post("/admin/v1/admins/#{admin_id}/activation_link")[:response]
556
+ end
557
+
558
+ def delete_existing_admin_activation_link(admin_id:)
559
+ delete("/admin/v1/admins/#{admin_id}/activation_link")[:response]
560
+ end
561
+
562
+ def email_existing_admin_activation_link(admin_id:)
563
+ post("/admin/v1/admins/#{admin_id}/activation_link/email")[:response]
564
+ end
565
+
566
+ def create_new_admin_activation_link(email:, **optional_params)
567
+ # optional_params: admin_name, admin_role, send_email, valid_days
568
+ params = optional_params.merge({ email: email })
569
+ post('/admin/v1/admins/activations', params)[:response]
570
+ end
571
+
572
+ def get_new_admin_pending_activations
573
+ get_all('/admin/v1/admins/activations')[:response]
574
+ end
575
+
576
+ def delete_new_admin_pending_activations(admin_activation_id:)
577
+ delete("/admin/v1/admins/activations/#{admin_activation_id}")[:response]
578
+ end
579
+
580
+ def sync_admin(directory_key:, email:)
581
+ params = { email: email }
582
+ post("/admin/v1/admins/directorysync/#{directory_key}/syncadmin", params)[:response]
583
+ end
584
+
585
+ def get_admin_password_mgmt_statuses
586
+ get_all('/admin/v1/admins/password_mgmt')[:response]
587
+ end
588
+
589
+ def get_admin_password_mgmt_status(admin_id:)
590
+ get("/admin/v1/admins/#{admin_id}/password_mgmt")[:response]
591
+ end
592
+
593
+ def update_admin_password_mgmt_status(admin_id:, **optional_params)
594
+ # optional_params: has_external_password_mgmt, password
595
+ post("/admin/v1/admins/#{admin_id}/password_mgmt", optional_params)[:response]
596
+ end
597
+
598
+ def get_admin_allowed_auth_factors
599
+ get('/admin/v1/admins/allowed_auth_methods')[:response]
600
+ end
601
+
602
+ def update_admin_allowed_auth_factors(**optional_params)
603
+ # optional_params: hardware_token_enabled, mobile_otp_enabled, push_enabled, sms_enabled,
604
+ # verified_push_enabled, verified_push_length, voice_enabled, webauthn_enabled,
605
+ # yubikey_enabled
606
+ post('/admin/v1/admins/allowed_auth_methods', optional_params)[:response]
607
+ end
608
+
609
+ ##
610
+ # Administrative Units
611
+ #
612
+ def get_administrative_units(**optional_params)
613
+ # optional_params: admin_id, group_id, integration_key
614
+ get_all('/admin/v1/administrative_units', optional_params)[:response]
615
+ end
616
+
617
+ def get_administrative_unit(admin_unit_id:)
618
+ get("/admin/v1/administrative_units/#{admin_unit_id}")[:response]
619
+ end
620
+
621
+ def create_administrative_unit(name:, description:, restrict_by_groups:, **optional_params)
622
+ # optional_params: restrict_by_integrations, admins, groups, integrations
623
+ params = optional_params.merge({ name: name, description: description,
624
+ restrict_by_groups: restrict_by_groups })
625
+ post('/admin/v1/administrative_units', params)[:response]
626
+ end
627
+
628
+ def update_administrative_unit(admin_unit_id:, **optional_params)
629
+ # optional_params: restrict_by_integrations, admins, groups, integrations,
630
+ # name, description, restrict_by_groups
631
+ post("/admin/v1/administrative_units/#{admin_unit_id}", optional_params)[:response]
632
+ end
633
+
634
+ def add_administrative_unit_admin(admin_unit_id:, admin_id:)
635
+ post("/admin/v1/administrative_units/#{admin_unit_id}/admin/#{admin_id}")[:response]
636
+ end
637
+
638
+ def remove_administrative_unit_admin(admin_unit_id:, admin_id:)
639
+ delete("/admin/v1/administrative_units/#{admin_unit_id}/admin/#{admin_id}")[:response]
640
+ end
641
+
642
+ def add_administrative_unit_group(admin_unit_id:, group_id:)
643
+ post("/admin/v1/administrative_units/#{admin_unit_id}/group/#{group_id}")[:response]
644
+ end
645
+
646
+ def remove_administrative_unit_group(admin_unit_id:, group_id:)
647
+ delete("/admin/v1/administrative_units/#{admin_unit_id}/group/#{group_id}")[:response]
648
+ end
649
+
650
+ def add_administrative_unit_integration(admin_unit_id:, integration_key:)
651
+ post("/admin/v1/administrative_units/#{admin_unit_id}/integration/#{integration_key}")[:response]
652
+ end
653
+
654
+ def remove_administrative_unit_integration(admin_unit_id:, integration_key:)
655
+ delete("/admin/v1/administrative_units/#{admin_unit_id}/integration/#{integration_key}")[:response]
656
+ end
657
+
658
+ def delete_administrative_unit(admin_unit_id:)
659
+ delete("/admin/v1/administrative_units/#{admin_unit_id}")[:response]
660
+ end
661
+
662
+ ##
663
+ # Logs
664
+ #
665
+ def get_authentication_logs(mintime:, maxtime:, **optional_params)
666
+ # optional_params: applications, users, assessment, detections, event_types, factors, formatter,
667
+ # groups, phone_numbers, reasons, results, tokens, sort
668
+ #
669
+ # more info: https://duo.com/docs/adminapi#authentication-logs
670
+ params = optional_params.merge({ mintime: mintime, maxtime: maxtime })
671
+ data_array_path = %i[response authlogs]
672
+ metadata_path = %i[response metadata]
673
+ get_all('/admin/v2/logs/authentication', params, data_array_path: data_array_path,
674
+ metadata_path: metadata_path).dig(*data_array_path)
675
+ end
676
+
677
+ def get_activity_logs(mintime:, maxtime:, **optional_params)
678
+ # optional_params: sort
679
+ params = optional_params.merge({ mintime: mintime, maxtime: maxtime })
680
+ data_array_path = %i[response items]
681
+ metadata_path = %i[response metadata]
682
+ get_all('/admin/v2/logs/activity', params, data_array_path: data_array_path,
683
+ metadata_path: metadata_path).dig(*data_array_path)
684
+ end
685
+
686
+ def get_admin_logs(**optional_params)
687
+ # optional_params: mintime
688
+ get('/admin/v1/logs/administrator', optional_params)[:response]
689
+ end
690
+
691
+ def get_telephony_logs(mintime:, maxtime:, **optional_params)
692
+ # optional_params: sort
693
+ params = optional_params.merge({ mintime: mintime, maxtime: maxtime })
694
+ data_array_path = %i[response items]
695
+ metadata_path = %i[response metadata]
696
+ get_all('/admin/v2/logs/telephony', params, data_array_path: data_array_path,
697
+ metadata_path: metadata_path).dig(*data_array_path)
698
+ end
699
+
700
+ def get_offline_enrollment_logs(**optional_params)
701
+ # optional_params: mintime
702
+ get('/admin/v1/logs/offline_enrollment', optional_params)[:response]
703
+ end
704
+
705
+ ##
706
+ # Trust Monitor
707
+ #
708
+ def get_trust_monitor_events(mintime:, maxtime:, **optional_params)
709
+ # optional_params: formatter, type
710
+ params = optional_params.merge({ mintime: mintime, maxtime: maxtime })
711
+ params[:limit] = 200 if !params[:limit] || (params[:limit].to_i > 200)
712
+ data_array_path = %i[response events]
713
+ metadata_path = %i[response metadata]
714
+ get_all('/admin/v1/trust_monitor/events', params, data_array_path: data_array_path,
715
+ metadata_path: metadata_path).dig(*data_array_path)
716
+ end
717
+
718
+ ##
719
+ # Settings
720
+ #
721
+ def get_settings
722
+ get('/admin/v1/settings')[:response]
723
+ end
724
+
725
+ def update_settings(**optional_params)
726
+ # optional_params: caller_id, duo_mobile_otp_type, email_activity_notification_enabled,
727
+ # enrollment_universal_prompt_enabled, fraud_email, fraud_email_enabled,
728
+ # global_ssp_policy_enforced, helpdesk_bypass, helpdesk_bypass_expiration,
729
+ # helpdesk_can_send_enroll_email, inactive_user_expiration, keypress_confirm,
730
+ # keypress_fraud, language, lockout_expire_duration, lockout_threshold,
731
+ # log_retention_days, minimum_password_length, name,
732
+ # password_requires_lower_alpha, password_requires_numeric,
733
+ # password_requires_special, password_requires_upper_alpha,
734
+ # push_activity_notification_enabled, sms_batch, sms_expiration, sms_message,
735
+ # sms_refresh, telephony_warning_min, timezone, unenrolled_user_lockout_threshold,
736
+ # user_managers_can_put_users_in_bypass, user_telephony_cost_max
737
+ post('/admin/v1/settings', optional_params)[:response]
738
+ end
739
+
740
+ def get_logo
741
+ get_image('/admin/v1/logo')
742
+ end
743
+
744
+ def update_logo(logo:)
745
+ # logo should be raw png data or base64 strict encoded raw png data
746
+ encoded_logo = base64?(logo) ? logo : Base64.strict_encode64(logo)
747
+ params = { logo: encoded_logo }
748
+ post('/admin/v1/logo', params)[:response]
749
+ end
750
+
751
+ def delete_logo
752
+ delete('/admin/v1/logo')[:response]
753
+ end
754
+
755
+ ##
756
+ # Custom Branding
757
+ #
758
+ def get_custom_branding
759
+ get('/admin/v1/branding')[:response]
760
+ end
761
+
762
+ def update_custom_branding(**optional_params)
763
+ # optional_params: background_img, card_accent_color, logo, page_background_color,
764
+ # powered_by_duo, sso_custom_username_label
765
+ post('/admin/v1/branding', optional_params)[:response]
766
+ end
767
+
768
+ def get_custom_branding_draft
769
+ get('/admin/v1/branding/draft')[:response]
770
+ end
771
+
772
+ def update_custom_branding_draft(**optional_params)
773
+ # optional_params: background_img, card_accent_color, logo, page_background_color,
774
+ # powered_by_duo, sso_custom_username_label, user_ids
775
+ post('/admin/v1/branding/draft', optional_params)[:response]
776
+ end
777
+
778
+ def add_custom_branding_draft_user(user_id:)
779
+ post("/admin/v1/branding/draft/users/#{user_id}")[:response]
780
+ end
781
+
782
+ def remove_custom_branding_draft_user(user_id:)
783
+ delete("/admin/v1/branding/draft/users/#{user_id}")[:response]
784
+ end
785
+
786
+ def publish_custom_branding_draft
787
+ post('/admin/v1/branding/draft/publish')[:response]
788
+ end
789
+
790
+ def get_custom_branding_messaging
791
+ get('/admin/v1/branding/custom_messaging')[:response]
792
+ end
793
+
794
+ def update_custom_branding_messaging(**optional_params)
795
+ # optional_params: help_links, help_text, locale
796
+ post('/admin/v1/branding/custom_messaging', optional_params)[:response]
797
+ end
798
+
799
+ ##
800
+ # Account Info
801
+ #
802
+ def get_account_info_summary
803
+ get('/admin/v1/info/summary')[:response]
804
+ end
805
+
806
+ def get_telephony_credits_used_report(**optional_params)
807
+ # optional_params: maxtime, mintime
808
+ get('/admin/v1/info/telephony_credits_used', optional_params)[:response]
809
+ end
810
+
811
+ def get_authentication_attempts_report(**optional_params)
812
+ get('/admin/v1/info/authentication_attempts', optional_params)[:response]
813
+ end
814
+
815
+ def get_user_authentication_attempts_report(**optional_params)
816
+ get('/admin/v1/info/user_authentication_attempts', optional_params)[:response]
817
+ end
818
+
819
+ private
820
+
821
+ def serialized_aliases(aliases)
822
+ case aliases
823
+ when Array
824
+ aliases.map.with_index{ |a, i| "alias#{i + 1}=#{a}" }.join('&')
825
+ when Hash
826
+ aliases.map{ |k, v| "#{k}=#{v}" }.join('&')
827
+ else
828
+ aliases
829
+ end
830
+ end
831
+ end
832
+ end