| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- const debug = require('debug')('growi:service:ConfigLoader');
- const { envUtils } = require('growi-commons');
- const isSecurityEnv = require('../../lib/util/isSecurityEnv');
- const TYPES = {
- NUMBER: { parse: (v) => { return parseInt(v, 10) } },
- STRING: { parse: (v) => { return v } },
- BOOLEAN: { parse: (v) => { return envUtils.toBoolean(v) } },
- };
- /**
- * The following env vars are excluded because these are currently used before the configuration setup.
- * - MONGO_URI
- * - NODE_ENV
- * - PORT
- * - REDIS_URI
- * - SESSION_NAME
- * - PASSWORD_SEED
- * - SECRET_TOKEN
- *
- * The commented out item has not yet entered the migration work.
- * So, parameters of these are under consideration.
- */
- const ENV_VAR_NAME_TO_CONFIG_INFO = {
- // FILE_UPLOAD: {
- // ns: ,
- // key: ,
- // type: ,
- // default:
- // },
- // HACKMD_URI: {
- // ns: ,
- // key: ,
- // type: ,
- // default:
- // },
- // HACKMD_URI_FOR_SERVER: {
- // ns: ,
- // key: ,
- // type: ,
- // default:
- // },
- // PLANTUML_URI: {
- // ns: ,
- // key: ,
- // type: ,
- // default:
- // },
- // BLOCKDIAG_URI: {
- // ns: ,
- // key: ,
- // type: ,
- // default:
- // },
- // OAUTH_GOOGLE_CLIENT_ID: {
- // ns: 'crowi',
- // key: 'security:passport-google:clientId',
- // type: ,
- // default:
- // },
- // OAUTH_GOOGLE_CLIENT_SECRET: {
- // ns: 'crowi',
- // key: 'security:passport-google:clientSecret',
- // type: ,
- // default:
- // },
- // OAUTH_GOOGLE_CALLBACK_URI: {
- // ns: 'crowi',
- // key: 'security:passport-google:callbackUrl',
- // type: ,
- // default:
- // },
- // OAUTH_GITHUB_CLIENT_ID: {
- // ns: 'crowi',
- // key: 'security:passport-github:clientId',
- // type: ,
- // default:
- // },
- // OAUTH_GITHUB_CLIENT_SECRET: {
- // ns: 'crowi',
- // key: 'security:passport-github:clientSecret',
- // type: ,
- // default:
- // },
- // OAUTH_GITHUB_CALLBACK_URI: {
- // ns: 'crowi',
- // key: 'security:passport-github:callbackUrl',
- // type: ,
- // default:
- // },
- // OAUTH_TWITTER_CONSUMER_KEY: {
- // ns: 'crowi',
- // key: 'security:passport-twitter:consumerKey',
- // type: ,
- // default:
- // },
- // OAUTH_TWITTER_CONSUMER_SECRET: {
- // ns: 'crowi',
- // key: 'security:passport-twitter:consumerSecret',
- // type: ,
- // default:
- // },
- // OAUTH_TWITTER_CALLBACK_URI: {
- // ns: 'crowi',
- // key: 'security:passport-twitter:callbackUrl',
- // type: ,
- // default:
- // },
- APP_SITE_URL: {
- ns: 'crowi',
- key: 'app:siteUrl',
- type: TYPES.STRING,
- default: null,
- },
- PUBLISH_OPEN_API: {
- ns: 'crowi',
- key: 'app:publishOpenAPI',
- type: TYPES.BOOLEAN,
- default: false,
- },
- MAX_FILE_SIZE: {
- ns: 'crowi',
- key: 'app:maxFileSize',
- type: TYPES.NUMBER,
- default: Infinity,
- },
- FILE_UPLOAD_TOTAL_LIMIT: {
- ns: 'crowi',
- key: 'app:fileUploadTotalLimit',
- type: TYPES.NUMBER,
- default: Infinity,
- },
- FILE_UPLOAD_DISABLED: {
- ns: 'crowi',
- key: 'app:fileUploadDisabled',
- type: TYPES.BOOLEAN,
- default: false,
- },
- ELASTICSEARCH_URI: {
- ns: 'crowi',
- key: 'app:elasticsearchUri',
- type: TYPES.STRING,
- default: null,
- },
- ELASTICSEARCH_REQUEST_TIMEOUT: {
- ns: 'crowi',
- key: 'app:elasticsearchRequestTimeout',
- type: TYPES.NUMBER,
- default: 8000, // msec
- },
- SEARCHBOX_SSL_URL: {
- ns: 'crowi',
- key: 'app:searchboxSslUrl',
- type: TYPES.STRING,
- default: null,
- },
- MONGO_GRIDFS_TOTAL_LIMIT: {
- ns: 'crowi',
- key: 'gridfs:totalLimit',
- type: TYPES.NUMBER,
- default: null, // set null in default for backward compatibility
- // cz: Newer system respects FILE_UPLOAD_TOTAL_LIMIT.
- // If the default value of MONGO_GRIDFS_TOTAL_LIMIT is Infinity,
- // the system can't distinguish between "not specified" and "Infinity is specified".
- },
- FORCE_WIKI_MODE: {
- ns: 'crowi',
- key: 'security:wikiMode',
- type: TYPES.STRING,
- default: undefined,
- },
- USER_UPPER_LIMIT: {
- ns: 'crowi',
- key: 'security:userUpperLimit',
- type: TYPES.NUMBER,
- default: Infinity,
- },
- LOCAL_STRATEGY_ENABLED: {
- ns: 'crowi',
- key: 'security:passport-local:isEnabled',
- type: TYPES.BOOLEAN,
- default: true,
- },
- LOCAL_STRATEGY_USES_ONLY_ENV_VARS_FOR_SOME_OPTIONS: {
- ns: 'crowi',
- key: 'security:passport-local:useOnlyEnvVarsForSomeOptions',
- type: TYPES.BOOLEAN,
- default: false,
- },
- SAML_USES_ONLY_ENV_VARS_FOR_SOME_OPTIONS: {
- ns: 'crowi',
- key: 'security:passport-saml:useOnlyEnvVarsForSomeOptions',
- type: TYPES.BOOLEAN,
- default: false,
- },
- SAML_ENABLED: {
- ns: 'crowi',
- key: 'security:passport-saml:isEnabled',
- type: TYPES.BOOLEAN,
- default: null,
- },
- SAML_ENTRY_POINT: {
- ns: 'crowi',
- key: 'security:passport-saml:entryPoint',
- type: TYPES.STRING,
- default: null,
- },
- SAML_CALLBACK_URI: {
- ns: 'crowi',
- key: 'security:passport-saml:callbackUrl',
- type: TYPES.STRING,
- default: null,
- },
- SAML_ISSUER: {
- ns: 'crowi',
- key: 'security:passport-saml:issuer',
- type: TYPES.STRING,
- default: null,
- },
- SAML_ATTR_MAPPING_ID: {
- ns: 'crowi',
- key: 'security:passport-saml:attrMapId',
- type: TYPES.STRING,
- default: null,
- },
- SAML_ATTR_MAPPING_USERNAME: {
- ns: 'crowi',
- key: 'security:passport-saml:attrMapUsername',
- type: TYPES.STRING,
- default: null,
- },
- SAML_ATTR_MAPPING_MAIL: {
- ns: 'crowi',
- key: 'security:passport-saml:attrMapMail',
- type: TYPES.STRING,
- default: null,
- },
- SAML_ATTR_MAPPING_FIRST_NAME: {
- ns: 'crowi',
- key: 'security:passport-saml:attrMapFirstName',
- type: TYPES.STRING,
- default: null,
- },
- SAML_ATTR_MAPPING_LAST_NAME: {
- ns: 'crowi',
- key: 'security:passport-saml:attrMapLastName',
- type: TYPES.STRING,
- default: null,
- },
- SAML_CERT: {
- ns: 'crowi',
- key: 'security:passport-saml:cert',
- type: TYPES.STRING,
- default: null,
- },
- SAML_ABLC_RULE: {
- ns: 'crowi',
- key: 'security:passport-saml:ABLCRule',
- type: TYPES.STRING,
- default: null,
- },
- GCS_API_KEY_JSON_PATH: {
- ns: 'crowi',
- key: 'gcs:apiKeyJsonPath',
- type: TYPES.STRING,
- default: null,
- },
- GCS_BUCKET: {
- ns: 'crowi',
- key: 'gcs:bucket',
- type: TYPES.STRING,
- default: null,
- },
- GCS_UPLOAD_NAMESPACE: {
- ns: 'crowi',
- key: 'gcs:uploadNamespace',
- type: TYPES.STRING,
- default: null,
- },
- };
- class ConfigLoader {
- constructor(configModel) {
- this.configModel = configModel;
- }
- /**
- * return a config object
- */
- async load() {
- const configFromDB = await this.loadFromDB();
- const configFromEnvVars = this.loadFromEnvVars();
- // merge defaults per ns
- const mergedConfigFromDB = {
- crowi: Object.assign(this.configModel.getDefaultCrowiConfigsObject(), configFromDB.crowi),
- markdown: Object.assign(this.configModel.getDefaultMarkdownConfigsObject(), configFromDB.markdown),
- notification: Object.assign(this.configModel.getDefaultNotificationConfigsObject(), configFromDB.notification),
- };
- // In getConfig API, only null is used as a value to indicate that a config is not set.
- // So, if a value loaded from the database is empty,
- // it is converted to null because an empty string is used as the same meaning in the config model.
- // By this processing, whether a value is loaded from the database or from the environment variable,
- // only null indicates a config is not set.
- for (const namespace of Object.keys(mergedConfigFromDB)) {
- for (const key of Object.keys(mergedConfigFromDB[namespace])) {
- if (mergedConfigFromDB[namespace][key] === '') {
- mergedConfigFromDB[namespace][key] = null;
- }
- }
- }
- return {
- fromDB: mergedConfigFromDB,
- fromEnvVars: configFromEnvVars,
- };
- }
- async loadFromDB() {
- const config = {};
- const docs = await this.configModel.find().exec();
- for (const doc of docs) {
- if (!config[doc.ns]) {
- config[doc.ns] = {};
- }
- config[doc.ns][doc.key] = JSON.parse(doc.value);
- }
- debug('ConfigLoader#loadFromDB', config);
- return config;
- }
- loadFromEnvVars() {
- const config = {};
- for (const ENV_VAR_NAME of Object.keys(ENV_VAR_NAME_TO_CONFIG_INFO)) {
- const configInfo = ENV_VAR_NAME_TO_CONFIG_INFO[ENV_VAR_NAME];
- if (config[configInfo.ns] === undefined) {
- config[configInfo.ns] = {};
- }
- if (process.env[ENV_VAR_NAME] === undefined) {
- config[configInfo.ns][configInfo.key] = configInfo.default;
- }
- else {
- config[configInfo.ns][configInfo.key] = configInfo.type.parse(process.env[ENV_VAR_NAME]);
- }
- }
- debug('ConfigLoader#loadFromEnvVars', config);
- return config;
- }
- /**
- * get config from the environment variables for display admin page
- *
- * **use this only admin home page.**
- */
- static getEnvVarsForDisplay(avoidSecurity = false) {
- const config = {};
- for (const ENV_VAR_NAME of Object.keys(ENV_VAR_NAME_TO_CONFIG_INFO)) {
- const configInfo = ENV_VAR_NAME_TO_CONFIG_INFO[ENV_VAR_NAME];
- if (process.env[ENV_VAR_NAME] === undefined) {
- continue;
- }
- if (isSecurityEnv(configInfo.key) && avoidSecurity) {
- continue;
- }
- config[ENV_VAR_NAME] = configInfo.type.parse(process.env[ENV_VAR_NAME]);
- }
- debug('ConfigLoader#getEnvVarsForDisplay', config);
- return config;
- }
- }
- module.exports = ConfigLoader;
|