@openmdm/core 0.7.0 → 0.9.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.
- package/dist/index.d.ts +205 -4
- package/dist/index.js +339 -35
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +203 -2
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/dashboard.ts +40 -0
- package/src/device-identity.ts +338 -0
- package/src/index.ts +222 -27
- package/src/logger.ts +98 -0
- package/src/plugin-storage.ts +25 -5
- package/src/types.ts +216 -1
- package/src/webhooks.ts +15 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { WebhookConfig, MDMEvent, WebhookEndpoint, EventType, DatabaseAdapter, TenantManager, AuthorizationManager, AuditConfig, AuditManager, ScheduleManager, MessageQueueManager, DashboardManager, PluginStorageAdapter, MDMConfig,
|
|
2
|
-
export { AppInstallationSummary, AppRollback, AppVersion, Application, ApplicationManager, ApplicationNotFoundError, AuditAction, AuditLog, AuditLogFilter, AuditLogListResult, AuditSummary, AuthConfig, AuthenticationError, AuthorizationError, Command, CommandFilter, CommandManager, CommandNotFoundError, CommandResult, CommandStatus, CommandSuccessRates, CommandType, CreateAppRollbackInput, CreateApplicationInput, CreateAuditLogInput, CreateDeviceInput, CreateGroupInput, CreatePolicyInput, CreateRoleInput, CreateScheduledTaskInput, CreateTenantInput, CreateUserInput, DashboardStats, DeployTarget, Device, DeviceFilter, DeviceListResult, DeviceLocation, DeviceManager, DeviceNotFoundError, DeviceStatus, DeviceStatusBreakdown, EnqueueMessageInput, EnrollmentConfig, EnrollmentError, EnrollmentMethod, EnrollmentResponse, EnrollmentTrendPoint, EventFilter, EventHandler, EventPayloadMap, Group, GroupHierarchyStats, GroupManager, GroupNotFoundError, GroupTreeNode, HardwareControl, Heartbeat, InstalledApp, MDMError, MDMPlugin, MaintenanceWindow, PasswordPolicy, Permission, PermissionAction, PermissionResource, PluginMiddleware, PluginRoute, PluginStorageEntry, Policy, PolicyApplication, PolicyManager, PolicyNotFoundError, PolicySettings, PushAdapter, PushBatchResult, PushConfig, PushMessage, PushProviderConfig, PushResult, PushToken, QueueMessageStatus, QueueStats, QueuedMessage, RegisterPushTokenInput, Role, RoleNotFoundError, ScheduledTask, ScheduledTaskFilter, ScheduledTaskListResult, ScheduledTaskStatus, SendCommandInput, StorageConfig, SystemUpdatePolicy, TaskExecution, TaskSchedule, TaskType, Tenant, TenantFilter, TenantListResult, TenantNotFoundError, TenantSettings, TenantStats, TenantStatus, TimeWindow, UpdateApplicationInput, UpdateDeviceInput, UpdateGroupInput, UpdatePolicyInput, UpdateRoleInput, UpdateScheduledTaskInput, UpdateTenantInput, UpdateUserInput, User, UserFilter, UserListResult, UserNotFoundError, UserWithRoles, ValidationError, VpnConfig, WebhookDeliveryResult, WebhookManager, WifiConfig } from './types.js';
|
|
1
|
+
import { WebhookConfig, Logger, MDMEvent, WebhookEndpoint, EventType, MDMInstance, DeviceIdentityVerification, DatabaseAdapter, TenantManager, AuthorizationManager, AuditConfig, AuditManager, ScheduleManager, MessageQueueManager, DashboardManager, PluginStorageAdapter, MDMConfig, EnrollmentRequest } from './types.js';
|
|
2
|
+
export { AppInstallationSummary, AppRollback, AppVersion, Application, ApplicationManager, ApplicationNotFoundError, AuditAction, AuditLog, AuditLogFilter, AuditLogListResult, AuditSummary, AuthConfig, AuthenticationError, AuthorizationError, Command, CommandFilter, CommandManager, CommandNotFoundError, CommandResult, CommandStatus, CommandSuccessRates, CommandType, CreateAppRollbackInput, CreateApplicationInput, CreateAuditLogInput, CreateDeviceInput, CreateGroupInput, CreatePolicyInput, CreateRoleInput, CreateScheduledTaskInput, CreateTenantInput, CreateUserInput, DashboardStats, DeployTarget, Device, DeviceFilter, DeviceListResult, DeviceLocation, DeviceManager, DeviceNotFoundError, DeviceStatus, DeviceStatusBreakdown, EnqueueMessageInput, EnrollmentChallenge, EnrollmentConfig, EnrollmentError, EnrollmentMethod, EnrollmentResponse, EnrollmentTrendPoint, EventFilter, EventHandler, EventPayloadMap, Group, GroupHierarchyStats, GroupManager, GroupNotFoundError, GroupTreeNode, HardwareControl, Heartbeat, InstalledApp, LogContext, MDMError, MDMPlugin, MaintenanceWindow, PasswordPolicy, Permission, PermissionAction, PermissionResource, PinnedKeyConfig, PluginMiddleware, PluginRoute, PluginStorageEntry, Policy, PolicyApplication, PolicyManager, PolicyNotFoundError, PolicySettings, PushAdapter, PushBatchResult, PushConfig, PushMessage, PushProviderConfig, PushResult, PushToken, QueueMessageStatus, QueueStats, QueuedMessage, RegisterPushTokenInput, Role, RoleNotFoundError, ScheduledTask, ScheduledTaskFilter, ScheduledTaskListResult, ScheduledTaskStatus, SendCommandInput, StorageConfig, SystemUpdatePolicy, TaskExecution, TaskSchedule, TaskType, Tenant, TenantFilter, TenantListResult, TenantNotFoundError, TenantSettings, TenantStats, TenantStatus, TimeWindow, UpdateApplicationInput, UpdateDeviceInput, UpdateGroupInput, UpdatePolicyInput, UpdateRoleInput, UpdateScheduledTaskInput, UpdateTenantInput, UpdateUserInput, User, UserFilter, UserListResult, UserNotFoundError, UserWithRoles, ValidationError, VpnConfig, WebhookDeliveryResult, WebhookManager, WifiConfig } from './types.js';
|
|
3
3
|
export { ColumnDefinition, ColumnType, IndexDefinition, SchemaDefinition, TableDefinition, camelToSnake, getColumnNames, getPrimaryKey, getTableNames, mdmSchema, snakeToCamel, transformToCamelCase, transformToSnakeCase } from './schema.js';
|
|
4
|
+
import { KeyObject } from 'crypto';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* OpenMDM Agent Wire Protocol v2.
|
|
@@ -164,13 +165,213 @@ interface WebhookManager {
|
|
|
164
165
|
/**
|
|
165
166
|
* Create a webhook manager instance
|
|
166
167
|
*/
|
|
167
|
-
declare function createWebhookManager(config: WebhookConfig): WebhookManager;
|
|
168
|
+
declare function createWebhookManager(config: WebhookConfig, logger?: Logger): WebhookManager;
|
|
168
169
|
/**
|
|
169
170
|
* Verify a webhook signature from incoming requests
|
|
170
171
|
* (Utility for consumers to verify our webhooks)
|
|
171
172
|
*/
|
|
172
173
|
declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
|
|
173
174
|
|
|
175
|
+
/**
|
|
176
|
+
* OpenMDM Logger
|
|
177
|
+
*
|
|
178
|
+
* Default logger implementations and helpers. Production users are
|
|
179
|
+
* expected to pass their own pino/winston/bunyan instance via
|
|
180
|
+
* `createMDM({ logger })`; these defaults are for development and
|
|
181
|
+
* for the zero-config path.
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Console-backed logger. Writes JSON-ish lines to stdout/stderr with
|
|
186
|
+
* an `[openmdm]` prefix so they stand out in a mixed-log stream.
|
|
187
|
+
*
|
|
188
|
+
* This is the zero-config default — it intentionally does the
|
|
189
|
+
* minimum viable thing. Hosts running in production should replace
|
|
190
|
+
* it with a real structured logger.
|
|
191
|
+
*/
|
|
192
|
+
declare function createConsoleLogger(scope?: string[]): Logger;
|
|
193
|
+
/**
|
|
194
|
+
* No-op logger. Use to silence OpenMDM entirely — e.g. in tests or in
|
|
195
|
+
* environments where log noise is inappropriate.
|
|
196
|
+
*/
|
|
197
|
+
declare function createSilentLogger(): Logger;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* OpenMDM Device Identity
|
|
201
|
+
*
|
|
202
|
+
* Device-pinned asymmetric identity, using an ECDSA P-256 keypair the
|
|
203
|
+
* device generates in its own Keystore and registers with the server on
|
|
204
|
+
* first enrollment. After pinning, every consumer can verify a signed
|
|
205
|
+
* request against the same pinned public key — no shared HMAC secret,
|
|
206
|
+
* no APK extraction footgun, no dependence on Google hardware
|
|
207
|
+
* attestation (which most non-GMS fleet hardware cannot produce).
|
|
208
|
+
*
|
|
209
|
+
* This module is the reusable primitive. `@openmdm/core` uses it to
|
|
210
|
+
* gate `/agent/enroll` and will use it for `/agent/*` in Phase 2c.
|
|
211
|
+
* External consumers (midiamob's `deviceValidation.ts`, other custom
|
|
212
|
+
* servers) import the same functions to verify requests against the
|
|
213
|
+
* same pinned key — one device identity, many consumers.
|
|
214
|
+
*
|
|
215
|
+
* Why zero dependencies: Node's built-in `node:crypto` supports EC
|
|
216
|
+
* P-256 SPKI import and `crypto.verify('sha256', ...)` over DER-encoded
|
|
217
|
+
* signatures, which is the default format the Android Keystore
|
|
218
|
+
* produces. We deliberately do not pull in `@peculiar/*` or `node-forge`
|
|
219
|
+
* for this primitive — the surface area we need is small enough that
|
|
220
|
+
* the built-in is the right call.
|
|
221
|
+
*
|
|
222
|
+
* @see docs/concepts/enrollment for the full flow
|
|
223
|
+
* @see docs/proposals/phase-2b-rollout for the Android + rollout story
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Import an EC P-256 public key from base64-encoded SubjectPublicKeyInfo
|
|
228
|
+
* (SPKI) bytes — the standard on-wire format the Android Keystore
|
|
229
|
+
* produces when you call `certificate.publicKey.encoded` on a
|
|
230
|
+
* `KeyStore.getCertificate(alias)` result.
|
|
231
|
+
*
|
|
232
|
+
* Throws `InvalidPublicKeyError` on any parse failure. This is a
|
|
233
|
+
* security boundary — we do NOT return `null` on malformed input,
|
|
234
|
+
* because a caller that forgot to handle the null case would silently
|
|
235
|
+
* treat bad keys as "no key configured" and fall through to an
|
|
236
|
+
* insecure path.
|
|
237
|
+
*/
|
|
238
|
+
declare function importPublicKeyFromSpki(spkiBase64: string): KeyObject;
|
|
239
|
+
/**
|
|
240
|
+
* Verify an ECDSA-P256 signature over a message using a previously-
|
|
241
|
+
* imported or raw SPKI public key.
|
|
242
|
+
*
|
|
243
|
+
* Signature must be DER-encoded — the default Android Keystore
|
|
244
|
+
* produces DER, and `Signature.sign()` on JVM/Kotlin returns DER, so
|
|
245
|
+
* this matches what every reasonable agent sends on the wire.
|
|
246
|
+
*
|
|
247
|
+
* Returns `true` iff the signature is valid. Never throws on a bad
|
|
248
|
+
* signature (that is the whole point of a verify call). Throws only
|
|
249
|
+
* on an invalid public-key encoding, because that indicates a caller
|
|
250
|
+
* bug rather than a forged request.
|
|
251
|
+
*/
|
|
252
|
+
declare function verifyEcdsaSignature(publicKey: KeyObject | string, message: string, signatureBase64: string): boolean;
|
|
253
|
+
/**
|
|
254
|
+
* Build the canonical message that an enrollment signature covers.
|
|
255
|
+
*
|
|
256
|
+
* Staying in lockstep with `@openmdm/client` and with the Android
|
|
257
|
+
* agent is load-bearing — any change here is a wire break across
|
|
258
|
+
* every enrolled device. The contract test in
|
|
259
|
+
* `packages/core/tests/device-identity.test.ts` guards against drift.
|
|
260
|
+
*
|
|
261
|
+
* Shape (order matters):
|
|
262
|
+
*
|
|
263
|
+
* publicKey |
|
|
264
|
+
* model | manufacturer | osVersion |
|
|
265
|
+
* serialNumber | imei | macAddress | androidId |
|
|
266
|
+
* method | timestamp | challenge
|
|
267
|
+
*
|
|
268
|
+
* The public key is prepended (rather than appended) because it's the
|
|
269
|
+
* field most likely to be the whole point of the message — putting it
|
|
270
|
+
* first makes the signature's intent visible at a glance in logs.
|
|
271
|
+
*/
|
|
272
|
+
declare function canonicalEnrollmentMessage(parts: {
|
|
273
|
+
publicKey: string;
|
|
274
|
+
model: string;
|
|
275
|
+
manufacturer: string;
|
|
276
|
+
osVersion: string;
|
|
277
|
+
serialNumber?: string;
|
|
278
|
+
imei?: string;
|
|
279
|
+
macAddress?: string;
|
|
280
|
+
androidId?: string;
|
|
281
|
+
method: string;
|
|
282
|
+
timestamp: string;
|
|
283
|
+
challenge: string;
|
|
284
|
+
}): string;
|
|
285
|
+
/**
|
|
286
|
+
* Build the canonical message that a *post-enrollment* request
|
|
287
|
+
* signature covers. Consumers (openmdm's `/agent/*` routes,
|
|
288
|
+
* midiamob's `deviceValidation.ts`, any custom server) call this
|
|
289
|
+
* with the fields they want committed to the signature.
|
|
290
|
+
*
|
|
291
|
+
* The shape is deliberately narrower than the enrollment form — only
|
|
292
|
+
* the parts every request has in common.
|
|
293
|
+
*
|
|
294
|
+
* deviceId | timestamp | body | nonce
|
|
295
|
+
*
|
|
296
|
+
* `nonce` is optional; pass an empty string when the request does not
|
|
297
|
+
* carry a challenge. Replay protection on non-enrollment traffic is
|
|
298
|
+
* the caller's job — if your server already has a timestamp window
|
|
299
|
+
* check, you don't need a nonce per request.
|
|
300
|
+
*/
|
|
301
|
+
declare function canonicalDeviceRequestMessage(parts: {
|
|
302
|
+
deviceId: string;
|
|
303
|
+
timestamp: string;
|
|
304
|
+
body: string;
|
|
305
|
+
nonce?: string;
|
|
306
|
+
}): string;
|
|
307
|
+
/**
|
|
308
|
+
* Verify a signed request from an enrolled device against the
|
|
309
|
+
* public key pinned on that device's row.
|
|
310
|
+
*
|
|
311
|
+
* This is the primitive every consumer of device-pinned-key identity
|
|
312
|
+
* calls. It performs exactly the checks required to know the request
|
|
313
|
+
* came from the device that originally enrolled, in constant-ish
|
|
314
|
+
* time:
|
|
315
|
+
*
|
|
316
|
+
* 1. Look up the device by id.
|
|
317
|
+
* 2. Confirm the device has a pinned public key (refusing silently
|
|
318
|
+
* if not — a device without a pinned key is still on the legacy
|
|
319
|
+
* HMAC path and cannot be verified here).
|
|
320
|
+
* 3. Verify the ECDSA signature over the provided canonical message.
|
|
321
|
+
*
|
|
322
|
+
* Returns a tagged union so callers can react to the specific failure
|
|
323
|
+
* mode:
|
|
324
|
+
*
|
|
325
|
+
* - `not-found` — the device id doesn't exist. Almost always a bug
|
|
326
|
+
* in the caller, or a stolen/revoked device id.
|
|
327
|
+
* Return 401 to the client.
|
|
328
|
+
* - `no-pinned-key` — the device is still on the HMAC path. Callers
|
|
329
|
+
* should fall through to their legacy verifier
|
|
330
|
+
* (or fail, if the caller has already migrated).
|
|
331
|
+
* - `signature-invalid` — the signature did not verify against the
|
|
332
|
+
* pinned key. Return 401. **Do NOT** re-pin the
|
|
333
|
+
* submitted public key in response to a failure
|
|
334
|
+
* here — that's how re-pinning becomes a hijack.
|
|
335
|
+
*/
|
|
336
|
+
declare function verifyDeviceRequest(opts: {
|
|
337
|
+
mdm: MDMInstance;
|
|
338
|
+
deviceId: string;
|
|
339
|
+
canonicalMessage: string;
|
|
340
|
+
signatureBase64: string;
|
|
341
|
+
}): Promise<DeviceIdentityVerification>;
|
|
342
|
+
/**
|
|
343
|
+
* Thrown when a submitted public key cannot be parsed. This is a
|
|
344
|
+
* caller-facing error — the device sent something that is not a
|
|
345
|
+
* well-formed SPKI EC P-256 public key.
|
|
346
|
+
*/
|
|
347
|
+
declare class InvalidPublicKeyError extends Error {
|
|
348
|
+
readonly cause?: Error | undefined;
|
|
349
|
+
readonly code = "INVALID_PUBLIC_KEY";
|
|
350
|
+
constructor(message: string, cause?: Error | undefined);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Thrown when a device attempts to re-enroll with a public key that
|
|
354
|
+
* does not match the one originally pinned for its enrollment id.
|
|
355
|
+
*
|
|
356
|
+
* This is the core "device identity continuity" check. The server
|
|
357
|
+
* will NEVER automatically re-pin on mismatch — rebinding a device
|
|
358
|
+
* identity requires an explicit admin action (future work).
|
|
359
|
+
*/
|
|
360
|
+
declare class PublicKeyMismatchError extends Error {
|
|
361
|
+
readonly deviceId: string;
|
|
362
|
+
readonly code = "PUBLIC_KEY_MISMATCH";
|
|
363
|
+
constructor(deviceId: string);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Thrown when an enrollment attempts to use a challenge that is
|
|
367
|
+
* missing, expired, or already consumed.
|
|
368
|
+
*/
|
|
369
|
+
declare class ChallengeInvalidError extends Error {
|
|
370
|
+
readonly challenge?: string | undefined;
|
|
371
|
+
readonly code = "CHALLENGE_INVALID";
|
|
372
|
+
constructor(message: string, challenge?: string | undefined);
|
|
373
|
+
}
|
|
374
|
+
|
|
174
375
|
/**
|
|
175
376
|
* OpenMDM Tenant Manager
|
|
176
377
|
*
|
|
@@ -303,4 +504,4 @@ declare function parsePluginKey(key: string): {
|
|
|
303
504
|
declare function createMDM(config: MDMConfig): MDMInstance;
|
|
304
505
|
declare function verifyEnrollmentSignature(request: EnrollmentRequest, secret: string): boolean;
|
|
305
506
|
|
|
306
|
-
export { AGENT_PROTOCOL_HEADER, AGENT_PROTOCOL_V2, type AgentAction, type AgentResponse, AuditConfig, AuditManager, AuthorizationManager, DashboardManager, DatabaseAdapter, EnrollmentRequest, EventType, MDMConfig, MDMEvent, MDMInstance, MessageQueueManager, PluginStorageAdapter, ScheduleManager, TenantManager, WebhookConfig, WebhookEndpoint, type WebhookPayload, agentFail, agentOk, createAuditManager, createAuthorizationManager, createDashboardManager, createMDM, createMemoryPluginStorageAdapter, createMessageQueueManager, createPluginKey, createPluginStorageAdapter, createScheduleManager, createTenantManager, createWebhookManager, parsePluginKey, verifyEnrollmentSignature, verifyWebhookSignature, wantsAgentProtocolV2 };
|
|
507
|
+
export { AGENT_PROTOCOL_HEADER, AGENT_PROTOCOL_V2, type AgentAction, type AgentResponse, AuditConfig, AuditManager, AuthorizationManager, ChallengeInvalidError, DashboardManager, DatabaseAdapter, DeviceIdentityVerification, EnrollmentRequest, EventType, InvalidPublicKeyError, Logger, MDMConfig, MDMEvent, MDMInstance, MessageQueueManager, PluginStorageAdapter, PublicKeyMismatchError, ScheduleManager, TenantManager, WebhookConfig, WebhookEndpoint, type WebhookPayload, agentFail, agentOk, canonicalDeviceRequestMessage, canonicalEnrollmentMessage, createAuditManager, createAuthorizationManager, createConsoleLogger, createDashboardManager, createMDM, createMemoryPluginStorageAdapter, createMessageQueueManager, createPluginKey, createPluginStorageAdapter, createScheduleManager, createSilentLogger, createTenantManager, createWebhookManager, importPublicKeyFromSpki, parsePluginKey, verifyDeviceRequest, verifyEcdsaSignature, verifyEnrollmentSignature, verifyWebhookSignature, wantsAgentProtocolV2 };
|