auth0-deploy-cli 7.5.0 → 7.6.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.
Files changed (75) hide show
  1. package/.eslintrc +66 -17
  2. package/CHANGELOG.md +25 -2
  3. package/lib/args.js +6 -3
  4. package/lib/commands/export.js +8 -6
  5. package/lib/commands/import.js +11 -10
  6. package/lib/context/directory/handlers/actions.js +3 -2
  7. package/lib/context/directory/handlers/attackProtection.js +3 -2
  8. package/lib/context/directory/handlers/branding.js +59 -0
  9. package/lib/context/directory/handlers/clientGrants.js +3 -2
  10. package/lib/context/directory/handlers/clients.js +3 -2
  11. package/lib/context/directory/handlers/connections.js +3 -2
  12. package/lib/context/directory/handlers/databases.js +18 -16
  13. package/lib/context/directory/handlers/emailProvider.js +3 -2
  14. package/lib/context/directory/handlers/emailTemplates.js +11 -9
  15. package/lib/context/directory/handlers/guardianFactorProviders.js +3 -2
  16. package/lib/context/directory/handlers/guardianFactorTemplates.js +3 -2
  17. package/lib/context/directory/handlers/guardianFactors.js +3 -2
  18. package/lib/context/directory/handlers/guardianPhoneFactorMessageTypes.js +3 -2
  19. package/lib/context/directory/handlers/guardianPhoneFactorSelectedProvider.js +3 -2
  20. package/lib/context/directory/handlers/guardianPolicies.js +3 -2
  21. package/lib/context/directory/handlers/hooks.js +3 -2
  22. package/lib/context/directory/handlers/index.js +3 -1
  23. package/lib/context/directory/handlers/migrations.js +8 -8
  24. package/lib/context/directory/handlers/organizations.js +3 -2
  25. package/lib/context/directory/handlers/pages.js +18 -18
  26. package/lib/context/directory/handlers/resourceServers.js +3 -2
  27. package/lib/context/directory/handlers/roles.js +3 -2
  28. package/lib/context/directory/handlers/rules.js +3 -2
  29. package/lib/context/directory/handlers/rulesConfigs.js +4 -3
  30. package/lib/context/directory/handlers/tenant.js +5 -3
  31. package/lib/context/directory/handlers/triggers.js +3 -2
  32. package/lib/context/directory/index.js +2 -4
  33. package/lib/context/yaml/handlers/actions.js +4 -3
  34. package/lib/context/yaml/handlers/attackProtection.js +5 -11
  35. package/lib/context/yaml/handlers/branding.js +65 -0
  36. package/lib/context/yaml/handlers/clientGrants.js +3 -2
  37. package/lib/context/yaml/handlers/clients.js +3 -2
  38. package/lib/context/yaml/handlers/connections.js +3 -2
  39. package/lib/context/yaml/handlers/databases.js +3 -2
  40. package/lib/context/yaml/handlers/emailProvider.js +3 -2
  41. package/lib/context/yaml/handlers/emailTemplates.js +3 -2
  42. package/lib/context/yaml/handlers/guardianFactorProviders.js +5 -12
  43. package/lib/context/yaml/handlers/guardianFactorTemplates.js +5 -12
  44. package/lib/context/yaml/handlers/guardianFactors.js +5 -12
  45. package/lib/context/yaml/handlers/guardianPhoneFactorMessageTypes.js +5 -12
  46. package/lib/context/yaml/handlers/guardianPhoneFactorSelectedProvider.js +5 -12
  47. package/lib/context/yaml/handlers/guardianPolicies.js +5 -12
  48. package/lib/context/yaml/handlers/hooks.js +3 -2
  49. package/lib/context/yaml/handlers/index.js +3 -1
  50. package/lib/context/yaml/handlers/migrations.js +3 -2
  51. package/lib/context/yaml/handlers/organizations.js +3 -2
  52. package/lib/context/yaml/handlers/pages.js +3 -2
  53. package/lib/context/yaml/handlers/resourceServers.js +3 -2
  54. package/lib/context/yaml/handlers/roles.js +3 -2
  55. package/lib/context/yaml/handlers/rules.js +3 -2
  56. package/lib/context/yaml/handlers/rulesConfigs.js +4 -3
  57. package/lib/context/yaml/handlers/tenant.js +3 -2
  58. package/lib/context/yaml/handlers/triggers.js +3 -2
  59. package/lib/context/yaml/index.js +2 -1
  60. package/lib/index.js +1 -1
  61. package/lib/tools/auth0/handlers/branding.js +67 -13
  62. package/lib/tools/auth0/handlers/default.js +7 -1
  63. package/lib/tools/auth0/handlers/organizations.js +7 -2
  64. package/lib/tools/auth0/handlers/resourceServers.js +7 -2
  65. package/lib/tools/auth0/handlers/roles.js +7 -2
  66. package/lib/tools/auth0/handlers/rules.js +7 -1
  67. package/lib/tools/calculateChanges.js +144 -0
  68. package/lib/tools/constants.js +7 -0
  69. package/lib/tools/utils.js +1 -142
  70. package/package.json +8 -15
  71. package/tsconfig.json +11 -89
  72. package/.babelrc +0 -17
  73. package/.nyc_output/60b76a45-577b-4171-9982-a8e836ab7fd6.json +0 -1
  74. package/.nyc_output/processinfo/60b76a45-577b-4171-9982-a8e836ab7fd6.json +0 -1
  75. package/.nyc_output/processinfo/index.json +0 -1
@@ -30,7 +30,8 @@ function dump(context) {
30
30
  };
31
31
  });
32
32
  }
33
- exports.default = {
33
+ const triggersHandler = {
34
34
  parse,
35
- dump
35
+ dump,
36
36
  };
37
+ exports.default = triggersHandler;
@@ -39,6 +39,7 @@ class default_1 {
39
39
  };
40
40
  this.basePath = config.AUTH0_BASE_PATH;
41
41
  if (!this.basePath) {
42
+ //@ts-ignore because this looks to be a bug, but do not want to introduce regression; more investigation needed
42
43
  this.basePath = (typeof configFile === 'object') ? process.cwd() : path_1.default.dirname(this.configFile);
43
44
  }
44
45
  }
@@ -69,7 +70,7 @@ class default_1 {
69
70
  }
70
71
  // Run initial schema check to ensure valid YAML
71
72
  const auth0 = new tools_1.Auth0(this.mgmtClient, this.assets, (0, utils_1.toConfigFn)(this.config));
72
- yield auth0.validate('validate');
73
+ yield auth0.validate();
73
74
  // Allow handlers to process the assets such as loading files etc
74
75
  yield Promise.all(Object.entries(handlers_1.default).map(([name, handler]) => __awaiter(this, void 0, void 0, function* () {
75
76
  try {
package/lib/index.js CHANGED
@@ -65,7 +65,7 @@ if (require.main === module) {
65
65
  logger_1.default.debug(error.stack);
66
66
  }
67
67
  if (typeof msg === 'string' && msg.includes('Payload validation error')) {
68
- logger_1.default.info('Please see https://github.com/auth0/auth0-deploy-cli#troubleshooting for common issues');
68
+ logger_1.default.info('Please refer to the Auth0 Management API docs for expected payloads: https://auth0.com/docs/api/management/v2');
69
69
  }
70
70
  process.exit(1);
71
71
  });
@@ -14,25 +14,59 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.schema = void 0;
16
16
  const default_1 = __importDefault(require("./default"));
17
- exports.schema = { type: 'object' };
17
+ const constants_1 = __importDefault(require("../../constants"));
18
+ const logger_1 = __importDefault(require("../../../logger"));
19
+ exports.schema = {
20
+ type: 'object',
21
+ properties: {
22
+ templates: {
23
+ type: 'array',
24
+ items: {
25
+ type: 'object',
26
+ properties: {
27
+ template: { type: 'string' },
28
+ body: { type: 'string' }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ };
18
34
  class BrandingHandler extends default_1.default {
19
35
  constructor(options) {
20
36
  super(Object.assign(Object.assign({}, options), { type: 'branding' }));
21
37
  }
22
38
  getType() {
23
39
  return __awaiter(this, void 0, void 0, function* () {
24
- // in case client version does not support branding
25
- if (!this.client.branding || typeof this.client.branding.getSettings !== 'function') {
26
- return {};
27
- }
40
+ let branding = {};
28
41
  try {
29
- return yield this.client.branding.getSettings();
42
+ // in case client version does not support branding
43
+ if (this.client.branding && typeof this.client.branding.getSettings === 'function') {
44
+ branding = yield this.client.branding.getSettings();
45
+ }
46
+ // in case client version does not custom domains
47
+ if (this.client.customDomains && typeof this.client.customDomains.getAll === 'function') {
48
+ const customDomains = yield this.client.customDomains.getAll();
49
+ // templates are only supported if there's custom domains.
50
+ if (customDomains && customDomains.length) {
51
+ const payload = yield this.client.branding.getUniversalLoginTemplate();
52
+ branding.templates = [
53
+ {
54
+ template: constants_1.default.UNIVERSAL_LOGIN_TEMPLATE,
55
+ body: payload.body
56
+ }
57
+ ];
58
+ }
59
+ }
60
+ return branding;
30
61
  }
31
62
  catch (err) {
63
+ logger_1.default.debug(`Error calling branding API, ${err.message}, status code: ${err.statusCode}`);
64
+ if (err.statusCode === 403)
65
+ return branding;
32
66
  if (err.statusCode === 404)
33
- return {};
67
+ return branding;
34
68
  if (err.statusCode === 501)
35
- return {};
69
+ return branding;
36
70
  throw err;
37
71
  }
38
72
  });
@@ -40,12 +74,32 @@ class BrandingHandler extends default_1.default {
40
74
  processChanges(assets) {
41
75
  return __awaiter(this, void 0, void 0, function* () {
42
76
  const { branding } = assets;
43
- // Do nothing if not set
44
- if (!branding || !Object.keys(branding).length)
77
+ // quit early if there's no branding to process.
78
+ if (!branding)
45
79
  return;
46
- yield this.client.branding.updateSettings({}, branding);
47
- this.updated += 1;
48
- this.didUpdate(branding);
80
+ // remove templates, we only want top level branding settings for this API call
81
+ const brandingSettings = Object.assign({}, branding);
82
+ delete brandingSettings.templates;
83
+ // Do nothing if not set
84
+ if (brandingSettings && Object.keys(brandingSettings).length) {
85
+ yield this.client.branding.updateSettings({}, brandingSettings);
86
+ this.updated += 1;
87
+ this.didUpdate(brandingSettings);
88
+ }
89
+ // handle templates
90
+ if (branding.templates && branding.templates.length) {
91
+ const unknownTemplates = branding.templates.filter((t) => !constants_1.default.SUPPORTED_BRANDING_TEMPLATES.includes(t.template)).map((t) => t.template);
92
+ if (unknownTemplates.length) {
93
+ // throw a helpful warning for unknown templates, the context handlers are unaware of which are supported, that's all handled here.
94
+ logger_1.default.warn(`Found unknown branding template(s): ${unknownTemplates.join().toString()}. Supported branding templates are: ${constants_1.default.SUPPORTED_BRANDING_TEMPLATES.join()}.`);
95
+ }
96
+ const templateDefinition = branding.templates.find((t) => t.template === constants_1.default.UNIVERSAL_LOGIN_TEMPLATE);
97
+ if (templateDefinition && templateDefinition.body) {
98
+ yield this.client.branding.setUniversalLoginTemplate({}, { template: templateDefinition.body });
99
+ this.updated += 1;
100
+ this.didUpdate(branding.templates);
101
+ }
102
+ }
49
103
  });
50
104
  }
51
105
  }
@@ -16,6 +16,7 @@ exports.order = void 0;
16
16
  const ValidationError_1 = __importDefault(require("../../ValidationError"));
17
17
  const logger_1 = __importDefault(require("../../logger"));
18
18
  const utils_1 = require("../../utils");
19
+ const calculateChanges_1 = require("../../calculateChanges");
19
20
  function order(value) {
20
21
  return function decorator(t, n, descriptor) {
21
22
  descriptor.value.order = value; // eslint-disable-line
@@ -82,7 +83,12 @@ class DefaultHandler {
82
83
  return {};
83
84
  const existing = yield this.getType();
84
85
  // Figure out what needs to be updated vs created
85
- return (0, utils_1.calcChanges)(this, typeAssets, existing, this.identifiers);
86
+ return (0, calculateChanges_1.calculateChanges)({
87
+ handler: this,
88
+ assets: typeAssets,
89
+ existing,
90
+ identifiers: this.identifiers
91
+ });
86
92
  });
87
93
  }
88
94
  validate(assets) {
@@ -44,7 +44,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
44
44
  exports.schema = void 0;
45
45
  const lodash_1 = __importDefault(require("lodash"));
46
46
  const default_1 = __importStar(require("./default"));
47
- const utils_1 = require("../../utils");
47
+ const calculateChanges_1 = require("../../calculateChanges");
48
48
  const logger_1 = __importDefault(require("../../logger"));
49
49
  exports.schema = {
50
50
  type: 'array',
@@ -208,7 +208,12 @@ class OrganizationsHandler extends default_1.default {
208
208
  return Object.assign(Object.assign({}, connection), { connection_id: (existingConnections.find((c) => c.name === name) || {}).id });
209
209
  }).filter((connection) => !!connection.connection_id);
210
210
  });
211
- const changes = (0, utils_1.calcChanges)(this, organizations, existing, ['id', 'name']);
211
+ const changes = (0, calculateChanges_1.calculateChanges)({
212
+ handler: this,
213
+ assets: organizations,
214
+ existing,
215
+ identifiers: ['id', 'name']
216
+ });
212
217
  logger_1.default.debug(`Start processChanges for organizations [delete:${changes.del.length}] [update:${changes.update.length}], [create:${changes.create.length}]`);
213
218
  const myChanges = [{ del: changes.del }, { create: changes.create }, { update: changes.update }];
214
219
  yield Promise.all(myChanges.map((change) => __awaiter(this, void 0, void 0, function* () {
@@ -16,7 +16,7 @@ exports.schema = exports.excludeSchema = void 0;
16
16
  const ValidationError_1 = __importDefault(require("../../ValidationError"));
17
17
  const constants_1 = __importDefault(require("../../constants"));
18
18
  const default_1 = __importDefault(require("./default"));
19
- const utils_1 = require("../../utils");
19
+ const calculateChanges_1 = require("../../calculateChanges");
20
20
  exports.excludeSchema = {
21
21
  type: 'array',
22
22
  items: { type: 'string' }
@@ -71,7 +71,12 @@ class ResourceServersHandler extends default_1.default {
71
71
  // Filter excluded
72
72
  resourceServers = resourceServers.filter((r) => !excluded.includes(r.name));
73
73
  existing = existing.filter((r) => !excluded.includes(r.name));
74
- return (0, utils_1.calcChanges)(this, resourceServers, existing, ['id', 'identifier']);
74
+ return (0, calculateChanges_1.calculateChanges)({
75
+ handler: this,
76
+ assets: resourceServers,
77
+ existing,
78
+ identifiers: ['id', 'identifier']
79
+ });
75
80
  });
76
81
  }
77
82
  validate(assets) {
@@ -43,7 +43,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
43
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
44
  exports.schema = void 0;
45
45
  const default_1 = __importStar(require("./default"));
46
- const utils_1 = require("../../utils");
46
+ const calculateChanges_1 = require("../../calculateChanges");
47
47
  const logger_1 = __importDefault(require("../../logger"));
48
48
  exports.schema = {
49
49
  type: 'array',
@@ -188,7 +188,12 @@ class RoleHandler extends default_1.default {
188
188
  return;
189
189
  // Gets roles from destination tenant
190
190
  const existing = yield this.getType();
191
- const changes = (0, utils_1.calcChanges)(this, roles, existing, ['id', 'name']);
191
+ const changes = (0, calculateChanges_1.calculateChanges)({
192
+ handler: this,
193
+ assets: roles,
194
+ existing,
195
+ identifiers: ['id', 'name']
196
+ });
192
197
  logger_1.default.debug(`Start processChanges for roles [delete:${changes.del.length}] [update:${changes.update.length}], [create:${changes.create.length}]`);
193
198
  const myChanges = [{ del: changes.del }, { create: changes.create }, { update: changes.update }];
194
199
  yield Promise.all(myChanges.map((change) => __awaiter(this, void 0, void 0, function* () {
@@ -17,6 +17,7 @@ const ValidationError_1 = __importDefault(require("../../ValidationError"));
17
17
  const utils_1 = require("../../utils");
18
18
  const default_1 = __importDefault(require("./default"));
19
19
  const logger_1 = __importDefault(require("../../logger"));
20
+ const calculateChanges_1 = require("../../calculateChanges");
20
21
  exports.excludeSchema = {
21
22
  type: 'array',
22
23
  items: { type: 'string' }
@@ -84,7 +85,12 @@ class RulesHandler extends default_1.default {
84
85
  existing = existing.filter((r) => !excludedRules.includes(r.name));
85
86
  }
86
87
  // Figure out what needs to be updated vs created
87
- const { del, update, create, conflicts } = (0, utils_1.calcChanges)(this, rules, existing, ['id', 'name']);
88
+ const { del, update, create, conflicts } = (0, calculateChanges_1.calculateChanges)({
89
+ handler: this,
90
+ assets: rules,
91
+ existing,
92
+ identifiers: ['id', 'name']
93
+ });
88
94
  // Figure out the rules that need to be re-ordered
89
95
  const futureRules = [...create, ...update];
90
96
  const futureMaxOrder = Math.max(...futureRules.map((r) => r.order));
@@ -0,0 +1,144 @@
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
+ exports.calculateChanges = exports.processChangedObjectFields = void 0;
7
+ const logger_1 = __importDefault(require("./logger"));
8
+ /**
9
+ * @template T
10
+ * @param {typeof import('./auth0/handlers/default').default} handler
11
+ * @param {T} desiredAssetState
12
+ * @param {T} currentAssetState
13
+ * @param {string[]} [objectFields=[]]
14
+ * @param {boolean} [allowDelete=false]
15
+ * @returns T
16
+ */
17
+ function processChangedObjectFields({ handler, desiredAssetState, currentAssetState, allowDelete = false }) {
18
+ const desiredAssetStateWithChanges = Object.assign({}, desiredAssetState);
19
+ // eslint-disable-next-line no-restricted-syntax
20
+ for (const fieldName of handler.objectFields) {
21
+ const areDesiredStateAndCurrentStateEmpty = Object.keys(desiredAssetState[fieldName] || {}).length === 0 && Object.keys(currentAssetState[fieldName] || {}).length === 0;
22
+ if (areDesiredStateAndCurrentStateEmpty) {
23
+ // If both the desired state and current state for a given object is empty, it is a no-op and can skip
24
+ // eslint-disable-next-line no-continue
25
+ continue;
26
+ }
27
+ // A desired state that omits the objectField OR that has it as an empty object should
28
+ // signal that all fields should be removed (subject to ALLOW_DELETE).
29
+ if (desiredAssetState[fieldName] && Object.keys(desiredAssetState[fieldName]).length) {
30
+ // Both the current and desired state have the object field. Here's where we need to map
31
+ // to the APIv2 protocol of setting `null` values for deleted fields.
32
+ // For new and modified properties of the object field, we can just pass them through to
33
+ // APIv2.
34
+ if (currentAssetState[fieldName]) {
35
+ // eslint-disable-next-line no-restricted-syntax
36
+ for (const currentObjectFieldPropertyName of Object.keys(currentAssetState[fieldName])) {
37
+ // Loop through each object property that exists currently
38
+ if (desiredAssetState[fieldName][currentObjectFieldPropertyName] === undefined) {
39
+ // If the object has a property that exists now but doesn't exist in the proposed state
40
+ if (allowDelete) {
41
+ desiredAssetStateWithChanges[fieldName][currentObjectFieldPropertyName] = null;
42
+ }
43
+ else {
44
+ // If deletes aren't allowed, do outright delete the property within the object
45
+ logger_1.default.warn(`Detected that the ${fieldName} of the following ${handler.name || handler.id || ''} should be deleted. Doing so may be destructive.\nYou can enable deletes by setting 'AUTH0_ALLOW_DELETE' to true in the config\n${handler.objString(currentAssetState)}`);
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ else if (allowDelete) {
52
+ // If the desired state does not have the object field and the current state does, we
53
+ // should mark *all* properties for deletion by specifying an empty object.
54
+ //
55
+ // See: https://auth0.com/docs/users/metadata/manage-metadata-api#delete-user-metadata
56
+ desiredAssetStateWithChanges[fieldName] = {};
57
+ }
58
+ else {
59
+ delete desiredAssetStateWithChanges[fieldName];
60
+ logger_1.default.warn(`Detected that the ${fieldName} of the following ${handler.name || handler.id || ''} should be emptied. Doing so may be destructive.\nYou can enable deletes by setting 'AUTH0_ALLOW_DELETE' to true in the config\n${handler.objString(currentAssetState)}`);
61
+ }
62
+ }
63
+ return desiredAssetStateWithChanges;
64
+ }
65
+ exports.processChangedObjectFields = processChangedObjectFields;
66
+ function calculateChanges({ handler, assets, existing, identifiers = ['id', 'name'], allowDelete }) {
67
+ // Calculate the changes required between two sets of assets.
68
+ const update = [];
69
+ let del = [...existing];
70
+ let create = [...assets];
71
+ const conflicts = [];
72
+ const findByKeyValue = (key, value, arr) => arr.find((e) => {
73
+ if (Array.isArray(key)) {
74
+ const values = key.map((k) => e[k]);
75
+ if (values.every((v) => v)) {
76
+ return value === values.join('-');
77
+ }
78
+ }
79
+ return e[key] === value;
80
+ });
81
+ const processAssets = (id, arr) => {
82
+ arr.forEach((asset) => {
83
+ const assetIdValue = (() => {
84
+ if (Array.isArray(id)) {
85
+ const values = id.map((i) => asset[i]);
86
+ if (values.every((v) => v)) {
87
+ return values.join('-');
88
+ }
89
+ }
90
+ return asset[id];
91
+ })();
92
+ if (assetIdValue !== undefined) {
93
+ const found = findByKeyValue(id, assetIdValue, del);
94
+ if (found !== undefined) {
95
+ // Delete from existing
96
+ del = del.filter((e) => e !== found);
97
+ // Delete from create as it's an update
98
+ create = create.filter((e) => e !== asset);
99
+ // Append identifiers to asset
100
+ update.push(Object.assign(Object.assign({}, identifiers.reduce((obj, i) => {
101
+ if (found[i])
102
+ obj[i] = found[i];
103
+ return obj;
104
+ }, {})), (handler.objectFields.length
105
+ ? processChangedObjectFields({
106
+ handler, desiredAssetState: asset, currentAssetState: found, allowDelete
107
+ })
108
+ : asset)));
109
+ }
110
+ }
111
+ });
112
+ };
113
+ // Loop through identifiers (in order) to try match assets to existing
114
+ // If existing then update if not create
115
+ // The remainder will be deleted
116
+ for (const id of identifiers) { // eslint-disable-line
117
+ processAssets(id, [...create]);
118
+ }
119
+ // Check if there are assets with names that will conflict with existing names during the update process
120
+ // This will rename those assets to a temp random name first
121
+ // This assumes the first identifiers is the unique identifier
122
+ if (identifiers.includes('name')) {
123
+ const uniqueID = identifiers[0];
124
+ const futureAssets = [...create, ...update];
125
+ futureAssets.forEach((a) => {
126
+ // If the conflicting item is going to be deleted then skip
127
+ const inDeleted = del.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
128
+ if (!inDeleted) {
129
+ const conflict = existing.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
130
+ if (conflict) {
131
+ const temp = Math.random().toString(36).substr(2, 5);
132
+ conflicts.push(Object.assign(Object.assign({}, conflict), { name: `${conflict.name}-${temp}` }));
133
+ }
134
+ }
135
+ });
136
+ }
137
+ return {
138
+ del,
139
+ update,
140
+ conflicts,
141
+ create
142
+ };
143
+ }
144
+ exports.calculateChanges = calculateChanges;
@@ -141,12 +141,19 @@ constants.GUARDIAN_FACTOR_PROVIDERS = {
141
141
  sms: ['twilio'],
142
142
  'push-notification': ['sns']
143
143
  };
144
+ constants.UNIVERSAL_LOGIN_TEMPLATE = 'universal_login';
145
+ constants.SUPPORTED_BRANDING_TEMPLATES = [
146
+ constants.UNIVERSAL_LOGIN_TEMPLATE
147
+ ];
144
148
  constants.RESOURCE_SERVERS_DIRECTORY = 'resource-servers';
145
149
  constants.RESOURCE_SERVERS_CLIENT_NAME = 'resourceServers';
146
150
  constants.RESOURCE_SERVERS_MANAGEMENT_API_NAME = 'Auth0 Management API';
147
151
  constants.RESOURCE_SERVERS_ID_NAME = 'id';
148
152
  constants.CLIENTS_DIRECTORY = 'clients';
149
153
  constants.CLIENTS_GRANTS_DIRECTORY = 'grants';
154
+ constants.BRANDING_DIRECTORY = 'branding';
155
+ constants.BRANDING_TEMPLATES_DIRECTORY = 'templates';
156
+ constants.BRANDING_TEMPLATES_YAML_DIRECTORY = 'branding_templates';
150
157
  constants.CLIENTS_CLIENT_NAME = 'clients';
151
158
  constants.CLIENTS_CLIENT_ID_NAME = 'client_id';
152
159
  constants.CONNECTIONS_DIRECTORY = 'connections';
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.areArraysEquals = exports.filterExcluded = exports.duplicateItems = exports.getEnabledClients = exports.stripFields = exports.calcChanges = exports.processChangedObjectFields = exports.dumpJSON = exports.flatten = exports.loadFileAndReplaceKeywords = exports.convertClientNamesToIds = exports.convertClientNameToId = exports.keywordReplace = exports.keywordStringReplace = exports.keywordArrayReplace = void 0;
6
+ exports.areArraysEquals = exports.filterExcluded = exports.duplicateItems = exports.getEnabledClients = exports.stripFields = exports.dumpJSON = exports.flatten = exports.loadFileAndReplaceKeywords = exports.convertClientNamesToIds = exports.convertClientNameToId = exports.keywordReplace = exports.keywordStringReplace = exports.keywordArrayReplace = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const dot_prop_1 = __importDefault(require("dot-prop"));
@@ -80,147 +80,6 @@ function dumpJSON(obj, spacing = 0) {
80
80
  return JSON.stringify(obj, null, spacing);
81
81
  }
82
82
  exports.dumpJSON = dumpJSON;
83
- /**
84
- * @template T
85
- * @param {typeof import('./auth0/handlers/default').default} handler
86
- * @param {T} desiredAssetState
87
- * @param {T} currentAssetState
88
- * @param {string[]} [objectFields=[]]
89
- * @param {boolean} [allowDelete=false]
90
- * @returns T
91
- */
92
- function processChangedObjectFields({ handler, desiredAssetState, currentAssetState, allowDelete = false }) {
93
- const desiredAssetStateWithChanges = Object.assign({}, desiredAssetState);
94
- // eslint-disable-next-line no-restricted-syntax
95
- for (const fieldName of handler.objectFields) {
96
- const areDesiredStateAndCurrentStateEmpty = Object.keys(desiredAssetState[fieldName] || {}).length === 0 && Object.keys(currentAssetState[fieldName] || {}).length === 0;
97
- if (areDesiredStateAndCurrentStateEmpty) {
98
- // If both the desired state and current state for a given object is empty, it is a no-op and can skip
99
- // eslint-disable-next-line no-continue
100
- continue;
101
- }
102
- // A desired state that omits the objectField OR that has it as an empty object should
103
- // signal that all fields should be removed (subject to ALLOW_DELETE).
104
- if (desiredAssetState[fieldName] && Object.keys(desiredAssetState[fieldName]).length) {
105
- // Both the current and desired state have the object field. Here's where we need to map
106
- // to the APIv2 protocol of setting `null` values for deleted fields.
107
- // For new and modified properties of the object field, we can just pass them through to
108
- // APIv2.
109
- if (currentAssetState[fieldName]) {
110
- // eslint-disable-next-line no-restricted-syntax
111
- for (const currentObjectFieldPropertyName of Object.keys(currentAssetState[fieldName])) {
112
- // Loop through each object property that exists currently
113
- if (desiredAssetState[fieldName][currentObjectFieldPropertyName] === undefined) {
114
- // If the object has a property that exists now but doesn't exist in the proposed state
115
- if (allowDelete) {
116
- desiredAssetStateWithChanges[fieldName][currentObjectFieldPropertyName] = null;
117
- }
118
- else {
119
- // If deletes aren't allowed, do outright delete the property within the object
120
- logger_1.default.warn(`Detected that the ${fieldName} of the following ${handler.name || handler.id || ''} should be deleted. Doing so may be destructive.\nYou can enable deletes by setting 'AUTH0_ALLOW_DELETE' to true in the config\n${handler.objString(currentAssetState)}`);
121
- }
122
- }
123
- }
124
- }
125
- }
126
- else if (allowDelete) {
127
- // If the desired state does not have the object field and the current state does, we
128
- // should mark *all* properties for deletion by specifying an empty object.
129
- //
130
- // See: https://auth0.com/docs/users/metadata/manage-metadata-api#delete-user-metadata
131
- desiredAssetStateWithChanges[fieldName] = {};
132
- }
133
- else {
134
- delete desiredAssetStateWithChanges[fieldName];
135
- logger_1.default.warn(`Detected that the ${fieldName} of the following ${handler.name || handler.id || ''} should be emptied. Doing so may be destructive.\nYou can enable deletes by setting 'AUTH0_ALLOW_DELETE' to true in the config\n${handler.objString(currentAssetState)}`);
136
- }
137
- }
138
- return desiredAssetStateWithChanges;
139
- }
140
- exports.processChangedObjectFields = processChangedObjectFields;
141
- function calcChanges(handler, assets, existing, identifiers = ['id', 'name'], allowDelete = false) {
142
- // Calculate the changes required between two sets of assets.
143
- const update = [];
144
- let del = [...existing];
145
- let create = [...assets];
146
- const conflicts = [];
147
- const findByKeyValue = (key, value, arr) => arr.find((e) => {
148
- if (Array.isArray(key)) {
149
- const values = key.map((k) => e[k]);
150
- if (values.every((v) => v)) {
151
- return value === values.join('-');
152
- }
153
- }
154
- else {
155
- return e[key] === value;
156
- }
157
- return false;
158
- });
159
- const processAssets = (id, arr) => {
160
- arr.forEach((asset) => {
161
- let assetIdValue;
162
- if (Array.isArray(id)) {
163
- const values = id.map((i) => asset[i]);
164
- if (values.every((v) => v)) {
165
- assetIdValue = values.join('-');
166
- }
167
- }
168
- else {
169
- assetIdValue = asset[id];
170
- }
171
- if (assetIdValue) {
172
- const found = findByKeyValue(id, assetIdValue, del);
173
- if (found) {
174
- // Delete from existing
175
- del = del.filter((e) => e !== found);
176
- // Delete from create as it's an update
177
- create = create.filter((e) => e !== asset);
178
- // Append identifiers to asset
179
- update.push(Object.assign(Object.assign({}, identifiers.reduce((obj, i) => {
180
- if (found[i])
181
- obj[i] = found[i];
182
- return obj;
183
- }, {})), (handler.objectFields.length
184
- ? processChangedObjectFields({
185
- handler, desiredAssetState: asset, currentAssetState: found, allowDelete
186
- })
187
- : asset)));
188
- }
189
- }
190
- });
191
- };
192
- // Loop through identifiers (in order) to try match assets to existing
193
- // If existing then update if not create
194
- // The remainder will be deleted
195
- for (const id of identifiers) { // eslint-disable-line
196
- processAssets(id, [...create]);
197
- }
198
- // Check if there are assets with names that will conflict with existing names during the update process
199
- // This will rename those assets to a temp random name first
200
- // This assumes the first identifiers is the unique identifier
201
- if (identifiers.includes('name')) {
202
- const uniqueID = identifiers[0];
203
- const futureAssets = [...create, ...update];
204
- futureAssets.forEach((a) => {
205
- // If the conflicting item is going to be deleted then skip
206
- const inDeleted = del.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
207
- if (!inDeleted) {
208
- const conflict = existing.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
209
- if (conflict) {
210
- const temp = Math.random().toString(36).substr(2, 5);
211
- conflicts.push(Object.assign(Object.assign({}, conflict), { name: `${conflict.name}-${temp}` }));
212
- }
213
- }
214
- });
215
- }
216
- return {
217
- del,
218
- update,
219
- conflicts,
220
- create
221
- };
222
- }
223
- exports.calcChanges = calcChanges;
224
83
  function stripFields(obj, fields) {
225
84
  // Strip object fields supporting dot notation (ie: a.deep.field)
226
85
  const stripped = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auth0-deploy-cli",
3
- "version": "7.5.0",
3
+ "version": "7.6.0",
4
4
  "description": "A command line tool for deploying updates to your Auth0 tenant",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "lint:fix": "eslint --fix --ignore-path .eslintignore --ignore-pattern webpack . && kacl lint",
11
11
  "lint": "eslint --ignore-path .eslintignore --ignore-pattern webpack . && kacl lint",
12
12
  "pretest": "rimraf ./.nyc_output",
13
- "test": "cross-env NODE_ENV=test nyc mocha --recursive --require @babel/register test",
13
+ "test": "ts-mocha -p tsconfig.json --recursive 'test/**/*.test*'",
14
14
  "build": "rimraf ./lib && npx tsc",
15
15
  "dev": "npx tsc --watch",
16
16
  "prepare": "npm run build",
@@ -40,33 +40,26 @@
40
40
  "nconf": "^0.11.0",
41
41
  "promise-pool-executor": "^1.1.1",
42
42
  "sanitize-filename": "^1.6.1",
43
- "typescript": "^4.5.5",
44
43
  "winston": "^2.3.0",
45
44
  "yargs": "^15.3.1"
46
45
  },
47
46
  "devDependencies": {
48
- "@babel/cli": "^7.14.5",
49
- "@babel/core": "^7.9.6",
50
- "@babel/eslint-parser": "^7.14.5",
51
- "@babel/plugin-proposal-decorators": "^7.8.3",
52
- "@babel/preset-env": "^7.9.6",
53
- "@babel/register": "^7.9.0",
54
- "babel-eslint": "^10.0.0",
55
- "babel-plugin-dynamic-import-node": "^2.3.3",
56
- "babel-plugin-module-resolver": "^4.0.0",
57
- "babel-preset-env": "^1.7.0",
47
+ "@types/expect": "^24.3.0",
48
+ "@types/mocha": "^9.1.0",
49
+ "@typescript-eslint/parser": "^5.14.0",
58
50
  "chai": "^4.1.2",
59
51
  "chai-as-promised": "^7.1.1",
60
52
  "cross-env": "^3.1.4",
61
53
  "eslint": "^7.28.0",
62
54
  "eslint-config-airbnb-base": "^14.2.1",
63
- "eslint-import-resolver-babel-module": "^5.1.2",
64
55
  "eslint-plugin-import": "^2.20.2",
65
56
  "husky": "^7.0.4",
66
57
  "kacl": "^1.1.1",
67
58
  "mocha": "^9.1.3",
68
59
  "nyc": "^15.0.1",
69
60
  "rimraf": "^3.0.2",
70
- "rmdir-sync": "^1.0.1"
61
+ "rmdir-sync": "^1.0.1",
62
+ "ts-mocha": "^9.0.2",
63
+ "typescript": "^4.6.2"
71
64
  }
72
65
  }