latchkey 3.9.0 → 3.10.1
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 +3 -3
- package/dist/src/cliCommands.d.ts.map +1 -1
- package/dist/src/cliCommands.js +26 -8
- package/dist/src/cliCommands.js.map +1 -1
- package/dist/src/dailyCounting.d.ts +3 -0
- package/dist/src/dailyCounting.d.ts.map +1 -1
- package/dist/src/dailyCounting.js +6 -2
- package/dist/src/dailyCounting.js.map +1 -1
- package/dist/src/services/core/loginFlows/registry.d.ts.map +1 -1
- package/dist/src/services/core/loginFlows/registry.js +11 -6
- package/dist/src/services/core/loginFlows/registry.js.map +1 -1
- package/dist/src/services/core/loginFlows/tokenCapture.d.ts +74 -0
- package/dist/src/services/core/loginFlows/tokenCapture.d.ts.map +1 -0
- package/dist/src/services/core/loginFlows/tokenCapture.js +201 -0
- package/dist/src/services/core/loginFlows/tokenCapture.js.map +1 -0
- package/dist/src/services/index.d.ts +1 -0
- package/dist/src/services/index.d.ts.map +1 -1
- package/dist/src/services/index.js +1 -0
- package/dist/src/services/index.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tests/cli.test.js +118 -0
- package/dist/tests/cli.test.js.map +1 -1
- package/dist/tests/dailyCounting.test.js +36 -1
- package/dist/tests/dailyCounting.test.js.map +1 -1
- package/dist/tests/tokenCapture.test.d.ts +10 -0
- package/dist/tests/tokenCapture.test.d.ts.map +1 -0
- package/dist/tests/tokenCapture.test.js +136 -0
- package/dist/tests/tokenCapture.test.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A generic browser login for services whose credential is a token the web app
|
|
3
|
+
* mints for itself: open a URL, watch the responses that arrive while the user
|
|
4
|
+
* signs in, and lift a token out of the JSON body of a named endpoint.
|
|
5
|
+
*
|
|
6
|
+
* This is the counterpart to `cookie-capture`, for the other common shape. In a
|
|
7
|
+
* cookie-authenticated service the cookie *is* the API credential, so capturing
|
|
8
|
+
* it is enough. In a token-minting one the cookie authenticates the *page*,
|
|
9
|
+
* which then calls an endpoint of its own to obtain a short-lived token, and it
|
|
10
|
+
* is that token the API wants — capturing the cookie would store something the
|
|
11
|
+
* API refuses. Single-page apps built on NextAuth are the common case, where
|
|
12
|
+
* the mint endpoint is conventionally `/api/auth/session`.
|
|
13
|
+
*
|
|
14
|
+
* Nothing here is service-specific: which endpoint to watch, which field holds
|
|
15
|
+
* the token, and which header carries it all come from whoever registered the
|
|
16
|
+
* service, as the parameters of the `token-capture` login flow.
|
|
17
|
+
*
|
|
18
|
+
* Two consequences of capturing a minted token are worth knowing, and are the
|
|
19
|
+
* registrant's to accept rather than this flow's to solve:
|
|
20
|
+
*
|
|
21
|
+
* - **The token expires, and there is no refresh path.** The refresh material
|
|
22
|
+
* is the browser session, which stays in the browser profile and never
|
|
23
|
+
* reaches us. A credential going invalid is routine; signing in again mints
|
|
24
|
+
* a fresh one.
|
|
25
|
+
* - **The token is only as scoped as the service says.** A minted token is
|
|
26
|
+
* usually broader than an API key a user would issue deliberately.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
import { RawCurlCredentials } from '../../../apiCredentials/base.js';
|
|
30
|
+
import { SimpleServiceSession } from '../base.js';
|
|
31
|
+
import { parseLoginFlowParams } from './base.js';
|
|
32
|
+
/** The placeholder a header template puts the captured token in. */
|
|
33
|
+
const TOKEN_PLACEHOLDER = '{token}';
|
|
34
|
+
/**
|
|
35
|
+
* The header a captured token goes into when the registration does not say.
|
|
36
|
+
* Bearer is what the great majority of token-minting web APIs expect.
|
|
37
|
+
*/
|
|
38
|
+
const DEFAULT_HEADER_TEMPLATE = `Authorization: Bearer ${TOKEN_PLACEHOLDER}`;
|
|
39
|
+
/**
|
|
40
|
+
* Identity of an endpoint for matching purposes: origin and path, with the
|
|
41
|
+
* query string and fragment dropped.
|
|
42
|
+
*
|
|
43
|
+
* Matching this way rather than on the URL verbatim is deliberate. A mint
|
|
44
|
+
* endpoint is commonly called with a cache-busting or callback parameter the
|
|
45
|
+
* registrant cannot predict — NextAuth appends one to `/api/auth/session` — and
|
|
46
|
+
* requiring the whole URL to match would silently never fire. The alternative,
|
|
47
|
+
* letting registrations carry a regular expression, puts an injection and
|
|
48
|
+
* catastrophic-backtracking surface into stored configuration to buy a
|
|
49
|
+
* generality nothing has asked for.
|
|
50
|
+
*/
|
|
51
|
+
function endpointIdentity(url) {
|
|
52
|
+
return `${url.origin}${url.pathname}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Read a dotted path out of parsed JSON — `accessToken`, or `data.token` when
|
|
56
|
+
* the endpoint nests it.
|
|
57
|
+
*
|
|
58
|
+
* Returns null for anything that is not a non-empty string, which is what makes
|
|
59
|
+
* the token's *presence* the completion signal: a signed-out visitor commonly
|
|
60
|
+
* gets the same endpoint answering `{}` or `{"accessToken": null}`, and the
|
|
61
|
+
* request having happened must not be mistaken for a finished login.
|
|
62
|
+
*/
|
|
63
|
+
function readTokenAtPath(body, path) {
|
|
64
|
+
let current = body;
|
|
65
|
+
for (const segment of path) {
|
|
66
|
+
if (typeof current !== 'object' || current === null || Array.isArray(current)) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
current = current[segment];
|
|
70
|
+
}
|
|
71
|
+
return typeof current === 'string' && current !== '' ? current : null;
|
|
72
|
+
}
|
|
73
|
+
const TokenCaptureParamsSchema = z
|
|
74
|
+
.object({
|
|
75
|
+
/**
|
|
76
|
+
* The endpoint whose response carries the token. Compared by origin and
|
|
77
|
+
* path, so a query string does not have to be predicted.
|
|
78
|
+
*/
|
|
79
|
+
tokenUrl: z.string().url(),
|
|
80
|
+
/**
|
|
81
|
+
* Where the token sits in the JSON body. Dotted for a nested field; each
|
|
82
|
+
* segment is a literal key, so a key containing a dot cannot be addressed —
|
|
83
|
+
* no such endpoint has come up, and leaving it out keeps the syntax
|
|
84
|
+
* obvious.
|
|
85
|
+
*/
|
|
86
|
+
tokenField: z.string().min(1),
|
|
87
|
+
/**
|
|
88
|
+
* The header the token is stored as. Must contain `{token}`, and must look
|
|
89
|
+
* like a header, so that a mistake here is caught at registration rather
|
|
90
|
+
* than as a puzzling 401 after a successful-looking login.
|
|
91
|
+
*/
|
|
92
|
+
header: z
|
|
93
|
+
.string()
|
|
94
|
+
.min(1)
|
|
95
|
+
.refine((value) => value.includes(TOKEN_PLACEHOLDER), {
|
|
96
|
+
message: `must contain the ${TOKEN_PLACEHOLDER} placeholder`,
|
|
97
|
+
})
|
|
98
|
+
.refine((value) => /^[^:\s]+:/.test(value), {
|
|
99
|
+
message: "must be a header, as in 'Authorization: Bearer {token}'",
|
|
100
|
+
})
|
|
101
|
+
.optional(),
|
|
102
|
+
})
|
|
103
|
+
.strict();
|
|
104
|
+
/** Store the captured token the way `latchkey auth set -H` would. */
|
|
105
|
+
function buildTokenCredentials(headerTemplate, token) {
|
|
106
|
+
return new RawCurlCredentials(['-H', headerTemplate.replaceAll(TOKEN_PLACEHOLDER, token)]);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* One run of the flow: responses go by, and the first one from the mint
|
|
110
|
+
* endpoint that carries a token ends the login.
|
|
111
|
+
*/
|
|
112
|
+
class TokenCaptureSession extends SimpleServiceSession {
|
|
113
|
+
tokenEndpointIdentity;
|
|
114
|
+
tokenPath;
|
|
115
|
+
headerTemplate;
|
|
116
|
+
constructor(service, appNamePrefix, tokenUrl, tokenPath, headerTemplate) {
|
|
117
|
+
super(service, appNamePrefix);
|
|
118
|
+
this.tokenEndpointIdentity = endpointIdentity(tokenUrl);
|
|
119
|
+
this.tokenPath = tokenPath;
|
|
120
|
+
this.headerTemplate = headerTemplate;
|
|
121
|
+
}
|
|
122
|
+
async getApiCredentialsFromResponse(response) {
|
|
123
|
+
let responseUrl;
|
|
124
|
+
try {
|
|
125
|
+
responseUrl = new URL(response.url());
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Not a URL we can compare — a data: or blob: response, say.
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
if (endpointIdentity(responseUrl) !== this.tokenEndpointIdentity) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
let token;
|
|
135
|
+
try {
|
|
136
|
+
token = readTokenAtPath(JSON.parse(await response.text()), this.tokenPath);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Unreadable or not JSON: the endpoint answered with something this
|
|
140
|
+
// registration does not describe. Keep waiting rather than fail the
|
|
141
|
+
// login — the response that carries the token may still be coming.
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
return token === null ? null : buildTokenCredentials(this.headerTemplate, token);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The flow. Its statics are the kind of automation — what `--login-flow`
|
|
149
|
+
* selects — and an instance is that kind configured with one service's
|
|
150
|
+
* parameters, ready to create a session per login.
|
|
151
|
+
*/
|
|
152
|
+
export class TokenCaptureLoginFlow {
|
|
153
|
+
/** Value of `--login-flow`. Not `name`, which every class already has. */
|
|
154
|
+
static flowName = 'token-capture';
|
|
155
|
+
static summary = 'Open the login URL and capture a token the web app mints for itself, from the JSON ' +
|
|
156
|
+
'body of a named endpoint.';
|
|
157
|
+
static details = [
|
|
158
|
+
'Parameters:',
|
|
159
|
+
' tokenUrl Full URL, including the scheme, of the endpoint whose response',
|
|
160
|
+
' carries the token. Required. Matched on origin and path, so a',
|
|
161
|
+
' query string the app appends does not have to be predicted.',
|
|
162
|
+
' tokenField Where the token sits in the JSON body. Required. Dotted for a',
|
|
163
|
+
' nested field, as in "data.accessToken".',
|
|
164
|
+
' header Header to store the token as. Optional; defaults to',
|
|
165
|
+
` "${DEFAULT_HEADER_TEMPLATE}". Must contain ${TOKEN_PLACEHOLDER}.`,
|
|
166
|
+
'',
|
|
167
|
+
'For services where the cookie authenticates the page rather than the API,',
|
|
168
|
+
'and the page calls an endpoint of its own to mint a short-lived token. Use',
|
|
169
|
+
'cookie-capture instead when the cookie is itself the API credential.',
|
|
170
|
+
'',
|
|
171
|
+
'The login finishes when that endpoint answers with a non-empty token, not',
|
|
172
|
+
'when it is merely requested: a signed-out visitor commonly gets the same',
|
|
173
|
+
'endpoint answering `{}`. A token minted this way expires and cannot be',
|
|
174
|
+
'refreshed — the refresh material stays in the browser — so expect to sign',
|
|
175
|
+
'in again periodically.',
|
|
176
|
+
'',
|
|
177
|
+
'Example:',
|
|
178
|
+
' $ latchkey services register my-app \\',
|
|
179
|
+
' --base-api-url="https://app.example.com/backend-api/" \\',
|
|
180
|
+
' --login-url="https://app.example.com/auth/login" \\',
|
|
181
|
+
' --login-flow=token-capture \\',
|
|
182
|
+
' --login-flow-params=\'{"tokenUrl": "https://app.example.com/api/auth/session",',
|
|
183
|
+
' "tokenField": "accessToken"}\'',
|
|
184
|
+
].join('\n');
|
|
185
|
+
static paramsSchema = TokenCaptureParamsSchema;
|
|
186
|
+
params;
|
|
187
|
+
constructor(rawParams) {
|
|
188
|
+
this.params = parseLoginFlowParams(TokenCaptureLoginFlow, rawParams);
|
|
189
|
+
}
|
|
190
|
+
describe(loginUrl) {
|
|
191
|
+
return (`\`latchkey auth browser\` opens ${loginUrl} and stores the '${this.params.tokenField}' ` +
|
|
192
|
+
`field of ${this.params.tokenUrl} as the credentials once it carries a token.`);
|
|
193
|
+
}
|
|
194
|
+
createSession(service, appNamePrefix) {
|
|
195
|
+
return new TokenCaptureSession(service, appNamePrefix, new URL(this.params.tokenUrl), this.params.tokenField.split('.'), this.params.header ?? DEFAULT_HEADER_TEMPLATE);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// TypeScript has no `static implements`, so this is where the class side of the
|
|
199
|
+
// flow is checked. The registry checks it again, but the error lands here.
|
|
200
|
+
TokenCaptureLoginFlow;
|
|
201
|
+
//# sourceMappingURL=tokenCapture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenCapture.js","sourceRoot":"","sources":["../../../../../src/services/core/loginFlows/tokenCapture.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ export { RegisteredService } from './core/registered.js';
|
|
|
7
7
|
export { LOGIN_FLOWS, UnknownLoginFlowError, formatLoginFlowsHelp, resolveLoginFlow, } from './core/loginFlows/registry.js';
|
|
8
8
|
export { LoginFlowParamsInvalidError, parseLoginFlowParams, type LoginFlow, type LoginFlowClass, } from './core/loginFlows/base.js';
|
|
9
9
|
export { CookieCaptureLoginFlow } from './core/loginFlows/cookieCapture.js';
|
|
10
|
+
export { TokenCaptureLoginFlow } from './core/loginFlows/tokenCapture.js';
|
|
10
11
|
export { Slack, SLACK } from './slack.js';
|
|
11
12
|
export { Discord, DISCORD } from './discord.js';
|
|
12
13
|
export { Github, GITHUB } from './github.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,OAAO,EACP,cAAc,EACd,oBAAoB,EACpB,6BAA6B,EAC7B,YAAY,EACZ,2BAA2B,EAC3B,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,kCAAkC,EAClC,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,cAAc,GACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ export { RegisteredService } from './core/registered.js';
|
|
|
7
7
|
export { LOGIN_FLOWS, UnknownLoginFlowError, formatLoginFlowsHelp, resolveLoginFlow, } from './core/loginFlows/registry.js';
|
|
8
8
|
export { LoginFlowParamsInvalidError, parseLoginFlowParams, } from './core/loginFlows/base.js';
|
|
9
9
|
export { CookieCaptureLoginFlow } from './core/loginFlows/cookieCapture.js';
|
|
10
|
+
export { TokenCaptureLoginFlow } from './core/loginFlows/tokenCapture.js';
|
|
10
11
|
export { Slack, SLACK } from './slack.js';
|
|
11
12
|
export { Discord, DISCORD } from './discord.js';
|
|
12
13
|
export { Github, GITHUB } from './github.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,OAAO,EACP,cAAc,EACd,oBAAoB,EACpB,6BAA6B,EAC7B,YAAY,EACZ,2BAA2B,GAE5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,kCAAkC,EAClC,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,oBAAoB,GAGrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/index.ts"],"names":[],"mappings":"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"}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "3.
|
|
1
|
+
export declare const VERSION = "3.10.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,WAAW,CAAC"}
|
package/dist/src/version.js
CHANGED
package/dist/src/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,yEAAyE;AACzE,MAAM,CAAC,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,yEAAyE;AACzE,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}
|
package/dist/tests/cli.test.js
CHANGED
|
@@ -1001,6 +1001,14 @@ describe('CLI commands with dependency injection', () => {
|
|
|
1001
1001
|
function writeStore(contents, preparations = {}) {
|
|
1002
1002
|
writeSecureFile(join(tempDir, STORE_FILENAME), JSON.stringify({ ...nestAccounts(contents), preparations }));
|
|
1003
1003
|
}
|
|
1004
|
+
// Write an on-disk store with explicit account keys.
|
|
1005
|
+
function writeAccountStore(credentials, preparations = {}) {
|
|
1006
|
+
writeSecureFile(join(tempDir, STORE_FILENAME), JSON.stringify({ credentials, preparations }));
|
|
1007
|
+
}
|
|
1008
|
+
function readAccountsWithKey(path, key) {
|
|
1009
|
+
const raw = JSON.parse(new EncryptedStorage(key).readFile(path) ?? '{}');
|
|
1010
|
+
return raw.credentials ?? {};
|
|
1011
|
+
}
|
|
1004
1012
|
function readPreparationsWithKey(path, key) {
|
|
1005
1013
|
const raw = JSON.parse(new EncryptedStorage(key).readFile(path) ?? '{}');
|
|
1006
1014
|
return raw.preparations ?? {};
|
|
@@ -1125,6 +1133,116 @@ describe('CLI commands with dependency injection', () => {
|
|
|
1125
1133
|
gitlab: GITLAB_PREPARATION,
|
|
1126
1134
|
});
|
|
1127
1135
|
});
|
|
1136
|
+
it('only includes the given account when using --account', async () => {
|
|
1137
|
+
writeAccountStore({
|
|
1138
|
+
slack: {
|
|
1139
|
+
'work@example.com': { objectType: 'slack', token: 'work-tok', dCookie: 'work-cookie' },
|
|
1140
|
+
'home@example.com': { objectType: 'slack', token: 'home-tok', dCookie: 'home-cookie' },
|
|
1141
|
+
},
|
|
1142
|
+
gitlab: {
|
|
1143
|
+
'work@example.com': { objectType: 'authorizationBearer', token: 'gitlab-token' },
|
|
1144
|
+
},
|
|
1145
|
+
}, { gitlab: GITLAB_PREPARATION });
|
|
1146
|
+
const destinationDirectory = join(tempDir, 'export');
|
|
1147
|
+
const deps = withStdinKey(NEW_ENCRYPTION_KEY);
|
|
1148
|
+
await runCommand(['--account', 'work@example.com', 'auth', 're-encrypt', destinationDirectory], deps);
|
|
1149
|
+
expect(exitCode).toBeNull();
|
|
1150
|
+
const destination = join(destinationDirectory, STORE_FILENAME);
|
|
1151
|
+
expect(readAccountsWithKey(destination, NEW_ENCRYPTION_KEY)).toEqual({
|
|
1152
|
+
slack: {
|
|
1153
|
+
'work@example.com': { objectType: 'slack', token: 'work-tok', dCookie: 'work-cookie' },
|
|
1154
|
+
},
|
|
1155
|
+
gitlab: {
|
|
1156
|
+
'work@example.com': { objectType: 'authorizationBearer', token: 'gitlab-token' },
|
|
1157
|
+
},
|
|
1158
|
+
});
|
|
1159
|
+
// Preparations are not account-specific and are carried over as usual.
|
|
1160
|
+
expect(readPreparationsWithKey(destination, NEW_ENCRYPTION_KEY)).toEqual({
|
|
1161
|
+
gitlab: GITLAB_PREPARATION,
|
|
1162
|
+
});
|
|
1163
|
+
});
|
|
1164
|
+
it('keeps the other accounts of the destination when using --account', async () => {
|
|
1165
|
+
writeAccountStore({
|
|
1166
|
+
slack: {
|
|
1167
|
+
'work@example.com': { objectType: 'slack', token: 'new-tok', dCookie: 'new-cookie' },
|
|
1168
|
+
},
|
|
1169
|
+
});
|
|
1170
|
+
const destinationDirectory = join(tempDir, 'export');
|
|
1171
|
+
const destination = join(destinationDirectory, STORE_FILENAME);
|
|
1172
|
+
stampDataFormatVersion(destinationDirectory);
|
|
1173
|
+
new EncryptedStorage(NEW_ENCRYPTION_KEY).writeFile(destination, JSON.stringify({
|
|
1174
|
+
credentials: {
|
|
1175
|
+
slack: {
|
|
1176
|
+
'work@example.com': { objectType: 'slack', token: 'old-tok', dCookie: 'old-cookie' },
|
|
1177
|
+
'home@example.com': {
|
|
1178
|
+
objectType: 'slack',
|
|
1179
|
+
token: 'home-tok',
|
|
1180
|
+
dCookie: 'home-cookie',
|
|
1181
|
+
},
|
|
1182
|
+
},
|
|
1183
|
+
},
|
|
1184
|
+
preparations: {},
|
|
1185
|
+
}));
|
|
1186
|
+
const deps = withStdinKey(NEW_ENCRYPTION_KEY);
|
|
1187
|
+
await runCommand(['--account', 'work@example.com', 'auth', 're-encrypt', destinationDirectory], deps);
|
|
1188
|
+
expect(exitCode).toBeNull();
|
|
1189
|
+
expect(readAccountsWithKey(destination, NEW_ENCRYPTION_KEY)).toEqual({
|
|
1190
|
+
slack: {
|
|
1191
|
+
'work@example.com': { objectType: 'slack', token: 'new-tok', dCookie: 'new-cookie' },
|
|
1192
|
+
'home@example.com': { objectType: 'slack', token: 'home-tok', dCookie: 'home-cookie' },
|
|
1193
|
+
},
|
|
1194
|
+
});
|
|
1195
|
+
});
|
|
1196
|
+
it('leaves the destination account alone when the source does not have it', async () => {
|
|
1197
|
+
writeAccountStore({
|
|
1198
|
+
slack: {
|
|
1199
|
+
'work@example.com': { objectType: 'slack', token: 'work-tok', dCookie: 'work-cookie' },
|
|
1200
|
+
},
|
|
1201
|
+
}, { gitlab: GITLAB_PREPARATION });
|
|
1202
|
+
const destinationDirectory = join(tempDir, 'export');
|
|
1203
|
+
const destination = join(destinationDirectory, STORE_FILENAME);
|
|
1204
|
+
stampDataFormatVersion(destinationDirectory);
|
|
1205
|
+
new EncryptedStorage(NEW_ENCRYPTION_KEY).writeFile(destination, JSON.stringify({
|
|
1206
|
+
credentials: {
|
|
1207
|
+
// gitlab only has a preparation in the source, so its credentials
|
|
1208
|
+
// in the destination must survive.
|
|
1209
|
+
gitlab: {
|
|
1210
|
+
'work@example.com': {
|
|
1211
|
+
objectType: 'authorizationBearer',
|
|
1212
|
+
token: 'destination-token',
|
|
1213
|
+
},
|
|
1214
|
+
},
|
|
1215
|
+
},
|
|
1216
|
+
preparations: {},
|
|
1217
|
+
}));
|
|
1218
|
+
const deps = withStdinKey(NEW_ENCRYPTION_KEY);
|
|
1219
|
+
await runCommand(['--account', 'work@example.com', 'auth', 're-encrypt', destinationDirectory], deps);
|
|
1220
|
+
expect(exitCode).toBeNull();
|
|
1221
|
+
expect(readAccountsWithKey(destination, NEW_ENCRYPTION_KEY)).toEqual({
|
|
1222
|
+
gitlab: {
|
|
1223
|
+
'work@example.com': { objectType: 'authorizationBearer', token: 'destination-token' },
|
|
1224
|
+
},
|
|
1225
|
+
slack: {
|
|
1226
|
+
'work@example.com': { objectType: 'slack', token: 'work-tok', dCookie: 'work-cookie' },
|
|
1227
|
+
},
|
|
1228
|
+
});
|
|
1229
|
+
expect(readPreparationsWithKey(destination, NEW_ENCRYPTION_KEY)).toEqual({
|
|
1230
|
+
gitlab: GITLAB_PREPARATION,
|
|
1231
|
+
});
|
|
1232
|
+
});
|
|
1233
|
+
it('errors when no stored credentials belong to the given account', async () => {
|
|
1234
|
+
writeAccountStore({
|
|
1235
|
+
slack: {
|
|
1236
|
+
'work@example.com': { objectType: 'slack', token: 'work-tok', dCookie: 'work-cookie' },
|
|
1237
|
+
},
|
|
1238
|
+
});
|
|
1239
|
+
const destinationDirectory = join(tempDir, 'export');
|
|
1240
|
+
const deps = withStdinKey(NEW_ENCRYPTION_KEY);
|
|
1241
|
+
await runCommand(['--account', 'missing@example.com', 'auth', 're-encrypt', destinationDirectory], deps);
|
|
1242
|
+
expect(exitCode).toBe(1);
|
|
1243
|
+
expect(errorLogs.join('\n')).toContain('missing@example.com');
|
|
1244
|
+
expect(existsSync(join(destinationDirectory, STORE_FILENAME))).toBe(false);
|
|
1245
|
+
});
|
|
1128
1246
|
it('errors for an invalid encryption key read from stdin', async () => {
|
|
1129
1247
|
writeStore({});
|
|
1130
1248
|
const destinationDirectory = join(tempDir, 'export');
|