@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,358 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DRecord,
|
|
3
|
+
DRecordMetadata,
|
|
4
|
+
DSharedFolder,
|
|
5
|
+
DSharedFolderFolder,
|
|
6
|
+
DUserFolder,
|
|
7
|
+
VaultStorageData,
|
|
8
|
+
VaultStorageKind,
|
|
9
|
+
} from '@keeper-security/keeperapi'
|
|
10
|
+
import { InMemoryStorage } from '../storage/InMemoryStorage'
|
|
11
|
+
import { KeeperSdkError } from '../utils'
|
|
12
|
+
import { getRecordTitle, getRecordType } from '../records/RecordUtils'
|
|
13
|
+
import {
|
|
14
|
+
FolderKind,
|
|
15
|
+
VaultObjectKind,
|
|
16
|
+
getUserFolderParentMap,
|
|
17
|
+
globToRegex,
|
|
18
|
+
sharedFolderFolderName,
|
|
19
|
+
sharedFolderName,
|
|
20
|
+
userFolderName,
|
|
21
|
+
} from './folderHelpers'
|
|
22
|
+
|
|
23
|
+
export type ListFolderOptions = {
|
|
24
|
+
folderUid?: string | null
|
|
25
|
+
pattern?: string | null
|
|
26
|
+
showFolders?: boolean
|
|
27
|
+
showRecords?: boolean
|
|
28
|
+
detail?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ListFolderFolderSimple = {
|
|
32
|
+
uid: string
|
|
33
|
+
name: string
|
|
34
|
+
folderKind: FolderKind
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type ListFolderRecordSimple = {
|
|
38
|
+
uid: string
|
|
39
|
+
name: string
|
|
40
|
+
type: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type ListFolderFolderDetail = ListFolderFolderSimple & {
|
|
44
|
+
flags: string
|
|
45
|
+
recordCount: number
|
|
46
|
+
subfolderCount: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type ListFolderRecordDetail = ListFolderRecordSimple & {
|
|
50
|
+
flags: string
|
|
51
|
+
version: number
|
|
52
|
+
isOwner: boolean
|
|
53
|
+
hasAttachments: boolean
|
|
54
|
+
isShared: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type ListFolderResult =
|
|
58
|
+
| {
|
|
59
|
+
detail: true
|
|
60
|
+
folders: ListFolderFolderDetail[]
|
|
61
|
+
records: ListFolderRecordDetail[]
|
|
62
|
+
}
|
|
63
|
+
| {
|
|
64
|
+
detail: false
|
|
65
|
+
folders: ListFolderFolderSimple[]
|
|
66
|
+
records: ListFolderRecordSimple[]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function recordHasAttachments(record: DRecord): boolean {
|
|
70
|
+
const fileIds = record.udata?.file_ids
|
|
71
|
+
if (Array.isArray(fileIds) && fileIds.length > 0) return true
|
|
72
|
+
const files = (record.extra as { files?: unknown[] } | undefined)?.files
|
|
73
|
+
return Array.isArray(files) && files.length > 0
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildFolderFlags(folderKind: ListFolderFolderSimple['folderKind']): string {
|
|
77
|
+
const isShared = folderKind !== FolderKind.UserFolder
|
|
78
|
+
return `f--${isShared ? 'S' : '-'}`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function buildRecordFlags(record: DRecord, metadata: DRecordMetadata | undefined): string {
|
|
82
|
+
const isOwner = metadata?.owner === true
|
|
83
|
+
const hasAttachments = recordHasAttachments(record)
|
|
84
|
+
const isShared = !!record.shared
|
|
85
|
+
return `r${isOwner ? 'O' : '-'}${hasAttachments ? 'A' : '-'}${isShared ? 'S' : '-'}`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function listRootUserFolders(storage: InMemoryStorage): Promise<DUserFolder[]> {
|
|
89
|
+
const userFolders = storage.getAll<DUserFolder>(FolderKind.UserFolder)
|
|
90
|
+
const childToParent = await getUserFolderParentMap(storage)
|
|
91
|
+
return userFolders.filter((userFolder) => !childToParent.has(userFolder.uid))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function buildFolderParentMap(
|
|
95
|
+
storage: InMemoryStorage
|
|
96
|
+
): Promise<Map<string, { uid: string; kind: VaultStorageKind }>> {
|
|
97
|
+
const childToParent = new Map<string, { uid: string; kind: VaultStorageKind }>()
|
|
98
|
+
const parentKinds: VaultStorageKind[] = [
|
|
99
|
+
FolderKind.UserFolder,
|
|
100
|
+
FolderKind.SharedFolder,
|
|
101
|
+
FolderKind.SharedFolderFolder,
|
|
102
|
+
]
|
|
103
|
+
for (const kind of parentKinds) {
|
|
104
|
+
for (const candidate of storage.getAll<{ uid: string } & VaultStorageData>(kind)) {
|
|
105
|
+
const candidateUid = (candidate as { uid: string }).uid
|
|
106
|
+
if (!candidateUid) continue
|
|
107
|
+
const dependencies = (await storage.getDependencies(candidateUid)) || []
|
|
108
|
+
for (const dependency of dependencies) {
|
|
109
|
+
if (
|
|
110
|
+
dependency.kind === FolderKind.UserFolder ||
|
|
111
|
+
dependency.kind === FolderKind.SharedFolder ||
|
|
112
|
+
dependency.kind === FolderKind.SharedFolderFolder
|
|
113
|
+
) {
|
|
114
|
+
if (!childToParent.has(dependency.uid)) {
|
|
115
|
+
childToParent.set(dependency.uid, { uid: candidateUid, kind })
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return childToParent
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function listVaultRootFolders(storage: InMemoryStorage): Promise<{
|
|
125
|
+
rows: ListFolderFolderSimple[]
|
|
126
|
+
promotedRootSharedUids: Set<string>
|
|
127
|
+
}> {
|
|
128
|
+
const rows: ListFolderFolderSimple[] = []
|
|
129
|
+
const seen = new Set<string>()
|
|
130
|
+
const promotedRootSharedUids = new Set<string>()
|
|
131
|
+
|
|
132
|
+
for (const userFolder of await listRootUserFolders(storage)) {
|
|
133
|
+
if (seen.has(userFolder.uid)) continue
|
|
134
|
+
seen.add(userFolder.uid)
|
|
135
|
+
rows.push({
|
|
136
|
+
uid: userFolder.uid,
|
|
137
|
+
name: userFolderName(userFolder),
|
|
138
|
+
folderKind: FolderKind.UserFolder,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const rootDependencies = (await storage.getDependencies('')) || []
|
|
143
|
+
for (const dependency of rootDependencies) {
|
|
144
|
+
if (dependency.kind === FolderKind.SharedFolder) {
|
|
145
|
+
const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, dependency.uid)
|
|
146
|
+
if (!sharedFolder || seen.has(sharedFolder.uid)) continue
|
|
147
|
+
seen.add(sharedFolder.uid)
|
|
148
|
+
rows.push({
|
|
149
|
+
uid: sharedFolder.uid,
|
|
150
|
+
name: sharedFolderName(sharedFolder),
|
|
151
|
+
folderKind: FolderKind.SharedFolder,
|
|
152
|
+
})
|
|
153
|
+
} else if (dependency.kind === FolderKind.SharedFolderFolder) {
|
|
154
|
+
const sharedFolderFolder = storage.getByUid<DSharedFolderFolder>(
|
|
155
|
+
FolderKind.SharedFolderFolder,
|
|
156
|
+
dependency.uid
|
|
157
|
+
)
|
|
158
|
+
if (!sharedFolderFolder || seen.has(sharedFolderFolder.uid)) continue
|
|
159
|
+
seen.add(sharedFolderFolder.uid)
|
|
160
|
+
rows.push({
|
|
161
|
+
uid: sharedFolderFolder.uid,
|
|
162
|
+
name: sharedFolderFolderName(sharedFolderFolder),
|
|
163
|
+
folderKind: FolderKind.SharedFolderFolder,
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const parentMap = await buildFolderParentMap(storage)
|
|
169
|
+
for (const sharedFolder of storage.getAll<DSharedFolder>(FolderKind.SharedFolder)) {
|
|
170
|
+
if (seen.has(sharedFolder.uid)) continue
|
|
171
|
+
const parent = parentMap.get(sharedFolder.uid)
|
|
172
|
+
if (parent && (parent.kind === FolderKind.SharedFolder || parent.kind === FolderKind.SharedFolderFolder)) {
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
seen.add(sharedFolder.uid)
|
|
176
|
+
promotedRootSharedUids.add(sharedFolder.uid)
|
|
177
|
+
rows.push({
|
|
178
|
+
uid: sharedFolder.uid,
|
|
179
|
+
name: sharedFolderName(sharedFolder),
|
|
180
|
+
folderKind: FolderKind.SharedFolder,
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
rows.sort((rowA, rowB) => rowA.name.localeCompare(rowB.name, undefined, { sensitivity: 'base' }))
|
|
185
|
+
|
|
186
|
+
return { rows, promotedRootSharedUids }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function resolveFolderContainer(storage: InMemoryStorage, folderUid: string): { kind: VaultStorageKind; uid: string } {
|
|
190
|
+
if (storage.getByUid<DUserFolder>(FolderKind.UserFolder, folderUid)) {
|
|
191
|
+
return { kind: FolderKind.UserFolder, uid: folderUid }
|
|
192
|
+
}
|
|
193
|
+
if (storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, folderUid)) {
|
|
194
|
+
return { kind: FolderKind.SharedFolder, uid: folderUid }
|
|
195
|
+
}
|
|
196
|
+
if (storage.getByUid<DSharedFolderFolder>(FolderKind.SharedFolderFolder, folderUid)) {
|
|
197
|
+
return { kind: FolderKind.SharedFolderFolder, uid: folderUid }
|
|
198
|
+
}
|
|
199
|
+
throw new KeeperSdkError(`Folder "${folderUid}" not found`, 'folder_not_found')
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getRecordMetadata(storage: InMemoryStorage, recordUid: string): DRecordMetadata | undefined {
|
|
203
|
+
return storage.getByUid<DRecordMetadata>(VaultObjectKind.Metadata, recordUid)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function findFolderUidByNameOrUid(storage: InMemoryStorage, nameOrUid: string): string | undefined {
|
|
207
|
+
const trimmedNameOrUid = nameOrUid.trim()
|
|
208
|
+
if (!trimmedNameOrUid) return undefined
|
|
209
|
+
|
|
210
|
+
if (
|
|
211
|
+
storage.getByUid<DUserFolder>(FolderKind.UserFolder, trimmedNameOrUid) ||
|
|
212
|
+
storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, trimmedNameOrUid) ||
|
|
213
|
+
storage.getByUid<DSharedFolderFolder>(FolderKind.SharedFolderFolder, trimmedNameOrUid)
|
|
214
|
+
) {
|
|
215
|
+
return trimmedNameOrUid
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const lowerNameOrUid = trimmedNameOrUid.toLowerCase()
|
|
219
|
+
for (const userFolder of storage.getAll<DUserFolder>(FolderKind.UserFolder)) {
|
|
220
|
+
if (userFolderName(userFolder).toLowerCase() === lowerNameOrUid) return userFolder.uid
|
|
221
|
+
}
|
|
222
|
+
for (const sharedFolder of storage.getAll<DSharedFolder>(FolderKind.SharedFolder)) {
|
|
223
|
+
if (sharedFolderName(sharedFolder).toLowerCase() === lowerNameOrUid) return sharedFolder.uid
|
|
224
|
+
}
|
|
225
|
+
for (const sharedFolderFolder of storage.getAll<DSharedFolderFolder>(FolderKind.SharedFolderFolder)) {
|
|
226
|
+
if (sharedFolderFolderName(sharedFolderFolder).toLowerCase() === lowerNameOrUid) return sharedFolderFolder.uid
|
|
227
|
+
}
|
|
228
|
+
return undefined
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function countFolderChildren(
|
|
232
|
+
storage: InMemoryStorage,
|
|
233
|
+
uid: string
|
|
234
|
+
): Promise<{ records: number; subfolders: number }> {
|
|
235
|
+
const dependencies = (await storage.getDependencies(uid)) || []
|
|
236
|
+
let records = 0
|
|
237
|
+
let subfolders = 0
|
|
238
|
+
for (const dependency of dependencies) {
|
|
239
|
+
if (dependency.kind === VaultObjectKind.Record) records++
|
|
240
|
+
else if (
|
|
241
|
+
dependency.kind === FolderKind.UserFolder ||
|
|
242
|
+
dependency.kind === FolderKind.SharedFolder ||
|
|
243
|
+
dependency.kind === FolderKind.SharedFolderFolder
|
|
244
|
+
) {
|
|
245
|
+
subfolders++
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return { records, subfolders }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export async function listFolder(storage: InMemoryStorage, options: ListFolderOptions = {}): Promise<ListFolderResult> {
|
|
252
|
+
const showFolders = options.showFolders !== false
|
|
253
|
+
const showRecords = options.showRecords !== false
|
|
254
|
+
const detail = options.detail === true
|
|
255
|
+
const patternRaw = options.pattern?.trim() || null
|
|
256
|
+
const regex = patternRaw ? globToRegex(patternRaw) : null
|
|
257
|
+
|
|
258
|
+
const folderUidOpt = options.folderUid
|
|
259
|
+
let parentKey: string | null = null
|
|
260
|
+
|
|
261
|
+
if (folderUidOpt !== undefined && folderUidOpt !== null && folderUidOpt !== '') {
|
|
262
|
+
parentKey = resolveFolderContainer(storage, folderUidOpt).uid
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const dependencies =
|
|
266
|
+
parentKey === null
|
|
267
|
+
? (await storage.getDependencies('')) || []
|
|
268
|
+
: (await storage.getDependencies(parentKey)) || []
|
|
269
|
+
|
|
270
|
+
const folderRows: ListFolderFolderSimple[] = []
|
|
271
|
+
const recordRows: ListFolderRecordSimple[] = []
|
|
272
|
+
|
|
273
|
+
const matches = (name: string, uid: string): boolean => {
|
|
274
|
+
if (!regex) return true
|
|
275
|
+
return regex.test(name) || regex.test(uid)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (showFolders && parentKey === null) {
|
|
279
|
+
const { rows } = await listVaultRootFolders(storage)
|
|
280
|
+
for (const row of rows) {
|
|
281
|
+
if (!matches(row.name, row.uid)) continue
|
|
282
|
+
folderRows.push(row)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
for (const dependency of dependencies) {
|
|
287
|
+
if (dependency.kind === FolderKind.UserFolder && showFolders && parentKey !== null) {
|
|
288
|
+
const userFolder = storage.getByUid<DUserFolder>(FolderKind.UserFolder, dependency.uid)
|
|
289
|
+
if (!userFolder) continue
|
|
290
|
+
const name = userFolderName(userFolder)
|
|
291
|
+
if (!matches(name, userFolder.uid)) continue
|
|
292
|
+
folderRows.push({ uid: userFolder.uid, name, folderKind: FolderKind.UserFolder })
|
|
293
|
+
} else if (dependency.kind === FolderKind.SharedFolder && showFolders && parentKey !== null) {
|
|
294
|
+
const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, dependency.uid)
|
|
295
|
+
if (!sharedFolder) continue
|
|
296
|
+
const name = sharedFolderName(sharedFolder)
|
|
297
|
+
if (!matches(name, sharedFolder.uid)) continue
|
|
298
|
+
folderRows.push({ uid: sharedFolder.uid, name, folderKind: FolderKind.SharedFolder })
|
|
299
|
+
} else if (dependency.kind === FolderKind.SharedFolderFolder && showFolders && parentKey !== null) {
|
|
300
|
+
const sharedFolderFolder = storage.getByUid<DSharedFolderFolder>(
|
|
301
|
+
FolderKind.SharedFolderFolder,
|
|
302
|
+
dependency.uid
|
|
303
|
+
)
|
|
304
|
+
if (!sharedFolderFolder) continue
|
|
305
|
+
const name = sharedFolderFolderName(sharedFolderFolder)
|
|
306
|
+
if (!matches(name, sharedFolderFolder.uid)) continue
|
|
307
|
+
folderRows.push({
|
|
308
|
+
uid: sharedFolderFolder.uid,
|
|
309
|
+
name,
|
|
310
|
+
folderKind: FolderKind.SharedFolderFolder,
|
|
311
|
+
})
|
|
312
|
+
} else if (dependency.kind === VaultObjectKind.Record && showRecords) {
|
|
313
|
+
const record = storage.getByUid<DRecord>(VaultObjectKind.Record, dependency.uid)
|
|
314
|
+
if (!record || (record.version !== 2 && record.version !== 3)) continue
|
|
315
|
+
const title = getRecordTitle(record)
|
|
316
|
+
if (!matches(title, record.uid)) continue
|
|
317
|
+
recordRows.push({
|
|
318
|
+
uid: record.uid,
|
|
319
|
+
name: title,
|
|
320
|
+
type: getRecordType(record),
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
folderRows.sort((rowA, rowB) => rowA.name.localeCompare(rowB.name, undefined, { sensitivity: 'base' }))
|
|
326
|
+
recordRows.sort((rowA, rowB) => rowA.name.localeCompare(rowB.name, undefined, { sensitivity: 'base' }))
|
|
327
|
+
|
|
328
|
+
if (!detail) {
|
|
329
|
+
return { detail: false, folders: folderRows, records: recordRows }
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const folderDetails: ListFolderFolderDetail[] = await Promise.all(
|
|
333
|
+
folderRows.map(async (row) => {
|
|
334
|
+
const counts = await countFolderChildren(storage, row.uid)
|
|
335
|
+
return {
|
|
336
|
+
...row,
|
|
337
|
+
flags: buildFolderFlags(row.folderKind),
|
|
338
|
+
recordCount: counts.records,
|
|
339
|
+
subfolderCount: counts.subfolders,
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
const recordDetails: ListFolderRecordDetail[] = recordRows.map((row) => {
|
|
345
|
+
const record = storage.getByUid<DRecord>(VaultObjectKind.Record, row.uid)!
|
|
346
|
+
const metadata = getRecordMetadata(storage, row.uid)
|
|
347
|
+
return {
|
|
348
|
+
...row,
|
|
349
|
+
flags: buildRecordFlags(record, metadata),
|
|
350
|
+
version: record.version,
|
|
351
|
+
isOwner: metadata?.owner === true,
|
|
352
|
+
hasAttachments: recordHasAttachments(record),
|
|
353
|
+
isShared: !!record.shared,
|
|
354
|
+
}
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
return { detail: true, folders: folderDetails, records: recordDetails }
|
|
358
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { Auth, FolderUpdateRequest } from '@keeper-security/keeperapi'
|
|
2
|
+
import {
|
|
3
|
+
encryptForStorage,
|
|
4
|
+
encryptObjectForStorage,
|
|
5
|
+
folderUpdateCommand,
|
|
6
|
+
platform,
|
|
7
|
+
} from '@keeper-security/keeperapi'
|
|
8
|
+
import type { DSharedFolder, DSharedFolderFolder, DUserFolder } from '@keeper-security/keeperapi'
|
|
9
|
+
import { InMemoryStorage } from '../storage/InMemoryStorage'
|
|
10
|
+
import { anyIsBoolean, isBoolean, isObject, KeeperSdkError, extractErrorMessage } from '../utils'
|
|
11
|
+
import { resolveSingleFolder, type VaultFolderSession } from './changeDirectory'
|
|
12
|
+
import { FolderKind, FolderResultStatus } from './folderHelpers'
|
|
13
|
+
|
|
14
|
+
export type UpdateFolderInput = {
|
|
15
|
+
folderUid: string
|
|
16
|
+
folderName?: string | null
|
|
17
|
+
manageUsers?: boolean | null
|
|
18
|
+
manageRecords?: boolean | null
|
|
19
|
+
canShare?: boolean | null
|
|
20
|
+
canEdit?: boolean | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type UpdateFolderResult = {
|
|
24
|
+
folderUid: string
|
|
25
|
+
success: boolean
|
|
26
|
+
message?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type RenameFolderResult = {
|
|
30
|
+
folderUid: string
|
|
31
|
+
oldName: string
|
|
32
|
+
newName: string
|
|
33
|
+
success: boolean
|
|
34
|
+
message?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type ResolvedFolder =
|
|
38
|
+
| { kind: FolderKind.UserFolder; folder: DUserFolder }
|
|
39
|
+
| { kind: FolderKind.SharedFolder; folder: DSharedFolder }
|
|
40
|
+
| { kind: FolderKind.SharedFolderFolder; folder: DSharedFolderFolder }
|
|
41
|
+
|
|
42
|
+
function resolveFolderEntity(storage: InMemoryStorage, folderUid: string): ResolvedFolder | undefined {
|
|
43
|
+
const userFolder = storage.getByUid<DUserFolder>(FolderKind.UserFolder, folderUid)
|
|
44
|
+
if (userFolder) return { kind: FolderKind.UserFolder, folder: userFolder }
|
|
45
|
+
const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, folderUid)
|
|
46
|
+
if (sharedFolder) return { kind: FolderKind.SharedFolder, folder: sharedFolder }
|
|
47
|
+
const sharedFolderFolder = storage.getByUid<DSharedFolderFolder>(FolderKind.SharedFolderFolder, folderUid)
|
|
48
|
+
if (sharedFolderFolder) return { kind: FolderKind.SharedFolderFolder, folder: sharedFolderFolder }
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function mergeFolderData(existing: unknown, folderName: string | null | undefined): Record<string, unknown> {
|
|
53
|
+
const base = isObject(existing) ? { ...existing } : {}
|
|
54
|
+
const trimmed = folderName?.trim()
|
|
55
|
+
if (trimmed) {
|
|
56
|
+
base.title = trimmed
|
|
57
|
+
base.name = trimmed
|
|
58
|
+
}
|
|
59
|
+
return base
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function updateFolder(
|
|
63
|
+
auth: Auth,
|
|
64
|
+
storage: InMemoryStorage,
|
|
65
|
+
input: UpdateFolderInput
|
|
66
|
+
): Promise<UpdateFolderResult> {
|
|
67
|
+
const folderUid = input.folderUid
|
|
68
|
+
const resolved = resolveFolderEntity(storage, folderUid)
|
|
69
|
+
if (!resolved) {
|
|
70
|
+
throw new KeeperSdkError(`Folder "${folderUid}" does not exist.`, 'folder_not_found')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const folderKey = await storage.getKeyBytes(folderUid)
|
|
74
|
+
if (!folderKey) {
|
|
75
|
+
throw new KeeperSdkError(
|
|
76
|
+
'Folder encryption key not available. Sync the vault and try again.',
|
|
77
|
+
'folder_key_missing'
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const trimmedName = input.folderName?.trim() || ''
|
|
82
|
+
|
|
83
|
+
const hasPermissionUpdate = anyIsBoolean(
|
|
84
|
+
input.manageUsers,
|
|
85
|
+
input.manageRecords,
|
|
86
|
+
input.canShare,
|
|
87
|
+
input.canEdit
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if (resolved.kind === FolderKind.UserFolder || resolved.kind === FolderKind.SharedFolderFolder) {
|
|
91
|
+
if (!trimmedName) {
|
|
92
|
+
throw new KeeperSdkError('Folder name is required.', 'folder_name_required')
|
|
93
|
+
}
|
|
94
|
+
} else if (resolved.kind === FolderKind.SharedFolder) {
|
|
95
|
+
if (!trimmedName && !hasPermissionUpdate) {
|
|
96
|
+
throw new KeeperSdkError(
|
|
97
|
+
'Provide a new name or at least one permission to update.',
|
|
98
|
+
'shared_folder_update_empty'
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const effectiveName =
|
|
104
|
+
resolved.kind === FolderKind.SharedFolder ? trimmedName || resolved.folder.name || undefined : trimmedName
|
|
105
|
+
const mergedData = mergeFolderData(resolved.folder.data, effectiveName)
|
|
106
|
+
|
|
107
|
+
const request: FolderUpdateRequest = {
|
|
108
|
+
folder_uid: folderUid,
|
|
109
|
+
folder_type: resolved.kind,
|
|
110
|
+
data: await encryptObjectForStorage(mergedData, folderKey),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (resolved.kind === FolderKind.SharedFolder) {
|
|
114
|
+
const sharedFolder = resolved.folder
|
|
115
|
+
const displayName = trimmedName || sharedFolder.name || folderUid
|
|
116
|
+
request.shared_folder_uid = folderUid
|
|
117
|
+
request.name = await encryptForStorage(platform.stringToBytes(displayName), folderKey)
|
|
118
|
+
request.manage_users = isBoolean(input.manageUsers) ? input.manageUsers : sharedFolder.defaultManageUsers
|
|
119
|
+
request.manage_records = isBoolean(input.manageRecords)
|
|
120
|
+
? input.manageRecords
|
|
121
|
+
: sharedFolder.defaultManageRecords
|
|
122
|
+
request.can_edit = isBoolean(input.canEdit) ? input.canEdit : sharedFolder.defaultCanEdit
|
|
123
|
+
request.can_share = isBoolean(input.canShare) ? input.canShare : sharedFolder.defaultCanShare
|
|
124
|
+
} else if (resolved.kind === FolderKind.SharedFolderFolder) {
|
|
125
|
+
request.shared_folder_uid = resolved.folder.sharedFolderUid
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const folderLabel = effectiveName || folderUid
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const response = await auth.executeRestCommand(folderUpdateCommand(request))
|
|
132
|
+
const succeeded =
|
|
133
|
+
response.result === FolderResultStatus.Success || response.result_code === FolderResultStatus.Success
|
|
134
|
+
if (!succeeded) {
|
|
135
|
+
const reason =
|
|
136
|
+
response.message ||
|
|
137
|
+
response.result_code ||
|
|
138
|
+
`folder_update failed for "${folderLabel}" (uid=${folderUid}, type=${resolved.kind}): server returned no message or result_code`
|
|
139
|
+
return {
|
|
140
|
+
folderUid,
|
|
141
|
+
success: false,
|
|
142
|
+
message: reason,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { folderUid, success: true }
|
|
146
|
+
} catch (err) {
|
|
147
|
+
return {
|
|
148
|
+
folderUid,
|
|
149
|
+
success: false,
|
|
150
|
+
message: `folder_update failed for "${folderLabel}" (uid=${folderUid}, type=${resolved.kind}): ${extractErrorMessage(err)}`,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function renameFolder(
|
|
156
|
+
auth: Auth,
|
|
157
|
+
storage: InMemoryStorage,
|
|
158
|
+
session: VaultFolderSession,
|
|
159
|
+
folderPath: string,
|
|
160
|
+
newName: string
|
|
161
|
+
): Promise<RenameFolderResult> {
|
|
162
|
+
const trimmedPath = folderPath.trim()
|
|
163
|
+
if (!trimmedPath) {
|
|
164
|
+
throw new KeeperSdkError('Folder cannot be empty.', 'folder_required')
|
|
165
|
+
}
|
|
166
|
+
const trimmedName = newName.trim()
|
|
167
|
+
if (!trimmedName) {
|
|
168
|
+
throw new KeeperSdkError('New folder name is required.', 'folder_name_required')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const resolved = await resolveSingleFolder(storage, session, trimmedPath)
|
|
172
|
+
if (resolved.folderUid === null) {
|
|
173
|
+
throw new KeeperSdkError('Cannot rename the root folder.', 'folder_root_rename')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const result = await updateFolder(auth, storage, {
|
|
177
|
+
folderUid: resolved.folderUid,
|
|
178
|
+
folderName: trimmedName,
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
folderUid: resolved.folderUid,
|
|
183
|
+
oldName: resolved.name,
|
|
184
|
+
newName: trimmedName,
|
|
185
|
+
success: result.success,
|
|
186
|
+
message: result.message,
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function updateSharedFolderPermissions(
|
|
191
|
+
auth: Auth,
|
|
192
|
+
storage: InMemoryStorage,
|
|
193
|
+
sharedFolderUid: string,
|
|
194
|
+
permissions: {
|
|
195
|
+
manageUsers?: boolean | null
|
|
196
|
+
manageRecords?: boolean | null
|
|
197
|
+
canShare?: boolean | null
|
|
198
|
+
canEdit?: boolean | null
|
|
199
|
+
}
|
|
200
|
+
): Promise<UpdateFolderResult> {
|
|
201
|
+
const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, sharedFolderUid)
|
|
202
|
+
if (!sharedFolder) {
|
|
203
|
+
throw new KeeperSdkError(`"${sharedFolderUid}" is not a shared folder.`, 'not_shared_folder')
|
|
204
|
+
}
|
|
205
|
+
return updateFolder(auth, storage, {
|
|
206
|
+
folderUid: sharedFolderUid,
|
|
207
|
+
folderName: null,
|
|
208
|
+
...permissions,
|
|
209
|
+
})
|
|
210
|
+
}
|