Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 96x 96x 96x 96x 96x 96x 96x 96x 96x 96x 20x 20x 20x 20x 43x 43x 43x 43x 43x 43x 1x 1x 42x 95x 95x 93x 42x 93x 93x 89x 42x 42x 41x 1189x 1189x 1189x 1188x 1188x 1x 1x 5x 5x 5x 5x 5x 5x 1x 1x 1x 1x 4x 5x 145x 145x 118x 118x 118x 118x 118x 118x 118x 5x 4x 4x 4x 4x 4x 4x | import fs from 'fs-extra'; import yaml from 'js-yaml'; import path from 'path'; import { loadFileAndReplaceKeywords, keywordReplace, wrapArrayReplaceMarkersInQuotes, Auth0, } from '../../tools'; import log from '../../logger'; import { isFile, toConfigFn, stripIdentifiers, formatResults, recordsSorter } from '../../utils'; import handlers, { YAMLHandler } from './handlers'; import cleanAssets from '../../readonly'; import { Assets, Config, Auth0APIClient, AssetTypes, KeywordMappings } from '../../types'; import { filterOnlyIncludedResourceTypes } from '..'; import { preserveKeywords } from '../../keywordPreservation'; export default class YAMLContext { basePath: string; configFile: string; config: Config; mappings: KeywordMappings; mgmtClient: Auth0APIClient; assets: Assets; disableKeywordReplacement: boolean; constructor(config: Config, mgmtClient) { this.configFile = config.AUTH0_INPUT_FILE; this.config = config; this.mappings = config.AUTH0_KEYWORD_REPLACE_MAPPINGS || {}; this.mgmtClient = mgmtClient; this.disableKeywordReplacement = false; //@ts-ignore because the assets property gets filled out throughout this.assets = {}; // Get excluded rules this.assets.exclude = { rules: config.AUTH0_EXCLUDED_RULES || [], clients: config.AUTH0_EXCLUDED_CLIENTS || [], databases: config.AUTH0_EXCLUDED_DATABASES || [], connections: config.AUTH0_EXCLUDED_CONNECTIONS || [], resourceServers: config.AUTH0_EXCLUDED_RESOURCE_SERVERS || [], defaults: config.AUTH0_EXCLUDED_DEFAULTS || [], }; this.basePath = (() => { Iif (!!config.AUTH0_BASE_PATH) return config.AUTH0_BASE_PATH; //@ts-ignore because this looks to be a bug, but do not want to introduce regression; more investigation needed return typeof configFile === 'object' ? process.cwd() : path.dirname(this.configFile); })(); } loadFile(f) { let toLoad = path.join(this.basePath, f); Eif (!isFile(toLoad)) { // try load not relative to yaml file toLoad = f; } return loadFileAndReplaceKeywords(path.resolve(toLoad), { mappings: this.mappings, disableKeywordReplacement: this.disableKeywordReplacement, }); } async loadAssetsFromLocal(opts = { disableKeywordReplacement: false }) { // Allow to send object/json directly this.disableKeywordReplacement = opts.disableKeywordReplacement; Iif (typeof this.configFile === 'object') { this.assets = this.configFile; } else { try { const fPath = path.resolve(this.configFile); log.debug(`Loading YAML from ${fPath}`); Object.assign( this.assets, yaml.load( opts.disableKeywordReplacement ? wrapArrayReplaceMarkersInQuotes(fs.readFileSync(fPath, 'utf8'), this.mappings) : keywordReplace(fs.readFileSync(fPath, 'utf8'), this.mappings) ) || {} ); } catch (err) { log.debug(err.stack); throw new Error(`Problem loading ${this.configFile}\n${err}`); } } this.assets = Object.keys(this.assets).reduce((acc: Assets, key: AssetTypes) => { const excludedAssetTypes = this.config.AUTH0_EXCLUDED || []; if (excludedAssetTypes.includes(key)) return acc; return { ...acc, [key]: this.assets[key], }; }, {}); this.assets = Object.keys(this.assets).reduce((acc: Assets, key: AssetTypes) => { const includedAssetTypes = this.config.AUTH0_INCLUDED_ONLY; if (includedAssetTypes !== undefined && !includedAssetTypes.includes(key)) return acc; return { ...acc, [key]: this.assets[key], }; }, {}); // Run initial schema check to ensure valid YAML const auth0 = new Auth0(this.mgmtClient, this.assets, toConfigFn(this.config)); if (!opts.disableKeywordReplacement) await auth0.validate(); //The schema validation needs to be disabled during keyword-preserved export because a field may be enforced as an array but will be expressed with an array replace marker (string). // Allow handlers to process the assets such as loading files etc await Promise.all( Object.entries(handlers).map(async ([name, handler]) => { try { const parsed = await handler.parse(this); Object.entries(parsed).forEach(([k, v]) => { this.assets[k] = v; }); } catch (err) { log.debug(err.stack); throw new Error(`Problem deploying ${name}, ${err}`); } }) ); } async dump(): Promise<void> { const auth0 = new Auth0(this.mgmtClient, this.assets, toConfigFn(this.config)); log.info('Loading Auth0 Tenant Data'); try { await auth0.loadAssetsFromAuth0(); const shouldPreserveKeywords = //@ts-ignore because the string=>boolean conversion may not have happened if passed-in as env var this.config.AUTH0_PRESERVE_KEYWORDS === 'true' || this.config.AUTH0_PRESERVE_KEYWORDS === true; if (shouldPreserveKeywords) { await this.loadAssetsFromLocal({ disableKeywordReplacement: true }); //Need to disable keyword replacement to retrieve the raw keyword markers (ex: ##KEYWORD##) const localAssets = { ...this.assets }; //@ts-ignore delete this['assets']; this.assets = preserveKeywords({ localAssets, remoteAssets: auth0.assets, keywordMappings: this.config.AUTH0_KEYWORD_REPLACE_MAPPINGS || {}, auth0Handlers: auth0.handlers, }); } else { this.assets = auth0.assets; } } catch (err) { const docUrl = 'https://auth0.com/docs/deploy/deploy-cli-tool/create-and-configure-the-deploy-cli-application#modify-deploy-cli-application-scopes'; const extraMessage = err.message.startsWith('Insufficient scope') ? `\nSee ${docUrl} for more information` : ''; throw new Error(`Problem loading tenant data from Auth0 ${err}${extraMessage}`); } await Promise.all( Object.entries(handlers) .filter(([handlerName]: [AssetTypes, YAMLHandler<any>]) => { const excludedAssetTypes = this.config.AUTH0_EXCLUDED || []; return !excludedAssetTypes.includes(handlerName); }) .filter(filterOnlyIncludedResourceTypes(this.config.AUTH0_INCLUDED_ONLY)) .map(async ([name, handler]) => { try { const data = await handler.dump(this); Eif (data) { Eif (data[name] !== null) log.info(`Exporting ${name}`); Object.entries(data).forEach(([k, v]) => { this.assets[k] = Array.isArray(v) ? v.map(formatResults).sort(recordsSorter) : formatResults(v); }); } } catch (err) { log.debug(err.stack); throw new Error(`Problem exporting ${name}`); } }) ); // Clean known read only fields let cleaned = cleanAssets(this.assets, this.config); // Delete exclude as it's not part of the auth0 tenant config delete cleaned.exclude; // Optionally Strip identifiers Eif (!this.config.AUTH0_EXPORT_IDENTIFIERS) { cleaned = stripIdentifiers(auth0, cleaned); } // Write YAML File const raw = yaml.dump(cleaned); log.info(`Writing ${this.configFile}`); fs.writeFileSync(this.configFile, raw); } } |