axvault 1.0.0 → 1.2.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 +38 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +20 -3
- package/dist/commands/credential.js +4 -2
- package/dist/commands/key-create.d.ts +13 -0
- package/dist/commands/key-create.js +99 -0
- package/dist/commands/key-list.d.ts +9 -0
- package/dist/commands/key-list.js +43 -0
- package/dist/commands/key-revoke.d.ts +8 -0
- package/dist/commands/key-revoke.js +47 -0
- package/dist/commands/key-update.d.ts +15 -0
- package/dist/commands/key-update.js +109 -0
- package/dist/commands/key.d.ts +6 -18
- package/dist/commands/key.js +6 -154
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.js +35 -1
- package/dist/db/repositories/api-keys.d.ts +11 -1
- package/dist/db/repositories/api-keys.js +34 -5
- package/dist/db/repositories/audit-log.d.ts +1 -1
- package/dist/db/repositories/credentials.d.ts +6 -1
- package/dist/db/repositories/credentials.js +15 -9
- package/dist/db/types.d.ts +4 -1
- package/dist/handlers/get-credential.js +28 -25
- package/dist/handlers/list-credentials.js +1 -0
- package/dist/handlers/put-credential.js +20 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/format.d.ts +1 -0
- package/dist/lib/format.js +2 -1
- package/dist/lib/parse-access-options.d.ts +37 -0
- package/dist/lib/parse-access-options.js +84 -0
- package/dist/refresh/refresh-manager.d.ts +3 -1
- package/dist/refresh/refresh-manager.js +5 -3
- package/package.json +3 -3
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse and validate access list options from CLI flags.
|
|
3
|
+
*/
|
|
4
|
+
import { getAccessListErrorMessage, normalizeAccessList, parseAccessList, } from "./format.js";
|
|
5
|
+
/**
|
|
6
|
+
* Parse all access list options and validate them.
|
|
7
|
+
* Returns parsed entries or logs error and sets exit code.
|
|
8
|
+
*/
|
|
9
|
+
export function parseAccessOptions(options) {
|
|
10
|
+
const addReadResult = parseAccessList(options.addRead);
|
|
11
|
+
const addWriteResult = parseAccessList(options.addWrite);
|
|
12
|
+
const addGrantResult = parseAccessList(options.addGrant);
|
|
13
|
+
const removeReadResult = parseAccessList(options.removeRead);
|
|
14
|
+
const removeWriteResult = parseAccessList(options.removeWrite);
|
|
15
|
+
const removeGrantResult = parseAccessList(options.removeGrant);
|
|
16
|
+
if (addReadResult.error) {
|
|
17
|
+
console.error(`Error in --add-read: ${getAccessListErrorMessage(addReadResult.error)}`);
|
|
18
|
+
process.exitCode = 2;
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
if (addWriteResult.error) {
|
|
22
|
+
console.error(`Error in --add-write: ${getAccessListErrorMessage(addWriteResult.error)}`);
|
|
23
|
+
process.exitCode = 2;
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (addGrantResult.error) {
|
|
27
|
+
console.error(`Error in --add-grant: ${getAccessListErrorMessage(addGrantResult.error)}`);
|
|
28
|
+
process.exitCode = 2;
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
if (removeReadResult.error) {
|
|
32
|
+
console.error(`Error in --remove-read: ${getAccessListErrorMessage(removeReadResult.error)}`);
|
|
33
|
+
process.exitCode = 2;
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
if (removeWriteResult.error) {
|
|
37
|
+
console.error(`Error in --remove-write: ${getAccessListErrorMessage(removeWriteResult.error)}`);
|
|
38
|
+
process.exitCode = 2;
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
if (removeGrantResult.error) {
|
|
42
|
+
console.error(`Error in --remove-grant: ${getAccessListErrorMessage(removeGrantResult.error)}`);
|
|
43
|
+
process.exitCode = 2;
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
addRead: addReadResult.entries,
|
|
48
|
+
addWrite: addWriteResult.entries,
|
|
49
|
+
addGrant: addGrantResult.entries,
|
|
50
|
+
removeRead: removeReadResult.entries,
|
|
51
|
+
removeWrite: removeWriteResult.entries,
|
|
52
|
+
removeGrant: removeGrantResult.entries,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compute updated access list by adding and removing entries.
|
|
57
|
+
*/
|
|
58
|
+
export function computeUpdatedAccess(current, toAdd, toRemove) {
|
|
59
|
+
const set = new Set(current);
|
|
60
|
+
for (const entry of toAdd)
|
|
61
|
+
set.add(entry);
|
|
62
|
+
for (const entry of toRemove)
|
|
63
|
+
set.delete(entry);
|
|
64
|
+
return [...set];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Normalize all access lists and print warnings.
|
|
68
|
+
*/
|
|
69
|
+
export function normalizeAllAccess(readAccess, writeAccess, grantAccess) {
|
|
70
|
+
const readNorm = normalizeAccessList(readAccess, "read");
|
|
71
|
+
const writeNorm = normalizeAccessList(writeAccess, "write");
|
|
72
|
+
const grantNorm = normalizeAccessList(grantAccess, "grant");
|
|
73
|
+
if (readNorm.warning)
|
|
74
|
+
console.warn(`Warning: ${readNorm.warning}`);
|
|
75
|
+
if (writeNorm.warning)
|
|
76
|
+
console.warn(`Warning: ${writeNorm.warning}`);
|
|
77
|
+
if (grantNorm.warning)
|
|
78
|
+
console.warn(`Warning: ${grantNorm.warning}`);
|
|
79
|
+
return {
|
|
80
|
+
read: readNorm.normalized,
|
|
81
|
+
write: writeNorm.normalized,
|
|
82
|
+
grant: grantNorm.normalized,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* with axauth's refresh functionality.
|
|
6
6
|
*/
|
|
7
7
|
import type Database from "better-sqlite3";
|
|
8
|
+
import type { CredentialType } from "../db/repositories/credentials.js";
|
|
8
9
|
/** Key for credential-specific mutex */
|
|
9
10
|
type CredentialKey = `${string}/${string}`;
|
|
10
11
|
/** Result type for the full refresh operation */
|
|
@@ -40,12 +41,13 @@ declare function buildKey(agent: string, name: string): CredentialKey;
|
|
|
40
41
|
* @param database - Database connection
|
|
41
42
|
* @param agent - Agent name
|
|
42
43
|
* @param name - Credential name
|
|
44
|
+
* @param type - Credential type (must be "oauth"; caller must gate on type)
|
|
43
45
|
* @param data - Current decrypted credential data
|
|
44
46
|
* @param apiKeyId - API key ID for audit logging
|
|
45
47
|
* @param originalUpdatedAt - Original updatedAt for optimistic locking
|
|
46
48
|
* @param options - Refresh options
|
|
47
49
|
* @returns Refresh result with new data, expiresAt, updatedAt or error
|
|
48
50
|
*/
|
|
49
|
-
declare function refreshWithMutex(database: Database.Database, agent: string, name: string, data: Record<string, unknown>, apiKeyId: string, originalUpdatedAt: Date, options: RefreshManagerOptions): Promise<FullRefreshResult>;
|
|
51
|
+
declare function refreshWithMutex(database: Database.Database, agent: string, name: string, type: CredentialType, data: Record<string, unknown>, apiKeyId: string, originalUpdatedAt: Date, options: RefreshManagerOptions): Promise<FullRefreshResult>;
|
|
50
52
|
export { extractExpiryDate, isRefreshable, needsRefresh, toAxauthCredentials, } from "./check-refresh.js";
|
|
51
53
|
export { buildKey, refreshWithMutex };
|
|
@@ -19,7 +19,7 @@ function buildKey(agent, name) {
|
|
|
19
19
|
* Execute the full refresh operation: call axauth, validate, persist.
|
|
20
20
|
* This is the inner operation that gets stored in the mutex.
|
|
21
21
|
*/
|
|
22
|
-
async function executeRefresh(database, agent, name, data, apiKeyId, originalUpdatedAt, refreshStartedAt, options) {
|
|
22
|
+
async function executeRefresh(database, agent, name, type, data, apiKeyId, originalUpdatedAt, refreshStartedAt, options) {
|
|
23
23
|
const logContext = { database, apiKeyId, agent, name };
|
|
24
24
|
// Map to axauth format
|
|
25
25
|
const creds = toAxauthCredentials(agent, data);
|
|
@@ -54,6 +54,7 @@ async function executeRefresh(database, agent, name, data, apiKeyId, originalUpd
|
|
|
54
54
|
upsertCredential(database, {
|
|
55
55
|
agent,
|
|
56
56
|
name,
|
|
57
|
+
type,
|
|
57
58
|
...encrypted,
|
|
58
59
|
expiresAt,
|
|
59
60
|
});
|
|
@@ -98,13 +99,14 @@ async function executeRefresh(database, agent, name, data, apiKeyId, originalUpd
|
|
|
98
99
|
* @param database - Database connection
|
|
99
100
|
* @param agent - Agent name
|
|
100
101
|
* @param name - Credential name
|
|
102
|
+
* @param type - Credential type (must be "oauth"; caller must gate on type)
|
|
101
103
|
* @param data - Current decrypted credential data
|
|
102
104
|
* @param apiKeyId - API key ID for audit logging
|
|
103
105
|
* @param originalUpdatedAt - Original updatedAt for optimistic locking
|
|
104
106
|
* @param options - Refresh options
|
|
105
107
|
* @returns Refresh result with new data, expiresAt, updatedAt or error
|
|
106
108
|
*/
|
|
107
|
-
async function refreshWithMutex(database, agent, name, data, apiKeyId, originalUpdatedAt, options) {
|
|
109
|
+
async function refreshWithMutex(database, agent, name, type, data, apiKeyId, originalUpdatedAt, options) {
|
|
108
110
|
const key = buildKey(agent, name);
|
|
109
111
|
// Check if refresh already in progress
|
|
110
112
|
const existing = pendingRefreshes.get(key);
|
|
@@ -115,7 +117,7 @@ async function refreshWithMutex(database, agent, name, data, apiKeyId, originalU
|
|
|
115
117
|
}
|
|
116
118
|
// Start new refresh - store promise for the FULL operation
|
|
117
119
|
const refreshStartedAt = new Date();
|
|
118
|
-
const fullOperationPromise = executeRefresh(database, agent, name, data, apiKeyId, originalUpdatedAt, refreshStartedAt, options);
|
|
120
|
+
const fullOperationPromise = executeRefresh(database, agent, name, type, data, apiKeyId, originalUpdatedAt, refreshStartedAt, options);
|
|
119
121
|
pendingRefreshes.set(key, {
|
|
120
122
|
promise: fullOperationPromise,
|
|
121
123
|
startedAt: refreshStartedAt,
|
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"name": "axvault",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "1.
|
|
6
|
-
"description": "Remote credential storage server for
|
|
5
|
+
"version": "1.2.0",
|
|
6
|
+
"description": "Remote credential storage server for axkit",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/Jercik/axvault.git"
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"@types/node": "^25.0.3",
|
|
82
82
|
"@vitest/coverage-v8": "^4.0.16",
|
|
83
83
|
"eslint": "^9.39.2",
|
|
84
|
-
"eslint-config-
|
|
84
|
+
"eslint-config-axkit": "^1.0.0",
|
|
85
85
|
"fta-check": "^1.5.1",
|
|
86
86
|
"fta-cli": "^3.0.0",
|
|
87
87
|
"knip": "^5.80.0",
|