auth0-deploy-cli 8.38.0 → 9.0.0-beta.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/CHANGELOG.md +19 -1
- package/lib/context/defaults.d.ts +1 -0
- package/lib/context/defaults.js +47 -0
- package/lib/context/directory/handlers/actionModules.js +1 -9
- package/lib/context/directory/handlers/actions.js +1 -11
- package/lib/context/directory/handlers/databases.js +1 -3
- package/lib/context/directory/handlers/eventStreams.d.ts +5 -0
- package/lib/context/directory/handlers/eventStreams.js +52 -0
- package/lib/context/directory/handlers/index.js +2 -0
- package/lib/context/directory/index.js +6 -8
- package/lib/context/yaml/handlers/eventStreams.d.ts +5 -0
- package/lib/context/yaml/handlers/eventStreams.js +24 -0
- package/lib/context/yaml/handlers/index.js +2 -0
- package/lib/context/yaml/index.js +5 -8
- package/lib/tools/auth0/handlers/connections.js +1 -0
- package/lib/tools/auth0/handlers/eventStreams.d.ts +58 -0
- package/lib/tools/auth0/handlers/eventStreams.js +178 -0
- package/lib/tools/auth0/handlers/index.js +2 -0
- package/lib/tools/auth0/index.js +6 -3
- package/lib/tools/constants.d.ts +1 -0
- package/lib/tools/constants.js +1 -0
- package/lib/types.d.ts +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [9.0.0-beta.1] - 2026-07-02
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- Prevent path traversal attacks in config file handlers for directory and YAML contexts. [#1418]
|
|
15
|
+
|
|
16
|
+
## [8.39.0] - 2026-06-30
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Add `eventStreams` support. [#1412]
|
|
21
|
+
- Add `id_token_session_expiry_supported` validation for enterprise connections. [#1411]
|
|
22
|
+
|
|
10
23
|
## [8.38.0] - 2026-06-25
|
|
11
24
|
|
|
12
25
|
### Added
|
|
@@ -1845,7 +1858,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1845
1858
|
[#1407]: https://github.com/auth0/auth0-deploy-cli/issues/1407
|
|
1846
1859
|
[#1408]: https://github.com/auth0/auth0-deploy-cli/issues/1408
|
|
1847
1860
|
[#1409]: https://github.com/auth0/auth0-deploy-cli/issues/1409
|
|
1848
|
-
[
|
|
1861
|
+
[#1411]: https://github.com/auth0/auth0-deploy-cli/issues/1411
|
|
1862
|
+
[#1412]: https://github.com/auth0/auth0-deploy-cli/issues/1412
|
|
1863
|
+
[#1418]: https://github.com/auth0/auth0-deploy-cli/issues/1418
|
|
1864
|
+
[Unreleased]: https://github.com/auth0/auth0-deploy-cli/compare/v9.0.0-beta.1...HEAD
|
|
1865
|
+
[9.0.0-beta.1]: https://github.com/auth0/auth0-deploy-cli/compare/v8.39.0...v9.0.0-beta.1
|
|
1866
|
+
[8.39.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.38.0...v8.39.0
|
|
1849
1867
|
[8.38.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.37.0...v8.38.0
|
|
1850
1868
|
[8.37.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.36.0...v8.37.0
|
|
1851
1869
|
[8.36.0]: https://github.com/auth0/auth0-deploy-cli/compare/v8.35.0...v8.36.0
|
|
@@ -5,4 +5,5 @@ export declare function phoneProviderDefaults(phoneProvider: any): any;
|
|
|
5
5
|
export declare function phoneTemplatesDefaults(phoneTemplate: any): any;
|
|
6
6
|
export declare function connectionDefaults(connection: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
|
|
7
7
|
export declare function logStreamDefaults(logStreams: any, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any;
|
|
8
|
+
export declare function eventStreamDefaults(eventStreams: any[], config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): any[];
|
|
8
9
|
export declare function attackProtectionDefaults(attackProtection: AttackProtection, config?: Pick<Config, 'AUTH0_EXPORT_SECRETS'>): AttackProtection;
|
package/lib/context/defaults.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.phoneProviderDefaults = phoneProviderDefaults;
|
|
|
5
5
|
exports.phoneTemplatesDefaults = phoneTemplatesDefaults;
|
|
6
6
|
exports.connectionDefaults = connectionDefaults;
|
|
7
7
|
exports.logStreamDefaults = logStreamDefaults;
|
|
8
|
+
exports.eventStreamDefaults = eventStreamDefaults;
|
|
8
9
|
exports.attackProtectionDefaults = attackProtectionDefaults;
|
|
9
10
|
const utils_1 = require("../tools/utils");
|
|
10
11
|
// env vars arrive as strings, so check both forms
|
|
@@ -146,6 +147,52 @@ function logStreamDefaults(logStreams, config) {
|
|
|
146
147
|
});
|
|
147
148
|
return maskedLogStreams;
|
|
148
149
|
}
|
|
150
|
+
function eventStreamDefaults(eventStreams, config) {
|
|
151
|
+
return eventStreams.map((stream) => {
|
|
152
|
+
const { created_at: _ca, updated_at: _ua, ...rest } = stream;
|
|
153
|
+
stream = rest;
|
|
154
|
+
if (isExportSecrets(config))
|
|
155
|
+
return stream;
|
|
156
|
+
if (stream.destination?.type !== 'webhook')
|
|
157
|
+
return stream;
|
|
158
|
+
const auth = stream.destination.configuration?.webhook_authorization;
|
|
159
|
+
if (!auth)
|
|
160
|
+
return stream;
|
|
161
|
+
const method = auth.method;
|
|
162
|
+
let maskedAuth;
|
|
163
|
+
if (method === 'basic') {
|
|
164
|
+
maskedAuth = {
|
|
165
|
+
method,
|
|
166
|
+
username: auth.username ?? '',
|
|
167
|
+
password: `##EVENT_STREAM_WEBHOOK_BASIC_AUTH_PASSWORD##`,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
else if (method === 'bearer') {
|
|
171
|
+
maskedAuth = { method, token: `##EVENT_STREAM_WEBHOOK_BEARER_TOKEN##` };
|
|
172
|
+
}
|
|
173
|
+
else if (method === 'custom_header') {
|
|
174
|
+
maskedAuth = {
|
|
175
|
+
method,
|
|
176
|
+
header_key: auth.header_key ?? '',
|
|
177
|
+
header_value: `##EVENT_STREAM_WEBHOOK_HEADER_VALUE##`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Unknown auth method — preserve as-is rather than silently dropping credentials
|
|
182
|
+
return stream;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
...stream,
|
|
186
|
+
destination: {
|
|
187
|
+
...stream.destination,
|
|
188
|
+
configuration: {
|
|
189
|
+
...stream.destination.configuration,
|
|
190
|
+
webhook_authorization: maskedAuth,
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
}
|
|
149
196
|
function attackProtectionDefaults(attackProtection, config) {
|
|
150
197
|
if (isExportSecrets(config))
|
|
151
198
|
return attackProtection;
|
|
@@ -25,15 +25,7 @@ function parse(context) {
|
|
|
25
25
|
// The `module.code` can be a file path. It needs to be loaded.
|
|
26
26
|
// It can be a relative path, so we need to handle both cases.
|
|
27
27
|
const unixPath = module.code.replace(/[\\/]+/g, '/').replace(/^([a-zA-Z]+:|\.\/)/, '');
|
|
28
|
-
|
|
29
|
-
logger_1.default.warn(`Support for absolute paths and paths outside the config root will be deprecated in a future version to improve the security of the tool. ` +
|
|
30
|
-
`Please update your configuration to use paths relative to the config directory. ` +
|
|
31
|
-
`Current absolute path used: ["${module.code}"]`);
|
|
32
|
-
module.code = context.loadFile(unixPath, moduleFolder);
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
module.code = context.loadFile(path_1.default.join(context.filePath, module.code), moduleFolder);
|
|
36
|
-
}
|
|
28
|
+
module.code = context.loadFile(unixPath, moduleFolder);
|
|
37
29
|
}
|
|
38
30
|
return module;
|
|
39
31
|
});
|
|
@@ -26,17 +26,7 @@ function parse(context) {
|
|
|
26
26
|
if (action.code) {
|
|
27
27
|
// Convert `action.code` path to Unix-style path by replacing backslashes and multiple slashes with a single forward slash, and remove leading drive letters or './'.
|
|
28
28
|
const unixPath = action.code.replace(/[\\/]+/g, '/').replace(/^([a-zA-Z]+:|\.\/)/, '');
|
|
29
|
-
|
|
30
|
-
// If the Unix-style path exists, load the file from that path
|
|
31
|
-
logger_1.default.warn(`Support for absolute paths and paths outside the config root will be deprecated in a future version to improve the security of the tool. ` +
|
|
32
|
-
`Please update your configuration to use paths relative to the config directory. ` +
|
|
33
|
-
`Current absolute path used: ["${action.code}"]`);
|
|
34
|
-
action.code = context.loadFile(unixPath, actionFolder);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
// Otherwise, load the file from the context's file path
|
|
38
|
-
action.code = context.loadFile(path_1.default.join(context.filePath, action.code), actionFolder);
|
|
39
|
-
}
|
|
29
|
+
action.code = context.loadFile(unixPath, actionFolder);
|
|
40
30
|
}
|
|
41
31
|
return action;
|
|
42
32
|
});
|
|
@@ -43,9 +43,7 @@ function getDatabase(folder, configRoot, mappingOpts) {
|
|
|
43
43
|
const resolvedBase = path_1.default.resolve(configRoot);
|
|
44
44
|
const toLoad = path_1.default.resolve(folder, script);
|
|
45
45
|
if (!toLoad.startsWith(resolvedBase + path_1.default.sep)) {
|
|
46
|
-
|
|
47
|
-
`Please update your configuration to use paths relative to the config directory. ` +
|
|
48
|
-
`Current absolute path used: ["${script}"]`);
|
|
46
|
+
throw new Error(`File reference "${script}" in database custom script "${name}" must be relative to the config directory. Absolute paths and paths outside the config root are not supported.`);
|
|
49
47
|
}
|
|
50
48
|
database.options.customScripts[name] = (0, tools_1.loadFileAndReplaceKeywords)(toLoad, mappingOpts);
|
|
51
49
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const tools_1 = require("../../../tools");
|
|
9
|
+
const utils_1 = require("../../../utils");
|
|
10
|
+
const defaults_1 = require("../../defaults");
|
|
11
|
+
function parse(context) {
|
|
12
|
+
const eventStreamsDirectory = path_1.default.join(context.filePath, tools_1.constants.EVENT_STREAMS_DIRECTORY);
|
|
13
|
+
if (!(0, utils_1.existsMustBeDir)(eventStreamsDirectory))
|
|
14
|
+
return { eventStreams: null };
|
|
15
|
+
const foundFiles = (0, utils_1.getFiles)(eventStreamsDirectory, ['.json']);
|
|
16
|
+
const eventStreams = foundFiles
|
|
17
|
+
.map((f) => (0, utils_1.loadJSON)(f, {
|
|
18
|
+
mappings: context.mappings,
|
|
19
|
+
disableKeywordReplacement: context.disableKeywordReplacement,
|
|
20
|
+
}))
|
|
21
|
+
.filter((p) => Object.keys(p).length > 0);
|
|
22
|
+
return {
|
|
23
|
+
eventStreams,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function dump(context) {
|
|
27
|
+
const { eventStreams } = context.assets;
|
|
28
|
+
if (!eventStreams)
|
|
29
|
+
return;
|
|
30
|
+
const eventStreamsDirectory = path_1.default.join(context.filePath, tools_1.constants.EVENT_STREAMS_DIRECTORY);
|
|
31
|
+
fs_extra_1.default.ensureDirSync(eventStreamsDirectory);
|
|
32
|
+
const maskedEventStreams = (0, defaults_1.eventStreamDefaults)(eventStreams, context.config);
|
|
33
|
+
const expectedFiles = new Set();
|
|
34
|
+
maskedEventStreams.forEach((eventStream) => {
|
|
35
|
+
const fileName = `${(0, utils_1.sanitize)(eventStream.name)}.json`;
|
|
36
|
+
const streamFile = path_1.default.join(eventStreamsDirectory, fileName);
|
|
37
|
+
(0, utils_1.dumpJSON)(streamFile, eventStream);
|
|
38
|
+
expectedFiles.add(fileName);
|
|
39
|
+
});
|
|
40
|
+
// Remove files for event streams no longer present in the tenant
|
|
41
|
+
for (const existing of fs_extra_1.default.readdirSync(eventStreamsDirectory)) {
|
|
42
|
+
const fullPath = path_1.default.join(eventStreamsDirectory, existing);
|
|
43
|
+
if (fs_extra_1.default.statSync(fullPath).isFile() && !expectedFiles.has(existing)) {
|
|
44
|
+
fs_extra_1.default.removeSync(fullPath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const eventStreamsHandler = {
|
|
49
|
+
parse,
|
|
50
|
+
dump,
|
|
51
|
+
};
|
|
52
|
+
exports.default = eventStreamsHandler;
|
|
@@ -43,6 +43,7 @@ const userAttributeProfiles_1 = __importDefault(require("./userAttributeProfiles
|
|
|
43
43
|
const connectionProfiles_1 = __importDefault(require("./connectionProfiles"));
|
|
44
44
|
const tokenExchangeProfiles_1 = __importDefault(require("./tokenExchangeProfiles"));
|
|
45
45
|
const supplementalSignals_1 = __importDefault(require("./supplementalSignals"));
|
|
46
|
+
const eventStreams_1 = __importDefault(require("./eventStreams"));
|
|
46
47
|
const selfServiceProfiles_1 = __importDefault(require("./selfServiceProfiles"));
|
|
47
48
|
const directoryHandlers = {
|
|
48
49
|
rules: rules_1.default,
|
|
@@ -86,5 +87,6 @@ const directoryHandlers = {
|
|
|
86
87
|
connectionProfiles: connectionProfiles_1.default,
|
|
87
88
|
tokenExchangeProfiles: tokenExchangeProfiles_1.default,
|
|
88
89
|
supplementalSignals: supplementalSignals_1.default,
|
|
90
|
+
eventStreams: eventStreams_1.default,
|
|
89
91
|
};
|
|
90
92
|
exports.default = directoryHandlers;
|
|
@@ -68,14 +68,12 @@ class DirectoryContext {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
loadFile(f, folder) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
`Please update your configuration to use paths relative to the config directory. ` +
|
|
78
|
-
`Current absolute path used: ["${f}"]`);
|
|
71
|
+
const resolvedBase = path.resolve(this.filePath);
|
|
72
|
+
const inSubfolder = path.resolve(this.filePath, folder, f);
|
|
73
|
+
const inRoot = path.resolve(this.filePath, f);
|
|
74
|
+
const toLoad = (0, utils_1.isFile)(inSubfolder) ? inSubfolder : inRoot;
|
|
75
|
+
if (!toLoad.startsWith(resolvedBase + path.sep)) {
|
|
76
|
+
throw new Error(`File reference "${f}" must be relative to the config directory. Absolute paths and paths outside the config root are not supported.`);
|
|
79
77
|
}
|
|
80
78
|
return (0, tools_1.loadFileAndReplaceKeywords)(toLoad, {
|
|
81
79
|
mappings: this.mappings,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const defaults_1 = require("../../defaults");
|
|
4
|
+
async function parse(context) {
|
|
5
|
+
const { eventStreams } = context.assets;
|
|
6
|
+
if (!eventStreams)
|
|
7
|
+
return { eventStreams: null };
|
|
8
|
+
return {
|
|
9
|
+
eventStreams,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
async function dump(context) {
|
|
13
|
+
const { eventStreams } = context.assets;
|
|
14
|
+
if (!eventStreams)
|
|
15
|
+
return { eventStreams: null };
|
|
16
|
+
return {
|
|
17
|
+
eventStreams: (0, defaults_1.eventStreamDefaults)(eventStreams, context.config),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const eventStreamsHandler = {
|
|
21
|
+
parse,
|
|
22
|
+
dump,
|
|
23
|
+
};
|
|
24
|
+
exports.default = eventStreamsHandler;
|
|
@@ -43,6 +43,7 @@ const userAttributeProfiles_1 = __importDefault(require("./userAttributeProfiles
|
|
|
43
43
|
const connectionProfiles_1 = __importDefault(require("./connectionProfiles"));
|
|
44
44
|
const tokenExchangeProfiles_1 = __importDefault(require("./tokenExchangeProfiles"));
|
|
45
45
|
const supplementalSignals_1 = __importDefault(require("./supplementalSignals"));
|
|
46
|
+
const eventStreams_1 = __importDefault(require("./eventStreams"));
|
|
46
47
|
const selfServiceProfiles_1 = __importDefault(require("./selfServiceProfiles"));
|
|
47
48
|
const yamlHandlers = {
|
|
48
49
|
rules: rules_1.default,
|
|
@@ -86,5 +87,6 @@ const yamlHandlers = {
|
|
|
86
87
|
connectionProfiles: connectionProfiles_1.default,
|
|
87
88
|
tokenExchangeProfiles: tokenExchangeProfiles_1.default,
|
|
88
89
|
supplementalSignals: supplementalSignals_1.default,
|
|
90
|
+
eventStreams: eventStreams_1.default,
|
|
89
91
|
};
|
|
90
92
|
exports.default = yamlHandlers;
|
|
@@ -43,15 +43,12 @@ class YAMLContext {
|
|
|
43
43
|
})();
|
|
44
44
|
}
|
|
45
45
|
loadFile(f) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
logger_1.default.warn(`Support for absolute paths and paths outside the config root will be deprecated in a future version to improve the security of the tool. ` +
|
|
51
|
-
`Please update your configuration to use paths relative to the config directory. ` +
|
|
52
|
-
`Current absolute path used: ["${f}"]`);
|
|
46
|
+
const resolvedBase = path_1.default.resolve(this.basePath);
|
|
47
|
+
const toLoad = path_1.default.resolve(this.basePath, f);
|
|
48
|
+
if (!toLoad.startsWith(resolvedBase + path_1.default.sep)) {
|
|
49
|
+
throw new Error(`File reference "${f}" must be relative to the config directory. Absolute paths and paths outside the config root are not supported.`);
|
|
53
50
|
}
|
|
54
|
-
return (0, tools_1.loadFileAndReplaceKeywords)(
|
|
51
|
+
return (0, tools_1.loadFileAndReplaceKeywords)(toLoad, {
|
|
55
52
|
mappings: this.mappings,
|
|
56
53
|
disableKeywordReplacement: this.disableKeywordReplacement,
|
|
57
54
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Management } from 'auth0';
|
|
2
|
+
import DefaultAPIHandler from './default';
|
|
3
|
+
import { Asset, Assets } from '../../../types';
|
|
4
|
+
import { Action } from './actions';
|
|
5
|
+
export declare const schema: {
|
|
6
|
+
type: string;
|
|
7
|
+
items: {
|
|
8
|
+
type: string;
|
|
9
|
+
properties: {
|
|
10
|
+
id: {
|
|
11
|
+
type: string;
|
|
12
|
+
};
|
|
13
|
+
name: {
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
status: {
|
|
17
|
+
type: string;
|
|
18
|
+
enum: string[];
|
|
19
|
+
};
|
|
20
|
+
subscriptions: {
|
|
21
|
+
type: string;
|
|
22
|
+
items: {
|
|
23
|
+
type: string;
|
|
24
|
+
properties: {
|
|
25
|
+
event_type: {
|
|
26
|
+
type: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
destination: {
|
|
32
|
+
type: string;
|
|
33
|
+
properties: {
|
|
34
|
+
type: {
|
|
35
|
+
type: string;
|
|
36
|
+
enum: string[];
|
|
37
|
+
};
|
|
38
|
+
configuration: {
|
|
39
|
+
type: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
required: string[];
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
required: string[];
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export type EventStream = Management.EventStreamResponseContent;
|
|
49
|
+
export default class EventStreamsHandler extends DefaultAPIHandler {
|
|
50
|
+
existing: Asset[] | null;
|
|
51
|
+
private actions;
|
|
52
|
+
constructor(config: DefaultAPIHandler);
|
|
53
|
+
objString(item: Asset): string;
|
|
54
|
+
getActions(): Promise<Action[]>;
|
|
55
|
+
getType(): Promise<Asset[]>;
|
|
56
|
+
private resolveActionId;
|
|
57
|
+
processChanges(assets: Assets): Promise<void>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.schema = void 0;
|
|
43
|
+
const default_1 = __importStar(require("./default"));
|
|
44
|
+
const client_1 = require("../client");
|
|
45
|
+
exports.schema = {
|
|
46
|
+
type: 'array',
|
|
47
|
+
items: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
id: { type: 'string' },
|
|
51
|
+
name: { type: 'string' },
|
|
52
|
+
status: { type: 'string', enum: ['enabled', 'disabled'] },
|
|
53
|
+
subscriptions: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
items: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
event_type: { type: 'string' },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
destination: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
type: { type: 'string', enum: ['webhook', 'eventbridge', 'action'] },
|
|
66
|
+
configuration: { type: 'object' },
|
|
67
|
+
},
|
|
68
|
+
required: ['type', 'configuration'],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ['name', 'destination'],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
class EventStreamsHandler extends default_1.default {
|
|
75
|
+
constructor(config) {
|
|
76
|
+
super({
|
|
77
|
+
...config,
|
|
78
|
+
type: 'eventStreams',
|
|
79
|
+
identifiers: ['id', 'name'],
|
|
80
|
+
// aws_partner_event_source is auto-populated by AWS after creation; sending it on create fails
|
|
81
|
+
stripCreateFields: [
|
|
82
|
+
'destination.configuration.aws_partner_event_source',
|
|
83
|
+
'created_at',
|
|
84
|
+
'updated_at',
|
|
85
|
+
],
|
|
86
|
+
stripUpdateFields: ['created_at', 'updated_at'],
|
|
87
|
+
});
|
|
88
|
+
this.actions = null;
|
|
89
|
+
}
|
|
90
|
+
objString(item) {
|
|
91
|
+
return super.objString(item.name);
|
|
92
|
+
}
|
|
93
|
+
async getActions() {
|
|
94
|
+
if (this.actions)
|
|
95
|
+
return this.actions;
|
|
96
|
+
this.actions = await (0, client_1.paginate)(this.client.actions.list, { paginate: true });
|
|
97
|
+
return this.actions;
|
|
98
|
+
}
|
|
99
|
+
async getType() {
|
|
100
|
+
if (this.existing) {
|
|
101
|
+
return this.existing;
|
|
102
|
+
}
|
|
103
|
+
const eventStreams = [];
|
|
104
|
+
let page = await this.client.eventStreams.list();
|
|
105
|
+
eventStreams.push(...page.data);
|
|
106
|
+
while (page.hasNextPage()) {
|
|
107
|
+
page = await page.getNextPage();
|
|
108
|
+
eventStreams.push(...page.data);
|
|
109
|
+
}
|
|
110
|
+
// For action-destination streams, replace action_id with the action name for export
|
|
111
|
+
const actions = await this.getActions();
|
|
112
|
+
this.existing = eventStreams.map((stream) => {
|
|
113
|
+
if (stream.destination?.type === 'action' && stream.destination.configuration?.action_id) {
|
|
114
|
+
const action = actions.find((a) => a.id === stream.destination.configuration.action_id);
|
|
115
|
+
if (action) {
|
|
116
|
+
return {
|
|
117
|
+
...stream,
|
|
118
|
+
destination: {
|
|
119
|
+
...stream.destination,
|
|
120
|
+
configuration: {
|
|
121
|
+
...stream.destination.configuration,
|
|
122
|
+
action_id: action.name,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return stream;
|
|
129
|
+
});
|
|
130
|
+
return this.existing;
|
|
131
|
+
}
|
|
132
|
+
resolveActionId(stream) {
|
|
133
|
+
if (stream.destination?.type !== 'action' || !stream.destination.configuration?.action_id) {
|
|
134
|
+
return stream;
|
|
135
|
+
}
|
|
136
|
+
const name = stream.destination.configuration.action_id;
|
|
137
|
+
const action = (this.actions ?? []).find((a) => a.name === name);
|
|
138
|
+
if (!action) {
|
|
139
|
+
throw new Error(`Event stream "${stream.name}" references action "${name}" which was not found`);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
...stream,
|
|
143
|
+
destination: {
|
|
144
|
+
...stream.destination,
|
|
145
|
+
configuration: {
|
|
146
|
+
...stream.destination.configuration,
|
|
147
|
+
action_id: action.id,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
async processChanges(assets) {
|
|
153
|
+
const { eventStreams } = assets;
|
|
154
|
+
if (!eventStreams)
|
|
155
|
+
return;
|
|
156
|
+
if (!this.actions) {
|
|
157
|
+
this.actions = await this.getActions();
|
|
158
|
+
}
|
|
159
|
+
const changes = await this.calcChanges(assets).then((changesResponse) => ({
|
|
160
|
+
...changesResponse,
|
|
161
|
+
create: changesResponse.create.map((stream) => this.resolveActionId(stream)),
|
|
162
|
+
update: changesResponse.update.map((stream) => {
|
|
163
|
+
const resolved = this.resolveActionId(stream);
|
|
164
|
+
// EventBridge destinations cannot be patched — the API rejects destination on PATCH
|
|
165
|
+
if (resolved.destination?.type === 'eventbridge') {
|
|
166
|
+
const { destination: _dest, ...rest } = resolved;
|
|
167
|
+
return rest;
|
|
168
|
+
}
|
|
169
|
+
return resolved;
|
|
170
|
+
}),
|
|
171
|
+
}));
|
|
172
|
+
await super.processChanges(assets, changes);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.default = EventStreamsHandler;
|
|
176
|
+
__decorate([
|
|
177
|
+
(0, default_1.order)('75')
|
|
178
|
+
], EventStreamsHandler.prototype, "processChanges", null);
|
|
@@ -74,6 +74,7 @@ const userAttributeProfiles = __importStar(require("./userAttributeProfiles"));
|
|
|
74
74
|
const connectionProfiles = __importStar(require("./connectionProfiles"));
|
|
75
75
|
const tokenExchangeProfiles = __importStar(require("./tokenExchangeProfiles"));
|
|
76
76
|
const supplementalSignals = __importStar(require("./supplementalSignals"));
|
|
77
|
+
const eventStreams = __importStar(require("./eventStreams"));
|
|
77
78
|
const auth0ApiHandlers = {
|
|
78
79
|
rules,
|
|
79
80
|
rulesConfigs,
|
|
@@ -117,5 +118,6 @@ const auth0ApiHandlers = {
|
|
|
117
118
|
connectionProfiles,
|
|
118
119
|
tokenExchangeProfiles,
|
|
119
120
|
supplementalSignals,
|
|
121
|
+
eventStreams,
|
|
120
122
|
};
|
|
121
123
|
exports.default = auth0ApiHandlers; // TODO: apply stronger types to schema properties
|
package/lib/tools/auth0/index.js
CHANGED
|
@@ -11,6 +11,10 @@ const promise_pool_executor_1 = require("promise-pool-executor");
|
|
|
11
11
|
const client_1 = __importDefault(require("./client"));
|
|
12
12
|
const schema_1 = __importDefault(require("./schema"));
|
|
13
13
|
const handlers_1 = __importDefault(require("./handlers"));
|
|
14
|
+
// Cache the compiled AJV validator to avoid recompiling the schema on every validate() call.
|
|
15
|
+
// Schema compilation is expensive (~5s) due to the complexity of the combined resource schema.
|
|
16
|
+
const ajv = new ajv_1.default({ useDefaults: true, nullable: true });
|
|
17
|
+
const validateSchema = ajv.compile(schema_1.default);
|
|
14
18
|
const calculateDryRunChanges_1 = require("../calculateDryRunChanges");
|
|
15
19
|
const utils_1 = require("../../utils");
|
|
16
20
|
const utils_2 = require("../utils");
|
|
@@ -105,13 +109,12 @@ class Auth0 {
|
|
|
105
109
|
}
|
|
106
110
|
}
|
|
107
111
|
async validate() {
|
|
108
|
-
const ajv = new ajv_1.default({ useDefaults: true, nullable: true });
|
|
109
112
|
const nonNullAssets = Object.keys(this.assets)
|
|
110
113
|
.filter((k) => this.assets[k] != null)
|
|
111
114
|
.reduce((a, k) => ({ ...a, [k]: this.assets[k] }), {});
|
|
112
|
-
const valid =
|
|
115
|
+
const valid = validateSchema(nonNullAssets);
|
|
113
116
|
if (!valid) {
|
|
114
|
-
throw new Error(`Schema validation failed loading ${JSON.stringify(
|
|
117
|
+
throw new Error(`Schema validation failed loading ${JSON.stringify(validateSchema.errors, null, 4)}`);
|
|
115
118
|
}
|
|
116
119
|
await this.runStage('validate');
|
|
117
120
|
}
|
package/lib/tools/constants.d.ts
CHANGED
package/lib/tools/constants.js
CHANGED
|
@@ -220,5 +220,6 @@ const constants = {
|
|
|
220
220
|
CONNECTION_PROFILES_DIRECTORY: 'connection-profiles',
|
|
221
221
|
TOKEN_EXCHANGE_PROFILES_DIRECTORY: 'token-exchange-profiles',
|
|
222
222
|
SUPPLEMENTAL_SIGNALS_DIRECTORY: 'supplemental-signals',
|
|
223
|
+
EVENT_STREAMS_DIRECTORY: 'event-streams',
|
|
223
224
|
};
|
|
224
225
|
exports.default = constants;
|
package/lib/types.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { AttackProtection } from './tools/auth0/handlers/attackProtection';
|
|
|
21
21
|
import { TokenExchangeProfile } from './tools/auth0/handlers/tokenExchangeProfiles';
|
|
22
22
|
import { RiskAssessment } from './tools/auth0/handlers/riskAssessment';
|
|
23
23
|
import { SupplementalSignals } from './tools/auth0/handlers/supplementalSignals';
|
|
24
|
+
import { EventStream } from './tools/auth0/handlers/eventStreams';
|
|
24
25
|
type SharedPaginationParams = {
|
|
25
26
|
checkpoint?: boolean;
|
|
26
27
|
paginate?: boolean;
|
|
@@ -158,6 +159,7 @@ export type Assets = Partial<{
|
|
|
158
159
|
userAttributeProfilesWithId: UserAttributeProfile[] | null;
|
|
159
160
|
connectionProfiles: Asset[] | null;
|
|
160
161
|
tokenExchangeProfiles: TokenExchangeProfile[] | null;
|
|
162
|
+
eventStreams: EventStream[] | null;
|
|
161
163
|
}>;
|
|
162
164
|
export type CalculatedChanges = {
|
|
163
165
|
del: Asset[];
|
|
@@ -178,7 +180,7 @@ export type DetailedDryRunChanges = {
|
|
|
178
180
|
changes: DetailedDryRunChange[];
|
|
179
181
|
};
|
|
180
182
|
};
|
|
181
|
-
export type AssetTypes = 'rules' | 'rulesConfigs' | 'hooks' | 'pages' | 'databases' | 'clientGrants' | 'resourceServers' | 'clients' | 'connections' | 'tenant' | 'emailProvider' | 'emailTemplates' | 'guardianFactors' | 'guardianFactorProviders' | 'guardianFactorTemplates' | 'guardianPhoneFactorMessageTypes' | 'guardianPhoneFactorSelectedProvider' | 'guardianPolicies' | 'roles' | 'actions' | 'actionModules' | 'organizations' | 'triggers' | 'attackProtection' | 'riskAssessment' | 'branding' | 'phoneProviders' | 'phoneTemplates' | 'logStreams' | 'prompts' | 'customDomains' | 'themes' | 'forms' | 'flows' | 'flowVaultConnections' | 'selfServiceProfiles' | 'networkACLs' | 'userAttributeProfiles' | 'connectionProfiles' | 'tokenExchangeProfiles' | 'supplementalSignals';
|
|
183
|
+
export type AssetTypes = 'rules' | 'rulesConfigs' | 'hooks' | 'pages' | 'databases' | 'clientGrants' | 'resourceServers' | 'clients' | 'connections' | 'tenant' | 'emailProvider' | 'emailTemplates' | 'guardianFactors' | 'guardianFactorProviders' | 'guardianFactorTemplates' | 'guardianPhoneFactorMessageTypes' | 'guardianPhoneFactorSelectedProvider' | 'guardianPolicies' | 'roles' | 'actions' | 'actionModules' | 'organizations' | 'triggers' | 'attackProtection' | 'riskAssessment' | 'branding' | 'phoneProviders' | 'phoneTemplates' | 'logStreams' | 'prompts' | 'customDomains' | 'themes' | 'forms' | 'flows' | 'flowVaultConnections' | 'selfServiceProfiles' | 'networkACLs' | 'userAttributeProfiles' | 'connectionProfiles' | 'tokenExchangeProfiles' | 'supplementalSignals' | 'eventStreams';
|
|
182
184
|
export type KeywordMappings = {
|
|
183
185
|
[key: string]: (string | number)[] | string | number;
|
|
184
186
|
};
|