@dbx-tools/auth 0.9.32 → 0.9.36
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 +49 -97
- package/index.ts +8 -26
- package/lib/index.d.ts +8 -26
- package/lib/index.js +6 -22
- package/lib/src/{databricks-cli.d.ts → _databricks-cli.d.ts} +3 -7
- package/lib/src/_databricks-cli.js +172 -0
- package/lib/src/_errors.d.ts +5 -0
- package/lib/src/_errors.js +10 -0
- package/lib/src/{lifecycle.d.ts → _lifecycle.d.ts} +5 -5
- package/lib/src/_lifecycle.js +214 -0
- package/lib/src/_logging.d.ts +1 -1
- package/lib/src/_logging.js +2 -2
- package/lib/src/{personal-access-token.d.ts → _personal-access-token.d.ts} +1 -1
- package/lib/src/_personal-access-token.js +30 -0
- package/lib/src/_profile-config.d.ts +43 -0
- package/lib/src/_profile-config.js +443 -0
- package/lib/src/{service-principal.d.ts → _service-principal.d.ts} +1 -1
- package/lib/src/_service-principal.js +69 -0
- package/lib/src/{storage.d.ts → _storage.d.ts} +2 -13
- package/lib/src/_storage.js +94 -0
- package/lib/src/_types.d.ts +40 -0
- package/lib/src/_types.js +2 -0
- package/lib/src/client.d.ts +30 -0
- package/lib/src/client.js +223 -0
- package/lib/src/config.d.ts +36 -0
- package/lib/src/config.js +15 -0
- package/lib/src/profile.d.ts +11 -0
- package/lib/src/profile.js +21 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +34 -10
- package/src/{databricks-cli.ts → _databricks-cli.ts} +20 -109
- package/src/_errors.ts +11 -0
- package/src/{lifecycle.ts → _lifecycle.ts} +12 -40
- package/src/_logging.ts +2 -2
- package/src/{personal-access-token.ts → _personal-access-token.ts} +1 -1
- package/src/_profile-config.ts +545 -0
- package/src/{service-principal.ts → _service-principal.ts} +2 -2
- package/src/{storage.ts → _storage.ts} +2 -31
- package/src/_types.ts +45 -0
- package/src/client.ts +291 -0
- package/src/config.ts +44 -0
- package/src/profile.ts +33 -0
- package/lib/exports.d.ts +0 -2
- package/lib/exports.js +0 -3
- package/lib/src/_profile-core.d.ts +0 -28
- package/lib/src/_profile-core.js +0 -216
- package/lib/src/_profile.d.ts +0 -15
- package/lib/src/_profile.js +0 -236
- package/lib/src/databricks-auth.d.ts +0 -11
- package/lib/src/databricks-auth.js +0 -314
- package/lib/src/databricks-cli.js +0 -239
- package/lib/src/errors.d.ts +0 -7
- package/lib/src/errors.js +0 -14
- package/lib/src/file-storage.d.ts +0 -20
- package/lib/src/file-storage.js +0 -207
- package/lib/src/generated/databricks-cli-assets.json +0 -33
- package/lib/src/http-client.d.ts +0 -23
- package/lib/src/http-client.js +0 -116
- package/lib/src/lifecycle.js +0 -233
- package/lib/src/personal-access-token.js +0 -30
- package/lib/src/service-principal.js +0 -69
- package/lib/src/storage.js +0 -112
- package/lib/src/types.d.ts +0 -165
- package/lib/src/types.js +0 -67
- package/src/_profile-core.ts +0 -258
- package/src/_profile.ts +0 -319
- package/src/databricks-auth.ts +0 -416
- package/src/errors.ts +0 -16
- package/src/file-storage.ts +0 -230
- package/src/generated/databricks-cli-assets.json +0 -33
- package/src/http-client.ts +0 -147
- package/src/types.ts +0 -196
package/src/_profile.ts
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { isAbsolute, resolve } from "node:path";
|
|
5
|
-
|
|
6
|
-
import * as environmentUtils from "@dbx-tools/shared-core/environment-utils";
|
|
7
|
-
|
|
8
|
-
import { authLogger } from "./_logging.ts";
|
|
9
|
-
import {
|
|
10
|
-
cleanList,
|
|
11
|
-
type Environment,
|
|
12
|
-
inferTarget,
|
|
13
|
-
type IniConfig,
|
|
14
|
-
loadRawProfile,
|
|
15
|
-
missing,
|
|
16
|
-
nonempty,
|
|
17
|
-
normalizeHost,
|
|
18
|
-
parseDatabricksConfig,
|
|
19
|
-
parseTarget,
|
|
20
|
-
requestOboToken,
|
|
21
|
-
resolveAuthKind,
|
|
22
|
-
resolveProfileName,
|
|
23
|
-
SETTINGS_SECTION,
|
|
24
|
-
} from "./_profile-core.ts";
|
|
25
|
-
import { AuthError } from "./errors.ts";
|
|
26
|
-
import {
|
|
27
|
-
AUTH_TYPE_APP_OBO,
|
|
28
|
-
AUTH_TYPE_APP_SP,
|
|
29
|
-
AuthKind,
|
|
30
|
-
type DatabricksAuthOptions,
|
|
31
|
-
type DatabricksProfile,
|
|
32
|
-
type DatabricksProfileSummary,
|
|
33
|
-
DEFAULT_CLIENT_ID,
|
|
34
|
-
DEFAULT_CONFIG_FILE,
|
|
35
|
-
} from "./types.ts";
|
|
36
|
-
|
|
37
|
-
const configCache = new Map<string, IniConfig | Error | undefined>();
|
|
38
|
-
const logger = authLogger("profile");
|
|
39
|
-
|
|
40
|
-
export { normalizeHost, parseDatabricksConfig } from "./_profile-core.ts";
|
|
41
|
-
|
|
42
|
-
/** Expand and resolve the selected Databricks CLI configuration path. */
|
|
43
|
-
export function resolveConfigFile(
|
|
44
|
-
explicit?: string,
|
|
45
|
-
environment: Environment = process.env,
|
|
46
|
-
): string {
|
|
47
|
-
const selected =
|
|
48
|
-
nonempty(explicit) ?? nonempty(environment.DATABRICKS_CONFIG_FILE) ?? DEFAULT_CONFIG_FILE;
|
|
49
|
-
const expanded =
|
|
50
|
-
selected === "~"
|
|
51
|
-
? homedir()
|
|
52
|
-
: selected.startsWith("~/")
|
|
53
|
-
? resolve(homedir(), selected.slice(2))
|
|
54
|
-
: selected;
|
|
55
|
-
const path = isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
56
|
-
logger.debug("resolved Databricks config file", {
|
|
57
|
-
path,
|
|
58
|
-
source: nonempty(explicit)
|
|
59
|
-
? "option"
|
|
60
|
-
: nonempty(environment.DATABRICKS_CONFIG_FILE)
|
|
61
|
-
? "environment"
|
|
62
|
-
: "default",
|
|
63
|
-
});
|
|
64
|
-
return path;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Invalidate the process-wide parsed configuration cache after an external write. */
|
|
68
|
-
export function invalidateConfigFile(configFile?: string): void {
|
|
69
|
-
const path = resolveConfigFile(configFile);
|
|
70
|
-
configCache.delete(path);
|
|
71
|
-
logger.debug("invalidated Databricks config cache", { path });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Return whether a named profile exists in the selected configuration. */
|
|
75
|
-
export function configProfileExists(profile: string, configFile?: string): boolean {
|
|
76
|
-
if (!profile.trim() || profile === SETTINGS_SECTION) return false;
|
|
77
|
-
return loadConfig(resolveConfigFile(configFile))?.has(profile) ?? false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Enumerate secret-free profile metadata from the cached CLI configuration. */
|
|
81
|
-
export function listDatabricksProfiles(
|
|
82
|
-
configFile?: string,
|
|
83
|
-
refresh = false,
|
|
84
|
-
environment: Environment = process.env,
|
|
85
|
-
): DatabricksProfileSummary[] {
|
|
86
|
-
const path = resolveConfigFile(configFile, environment);
|
|
87
|
-
if (refresh) configCache.delete(path);
|
|
88
|
-
const config = loadConfig(path);
|
|
89
|
-
if (!config) {
|
|
90
|
-
logger.debug("listed Databricks profiles", { path, refresh, count: 0 });
|
|
91
|
-
return [];
|
|
92
|
-
}
|
|
93
|
-
const profiles = [...config.keys()]
|
|
94
|
-
.filter((name) => name !== SETTINGS_SECTION)
|
|
95
|
-
.map((name) => {
|
|
96
|
-
const profile = loadRawProfile(config, name);
|
|
97
|
-
const host = nonempty(profile.host);
|
|
98
|
-
const accountId = nonempty(profile.accountId);
|
|
99
|
-
return {
|
|
100
|
-
name,
|
|
101
|
-
...(host ? { host } : {}),
|
|
102
|
-
...(accountId ? { accountId } : {}),
|
|
103
|
-
...(nonempty(profile.workspaceId) ? { workspaceId: nonempty(profile.workspaceId) } : {}),
|
|
104
|
-
target: inferTarget(host, accountId),
|
|
105
|
-
authKind: resolveAuthKind(
|
|
106
|
-
profile.authType,
|
|
107
|
-
profile.clientId,
|
|
108
|
-
profile.clientSecret,
|
|
109
|
-
profile.accessToken,
|
|
110
|
-
),
|
|
111
|
-
};
|
|
112
|
-
})
|
|
113
|
-
.sort((left, right) => left.name.localeCompare(right.name));
|
|
114
|
-
logger.debug("listed Databricks profiles", { path, refresh, count: profiles.length });
|
|
115
|
-
return profiles;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** Resolve options, environment, request headers, and CLI configuration into one profile. */
|
|
119
|
-
export function resolveDatabricksProfile(
|
|
120
|
-
options: DatabricksAuthOptions,
|
|
121
|
-
environment: Environment = process.env,
|
|
122
|
-
): DatabricksProfile {
|
|
123
|
-
const inApp = environmentUtils.isDatabricksAppEnv({ ...environment });
|
|
124
|
-
const environmentProfile = nonempty(environment.DATABRICKS_CONFIG_PROFILE);
|
|
125
|
-
const explicitProfile = Boolean(nonempty(options.profile) ?? environmentProfile);
|
|
126
|
-
const requestToken = requestOboToken(options.requestHeaders, options.accessTokenHeader);
|
|
127
|
-
const explicitAuthType =
|
|
128
|
-
nonempty(options.authType)?.toLowerCase() ??
|
|
129
|
-
(!inApp && !explicitProfile
|
|
130
|
-
? nonempty(environment.DATABRICKS_AUTH_TYPE)?.toLowerCase()
|
|
131
|
-
: undefined);
|
|
132
|
-
const appServicePrincipal = [
|
|
133
|
-
environment.DATABRICKS_HOST,
|
|
134
|
-
environment.DATABRICKS_CLIENT_ID,
|
|
135
|
-
environment.DATABRICKS_CLIENT_SECRET,
|
|
136
|
-
].every(nonempty);
|
|
137
|
-
const selectedAuthType =
|
|
138
|
-
!inApp || explicitProfile || explicitAuthType
|
|
139
|
-
? explicitAuthType
|
|
140
|
-
: requestToken
|
|
141
|
-
? AUTH_TYPE_APP_OBO
|
|
142
|
-
: appServicePrincipal
|
|
143
|
-
? AUTH_TYPE_APP_SP
|
|
144
|
-
: undefined;
|
|
145
|
-
const appAuth = [AUTH_TYPE_APP_OBO, "app-obo", AUTH_TYPE_APP_SP, "app-sp"].includes(
|
|
146
|
-
selectedAuthType ?? "",
|
|
147
|
-
);
|
|
148
|
-
const ignoreAmbientCredentials = explicitProfile && !appAuth;
|
|
149
|
-
const configPath = resolveConfigFile(options.configFile, environment);
|
|
150
|
-
const config = loadConfig(configPath);
|
|
151
|
-
const requestedName = nonempty(options.profile) ?? environmentProfile;
|
|
152
|
-
const profileName = resolveProfileName(
|
|
153
|
-
requestedName,
|
|
154
|
-
explicitProfile,
|
|
155
|
-
config,
|
|
156
|
-
options.preferUserToMachine,
|
|
157
|
-
);
|
|
158
|
-
const configured = loadRawProfile(config, profileName);
|
|
159
|
-
const ambient = (name: keyof NodeJS.ProcessEnv): string | undefined =>
|
|
160
|
-
ignoreAmbientCredentials ? undefined : nonempty(environment[name]);
|
|
161
|
-
const host = normalizeHost(
|
|
162
|
-
options.host ?? ambient("DATABRICKS_HOST") ?? configured.host,
|
|
163
|
-
profileName,
|
|
164
|
-
);
|
|
165
|
-
const accountId =
|
|
166
|
-
nonempty(options.accountId) ??
|
|
167
|
-
ambient("DATABRICKS_ACCOUNT_ID") ??
|
|
168
|
-
nonempty(configured.accountId);
|
|
169
|
-
const workspaceId =
|
|
170
|
-
nonempty(options.workspaceId) ??
|
|
171
|
-
ambient("DATABRICKS_WORKSPACE_ID") ??
|
|
172
|
-
nonempty(configured.workspaceId);
|
|
173
|
-
const clientIdValue =
|
|
174
|
-
nonempty(options.clientId) ?? ambient("DATABRICKS_CLIENT_ID") ?? nonempty(configured.clientId);
|
|
175
|
-
const clientSecret =
|
|
176
|
-
nonempty(options.clientSecret) ??
|
|
177
|
-
ambient("DATABRICKS_CLIENT_SECRET") ??
|
|
178
|
-
nonempty(configured.clientSecret);
|
|
179
|
-
const accessToken =
|
|
180
|
-
selectedAuthType === AUTH_TYPE_APP_OBO || selectedAuthType === "app-obo"
|
|
181
|
-
? requestToken
|
|
182
|
-
: (nonempty(options.accessToken) ??
|
|
183
|
-
ambient("DATABRICKS_TOKEN") ??
|
|
184
|
-
nonempty(configured.accessToken));
|
|
185
|
-
const authType =
|
|
186
|
-
selectedAuthType ??
|
|
187
|
-
ambient("DATABRICKS_AUTH_TYPE") ??
|
|
188
|
-
nonempty(configured.authType)?.toLowerCase();
|
|
189
|
-
const authKind = resolveAuthKind(authType, clientIdValue, clientSecret, accessToken);
|
|
190
|
-
const clientId =
|
|
191
|
-
authKind === AuthKind.UserToMachine
|
|
192
|
-
? (clientIdValue ?? DEFAULT_CLIENT_ID)
|
|
193
|
-
: authKind === AuthKind.MachineToMachine || authKind === AuthKind.AppServicePrincipal
|
|
194
|
-
? (clientIdValue ?? missing(profileName, "client_id"))
|
|
195
|
-
: (clientIdValue ?? "");
|
|
196
|
-
const scopes = options.scopes?.length
|
|
197
|
-
? cleanList(options.scopes)
|
|
198
|
-
: cleanList(configured.scopes?.split(",") ?? ["all-apis"]);
|
|
199
|
-
const target = options.target ? parseTarget(options.target) : inferTarget(host, accountId);
|
|
200
|
-
const groupId =
|
|
201
|
-
nonempty(options.groupId) ?? ambient("DATABRICKS_GROUP_ID") ?? nonempty(configured.groupId);
|
|
202
|
-
const principal =
|
|
203
|
-
authKind === AuthKind.MachineToMachine || authKind === AuthKind.AppServicePrincipal
|
|
204
|
-
? clientId
|
|
205
|
-
: profileName;
|
|
206
|
-
const cacheKey = credentialCacheKey({
|
|
207
|
-
name: profileName,
|
|
208
|
-
host,
|
|
209
|
-
accountId,
|
|
210
|
-
workspaceId,
|
|
211
|
-
clientId,
|
|
212
|
-
groupId,
|
|
213
|
-
scopes,
|
|
214
|
-
authKind,
|
|
215
|
-
accessToken,
|
|
216
|
-
});
|
|
217
|
-
logger.debug("resolved Databricks profile", {
|
|
218
|
-
profile: profileName,
|
|
219
|
-
host,
|
|
220
|
-
authKind,
|
|
221
|
-
target,
|
|
222
|
-
inApp,
|
|
223
|
-
explicitProfile,
|
|
224
|
-
authType: authType ?? "automatic",
|
|
225
|
-
hasAccountId: Boolean(accountId),
|
|
226
|
-
hasWorkspaceId: Boolean(workspaceId),
|
|
227
|
-
hasGroupId: Boolean(groupId),
|
|
228
|
-
scopeCount: scopes.length,
|
|
229
|
-
configPath,
|
|
230
|
-
});
|
|
231
|
-
return {
|
|
232
|
-
name: profileName,
|
|
233
|
-
host,
|
|
234
|
-
...(accountId ? { accountId } : {}),
|
|
235
|
-
...(workspaceId ? { workspaceId } : {}),
|
|
236
|
-
clientId,
|
|
237
|
-
...(groupId ? { groupId } : {}),
|
|
238
|
-
scopes,
|
|
239
|
-
target,
|
|
240
|
-
authKind,
|
|
241
|
-
...(authType ? { authType } : {}),
|
|
242
|
-
...(clientSecret ? { clientSecret } : {}),
|
|
243
|
-
...(accessToken ? { accessToken } : {}),
|
|
244
|
-
cacheKey,
|
|
245
|
-
principal,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function loadConfig(path: string): IniConfig | undefined {
|
|
250
|
-
if (configCache.has(path)) {
|
|
251
|
-
const cached = configCache.get(path);
|
|
252
|
-
if (cached instanceof Error) throw cached;
|
|
253
|
-
logger.debug("reused Databricks config cache", {
|
|
254
|
-
path,
|
|
255
|
-
exists: Boolean(cached),
|
|
256
|
-
profileCount: cached?.size ?? 0,
|
|
257
|
-
});
|
|
258
|
-
return cached;
|
|
259
|
-
}
|
|
260
|
-
if (!existsSync(path)) {
|
|
261
|
-
configCache.set(path, undefined);
|
|
262
|
-
logger.debug("Databricks config file not found", { path });
|
|
263
|
-
return undefined;
|
|
264
|
-
}
|
|
265
|
-
try {
|
|
266
|
-
const config = parseDatabricksConfig(readFileSync(path, "utf8"));
|
|
267
|
-
configCache.set(path, config);
|
|
268
|
-
logger.debug("parsed Databricks config file", { path, profileCount: config.size });
|
|
269
|
-
return config;
|
|
270
|
-
} catch (cause) {
|
|
271
|
-
const error =
|
|
272
|
-
cause instanceof Error ? cause : new AuthError("config", `Could not read ${path}`, { cause });
|
|
273
|
-
configCache.set(path, error);
|
|
274
|
-
throw error;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function credentialCacheKey(
|
|
279
|
-
profile: Pick<
|
|
280
|
-
DatabricksProfile,
|
|
281
|
-
| "name"
|
|
282
|
-
| "host"
|
|
283
|
-
| "accountId"
|
|
284
|
-
| "workspaceId"
|
|
285
|
-
| "clientId"
|
|
286
|
-
| "groupId"
|
|
287
|
-
| "scopes"
|
|
288
|
-
| "authKind"
|
|
289
|
-
| "accessToken"
|
|
290
|
-
>,
|
|
291
|
-
): string {
|
|
292
|
-
if (profile.authKind === AuthKind.UserToMachine) return profile.name;
|
|
293
|
-
if (profile.authKind === AuthKind.PersonalAccessToken) {
|
|
294
|
-
const digest = createHash("sha256")
|
|
295
|
-
.update(profile.accessToken ?? "")
|
|
296
|
-
.digest("hex");
|
|
297
|
-
return `${profile.name}-pat-${digest}`;
|
|
298
|
-
}
|
|
299
|
-
if (profile.authKind === AuthKind.AppOnBehalfOf) return `${profile.name}-app-obo`;
|
|
300
|
-
const identity = [
|
|
301
|
-
profile.host,
|
|
302
|
-
profile.accountId ?? "",
|
|
303
|
-
profile.workspaceId ?? "",
|
|
304
|
-
profile.clientId,
|
|
305
|
-
profile.groupId ?? "",
|
|
306
|
-
machineScopes(profile.scopes).join(" "),
|
|
307
|
-
].join("\0");
|
|
308
|
-
const digest = createHash("sha256").update(identity).digest("hex");
|
|
309
|
-
return `${profile.name}-${profile.authKind === AuthKind.AppServicePrincipal ? "app-sp" : "oauth-m2m"}-${digest}`;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export function effectiveScopes(scopes: readonly string[]): string[] {
|
|
313
|
-
return cleanList(["offline_access", ...scopes]);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export function machineScopes(scopes: readonly string[]): string[] {
|
|
317
|
-
const values = cleanList(scopes.length ? scopes : ["all-apis"]);
|
|
318
|
-
return [...values].sort();
|
|
319
|
-
}
|