@keeper-security/keeper-sdk-javascript 0.1.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/auth/ConsoleAuthUI.d.ts +10 -0
- package/dist/auth/ConsoleAuthUI.js +152 -0
- package/dist/auth/ConsoleAuthUI.js.map +1 -0
- package/dist/auth/ConsoleLogin.d.ts +8 -0
- package/dist/auth/ConsoleLogin.js +266 -0
- package/dist/auth/ConsoleLogin.js.map +1 -0
- package/dist/auth/SessionManager.d.ts +66 -0
- package/dist/auth/SessionManager.js +211 -0
- package/dist/auth/SessionManager.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/records/RecordOperations.d.ts +79 -0
- package/dist/records/RecordOperations.js +346 -0
- package/dist/records/RecordOperations.js.map +1 -0
- package/dist/records/RecordUtils.d.ts +36 -0
- package/dist/records/RecordUtils.js +224 -0
- package/dist/records/RecordUtils.js.map +1 -0
- package/dist/sharing/Sharing.d.ts +27 -0
- package/dist/sharing/Sharing.js +125 -0
- package/dist/sharing/Sharing.js.map +1 -0
- package/dist/src/auth/ConsoleAuthUI.d.ts +10 -0
- package/dist/src/auth/ConsoleAuthUI.js +161 -0
- package/dist/src/auth/ConsoleAuthUI.js.map +1 -0
- package/dist/src/auth/ConsoleLogin.d.ts +8 -0
- package/dist/src/auth/ConsoleLogin.js +311 -0
- package/dist/src/auth/ConsoleLogin.js.map +1 -0
- package/dist/src/auth/SessionManager.d.ts +67 -0
- package/dist/src/auth/SessionManager.js +212 -0
- package/dist/src/auth/SessionManager.js.map +1 -0
- package/dist/src/folders/FolderManager.d.ts +57 -0
- package/dist/src/folders/FolderManager.js +108 -0
- package/dist/src/folders/FolderManager.js.map +1 -0
- package/dist/src/folders/addFolder.d.ts +32 -0
- package/dist/src/folders/addFolder.js +207 -0
- package/dist/src/folders/addFolder.js.map +1 -0
- package/dist/src/folders/changeDirectory.d.ts +19 -0
- package/dist/src/folders/changeDirectory.js +171 -0
- package/dist/src/folders/changeDirectory.js.map +1 -0
- package/dist/src/folders/deleteFolder.d.ts +17 -0
- package/dist/src/folders/deleteFolder.js +237 -0
- package/dist/src/folders/deleteFolder.js.map +1 -0
- package/dist/src/folders/folderHelpers.d.ts +48 -0
- package/dist/src/folders/folderHelpers.js +100 -0
- package/dist/src/folders/folderHelpers.js.map +1 -0
- package/dist/src/folders/folderTree.d.ts +29 -0
- package/dist/src/folders/folderTree.js +250 -0
- package/dist/src/folders/folderTree.js.map +1 -0
- package/dist/src/folders/getFolder.d.ts +56 -0
- package/dist/src/folders/getFolder.js +143 -0
- package/dist/src/folders/getFolder.js.map +1 -0
- package/dist/src/folders/listFolder.d.ts +48 -0
- package/dist/src/folders/listFolder.js +276 -0
- package/dist/src/folders/listFolder.js.map +1 -0
- package/dist/src/folders/updateFolder.d.ts +31 -0
- package/dist/src/folders/updateFolder.js +137 -0
- package/dist/src/folders/updateFolder.js.map +1 -0
- package/dist/src/index.d.ts +49 -0
- package/dist/src/index.js +151 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/records/RecordOperations.d.ts +80 -0
- package/dist/src/records/RecordOperations.js +356 -0
- package/dist/src/records/RecordOperations.js.map +1 -0
- package/dist/src/records/RecordUtils.d.ts +37 -0
- package/dist/src/records/RecordUtils.js +263 -0
- package/dist/src/records/RecordUtils.js.map +1 -0
- package/dist/src/records/Totp.d.ts +14 -0
- package/dist/src/records/Totp.js +111 -0
- package/dist/src/records/Totp.js.map +1 -0
- package/dist/src/sharedFolders/SharedFolderManager.d.ts +20 -0
- package/dist/src/sharedFolders/SharedFolderManager.js +33 -0
- package/dist/src/sharedFolders/SharedFolderManager.js.map +1 -0
- package/dist/src/sharedFolders/listSharedFolders.d.ts +29 -0
- package/dist/src/sharedFolders/listSharedFolders.js +127 -0
- package/dist/src/sharedFolders/listSharedFolders.js.map +1 -0
- package/dist/src/sharedFolders/shareFolder.d.ts +36 -0
- package/dist/src/sharedFolders/shareFolder.js +352 -0
- package/dist/src/sharedFolders/shareFolder.js.map +1 -0
- package/dist/src/sharing/Sharing.d.ts +50 -0
- package/dist/src/sharing/Sharing.js +195 -0
- package/dist/src/sharing/Sharing.js.map +1 -0
- package/dist/src/storage/InMemoryStorage.d.ts +24 -0
- package/dist/src/storage/InMemoryStorage.js +139 -0
- package/dist/src/storage/InMemoryStorage.js.map +1 -0
- package/dist/src/teams/TeamManager.d.ts +17 -0
- package/dist/src/teams/TeamManager.js +38 -0
- package/dist/src/teams/TeamManager.js.map +1 -0
- package/dist/src/teams/enterpriseData.d.ts +106 -0
- package/dist/src/teams/enterpriseData.js +319 -0
- package/dist/src/teams/enterpriseData.js.map +1 -0
- package/dist/src/teams/listTeams.d.ts +42 -0
- package/dist/src/teams/listTeams.js +308 -0
- package/dist/src/teams/listTeams.js.map +1 -0
- package/dist/src/teams/viewTeam.d.ts +35 -0
- package/dist/src/teams/viewTeam.js +177 -0
- package/dist/src/teams/viewTeam.js.map +1 -0
- package/dist/src/utils/Logger.d.ts +28 -0
- package/dist/src/utils/Logger.js +62 -0
- package/dist/src/utils/Logger.js.map +1 -0
- package/dist/src/utils/constants.d.ts +50 -0
- package/dist/src/utils/constants.js +64 -0
- package/dist/src/utils/constants.js.map +1 -0
- package/dist/src/utils/errors.d.ts +10 -0
- package/dist/src/utils/errors.js +117 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/guards.d.ts +7 -0
- package/dist/src/utils/guards.js +29 -0
- package/dist/src/utils/guards.js.map +1 -0
- package/dist/src/utils/index.d.ts +7 -0
- package/dist/src/utils/index.js +39 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/patterns.d.ts +9 -0
- package/dist/src/utils/patterns.js +20 -0
- package/dist/src/utils/patterns.js.map +1 -0
- package/dist/src/utils/types.d.ts +12 -0
- package/dist/src/utils/types.js +3 -0
- package/dist/src/utils/types.js.map +1 -0
- package/dist/src/vault/KeeperVault.d.ts +116 -0
- package/dist/src/vault/KeeperVault.js +443 -0
- package/dist/src/vault/KeeperVault.js.map +1 -0
- package/dist/storage/InMemoryStorage.d.ts +24 -0
- package/dist/storage/InMemoryStorage.js +132 -0
- package/dist/storage/InMemoryStorage.js.map +1 -0
- package/dist/utils/Logger.d.ts +28 -0
- package/dist/utils/Logger.js +62 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/constants.d.ts +26 -0
- package/dist/utils/constants.js +37 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/errors.d.ts +10 -0
- package/dist/utils/errors.js +117 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +22 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/vault/KeeperVault.d.ts +72 -0
- package/dist/vault/KeeperVault.js +338 -0
- package/dist/vault/KeeperVault.js.map +1 -0
- package/package.json +32 -0
- package/src/auth/ConsoleAuthUI.ts +169 -0
- package/src/auth/ConsoleLogin.ts +351 -0
- package/src/auth/SessionManager.ts +293 -0
- package/src/folders/FolderManager.ts +174 -0
- package/src/folders/addFolder.ts +294 -0
- package/src/folders/changeDirectory.ts +217 -0
- package/src/folders/deleteFolder.ts +293 -0
- package/src/folders/folderHelpers.ts +99 -0
- package/src/folders/folderTree.ts +321 -0
- package/src/folders/getFolder.ts +234 -0
- package/src/folders/listFolder.ts +358 -0
- package/src/folders/updateFolder.ts +210 -0
- package/src/index.ts +242 -0
- package/src/records/RecordOperations.ts +549 -0
- package/src/records/RecordUtils.ts +282 -0
- package/src/records/Totp.ts +119 -0
- package/src/sharedFolders/SharedFolderManager.ts +57 -0
- package/src/sharedFolders/listSharedFolders.ts +173 -0
- package/src/sharedFolders/shareFolder.ts +457 -0
- package/src/sharing/Sharing.ts +282 -0
- package/src/storage/InMemoryStorage.ts +163 -0
- package/src/teams/TeamManager.ts +61 -0
- package/src/teams/enterpriseData.ts +453 -0
- package/src/teams/listTeams.ts +373 -0
- package/src/teams/viewTeam.ts +248 -0
- package/src/utils/Logger.ts +71 -0
- package/src/utils/constants.ts +63 -0
- package/src/utils/errors.ts +108 -0
- package/src/utils/guards.ts +24 -0
- package/src/utils/index.ts +22 -0
- package/src/utils/patterns.ts +20 -0
- package/src/utils/types.ts +11 -0
- package/src/vault/KeeperVault.ts +612 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Auth,
|
|
3
|
+
Records,
|
|
4
|
+
platform,
|
|
5
|
+
generateEncryptionKey,
|
|
6
|
+
generateUidBytes,
|
|
7
|
+
webSafe64FromBytes,
|
|
8
|
+
recordsAddMessage,
|
|
9
|
+
recordsUpdateMessage,
|
|
10
|
+
recordPreDeleteCommand,
|
|
11
|
+
recordDeleteCommand,
|
|
12
|
+
recordAddCommand,
|
|
13
|
+
moveCommand,
|
|
14
|
+
getRecordHistoryCommand,
|
|
15
|
+
normal64Bytes,
|
|
16
|
+
} from '@keeper-security/keeperapi'
|
|
17
|
+
import type {
|
|
18
|
+
DSharedFolder,
|
|
19
|
+
DSharedFolderFolder,
|
|
20
|
+
DSharedFolderRecord,
|
|
21
|
+
DUserFolder,
|
|
22
|
+
DRecord,
|
|
23
|
+
KeeperPreDeleteResponse,
|
|
24
|
+
MoveObject,
|
|
25
|
+
MoveRequest,
|
|
26
|
+
TransitionKeyObject,
|
|
27
|
+
RecordPreDeleteObject,
|
|
28
|
+
GetRecordHistoryResponse,
|
|
29
|
+
} from '@keeper-security/keeperapi'
|
|
30
|
+
import { extractErrorMessage, KeeperSdkError, logger } from '../utils'
|
|
31
|
+
import { RecordVersion } from './RecordUtils'
|
|
32
|
+
import { InMemoryStorage } from '../storage/InMemoryStorage'
|
|
33
|
+
import { DeleteResolution, FolderKind, VaultObjectKind } from '../folders/folderHelpers'
|
|
34
|
+
|
|
35
|
+
enum ResultCode {
|
|
36
|
+
Success = 'success',
|
|
37
|
+
OK = 'OK',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const MIN_RECORD_PAD_BYTES = 384
|
|
41
|
+
const PAD_BLOCK_SIZE = 16
|
|
42
|
+
|
|
43
|
+
function getPaddedJsonBytes(data: Record<string, any>): Uint8Array {
|
|
44
|
+
const json = JSON.stringify(data)
|
|
45
|
+
const paddedLength = Math.ceil(Math.max(MIN_RECORD_PAD_BYTES, json.length) / PAD_BLOCK_SIZE) * PAD_BLOCK_SIZE
|
|
46
|
+
const padded = json.padEnd(paddedLength, ' ')
|
|
47
|
+
return new TextEncoder().encode(padded)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type PasswordRecordData = {
|
|
51
|
+
title: string
|
|
52
|
+
login?: string
|
|
53
|
+
password?: string
|
|
54
|
+
url?: string
|
|
55
|
+
notes?: string
|
|
56
|
+
custom?: { name: string; value: string; type?: string }[]
|
|
57
|
+
totp?: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function buildLegacyPasswordExtra(data: PasswordRecordData, recordUid: string): Record<string, unknown> {
|
|
61
|
+
const totp = data.totp?.trim()
|
|
62
|
+
if (!totp) {
|
|
63
|
+
return {}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
files: [],
|
|
67
|
+
fields: [
|
|
68
|
+
{
|
|
69
|
+
id: recordUid,
|
|
70
|
+
field_type: 'totp',
|
|
71
|
+
field_title: 'One-Time Password',
|
|
72
|
+
data: totp,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type RecordFieldInput = {
|
|
79
|
+
type: string
|
|
80
|
+
value: any[]
|
|
81
|
+
label?: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type TypedRecordData = {
|
|
85
|
+
type: string
|
|
86
|
+
title: string
|
|
87
|
+
fields?: RecordFieldInput[]
|
|
88
|
+
custom?: RecordFieldInput[]
|
|
89
|
+
notes?: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type NewRecordInput =
|
|
93
|
+
| { version: 2; data: PasswordRecordData; folderUid?: string }
|
|
94
|
+
| { version: 3; data: TypedRecordData; folderUid?: string }
|
|
95
|
+
|
|
96
|
+
export type AddRecordResult = {
|
|
97
|
+
recordUid: string
|
|
98
|
+
success: boolean
|
|
99
|
+
status?: string
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type UpdateRecordResult = {
|
|
103
|
+
recordUid: string
|
|
104
|
+
success: boolean
|
|
105
|
+
status?: string
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type DeleteRecordResult = {
|
|
109
|
+
recordUid: string
|
|
110
|
+
success: boolean
|
|
111
|
+
message?: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function addRecord(
|
|
115
|
+
auth: Auth,
|
|
116
|
+
input: NewRecordInput
|
|
117
|
+
): Promise<AddRecordResult> {
|
|
118
|
+
if (!input.data.title || !input.data.title.trim()) {
|
|
119
|
+
throw new KeeperSdkError('Record title is required.', 'missing_record_title')
|
|
120
|
+
}
|
|
121
|
+
if (input.version === 3 && !input.data.type?.trim()) {
|
|
122
|
+
throw new KeeperSdkError('Record type is required for v3 records.', 'missing_record_type')
|
|
123
|
+
}
|
|
124
|
+
if (input.version === 2) {
|
|
125
|
+
return addPasswordRecord(auth, input.data, input.folderUid)
|
|
126
|
+
}
|
|
127
|
+
return addTypedRecord(auth, input.data, input.folderUid)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function addPasswordRecord(
|
|
131
|
+
auth: Auth,
|
|
132
|
+
data: PasswordRecordData,
|
|
133
|
+
folderUid?: string
|
|
134
|
+
): Promise<AddRecordResult> {
|
|
135
|
+
const recordUidBytes = generateUidBytes()
|
|
136
|
+
const recordKey = generateEncryptionKey()
|
|
137
|
+
const recordUid = webSafe64FromBytes(recordUidBytes)
|
|
138
|
+
|
|
139
|
+
const recordDataJson = JSON.stringify({
|
|
140
|
+
title: data.title || '',
|
|
141
|
+
secret1: data.login || '',
|
|
142
|
+
secret2: data.password || '',
|
|
143
|
+
link: data.url || '',
|
|
144
|
+
notes: data.notes || '',
|
|
145
|
+
custom: (data.custom || []).map((c) => ({
|
|
146
|
+
name: c.name,
|
|
147
|
+
value: c.value,
|
|
148
|
+
type: c.type || 'text',
|
|
149
|
+
})),
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const extraJson = JSON.stringify(buildLegacyPasswordExtra(data, recordUid))
|
|
153
|
+
|
|
154
|
+
const dataBytes = new TextEncoder().encode(recordDataJson)
|
|
155
|
+
const extraBytes = new TextEncoder().encode(extraJson)
|
|
156
|
+
|
|
157
|
+
const encryptedData = await platform.aesCbcEncrypt(dataBytes, recordKey, true)
|
|
158
|
+
const encryptedExtra = await platform.aesCbcEncrypt(extraBytes, recordKey, true)
|
|
159
|
+
const encryptedRecordKey = await platform.aesCbcEncrypt(recordKey, auth.dataKey!, true)
|
|
160
|
+
|
|
161
|
+
const cmd = recordAddCommand({
|
|
162
|
+
record_uid: recordUid,
|
|
163
|
+
record_key: webSafe64FromBytes(encryptedRecordKey),
|
|
164
|
+
record_type: 'password',
|
|
165
|
+
folder_type: FolderKind.UserFolder,
|
|
166
|
+
how_long_ago: 0,
|
|
167
|
+
folder_uid: folderUid || '',
|
|
168
|
+
folder_key: '',
|
|
169
|
+
data: webSafe64FromBytes(encryptedData),
|
|
170
|
+
extra: webSafe64FromBytes(encryptedExtra),
|
|
171
|
+
non_shared_data: '',
|
|
172
|
+
file_ids: [],
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
const response = await auth.executeRestCommand(cmd)
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
recordUid,
|
|
179
|
+
success: response.result_code === ResultCode.Success,
|
|
180
|
+
status: response.result_code,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function addTypedRecord(
|
|
185
|
+
auth: Auth,
|
|
186
|
+
data: TypedRecordData,
|
|
187
|
+
folderUid?: string
|
|
188
|
+
): Promise<AddRecordResult> {
|
|
189
|
+
const recordUidBytes = generateUidBytes()
|
|
190
|
+
const recordKey = generateEncryptionKey()
|
|
191
|
+
const recordUid = webSafe64FromBytes(recordUidBytes)
|
|
192
|
+
|
|
193
|
+
const recordPayload = {
|
|
194
|
+
type: data.type,
|
|
195
|
+
title: data.title,
|
|
196
|
+
fields: data.fields || [],
|
|
197
|
+
custom: data.custom || [],
|
|
198
|
+
notes: data.notes || '',
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const dataBytes = getPaddedJsonBytes(recordPayload)
|
|
202
|
+
const encryptedData = await platform.aesGcmEncrypt(dataBytes, recordKey)
|
|
203
|
+
const encryptedRecordKey = await platform.aesGcmEncrypt(recordKey, auth.dataKey!)
|
|
204
|
+
|
|
205
|
+
const recordAdd: Records.IRecordAdd = {
|
|
206
|
+
recordUid: recordUidBytes,
|
|
207
|
+
recordKey: encryptedRecordKey,
|
|
208
|
+
clientModifiedTime: Date.now(),
|
|
209
|
+
data: encryptedData,
|
|
210
|
+
folderType: Records.RecordFolderType.user_folder,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (folderUid) {
|
|
214
|
+
recordAdd.folderUid = normal64Bytes(folderUid)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const request: Records.IRecordsAddRequest = {
|
|
218
|
+
records: [recordAdd],
|
|
219
|
+
clientTime: Date.now(),
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const msg = recordsAddMessage(request)
|
|
223
|
+
const response = await auth.executeRest(msg)
|
|
224
|
+
|
|
225
|
+
const recordStatus = response.records?.[0]
|
|
226
|
+
const success =
|
|
227
|
+
recordStatus?.status === Records.RecordModifyResult.RS_SUCCESS ||
|
|
228
|
+
!recordStatus?.status
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
recordUid,
|
|
232
|
+
success,
|
|
233
|
+
status: recordStatus?.status != null
|
|
234
|
+
? Records.RecordModifyResult[recordStatus.status]
|
|
235
|
+
: ResultCode.OK,
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export async function updateRecord(
|
|
240
|
+
auth: Auth,
|
|
241
|
+
recordUid: string,
|
|
242
|
+
data: TypedRecordData,
|
|
243
|
+
revision: number,
|
|
244
|
+
recordKey: Uint8Array
|
|
245
|
+
): Promise<UpdateRecordResult> {
|
|
246
|
+
if (!data.title || !data.title.trim()) {
|
|
247
|
+
throw new KeeperSdkError('Record title is required.', 'missing_record_title')
|
|
248
|
+
}
|
|
249
|
+
if (!data.type?.trim()) {
|
|
250
|
+
throw new KeeperSdkError('Record type is required.', 'missing_record_type')
|
|
251
|
+
}
|
|
252
|
+
const recordUidBytes = normal64Bytes(recordUid)
|
|
253
|
+
|
|
254
|
+
const recordPayload = {
|
|
255
|
+
type: data.type,
|
|
256
|
+
title: data.title,
|
|
257
|
+
fields: data.fields || [],
|
|
258
|
+
custom: data.custom || [],
|
|
259
|
+
notes: data.notes || '',
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const dataBytes = getPaddedJsonBytes(recordPayload)
|
|
263
|
+
const encryptedData = await platform.aesGcmEncrypt(dataBytes, recordKey)
|
|
264
|
+
|
|
265
|
+
const recordUpdate: Records.IRecordUpdate = {
|
|
266
|
+
recordUid: recordUidBytes,
|
|
267
|
+
clientModifiedTime: Date.now(),
|
|
268
|
+
revision,
|
|
269
|
+
data: encryptedData,
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const request: Records.IRecordsUpdateRequest = {
|
|
273
|
+
records: [recordUpdate],
|
|
274
|
+
clientTime: Date.now(),
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const msg = recordsUpdateMessage(request)
|
|
278
|
+
const response = await auth.executeRest(msg)
|
|
279
|
+
|
|
280
|
+
const recordStatus = response.records?.[0]
|
|
281
|
+
const success =
|
|
282
|
+
recordStatus?.status === Records.RecordModifyResult.RS_SUCCESS ||
|
|
283
|
+
!recordStatus?.status
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
recordUid,
|
|
287
|
+
success,
|
|
288
|
+
status: recordStatus?.status != null
|
|
289
|
+
? Records.RecordModifyResult[recordStatus.status]
|
|
290
|
+
: ResultCode.OK,
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export async function deleteRecord(
|
|
295
|
+
auth: Auth,
|
|
296
|
+
recordUid: string
|
|
297
|
+
): Promise<DeleteRecordResult> {
|
|
298
|
+
const preDeleteRequest = {
|
|
299
|
+
objects: [
|
|
300
|
+
{
|
|
301
|
+
object_uid: recordUid,
|
|
302
|
+
object_type: VaultObjectKind.Record,
|
|
303
|
+
from_uid: '',
|
|
304
|
+
from_type: FolderKind.UserFolder,
|
|
305
|
+
delete_resolution: DeleteResolution.Unlink,
|
|
306
|
+
} as RecordPreDeleteObject,
|
|
307
|
+
],
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let preDeleteResponse: KeeperPreDeleteResponse
|
|
311
|
+
try {
|
|
312
|
+
const preDeleteCmd = recordPreDeleteCommand(preDeleteRequest)
|
|
313
|
+
preDeleteResponse = await auth.executeRestCommand(preDeleteCmd)
|
|
314
|
+
} catch (err) {
|
|
315
|
+
return { recordUid, success: false, message: extractErrorMessage(err) }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const token = preDeleteResponse?.pre_delete_response?.pre_delete_token
|
|
319
|
+
if (!token) {
|
|
320
|
+
return {
|
|
321
|
+
recordUid,
|
|
322
|
+
success: false,
|
|
323
|
+
message: preDeleteResponse?.message || preDeleteResponse?.result_code || 'pre_delete failed: no token',
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
const deleteCmd = recordDeleteCommand({ pre_delete_token: token })
|
|
329
|
+
await auth.executeRestCommand(deleteCmd)
|
|
330
|
+
} catch (err) {
|
|
331
|
+
return { recordUid, success: false, message: extractErrorMessage(err) }
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return { recordUid, success: true, message: ResultCode.Success }
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export type HistoryEntry = {
|
|
338
|
+
revision: number
|
|
339
|
+
version: number
|
|
340
|
+
userName: string
|
|
341
|
+
clientModifiedTime: number
|
|
342
|
+
data: Record<string, any> | null
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export type RecordHistoryResult = {
|
|
346
|
+
recordUid: string
|
|
347
|
+
history: HistoryEntry[]
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export async function getRecordHistory(
|
|
351
|
+
auth: Auth,
|
|
352
|
+
recordUid: string,
|
|
353
|
+
recordKey: Uint8Array
|
|
354
|
+
): Promise<RecordHistoryResult> {
|
|
355
|
+
const cmd = getRecordHistoryCommand({
|
|
356
|
+
record_uid: recordUid,
|
|
357
|
+
client_time: Date.now(),
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
let response: GetRecordHistoryResponse
|
|
361
|
+
try {
|
|
362
|
+
response = await auth.executeRestCommand(cmd)
|
|
363
|
+
} catch (err) {
|
|
364
|
+
throw KeeperSdkError.from(err)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const rawHistory = response.history || []
|
|
368
|
+
const history: HistoryEntry[] = []
|
|
369
|
+
|
|
370
|
+
for (const entry of rawHistory) {
|
|
371
|
+
let decryptedData: Record<string, any> | null = null
|
|
372
|
+
|
|
373
|
+
if (entry.data) {
|
|
374
|
+
try {
|
|
375
|
+
const dataBytes = normal64Bytes(entry.data)
|
|
376
|
+
const version = entry.version || 0
|
|
377
|
+
let decrypted: Uint8Array
|
|
378
|
+
|
|
379
|
+
if (version <= RecordVersion.Legacy) {
|
|
380
|
+
decrypted = await platform.aesCbcDecrypt(dataBytes, recordKey, true)
|
|
381
|
+
} else {
|
|
382
|
+
decrypted = await platform.aesGcmDecrypt(dataBytes, recordKey)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
decryptedData = JSON.parse(platform.bytesToString(decrypted))
|
|
386
|
+
} catch (err) {
|
|
387
|
+
logger.debug(`Failed to decrypt history revision ${entry.revision}:`, extractErrorMessage(err))
|
|
388
|
+
decryptedData = null
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
history.push({
|
|
393
|
+
revision: entry.revision,
|
|
394
|
+
version: entry.version,
|
|
395
|
+
userName: entry.user_name || '',
|
|
396
|
+
clientModifiedTime: entry.client_modified_time || 0,
|
|
397
|
+
data: decryptedData,
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return { recordUid, history }
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export type MoveRecordInput = {
|
|
405
|
+
recordUid: string
|
|
406
|
+
dstFolderUid: string
|
|
407
|
+
srcFolderUid?: string
|
|
408
|
+
link?: boolean
|
|
409
|
+
canEdit?: boolean
|
|
410
|
+
canShare?: boolean
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export type MoveRecordResult = {
|
|
414
|
+
recordUid: string
|
|
415
|
+
success: boolean
|
|
416
|
+
message: string
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
type FolderInfo = {
|
|
420
|
+
uid: string
|
|
421
|
+
folderType: FolderKind
|
|
422
|
+
scopeUid: string
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function resolveFolder(uid: string, storage: InMemoryStorage): FolderInfo {
|
|
426
|
+
if (!uid) {
|
|
427
|
+
return { uid: '', folderType: FolderKind.UserFolder, scopeUid: '' }
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (storage.getByUid<DUserFolder>(FolderKind.UserFolder, uid)) {
|
|
431
|
+
return { uid, folderType: FolderKind.UserFolder, scopeUid: '' }
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, uid)) {
|
|
435
|
+
return { uid, folderType: FolderKind.SharedFolder, scopeUid: uid }
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const sfFolder = storage.getByUid<DSharedFolderFolder>(FolderKind.SharedFolderFolder, uid)
|
|
439
|
+
if (sfFolder) {
|
|
440
|
+
return { uid, folderType: FolderKind.SharedFolderFolder, scopeUid: sfFolder.sharedFolderUid }
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return { uid, folderType: FolderKind.UserFolder, scopeUid: '' }
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function findRecordSourceFolder(recordUid: string, storage: InMemoryStorage): Promise<string> {
|
|
447
|
+
const folderKinds = [
|
|
448
|
+
FolderKind.UserFolder,
|
|
449
|
+
FolderKind.SharedFolder,
|
|
450
|
+
FolderKind.SharedFolderFolder,
|
|
451
|
+
] as const
|
|
452
|
+
|
|
453
|
+
for (const kind of folderKinds) {
|
|
454
|
+
for (const folder of storage.getAll<DUserFolder | DSharedFolder | DSharedFolderFolder>(kind)) {
|
|
455
|
+
const dependencies = (await storage.getDependencies(folder.uid)) || []
|
|
456
|
+
if (
|
|
457
|
+
dependencies.some(
|
|
458
|
+
(dependency) => dependency.kind === VaultObjectKind.Record && dependency.uid === recordUid
|
|
459
|
+
)
|
|
460
|
+
) {
|
|
461
|
+
return folder.uid
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const sharedFolderRecord = storage
|
|
467
|
+
.getAll<DSharedFolderRecord>(VaultObjectKind.SharedFolderRecord)
|
|
468
|
+
.find((candidate) => candidate.recordUid === recordUid)
|
|
469
|
+
return sharedFolderRecord ? sharedFolderRecord.sharedFolderUid : ''
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export async function moveRecord(
|
|
473
|
+
auth: Auth,
|
|
474
|
+
storage: InMemoryStorage,
|
|
475
|
+
input: MoveRecordInput
|
|
476
|
+
): Promise<MoveRecordResult> {
|
|
477
|
+
const {
|
|
478
|
+
recordUid,
|
|
479
|
+
dstFolderUid,
|
|
480
|
+
link = false,
|
|
481
|
+
canEdit,
|
|
482
|
+
canShare,
|
|
483
|
+
} = input
|
|
484
|
+
|
|
485
|
+
const dst = resolveFolder(dstFolderUid, storage)
|
|
486
|
+
|
|
487
|
+
const srcUid =
|
|
488
|
+
input.srcFolderUid !== undefined ? input.srcFolderUid : await findRecordSourceFolder(recordUid, storage)
|
|
489
|
+
const src = resolveFolder(srcUid, storage)
|
|
490
|
+
|
|
491
|
+
const moveObj: MoveObject = {
|
|
492
|
+
uid: recordUid,
|
|
493
|
+
type: VaultObjectKind.Record,
|
|
494
|
+
cascade: false,
|
|
495
|
+
from_type: src.folderType,
|
|
496
|
+
from_uid: src.uid || undefined,
|
|
497
|
+
can_edit: canEdit,
|
|
498
|
+
can_reshare: canShare,
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const transitionKeys: TransitionKeyObject[] = []
|
|
502
|
+
|
|
503
|
+
if (src.scopeUid !== dst.scopeUid) {
|
|
504
|
+
const recordKey = await storage.getKeyBytes(recordUid)
|
|
505
|
+
if (!recordKey) {
|
|
506
|
+
return { recordUid, success: false, message: 'Record key not found' }
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
let dstKey: Uint8Array | undefined
|
|
510
|
+
if (dst.scopeUid) {
|
|
511
|
+
dstKey = await storage.getKeyBytes(dst.scopeUid)
|
|
512
|
+
} else {
|
|
513
|
+
dstKey = auth.dataKey
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (!dstKey) {
|
|
517
|
+
return { recordUid, success: false, message: 'Destination folder key not found' }
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const record = storage.getByUid<DRecord>(VaultObjectKind.Record, recordUid)
|
|
521
|
+
const version = record?.version || RecordVersion.Typed
|
|
522
|
+
|
|
523
|
+
let encryptedKey: Uint8Array
|
|
524
|
+
if (version >= RecordVersion.Typed) {
|
|
525
|
+
encryptedKey = await platform.aesGcmEncrypt(recordKey, dstKey)
|
|
526
|
+
} else {
|
|
527
|
+
encryptedKey = await platform.aesCbcEncrypt(recordKey, dstKey, true)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
transitionKeys.push({ uid: recordUid, key: webSafe64FromBytes(encryptedKey) })
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const request: MoveRequest = {
|
|
534
|
+
to_type: dst.folderType,
|
|
535
|
+
to_uid: dst.uid || undefined,
|
|
536
|
+
link,
|
|
537
|
+
move: [moveObj],
|
|
538
|
+
transition_keys: transitionKeys,
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
const cmd = moveCommand(request)
|
|
543
|
+
await auth.executeRestCommand(cmd)
|
|
544
|
+
} catch (err) {
|
|
545
|
+
return { recordUid, success: false, message: extractErrorMessage(err) }
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return { recordUid, success: true, message: 'Record moved successfully' }
|
|
549
|
+
}
|