@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,321 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DRecord,
|
|
3
|
+
DSharedFolder,
|
|
4
|
+
DSharedFolderFolder,
|
|
5
|
+
DSharedFolderTeam,
|
|
6
|
+
DSharedFolderUser,
|
|
7
|
+
DUser,
|
|
8
|
+
DUserFolder,
|
|
9
|
+
} from '@keeper-security/keeperapi'
|
|
10
|
+
import { webSafe64FromBytes } from '@keeper-security/keeperapi'
|
|
11
|
+
import { InMemoryStorage } from '../storage/InMemoryStorage'
|
|
12
|
+
import { getRecordTitle } from '../records/RecordUtils'
|
|
13
|
+
import { listFolder, listVaultRootFolders } from './listFolder'
|
|
14
|
+
import { resolveSingleFolder, type VaultFolderSession } from './changeDirectory'
|
|
15
|
+
import { FolderKind, VaultObjectKind, sharedFolderFolderName, sharedFolderName, userFolderName } from './folderHelpers'
|
|
16
|
+
|
|
17
|
+
enum TreeItemKind {
|
|
18
|
+
Permission = 'perm',
|
|
19
|
+
Record = 'record',
|
|
20
|
+
Folder = 'folder',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type FolderTreeBuildOptions = {
|
|
24
|
+
folderPath?: string | null
|
|
25
|
+
verbose?: boolean
|
|
26
|
+
showRecords?: boolean
|
|
27
|
+
showShares?: boolean
|
|
28
|
+
hideSharesKey?: boolean
|
|
29
|
+
title?: string | null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type FolderTreeNode = {
|
|
33
|
+
displayName: string
|
|
34
|
+
children: FolderTreeNode[]
|
|
35
|
+
permissions?: { display: string }[]
|
|
36
|
+
records?: { display: string }[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type FolderTreeResult = {
|
|
40
|
+
title?: string | null
|
|
41
|
+
root: FolderTreeNode
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function userPermissionToText(manageUsers: boolean, manageRecords: boolean): string {
|
|
45
|
+
if (manageUsers && manageRecords) return 'Can Manage Users & Records'
|
|
46
|
+
if (manageUsers) return 'Can Manage Users'
|
|
47
|
+
if (manageRecords) return 'Can Manage Records'
|
|
48
|
+
return 'No User Permissions'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function recordPermissionToText(canEdit: boolean, canShare: boolean): string {
|
|
52
|
+
if (canEdit && canShare) return 'Can Edit & Share'
|
|
53
|
+
if (canEdit) return 'Can Edit'
|
|
54
|
+
if (canShare) return 'Can Share'
|
|
55
|
+
return 'View Only'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildAccountUidEmailMap(storage: InMemoryStorage): Map<string, string> {
|
|
59
|
+
const accountUidToEmail = new Map<string, string>()
|
|
60
|
+
for (const user of storage.getAll<DUser>(VaultObjectKind.User)) {
|
|
61
|
+
const uid = user.accountUid ? webSafe64FromBytes(user.accountUid) : ''
|
|
62
|
+
const email = (user.username || '').trim()
|
|
63
|
+
if (uid && email) accountUidToEmail.set(uid, email)
|
|
64
|
+
}
|
|
65
|
+
return accountUidToEmail
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveUserDisplayName(
|
|
69
|
+
accountUid: string | undefined,
|
|
70
|
+
accountUsername: string | undefined,
|
|
71
|
+
emailMap: Map<string, string>
|
|
72
|
+
): string {
|
|
73
|
+
const explicit = (accountUsername || '').trim()
|
|
74
|
+
if (explicit) return explicit
|
|
75
|
+
if (accountUid) {
|
|
76
|
+
const fromMap = emailMap.get(accountUid)
|
|
77
|
+
if (fromMap) return fromMap
|
|
78
|
+
return accountUid
|
|
79
|
+
}
|
|
80
|
+
return 'unknown'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function collectSharedFolderPermissions(
|
|
84
|
+
storage: InMemoryStorage,
|
|
85
|
+
sharedFolder: DSharedFolder,
|
|
86
|
+
verbose: boolean,
|
|
87
|
+
hideSharesKey: boolean,
|
|
88
|
+
emailMap: Map<string, string>
|
|
89
|
+
): Promise<{ display: string }[]> {
|
|
90
|
+
const rows: { display: string; sortKey: string }[] = []
|
|
91
|
+
const seenUserUids = new Set<string>()
|
|
92
|
+
|
|
93
|
+
for (const sharedUser of storage.getAll<DSharedFolderUser>(VaultObjectKind.SharedFolderUser)) {
|
|
94
|
+
if (sharedUser.sharedFolderUid !== sharedFolder.uid) continue
|
|
95
|
+
if (sharedUser.accountUid) seenUserUids.add(sharedUser.accountUid)
|
|
96
|
+
const permissionText = userPermissionToText(sharedUser.manageUsers, sharedUser.manageRecords)
|
|
97
|
+
let name = resolveUserDisplayName(sharedUser.accountUid, sharedUser.accountUsername, emailMap)
|
|
98
|
+
if (verbose && sharedUser.accountUid) name += ` (${sharedUser.accountUid})`
|
|
99
|
+
const suffix = hideSharesKey ? '' : ` [User]`
|
|
100
|
+
rows.push({
|
|
101
|
+
display: `${name}: ${permissionText}${suffix}`,
|
|
102
|
+
sortKey: name.toLowerCase(),
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (sharedFolder.ownerAccountUid && !seenUserUids.has(sharedFolder.ownerAccountUid)) {
|
|
107
|
+
const ownerName = resolveUserDisplayName(sharedFolder.ownerAccountUid, sharedFolder.ownerUsername, emailMap)
|
|
108
|
+
let display = ownerName
|
|
109
|
+
if (verbose) display += ` (${sharedFolder.ownerAccountUid})`
|
|
110
|
+
const suffix = hideSharesKey ? '' : ` [User]`
|
|
111
|
+
rows.push({
|
|
112
|
+
display: `${display}: ${userPermissionToText(true, true)}${suffix}`,
|
|
113
|
+
sortKey: ownerName.toLowerCase(),
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (const sharedTeam of storage.getAll<DSharedFolderTeam>(VaultObjectKind.SharedFolderTeam)) {
|
|
118
|
+
if (sharedTeam.sharedFolderUid !== sharedFolder.uid) continue
|
|
119
|
+
const permissionText = userPermissionToText(sharedTeam.manageUsers, sharedTeam.manageRecords)
|
|
120
|
+
let name = sharedTeam.name || `(${sharedTeam.teamUid})`
|
|
121
|
+
if (verbose && sharedTeam.teamUid) name += ` (${sharedTeam.teamUid})`
|
|
122
|
+
const suffix = hideSharesKey ? '' : ` [Team]`
|
|
123
|
+
rows.push({
|
|
124
|
+
display: `${name}: ${permissionText}${suffix}`,
|
|
125
|
+
sortKey: name.toLowerCase(),
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
rows.sort((rowA, rowB) => rowA.sortKey.localeCompare(rowB.sortKey))
|
|
130
|
+
return rows.map((row) => ({ display: row.display }))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
type BuildOpts = Required<Pick<FolderTreeBuildOptions, 'verbose' | 'showRecords' | 'showShares' | 'hideSharesKey'>> & {
|
|
134
|
+
promotedRootSharedUids?: Set<string>
|
|
135
|
+
accountUidEmailMap: Map<string, string>
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function buildFolderSubtree(
|
|
139
|
+
storage: InMemoryStorage,
|
|
140
|
+
folderUid: string,
|
|
141
|
+
opts: BuildOpts
|
|
142
|
+
): Promise<FolderTreeNode> {
|
|
143
|
+
const userFolder = storage.getByUid<DUserFolder>(FolderKind.UserFolder, folderUid)
|
|
144
|
+
const sharedFolder = storage.getByUid<DSharedFolder>(FolderKind.SharedFolder, folderUid)
|
|
145
|
+
const sharedFolderFolder = storage.getByUid<DSharedFolderFolder>(FolderKind.SharedFolderFolder, folderUid)
|
|
146
|
+
if (!userFolder && !sharedFolder && !sharedFolderFolder) {
|
|
147
|
+
return { displayName: `(missing folder ${folderUid})`, children: [] }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let baseName: string
|
|
151
|
+
if (userFolder) baseName = userFolderName(userFolder)
|
|
152
|
+
else if (sharedFolder) baseName = sharedFolderName(sharedFolder)
|
|
153
|
+
else baseName = sharedFolderFolderName(sharedFolderFolder!)
|
|
154
|
+
|
|
155
|
+
let displayName = baseName
|
|
156
|
+
if (opts.verbose) {
|
|
157
|
+
displayName = `${baseName} (${folderUid})`
|
|
158
|
+
}
|
|
159
|
+
if (sharedFolder) {
|
|
160
|
+
displayName += ' [Shared]'
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const node: FolderTreeNode = { displayName, children: [] }
|
|
164
|
+
|
|
165
|
+
if (opts.showShares && sharedFolder) {
|
|
166
|
+
node.permissions = await collectSharedFolderPermissions(
|
|
167
|
+
storage,
|
|
168
|
+
sharedFolder,
|
|
169
|
+
opts.verbose,
|
|
170
|
+
opts.hideSharesKey,
|
|
171
|
+
opts.accountUidEmailMap
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const listed = await listFolder(storage, {
|
|
176
|
+
folderUid: folderUid,
|
|
177
|
+
showFolders: true,
|
|
178
|
+
showRecords: opts.showRecords,
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
for (const childFolder of listed.folders) {
|
|
182
|
+
if (opts.promotedRootSharedUids?.has(childFolder.uid)) continue
|
|
183
|
+
node.children.push(await buildFolderSubtree(storage, childFolder.uid, opts))
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (opts.showRecords && 'records' in listed) {
|
|
187
|
+
const records = listed.records
|
|
188
|
+
node.records = records.map((recordRow) => {
|
|
189
|
+
const record = storage.getByUid<DRecord>(VaultObjectKind.Record, recordRow.uid)
|
|
190
|
+
const title = record ? getRecordTitle(record) : recordRow.name
|
|
191
|
+
const display = opts.verbose && record ? `${title} (${recordRow.uid}) [Record]` : `${title} [Record]`
|
|
192
|
+
return { display }
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return node
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function buildVaultRootTree(storage: InMemoryStorage, opts: BuildOpts): Promise<FolderTreeNode> {
|
|
200
|
+
const node: FolderTreeNode = { displayName: '', children: [] }
|
|
201
|
+
const { rows, promotedRootSharedUids } = await listVaultRootFolders(storage)
|
|
202
|
+
const optsWithPromoted: BuildOpts = { ...opts, promotedRootSharedUids }
|
|
203
|
+
for (const folderRow of rows) {
|
|
204
|
+
node.children.push(await buildFolderSubtree(storage, folderRow.uid, optsWithPromoted))
|
|
205
|
+
}
|
|
206
|
+
const listed = await listFolder(storage, {
|
|
207
|
+
folderUid: undefined,
|
|
208
|
+
showFolders: true,
|
|
209
|
+
showRecords: opts.showRecords,
|
|
210
|
+
})
|
|
211
|
+
if (opts.showRecords && listed.records?.length) {
|
|
212
|
+
node.records = listed.records.map((recordRow) => {
|
|
213
|
+
const record = storage.getByUid<DRecord>(VaultObjectKind.Record, recordRow.uid)
|
|
214
|
+
const title = record ? getRecordTitle(record) : recordRow.name
|
|
215
|
+
const display = opts.verbose && record ? `${title} (${recordRow.uid}) [Record]` : `${title} [Record]`
|
|
216
|
+
return { display }
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
return node
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function resolveTreeStart(
|
|
223
|
+
storage: InMemoryStorage,
|
|
224
|
+
session: VaultFolderSession,
|
|
225
|
+
folderPath?: string | null
|
|
226
|
+
): Promise<{ folderUid: string | null }> {
|
|
227
|
+
const trimmedPath = folderPath?.trim()
|
|
228
|
+
if (trimmedPath) {
|
|
229
|
+
const resolved = await resolveSingleFolder(storage, session, trimmedPath)
|
|
230
|
+
return { folderUid: resolved.folderUid }
|
|
231
|
+
}
|
|
232
|
+
return { folderUid: session.currentFolderUid || null }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export async function buildFolderTree(
|
|
236
|
+
storage: InMemoryStorage,
|
|
237
|
+
session: VaultFolderSession,
|
|
238
|
+
options: FolderTreeBuildOptions = {}
|
|
239
|
+
): Promise<FolderTreeResult> {
|
|
240
|
+
const opts: BuildOpts = {
|
|
241
|
+
verbose: options.verbose === true,
|
|
242
|
+
showRecords: options.showRecords === true,
|
|
243
|
+
showShares: options.showShares === true,
|
|
244
|
+
hideSharesKey: options.hideSharesKey === true,
|
|
245
|
+
accountUidEmailMap: buildAccountUidEmailMap(storage),
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const { folderUid } = await resolveTreeStart(storage, session, options.folderPath)
|
|
249
|
+
|
|
250
|
+
const root =
|
|
251
|
+
folderUid === null
|
|
252
|
+
? await buildVaultRootTree(storage, opts)
|
|
253
|
+
: await buildFolderSubtree(storage, folderUid, opts)
|
|
254
|
+
|
|
255
|
+
return { title: options.title || undefined, root }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
type TreeItem =
|
|
259
|
+
| { kind: TreeItemKind.Permission; display: string }
|
|
260
|
+
| { kind: TreeItemKind.Record; display: string }
|
|
261
|
+
| { kind: TreeItemKind.Folder; node: FolderTreeNode }
|
|
262
|
+
|
|
263
|
+
function gatherItems(node: FolderTreeNode): TreeItem[] {
|
|
264
|
+
const items: TreeItem[] = []
|
|
265
|
+
if (node.permissions) {
|
|
266
|
+
for (const permission of node.permissions) {
|
|
267
|
+
items.push({ kind: TreeItemKind.Permission, display: permission.display })
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
for (const child of node.children) {
|
|
271
|
+
items.push({ kind: TreeItemKind.Folder, node: child })
|
|
272
|
+
}
|
|
273
|
+
if (node.records) {
|
|
274
|
+
for (const record of node.records) {
|
|
275
|
+
items.push({ kind: TreeItemKind.Record, display: record.display })
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return items
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function renderFolderTreeAscii(result: FolderTreeResult): string {
|
|
282
|
+
const lines: string[] = []
|
|
283
|
+
if (result.title) {
|
|
284
|
+
lines.push(result.title)
|
|
285
|
+
}
|
|
286
|
+
renderNode(result.root, lines, true, '', true)
|
|
287
|
+
return lines.join('\n')
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function renderNode(node: FolderTreeNode, lines: string[], isRoot: boolean, prefix: string, isLast: boolean): void {
|
|
291
|
+
if (isRoot) {
|
|
292
|
+
if (node.displayName) {
|
|
293
|
+
lines.push(node.displayName)
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
const connector = isLast ? '\\-- ' : '+-- '
|
|
297
|
+
lines.push(prefix + connector + node.displayName)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const childBase = isRoot ? ' ' : prefix + (isLast ? ' ' : '| ')
|
|
301
|
+
const items = gatherItems(node)
|
|
302
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
303
|
+
const isLastItem = itemIndex === items.length - 1
|
|
304
|
+
const item = items[itemIndex]
|
|
305
|
+
if (item.kind === TreeItemKind.Permission || item.kind === TreeItemKind.Record) {
|
|
306
|
+
const connector = isLastItem ? '\\-- ' : '+-- '
|
|
307
|
+
lines.push(childBase + connector + item.display)
|
|
308
|
+
} else {
|
|
309
|
+
renderNode(item.node, lines, false, childBase, isLastItem)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export async function folderTreeAscii(
|
|
315
|
+
storage: InMemoryStorage,
|
|
316
|
+
session: VaultFolderSession,
|
|
317
|
+
options?: FolderTreeBuildOptions
|
|
318
|
+
): Promise<string> {
|
|
319
|
+
const built = await buildFolderTree(storage, session, options || {})
|
|
320
|
+
return renderFolderTreeAscii(built)
|
|
321
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DSharedFolder,
|
|
3
|
+
DSharedFolderFolder,
|
|
4
|
+
DSharedFolderRecord,
|
|
5
|
+
DSharedFolderUser,
|
|
6
|
+
DUserFolder,
|
|
7
|
+
} from '@keeper-security/keeperapi'
|
|
8
|
+
import { InMemoryStorage } from '../storage/InMemoryStorage'
|
|
9
|
+
import { KeeperSdkError } from '../utils'
|
|
10
|
+
import { findParentFolderUid } from './changeDirectory'
|
|
11
|
+
import { FolderKind, FolderObjectType, VaultObjectKind, sharedFolderFolderName, userFolderName } from './folderHelpers'
|
|
12
|
+
|
|
13
|
+
export enum GetFolderFormat {
|
|
14
|
+
Detail = 'detail',
|
|
15
|
+
JSON = 'json',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type GetFolderFormatInput = GetFolderFormat | `${GetFolderFormat}`
|
|
19
|
+
|
|
20
|
+
export type GetFolderOptions = {
|
|
21
|
+
format?: GetFolderFormatInput
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type GetFolderResult = GetFolderResultFolder | GetFolderResultSharedFolder
|
|
25
|
+
|
|
26
|
+
export type GetFolderResultFolder = {
|
|
27
|
+
objectType: FolderObjectType.Folder
|
|
28
|
+
format: GetFolderFormat
|
|
29
|
+
folder_uid: string
|
|
30
|
+
folder_type: FolderKind.UserFolder | FolderKind.SharedFolderFolder
|
|
31
|
+
name: string
|
|
32
|
+
parent_uid: string | null
|
|
33
|
+
shared_folder_scope_uid?: string
|
|
34
|
+
json?: Record<string, unknown>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type GetFolderResultSharedFolder = {
|
|
38
|
+
objectType: FolderObjectType.SharedFolder
|
|
39
|
+
format: GetFolderFormat
|
|
40
|
+
shared_folder_uid: string
|
|
41
|
+
name: string
|
|
42
|
+
default_can_edit: boolean
|
|
43
|
+
default_can_share: boolean
|
|
44
|
+
default_manage_records: boolean
|
|
45
|
+
default_manage_users: boolean
|
|
46
|
+
record_permissions?: {
|
|
47
|
+
record_uid: string
|
|
48
|
+
can_edit: boolean
|
|
49
|
+
can_share: boolean
|
|
50
|
+
owner: boolean
|
|
51
|
+
}[]
|
|
52
|
+
user_permissions?: {
|
|
53
|
+
account_username?: string
|
|
54
|
+
manage_records: boolean
|
|
55
|
+
manage_users: boolean
|
|
56
|
+
}[]
|
|
57
|
+
json?: Record<string, unknown>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type FoundFolder =
|
|
61
|
+
| { kind: FolderKind.UserFolder; folder: DUserFolder }
|
|
62
|
+
| { kind: FolderKind.SharedFolder; folder: DSharedFolder }
|
|
63
|
+
| { kind: FolderKind.SharedFolderFolder; folder: DSharedFolderFolder }
|
|
64
|
+
|
|
65
|
+
function findByUidOrName<T>(
|
|
66
|
+
items: Iterable<T>,
|
|
67
|
+
nameOrUid: string,
|
|
68
|
+
getUid: (item: T) => string,
|
|
69
|
+
getName: (item: T) => string
|
|
70
|
+
): T | undefined {
|
|
71
|
+
const trimmedNameOrUid = nameOrUid.trim()
|
|
72
|
+
if (!trimmedNameOrUid) return undefined
|
|
73
|
+
const lowerNameOrUid = trimmedNameOrUid.toLowerCase()
|
|
74
|
+
let exactNameHit: T | undefined
|
|
75
|
+
let lowerNameHit: T | undefined
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
if (getUid(item) === trimmedNameOrUid) return item
|
|
78
|
+
if (!exactNameHit) {
|
|
79
|
+
const itemName = getName(item)
|
|
80
|
+
if (itemName === trimmedNameOrUid) {
|
|
81
|
+
exactNameHit = item
|
|
82
|
+
} else if (!lowerNameHit && itemName.toLowerCase() === lowerNameOrUid) {
|
|
83
|
+
lowerNameHit = item
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return exactNameHit || lowerNameHit
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function findSharedFolder(storage: InMemoryStorage, nameOrUid: string): DSharedFolder | undefined {
|
|
91
|
+
return findByUidOrName(
|
|
92
|
+
storage.getAll<DSharedFolder>(FolderKind.SharedFolder),
|
|
93
|
+
nameOrUid,
|
|
94
|
+
(sharedFolder) => sharedFolder.uid,
|
|
95
|
+
(sharedFolder) => (sharedFolder.name || '').trim()
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function findUserFolder(storage: InMemoryStorage, nameOrUid: string): DUserFolder | undefined {
|
|
100
|
+
return findByUidOrName(
|
|
101
|
+
storage.getAll<DUserFolder>(FolderKind.UserFolder),
|
|
102
|
+
nameOrUid,
|
|
103
|
+
(userFolder) => userFolder.uid,
|
|
104
|
+
(userFolder) => userFolderName(userFolder)
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function findSharedFolderFolder(storage: InMemoryStorage, nameOrUid: string): DSharedFolderFolder | undefined {
|
|
109
|
+
return findByUidOrName(
|
|
110
|
+
storage.getAll<DSharedFolderFolder>(FolderKind.SharedFolderFolder),
|
|
111
|
+
nameOrUid,
|
|
112
|
+
(sharedFolderFolder) => sharedFolderFolder.uid,
|
|
113
|
+
(sharedFolderFolder) => sharedFolderFolderName(sharedFolderFolder)
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function findFolder(storage: InMemoryStorage, nameOrUid: string): FoundFolder | undefined {
|
|
118
|
+
const trimmedNameOrUid = nameOrUid.trim()
|
|
119
|
+
if (!trimmedNameOrUid) return undefined
|
|
120
|
+
|
|
121
|
+
const sharedFolder = findSharedFolder(storage, trimmedNameOrUid)
|
|
122
|
+
if (sharedFolder) return { kind: FolderKind.SharedFolder, folder: sharedFolder }
|
|
123
|
+
|
|
124
|
+
const userFolder = findUserFolder(storage, trimmedNameOrUid)
|
|
125
|
+
if (userFolder) return { kind: FolderKind.UserFolder, folder: userFolder }
|
|
126
|
+
|
|
127
|
+
const sharedFolderFolder = findSharedFolderFolder(storage, trimmedNameOrUid)
|
|
128
|
+
if (sharedFolderFolder) return { kind: FolderKind.SharedFolderFolder, folder: sharedFolderFolder }
|
|
129
|
+
|
|
130
|
+
return undefined
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function formatRegularFolder(
|
|
134
|
+
storage: InMemoryStorage,
|
|
135
|
+
folder: DUserFolder | DSharedFolderFolder,
|
|
136
|
+
format: GetFolderFormat
|
|
137
|
+
): Promise<GetFolderResultFolder> {
|
|
138
|
+
const isUser = folder.kind === FolderKind.UserFolder
|
|
139
|
+
const folder_uid = folder.uid
|
|
140
|
+
const folder_type: GetFolderResultFolder['folder_type'] = isUser
|
|
141
|
+
? FolderKind.UserFolder
|
|
142
|
+
: FolderKind.SharedFolderFolder
|
|
143
|
+
const name = isUser ? userFolderName(folder) : sharedFolderFolderName(folder)
|
|
144
|
+
const parent_uid = await findParentFolderUid(storage, folder_uid)
|
|
145
|
+
|
|
146
|
+
const base: GetFolderResultFolder = {
|
|
147
|
+
objectType: FolderObjectType.Folder,
|
|
148
|
+
format,
|
|
149
|
+
folder_uid,
|
|
150
|
+
folder_type,
|
|
151
|
+
name,
|
|
152
|
+
parent_uid,
|
|
153
|
+
}
|
|
154
|
+
if (!isUser) {
|
|
155
|
+
base.shared_folder_scope_uid = (folder as DSharedFolderFolder).sharedFolderUid
|
|
156
|
+
}
|
|
157
|
+
if (format === GetFolderFormat.JSON) {
|
|
158
|
+
base.json = { ...base, objectType: FolderObjectType.Folder }
|
|
159
|
+
}
|
|
160
|
+
return base
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function formatSharedFolder(
|
|
164
|
+
storage: InMemoryStorage,
|
|
165
|
+
sharedFolder: DSharedFolder,
|
|
166
|
+
format: GetFolderFormat
|
|
167
|
+
): GetFolderResultSharedFolder {
|
|
168
|
+
const sharedFolderUid = sharedFolder.uid
|
|
169
|
+
const recordPermissions = storage
|
|
170
|
+
.getAll<DSharedFolderRecord>(VaultObjectKind.SharedFolderRecord)
|
|
171
|
+
.filter((record) => record.sharedFolderUid === sharedFolderUid)
|
|
172
|
+
.map((record) => ({
|
|
173
|
+
record_uid: record.recordUid,
|
|
174
|
+
can_edit: record.canEdit,
|
|
175
|
+
can_share: record.canShare,
|
|
176
|
+
owner: record.owner,
|
|
177
|
+
}))
|
|
178
|
+
const userPermissions = storage
|
|
179
|
+
.getAll<DSharedFolderUser>(VaultObjectKind.SharedFolderUser)
|
|
180
|
+
.filter((user) => user.sharedFolderUid === sharedFolderUid)
|
|
181
|
+
.map((user) => ({
|
|
182
|
+
account_username: user.accountUsername,
|
|
183
|
+
manage_records: user.manageRecords,
|
|
184
|
+
manage_users: user.manageUsers,
|
|
185
|
+
}))
|
|
186
|
+
|
|
187
|
+
const base: GetFolderResultSharedFolder = {
|
|
188
|
+
objectType: FolderObjectType.SharedFolder,
|
|
189
|
+
format,
|
|
190
|
+
shared_folder_uid: sharedFolder.uid,
|
|
191
|
+
name: (sharedFolder.name || '').trim() || sharedFolder.uid,
|
|
192
|
+
default_can_edit: sharedFolder.defaultCanEdit,
|
|
193
|
+
default_can_share: sharedFolder.defaultCanShare,
|
|
194
|
+
default_manage_records: sharedFolder.defaultManageRecords,
|
|
195
|
+
default_manage_users: sharedFolder.defaultManageUsers,
|
|
196
|
+
record_permissions: recordPermissions.length ? recordPermissions : undefined,
|
|
197
|
+
user_permissions: userPermissions.length ? userPermissions : undefined,
|
|
198
|
+
}
|
|
199
|
+
if (format === GetFolderFormat.JSON) {
|
|
200
|
+
base.json = { ...base, objectType: FolderObjectType.SharedFolder }
|
|
201
|
+
}
|
|
202
|
+
return base
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function normalizeFormat(format: GetFolderFormatInput | undefined): GetFolderFormat {
|
|
206
|
+
if (format === GetFolderFormat.JSON) return GetFolderFormat.JSON
|
|
207
|
+
return GetFolderFormat.Detail
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function getFolder(
|
|
211
|
+
storage: InMemoryStorage,
|
|
212
|
+
uidOrName: string,
|
|
213
|
+
options: GetFolderOptions = {}
|
|
214
|
+
): Promise<GetFolderResult> {
|
|
215
|
+
const trimmed = uidOrName.trim()
|
|
216
|
+
if (!trimmed) {
|
|
217
|
+
throw new KeeperSdkError('Folder UID or name is required.', 'missing_folder_ref')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const found = findFolder(storage, trimmed)
|
|
221
|
+
if (!found) {
|
|
222
|
+
throw new KeeperSdkError(`"${trimmed}" not found as a folder or shared folder`, 'folder_not_found')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const format: GetFolderFormat = normalizeFormat(options.format)
|
|
226
|
+
|
|
227
|
+
switch (found.kind) {
|
|
228
|
+
case FolderKind.SharedFolder:
|
|
229
|
+
return formatSharedFolder(storage, found.folder, format)
|
|
230
|
+
case FolderKind.UserFolder:
|
|
231
|
+
case FolderKind.SharedFolderFolder:
|
|
232
|
+
return formatRegularFolder(storage, found.folder, format)
|
|
233
|
+
}
|
|
234
|
+
}
|