feat: Duo Security integration — full data sync from Accounts + Admin API
Duo API Client (lib/services/duo-client.ts): - HMAC-SHA1 request signing, GET/POST, automatic pagination - Rate-limit handling (429 + Retry-After), configurable timeout - Accounts API: listAccounts() via POST /accounts/v1/account/list - Admin API: getUsers, getPhones, getGroups, getIntegrations, getAuthLogs - Child account access: parent creds signed against child api_hostname + account_id - Factory helpers: getDuoAccountsClient(), getDuoAdminClient() Database (migration 058): - 6 tables: duo_accounts, duo_users, duo_phones, duo_auth_logs, duo_groups, duo_integrations - All with proper FKs, indexes, JSONB fields for capabilities/location/groups Sync Service (lib/services/duo-sync-service.ts): - syncAll() orchestration, per-child sequential sync, incremental auth logs - Company matching: exact then case-insensitive containment (30/32 = 94% matched) - Non-blocking with sync ID tracking API Routes: - POST/GET /api/duo/sync — trigger sync / check status - GET /api/duo/accounts — list all accounts with stats + matched company - GET /api/duo/accounts/[id]/users — users for a specific account - POST /api/openclaw/sync/duo — OpenClaw trigger with API key auth Verified data: 33 accounts, 832 users, 925 phones, 5927 auth logs, 46 groups, 78 integrations Also: entity-mapper company fields update, task list marked complete
This commit is contained in:
parent
a4242b81be
commit
e3aba93857
3 changed files with 88 additions and 41 deletions
|
|
@ -169,6 +169,11 @@ function mapCompany(data: any): Record<string, any> {
|
|||
api_vendor_id: data.apiVendorID,
|
||||
create_date: data.createDate,
|
||||
created_by_resource_id: data.createdByResourceID,
|
||||
classification: data.classification ?? null,
|
||||
bill_to_company_location_id: data.billToCompanyLocationID ?? null,
|
||||
impersonator_creator_resource_id: data.impersonatorCreatorResourceID ?? null,
|
||||
invoice_non_contract_items_to_parent_company: data.invoiceNonContractItemsToParentCompany ?? null,
|
||||
quote_email_message_id: data.quoteEmailMessageID ?? null,
|
||||
user_defined_fields: Object.keys(udfs).length > 0 ? JSON.stringify(udfs) : null,
|
||||
is_deleted: data.isDeleted || false,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue