@agoric/access-token 0.4.22-dev-2b7c650.0 → 0.4.22-mainnet1B-dev-c0e1089.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/{tsconfig.json → jsconfig.json} +0 -3
- package/package.json +5 -12
- package/src/access-token.js +10 -23
- package/src/json-store.js +8 -36
- package/test/test-state.js +19 -3
- package/test/test-token.js +0 -17
- package/test/tmp.js +0 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/access-token",
|
|
3
|
-
"version": "0.4.22-dev-
|
|
3
|
+
"version": "0.4.22-mainnet1B-dev-c0e1089.0+c0e1089",
|
|
4
4
|
"description": "Persistent credentials for Agoric users, backed by a simple JSON file",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/access-token.js",
|
|
@@ -12,20 +12,16 @@
|
|
|
12
12
|
"test": "ava",
|
|
13
13
|
"test:c8": "c8 $C8_OPTIONS ava --config=ava-nesm.config.js",
|
|
14
14
|
"test:xs": "exit 0",
|
|
15
|
-
"lint": "run-s --continue-on-error lint:*",
|
|
16
15
|
"lint-fix": "yarn lint:eslint --fix",
|
|
17
|
-
"lint:eslint": "eslint ."
|
|
18
|
-
"lint:types": "tsc"
|
|
16
|
+
"lint:eslint": "eslint ."
|
|
19
17
|
},
|
|
20
18
|
"dependencies": {
|
|
19
|
+
"@agoric/assert": "0.6.1-mainnet1B-dev-c0e1089.0+c0e1089",
|
|
21
20
|
"n-readlines": "^1.0.0",
|
|
22
|
-
"proper-lockfile": "^4.1.2",
|
|
23
21
|
"tmp": "^0.2.1"
|
|
24
22
|
},
|
|
25
23
|
"devDependencies": {
|
|
26
|
-
"
|
|
27
|
-
"@types/proper-lockfile": "^4.1.2",
|
|
28
|
-
"ava": "^5.3.0",
|
|
24
|
+
"ava": "^5.2.0",
|
|
29
25
|
"c8": "^7.13.0"
|
|
30
26
|
},
|
|
31
27
|
"publishConfig": {
|
|
@@ -35,10 +31,7 @@
|
|
|
35
31
|
"files": [
|
|
36
32
|
"test/**/test-*.js"
|
|
37
33
|
],
|
|
38
|
-
"require": [
|
|
39
|
-
"@endo/init/debug.js"
|
|
40
|
-
],
|
|
41
34
|
"timeout": "2m"
|
|
42
35
|
},
|
|
43
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "c0e10895096f10703536fec603ebf1e2c2f948bc"
|
|
44
37
|
}
|
package/src/access-token.js
CHANGED
|
@@ -6,11 +6,6 @@ import path from 'path';
|
|
|
6
6
|
import { openJSONStore } from './json-store.js';
|
|
7
7
|
|
|
8
8
|
// Adapted from https://stackoverflow.com/a/43866992/14073862
|
|
9
|
-
/**
|
|
10
|
-
* @param {object} opts
|
|
11
|
-
* @param {BufferEncoding} [opts.stringBase]
|
|
12
|
-
* @param {number} [opts.byteLength]
|
|
13
|
-
*/
|
|
14
9
|
export function generateAccessToken({
|
|
15
10
|
stringBase = 'base64url',
|
|
16
11
|
byteLength = 48,
|
|
@@ -33,13 +28,9 @@ export function generateAccessToken({
|
|
|
33
28
|
|
|
34
29
|
/**
|
|
35
30
|
* @param {string|number} port
|
|
36
|
-
* @param {string} [sharedStateDir]
|
|
37
31
|
* @returns {Promise<string>}
|
|
38
32
|
*/
|
|
39
|
-
export async function getAccessToken(
|
|
40
|
-
port,
|
|
41
|
-
sharedStateDir = path.join(os.homedir(), '.agoric'),
|
|
42
|
-
) {
|
|
33
|
+
export async function getAccessToken(port) {
|
|
43
34
|
if (typeof port === 'string') {
|
|
44
35
|
const match = port.match(/^(.*:)?(\d+)$/);
|
|
45
36
|
if (match) {
|
|
@@ -48,22 +39,18 @@ export async function getAccessToken(
|
|
|
48
39
|
}
|
|
49
40
|
|
|
50
41
|
// Ensure we're protected with a unique accessToken for this basedir.
|
|
42
|
+
const sharedStateDir = path.join(os.homedir(), '.agoric');
|
|
51
43
|
await fs.promises.mkdir(sharedStateDir, { mode: 0o700, recursive: true });
|
|
52
44
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
storage.set(accessTokenKey, await generateAccessToken());
|
|
60
|
-
await commit();
|
|
61
|
-
}
|
|
62
|
-
accessToken = storage.get(accessTokenKey);
|
|
63
|
-
} finally {
|
|
64
|
-
await close();
|
|
45
|
+
// Ensure an access token exists.
|
|
46
|
+
const { storage, commit, close } = openJSONStore(sharedStateDir);
|
|
47
|
+
const accessTokenKey = `accessToken/${port}`;
|
|
48
|
+
if (!storage.has(accessTokenKey)) {
|
|
49
|
+
storage.set(accessTokenKey, await generateAccessToken());
|
|
50
|
+
await commit();
|
|
65
51
|
}
|
|
66
|
-
|
|
52
|
+
const accessToken = storage.get(accessTokenKey);
|
|
53
|
+
await close();
|
|
67
54
|
if (typeof accessToken !== 'string') {
|
|
68
55
|
throw Error(`Could not find access token for ${port}`);
|
|
69
56
|
}
|
package/src/json-store.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import process from 'process';
|
|
5
|
-
import lockfile from 'proper-lockfile';
|
|
6
5
|
import Readlines from 'n-readlines';
|
|
7
6
|
|
|
8
7
|
// TODO: Update this when we make a breaking change.
|
|
@@ -12,8 +11,6 @@ import Readlines from 'n-readlines';
|
|
|
12
11
|
// changed when you're just trying to use the wallet from the browser.
|
|
13
12
|
const DATA_FILE = 'swingset-kernel-state.jsonlines';
|
|
14
13
|
|
|
15
|
-
const DEFAULT_LOCK_RETRIES = 10;
|
|
16
|
-
|
|
17
14
|
/**
|
|
18
15
|
* @typedef {ReturnType<typeof makeStorageInMemory>['storage']} JSONStore
|
|
19
16
|
*/
|
|
@@ -159,37 +156,20 @@ function makeStorageInMemory() {
|
|
|
159
156
|
* @param {string} [dirPath] Path to a directory in which database files may be kept, or
|
|
160
157
|
* null.
|
|
161
158
|
* @param {boolean} [forceReset] If true, initialize the database to an empty state
|
|
162
|
-
* @param {null | import('proper-lockfile').LockOptions['retries']} [lockRetries] If null, do not lock the database.
|
|
163
159
|
*
|
|
164
|
-
* @returns {
|
|
160
|
+
* @returns {{
|
|
165
161
|
* storage: JSONStore, // a storage API object to load and store data
|
|
166
162
|
* commit: () => Promise<void>, // commit changes made since the last commit
|
|
167
163
|
* close: () => Promise<void>, // shutdown the store, abandoning any uncommitted changes
|
|
168
|
-
* }
|
|
164
|
+
* }}
|
|
169
165
|
*/
|
|
170
|
-
|
|
171
|
-
dirPath,
|
|
172
|
-
forceReset = false,
|
|
173
|
-
lockRetries = DEFAULT_LOCK_RETRIES,
|
|
174
|
-
) {
|
|
175
|
-
await null;
|
|
166
|
+
function makeJSONStore(dirPath, forceReset = false) {
|
|
176
167
|
const { storage, state } = makeStorageInMemory();
|
|
177
168
|
|
|
178
|
-
let releaseLock = async () => {};
|
|
179
169
|
let storeFile;
|
|
180
170
|
if (dirPath) {
|
|
181
171
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
182
172
|
storeFile = path.resolve(dirPath, DATA_FILE);
|
|
183
|
-
|
|
184
|
-
if (lockRetries !== null) {
|
|
185
|
-
// We need to lock the database to prevent multiple divergent instances.
|
|
186
|
-
releaseLock = await lockfile.lock(dirPath, {
|
|
187
|
-
// @ts-expect-error TS(2345) lockFilePath really does exist on LockOptions
|
|
188
|
-
lockFilePath: `${storeFile}.lock`,
|
|
189
|
-
retries: lockRetries,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
173
|
if (forceReset) {
|
|
194
174
|
safeUnlink(storeFile);
|
|
195
175
|
} else {
|
|
@@ -206,7 +186,8 @@ async function makeJSONStore(
|
|
|
206
186
|
if (lines) {
|
|
207
187
|
let line = lines.next();
|
|
208
188
|
while (line) {
|
|
209
|
-
|
|
189
|
+
// @ts-expect-error JSON.parse can take a Buffer
|
|
190
|
+
const [key, value] = JSON.parse(line);
|
|
210
191
|
storage.set(key, value);
|
|
211
192
|
line = lines.next();
|
|
212
193
|
}
|
|
@@ -214,25 +195,18 @@ async function makeJSONStore(
|
|
|
214
195
|
}
|
|
215
196
|
}
|
|
216
197
|
|
|
217
|
-
const assertNotClosed = () => {
|
|
218
|
-
if (!dirPath || storeFile) {
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
throw Error('JSON store is already closed');
|
|
222
|
-
};
|
|
223
|
-
|
|
224
198
|
/**
|
|
225
199
|
* Commit unsaved changes.
|
|
226
200
|
*/
|
|
227
201
|
async function commit() {
|
|
228
|
-
assertNotClosed();
|
|
229
202
|
if (dirPath) {
|
|
230
203
|
const tempFile = `${storeFile}-${process.pid}.tmp`;
|
|
231
204
|
const fd = fs.openSync(tempFile, 'w');
|
|
232
205
|
|
|
233
206
|
for (const [key, value] of state.entries()) {
|
|
234
207
|
const line = JSON.stringify([key, value]);
|
|
235
|
-
fs.writeSync(fd,
|
|
208
|
+
fs.writeSync(fd, line);
|
|
209
|
+
fs.writeSync(fd, '\n');
|
|
236
210
|
}
|
|
237
211
|
fs.closeSync(fd);
|
|
238
212
|
fs.renameSync(tempFile, storeFile);
|
|
@@ -244,9 +218,7 @@ async function makeJSONStore(
|
|
|
244
218
|
* (if you want to save them, call commit() first).
|
|
245
219
|
*/
|
|
246
220
|
async function close() {
|
|
247
|
-
|
|
248
|
-
storeFile = undefined;
|
|
249
|
-
await releaseLock();
|
|
221
|
+
// Nothing to do here.
|
|
250
222
|
}
|
|
251
223
|
|
|
252
224
|
return { storage, commit, close };
|
package/test/test-state.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import tmp from 'tmp';
|
|
2
|
+
|
|
1
3
|
import test from 'ava';
|
|
2
|
-
import { tmpDir } from './tmp.js';
|
|
3
4
|
import {
|
|
4
5
|
initJSONStore,
|
|
5
6
|
openJSONStore,
|
|
@@ -7,6 +8,21 @@ import {
|
|
|
7
8
|
isJSONStore,
|
|
8
9
|
} from '../src/json-store.js';
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} [prefix]
|
|
13
|
+
* @returns {Promise<[string, () => void]>}
|
|
14
|
+
*/
|
|
15
|
+
const tmpDir = prefix =>
|
|
16
|
+
new Promise((resolve, reject) => {
|
|
17
|
+
tmp.dir({ unsafeCleanup: true, prefix }, (err, name, removeCallback) => {
|
|
18
|
+
if (err) {
|
|
19
|
+
reject(err);
|
|
20
|
+
} else {
|
|
21
|
+
resolve([name, removeCallback]);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
10
26
|
function testStorage(t, storage) {
|
|
11
27
|
t.falsy(storage.has('missing'));
|
|
12
28
|
t.is(storage.get('missing'), undefined);
|
|
@@ -42,14 +58,14 @@ test('storageInFile', async t => {
|
|
|
42
58
|
const [dbDir, cleanup] = await tmpDir('testdb');
|
|
43
59
|
t.teardown(cleanup);
|
|
44
60
|
t.is(isJSONStore(dbDir), false);
|
|
45
|
-
const { storage, commit, close } =
|
|
61
|
+
const { storage, commit, close } = initJSONStore(dbDir);
|
|
46
62
|
testStorage(t, storage);
|
|
47
63
|
await commit();
|
|
48
64
|
const before = getAllState(storage);
|
|
49
65
|
await close();
|
|
50
66
|
t.is(isJSONStore(dbDir), true);
|
|
51
67
|
|
|
52
|
-
const { storage: after } =
|
|
68
|
+
const { storage: after } = openJSONStore(dbDir);
|
|
53
69
|
t.deepEqual(getAllState(after), before, 'check state after reread');
|
|
54
70
|
t.is(isJSONStore(dbDir), true);
|
|
55
71
|
});
|
package/test/test-token.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import test from 'ava';
|
|
2
|
-
import { tmpDir } from './tmp.js';
|
|
3
|
-
|
|
4
|
-
import { getAccessToken } from '../src/access-token.js';
|
|
5
|
-
|
|
6
|
-
test('access tokens', async t => {
|
|
7
|
-
const [sharedStateDir, removeCallback] = await tmpDir('access-token-test');
|
|
8
|
-
const [a, b, c] = await Promise.all([
|
|
9
|
-
getAccessToken(1234, sharedStateDir),
|
|
10
|
-
getAccessToken(1234, sharedStateDir),
|
|
11
|
-
getAccessToken(1234, sharedStateDir),
|
|
12
|
-
]);
|
|
13
|
-
|
|
14
|
-
t.is(a, b);
|
|
15
|
-
t.is(a, c);
|
|
16
|
-
await removeCallback();
|
|
17
|
-
});
|
package/test/tmp.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import tmp from 'tmp';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @param {string} [prefix]
|
|
5
|
-
* @returns {Promise<[string, () => void]>}
|
|
6
|
-
*/
|
|
7
|
-
export const tmpDir = prefix =>
|
|
8
|
-
new Promise((resolve, reject) => {
|
|
9
|
-
// We use `unsafeCleanup` because we want to remove the directory even if it
|
|
10
|
-
// still contains files.
|
|
11
|
-
const unsafeCleanup = true;
|
|
12
|
-
tmp.dir({ unsafeCleanup, prefix }, (err, name, removeCallback) => {
|
|
13
|
-
if (err) {
|
|
14
|
-
reject(err);
|
|
15
|
-
} else {
|
|
16
|
-
resolve([name, removeCallback]);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
});
|