@nightlybuildgroup/vault 1.5.0 → 1.7.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/README.md +17 -0
- package/package.json +1 -1
- package/src/cli.js +7 -4
- package/src/commands/collections.js +12 -8
- package/src/commands/folders.js +12 -8
- package/src/commands/items.js +10 -7
- package/src/commands/organizations.js +39 -0
- package/src/commands/setup.js +48 -2
- package/src/config.js +2 -0
package/README.md
CHANGED
|
@@ -20,6 +20,9 @@ nbg-pw setup
|
|
|
20
20
|
|
|
21
21
|
You'll be asked for your server URL, email, API-key client id/secret, and master
|
|
22
22
|
password. Credentials are verified against the server before anything is saved.
|
|
23
|
+
If your account belongs to an organization, setup then offers to pick a default
|
|
24
|
+
**collection** to share newly added items into (or keep them in your personal
|
|
25
|
+
vault) — you can change this any time with [`nbg-pw config`](#sharing-new-items-with-an-organization-config).
|
|
23
26
|
|
|
24
27
|
> Get your API key from your Vaultwarden web vault: **Settings → Security →
|
|
25
28
|
> Keys → API Key** (`client_id` / `client_secret`).
|
|
@@ -181,8 +184,22 @@ nbg-pw items --search git # filtered by search
|
|
|
181
184
|
nbg-pw items --folder "Agents" # filtered by folder
|
|
182
185
|
nbg-pw folders # folder names
|
|
183
186
|
nbg-pw collections # collection names
|
|
187
|
+
nbg-pw organizations # organization names
|
|
184
188
|
```
|
|
185
189
|
|
|
190
|
+
Add `--ids` to any of them to print ids alongside names — useful for `config set`
|
|
191
|
+
or scripting. `collections --ids` also shows each collection's org id:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
$ nbg-pw organizations --ids
|
|
195
|
+
dfff… Acme Inc
|
|
196
|
+
$ nbg-pw collections --ids
|
|
197
|
+
bf2b… Shared (org dfff…)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
(You can pass either a **name or an id** to `config set` / `add --collection`, so
|
|
201
|
+
ids are optional — handy mainly when names are ambiguous.)
|
|
202
|
+
|
|
186
203
|
### Generating secrets (`generate`)
|
|
187
204
|
|
|
188
205
|
Pure generator — needs no vault session.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -10,9 +10,10 @@ Commands:
|
|
|
10
10
|
delete Delete an item or folder (alias: rm; --permanent skips trash)
|
|
11
11
|
attach Manage an item's attachments (--file/--list/--get/--delete)
|
|
12
12
|
list Show an item's field names + types, no values (nbg-pw list "My Visa")
|
|
13
|
-
items List item names (--search, --folder, --collection)
|
|
14
|
-
folders List folder names
|
|
15
|
-
collections List collection names
|
|
13
|
+
items List item names (--search, --folder, --collection; --ids)
|
|
14
|
+
folders List folder names (--ids)
|
|
15
|
+
collections List collection names (--ids shows collection + org ids)
|
|
16
|
+
organizations List organization names (--ids)
|
|
16
17
|
generate Generate a password or passphrase (bw generate)
|
|
17
18
|
config Show/set defaults, e.g. the org collection new items share into
|
|
18
19
|
serve Start a local bw API daemon (unlocked) for fast repeated reads
|
|
@@ -55,7 +56,7 @@ export async function runCli(argv, deps = {}) {
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
async function loadCommands() {
|
|
58
|
-
const [setup, get, add, edit, del, attach, list, items, folders, collections, generate, config, status, doctor, serve, reset] =
|
|
59
|
+
const [setup, get, add, edit, del, attach, list, items, folders, collections, organizations, generate, config, status, doctor, serve, reset] =
|
|
59
60
|
await Promise.all([
|
|
60
61
|
import('./commands/setup.js'),
|
|
61
62
|
import('./commands/get.js'),
|
|
@@ -67,6 +68,7 @@ async function loadCommands() {
|
|
|
67
68
|
import('./commands/items.js'),
|
|
68
69
|
import('./commands/folders.js'),
|
|
69
70
|
import('./commands/collections.js'),
|
|
71
|
+
import('./commands/organizations.js'),
|
|
70
72
|
import('./commands/generate.js'),
|
|
71
73
|
import('./commands/config.js'),
|
|
72
74
|
import('./commands/status.js'),
|
|
@@ -86,6 +88,7 @@ async function loadCommands() {
|
|
|
86
88
|
items: items.default,
|
|
87
89
|
folders: folders.default,
|
|
88
90
|
collections: collections.default,
|
|
91
|
+
organizations: organizations.default,
|
|
89
92
|
generate: generate.default,
|
|
90
93
|
config: config.default,
|
|
91
94
|
status: status.default,
|
|
@@ -2,13 +2,17 @@ import * as bwModule from '../bw.js';
|
|
|
2
2
|
import * as keychainModule from '../keychain.js';
|
|
3
3
|
|
|
4
4
|
export function parseCollectionsArgs(args) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let ids = false;
|
|
6
|
+
for (const a of args) {
|
|
7
|
+
if (a === '--ids') ids = true;
|
|
8
|
+
else throw new Error(`unexpected argument: ${a}`);
|
|
9
|
+
}
|
|
10
|
+
return { ids };
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export async function runCollections(args, deps) {
|
|
10
14
|
const { keychain, bw, out, err } = deps;
|
|
11
|
-
parseCollectionsArgs(args);
|
|
15
|
+
const { ids } = parseCollectionsArgs(args);
|
|
12
16
|
|
|
13
17
|
const config = await keychain.readConfig();
|
|
14
18
|
if (!config) {
|
|
@@ -17,11 +21,11 @@ export async function runCollections(args, deps) {
|
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const session = await bw.ensureSession(config);
|
|
20
|
-
const collections = await bw.listCollections({ session })
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const collections = (await bw.listCollections({ session }))
|
|
25
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
26
|
+
for (const c of collections) {
|
|
27
|
+
out(ids ? `${c.id} ${c.name} (org ${c.organizationId})\n` : c.name + '\n');
|
|
28
|
+
}
|
|
25
29
|
return 0;
|
|
26
30
|
}
|
|
27
31
|
|
package/src/commands/folders.js
CHANGED
|
@@ -2,13 +2,17 @@ import * as bwModule from '../bw.js';
|
|
|
2
2
|
import * as keychainModule from '../keychain.js';
|
|
3
3
|
|
|
4
4
|
export function parseFoldersArgs(args) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let ids = false;
|
|
6
|
+
for (const a of args) {
|
|
7
|
+
if (a === '--ids') ids = true;
|
|
8
|
+
else throw new Error(`unexpected argument: ${a}`);
|
|
9
|
+
}
|
|
10
|
+
return { ids };
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export async function runFolders(args, deps) {
|
|
10
14
|
const { keychain, bw, out, err } = deps;
|
|
11
|
-
parseFoldersArgs(args);
|
|
15
|
+
const { ids } = parseFoldersArgs(args);
|
|
12
16
|
|
|
13
17
|
const config = await keychain.readConfig();
|
|
14
18
|
if (!config) {
|
|
@@ -17,11 +21,11 @@ export async function runFolders(args, deps) {
|
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const session = await bw.ensureSession(config);
|
|
20
|
-
const folders = await bw.listFolders({ session })
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const folders = (await bw.listFolders({ session }))
|
|
25
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
26
|
+
for (const f of folders) {
|
|
27
|
+
out(ids ? `${f.id ?? '-'} ${f.name}\n` : f.name + '\n');
|
|
28
|
+
}
|
|
25
29
|
return 0;
|
|
26
30
|
}
|
|
27
31
|
|
package/src/commands/items.js
CHANGED
|
@@ -5,6 +5,7 @@ export function parseItemsArgs(args) {
|
|
|
5
5
|
let search = null;
|
|
6
6
|
let folder = null;
|
|
7
7
|
let collection = null;
|
|
8
|
+
let ids = false;
|
|
8
9
|
for (let i = 0; i < args.length; i++) {
|
|
9
10
|
if (args[i] === '--search') {
|
|
10
11
|
const v = args[++i];
|
|
@@ -18,16 +19,18 @@ export function parseItemsArgs(args) {
|
|
|
18
19
|
const v = args[++i];
|
|
19
20
|
if (v === undefined) throw new Error('--collection requires a value');
|
|
20
21
|
collection = v;
|
|
22
|
+
} else if (args[i] === '--ids') {
|
|
23
|
+
ids = true;
|
|
21
24
|
} else {
|
|
22
25
|
throw new Error(`unknown option: ${args[i]}`);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
|
-
return { search, folder, collection };
|
|
28
|
+
return { search, folder, collection, ids };
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export async function runItems(args, deps) {
|
|
29
32
|
const { keychain, bw, out, err } = deps;
|
|
30
|
-
const { search, folder, collection } = parseItemsArgs(args);
|
|
33
|
+
const { search, folder, collection, ids } = parseItemsArgs(args);
|
|
31
34
|
|
|
32
35
|
const config = await keychain.readConfig();
|
|
33
36
|
if (!config) {
|
|
@@ -58,11 +61,11 @@ export async function runItems(args, deps) {
|
|
|
58
61
|
if (folderId !== undefined) filters.folderId = folderId;
|
|
59
62
|
if (collectionId !== undefined) filters.collectionId = collectionId;
|
|
60
63
|
|
|
61
|
-
const items = await bw.listItems(filters)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
const items = (await bw.listItems(filters))
|
|
65
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
66
|
+
for (const i of items) {
|
|
67
|
+
out(ids ? `${i.id} ${i.name}\n` : i.name + '\n');
|
|
68
|
+
}
|
|
66
69
|
return 0;
|
|
67
70
|
}
|
|
68
71
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as bwModule from '../bw.js';
|
|
2
|
+
import * as keychainModule from '../keychain.js';
|
|
3
|
+
|
|
4
|
+
export function parseOrganizationsArgs(args) {
|
|
5
|
+
let ids = false;
|
|
6
|
+
for (const a of args) {
|
|
7
|
+
if (a === '--ids') ids = true;
|
|
8
|
+
else throw new Error(`unexpected argument: ${a}`);
|
|
9
|
+
}
|
|
10
|
+
return { ids };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function runOrganizations(args, deps) {
|
|
14
|
+
const { keychain, bw, out, err } = deps;
|
|
15
|
+
const { ids } = parseOrganizationsArgs(args);
|
|
16
|
+
|
|
17
|
+
const config = await keychain.readConfig();
|
|
18
|
+
if (!config) {
|
|
19
|
+
err('No stored credentials. Run "nbg-pw setup" first.\n');
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const session = await bw.ensureSession(config);
|
|
24
|
+
const orgs = (await bw.listOrganizations({ session }))
|
|
25
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
26
|
+
for (const o of orgs) {
|
|
27
|
+
out(ids ? `${o.id} ${o.name}\n` : o.name + '\n');
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default async function organizations(args) {
|
|
33
|
+
return runOrganizations(args, {
|
|
34
|
+
keychain: keychainModule,
|
|
35
|
+
bw: bwModule,
|
|
36
|
+
out: (s) => process.stdout.write(s),
|
|
37
|
+
err: (s) => process.stderr.write(s),
|
|
38
|
+
});
|
|
39
|
+
}
|
package/src/commands/setup.js
CHANGED
|
@@ -15,6 +15,42 @@ export async function validateCredentials(input, deps) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// After credentials verify, offer to share new items into an org collection by
|
|
19
|
+
// default. Returns { defaultOrganization, defaultCollection } (by name) or {}.
|
|
20
|
+
// Stays optional: no orgs, a cancel, or "personal vault" all yield {}.
|
|
21
|
+
export async function chooseDefaultSharing(deps) {
|
|
22
|
+
const { prompts, bw, verified } = deps;
|
|
23
|
+
const session = await bw.ensureSession(verified);
|
|
24
|
+
try {
|
|
25
|
+
const orgs = await bw.listOrganizations({ session });
|
|
26
|
+
if (!orgs || orgs.length === 0) return {};
|
|
27
|
+
|
|
28
|
+
const orgChoice = await prompts.select({
|
|
29
|
+
message: 'Default: share newly added items into an organization?',
|
|
30
|
+
options: [
|
|
31
|
+
{ value: '__personal__', label: 'No — keep new items in my personal vault' },
|
|
32
|
+
...orgs.map((o) => ({ value: o.id, label: o.name })),
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
if (prompts.isCancel(orgChoice) || orgChoice === '__personal__') return {};
|
|
36
|
+
const org = orgs.find((o) => o.id === orgChoice);
|
|
37
|
+
|
|
38
|
+
const cols = (await bw.listCollections({ session })).filter((c) => c.organizationId === org.id);
|
|
39
|
+
if (cols.length === 0) return {};
|
|
40
|
+
|
|
41
|
+
const colChoice = await prompts.select({
|
|
42
|
+
message: `Which collection in "${org.name}"?`,
|
|
43
|
+
options: cols.map((c) => ({ value: c.id, label: c.name })),
|
|
44
|
+
});
|
|
45
|
+
if (prompts.isCancel(colChoice)) return {};
|
|
46
|
+
const col = cols.find((c) => c.id === colChoice);
|
|
47
|
+
|
|
48
|
+
return { defaultOrganization: org.name, defaultCollection: col.name };
|
|
49
|
+
} finally {
|
|
50
|
+
await bw.logout();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
18
54
|
export async function runSetup(deps) {
|
|
19
55
|
const { prompts, bw, keychain, buildConfig, nowIso, out } = deps;
|
|
20
56
|
|
|
@@ -74,10 +110,20 @@ export async function runSetup(deps) {
|
|
|
74
110
|
}
|
|
75
111
|
spin.stop('Credentials verified.');
|
|
76
112
|
|
|
77
|
-
|
|
113
|
+
let sharing = {};
|
|
114
|
+
try {
|
|
115
|
+
sharing = await chooseDefaultSharing({ prompts, bw, verified: input });
|
|
116
|
+
} catch {
|
|
117
|
+
out('Skipped organization setup — set a default later with "nbg-pw config set collection <name>".\n');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const config = buildConfig({ ...input, ...sharing }, nowIso);
|
|
78
121
|
await keychain.writeConfig(config);
|
|
79
122
|
|
|
80
|
-
|
|
123
|
+
const where = sharing.defaultCollection
|
|
124
|
+
? `New items will be shared into "${sharing.defaultCollection}".`
|
|
125
|
+
: 'New items go to your personal vault (configure sharing with "nbg-pw config set").';
|
|
126
|
+
prompts.outro(`Saved to your macOS Keychain. ${where} Try: nbg-pw status`);
|
|
81
127
|
return 0;
|
|
82
128
|
}
|
|
83
129
|
|
package/src/config.js
CHANGED
|
@@ -45,5 +45,7 @@ export function buildConfig(input, nowIso) {
|
|
|
45
45
|
masterPassword: input.masterPassword,
|
|
46
46
|
savedAt: nowIso,
|
|
47
47
|
};
|
|
48
|
+
if (input.defaultOrganization) c.defaultOrganization = input.defaultOrganization;
|
|
49
|
+
if (input.defaultCollection) c.defaultCollection = input.defaultCollection;
|
|
48
50
|
return c;
|
|
49
51
|
}
|