@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.
Files changed (72) hide show
  1. package/README.md +49 -97
  2. package/index.ts +8 -26
  3. package/lib/index.d.ts +8 -26
  4. package/lib/index.js +6 -22
  5. package/lib/src/{databricks-cli.d.ts → _databricks-cli.d.ts} +3 -7
  6. package/lib/src/_databricks-cli.js +172 -0
  7. package/lib/src/_errors.d.ts +5 -0
  8. package/lib/src/_errors.js +10 -0
  9. package/lib/src/{lifecycle.d.ts → _lifecycle.d.ts} +5 -5
  10. package/lib/src/_lifecycle.js +214 -0
  11. package/lib/src/_logging.d.ts +1 -1
  12. package/lib/src/_logging.js +2 -2
  13. package/lib/src/{personal-access-token.d.ts → _personal-access-token.d.ts} +1 -1
  14. package/lib/src/_personal-access-token.js +30 -0
  15. package/lib/src/_profile-config.d.ts +43 -0
  16. package/lib/src/_profile-config.js +443 -0
  17. package/lib/src/{service-principal.d.ts → _service-principal.d.ts} +1 -1
  18. package/lib/src/_service-principal.js +69 -0
  19. package/lib/src/{storage.d.ts → _storage.d.ts} +2 -13
  20. package/lib/src/_storage.js +94 -0
  21. package/lib/src/_types.d.ts +40 -0
  22. package/lib/src/_types.js +2 -0
  23. package/lib/src/client.d.ts +30 -0
  24. package/lib/src/client.js +223 -0
  25. package/lib/src/config.d.ts +36 -0
  26. package/lib/src/config.js +15 -0
  27. package/lib/src/profile.d.ts +11 -0
  28. package/lib/src/profile.js +21 -0
  29. package/lib/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +34 -10
  31. package/src/{databricks-cli.ts → _databricks-cli.ts} +20 -109
  32. package/src/_errors.ts +11 -0
  33. package/src/{lifecycle.ts → _lifecycle.ts} +12 -40
  34. package/src/_logging.ts +2 -2
  35. package/src/{personal-access-token.ts → _personal-access-token.ts} +1 -1
  36. package/src/_profile-config.ts +545 -0
  37. package/src/{service-principal.ts → _service-principal.ts} +2 -2
  38. package/src/{storage.ts → _storage.ts} +2 -31
  39. package/src/_types.ts +45 -0
  40. package/src/client.ts +291 -0
  41. package/src/config.ts +44 -0
  42. package/src/profile.ts +33 -0
  43. package/lib/exports.d.ts +0 -2
  44. package/lib/exports.js +0 -3
  45. package/lib/src/_profile-core.d.ts +0 -28
  46. package/lib/src/_profile-core.js +0 -216
  47. package/lib/src/_profile.d.ts +0 -15
  48. package/lib/src/_profile.js +0 -236
  49. package/lib/src/databricks-auth.d.ts +0 -11
  50. package/lib/src/databricks-auth.js +0 -314
  51. package/lib/src/databricks-cli.js +0 -239
  52. package/lib/src/errors.d.ts +0 -7
  53. package/lib/src/errors.js +0 -14
  54. package/lib/src/file-storage.d.ts +0 -20
  55. package/lib/src/file-storage.js +0 -207
  56. package/lib/src/generated/databricks-cli-assets.json +0 -33
  57. package/lib/src/http-client.d.ts +0 -23
  58. package/lib/src/http-client.js +0 -116
  59. package/lib/src/lifecycle.js +0 -233
  60. package/lib/src/personal-access-token.js +0 -30
  61. package/lib/src/service-principal.js +0 -69
  62. package/lib/src/storage.js +0 -112
  63. package/lib/src/types.d.ts +0 -165
  64. package/lib/src/types.js +0 -67
  65. package/src/_profile-core.ts +0 -258
  66. package/src/_profile.ts +0 -319
  67. package/src/databricks-auth.ts +0 -416
  68. package/src/errors.ts +0 -16
  69. package/src/file-storage.ts +0 -230
  70. package/src/generated/databricks-cli-assets.json +0 -33
  71. package/src/http-client.ts +0 -147
  72. 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
- }