diff --git a/backend/migrations/20200522144240_openid_allowed_users.js b/backend/migrations/20200522144240_openid_allowed_users.js new file mode 100644 index 00000000..9b4e0aad --- /dev/null +++ b/backend/migrations/20200522144240_openid_allowed_users.js @@ -0,0 +1,40 @@ +const migrate_name = 'openid_allowed_users'; +const logger = require('../logger').migrate; + +/** + * Migrate + * + * @see http://knexjs.org/#Schema + * + * @param {Object} knex + * @param {Promise} Promise + * @returns {Promise} + */ +exports.up = function (knex/*, Promise*/) { + logger.info('[' + migrate_name + '] Migrating Up...'); + + return knex.schema.table('proxy_host', function (proxy_host) { + proxy_host.integer('openidc_restrict_users_enabled').notNull().unsigned().defaultTo(0); + proxy_host.json('openidc_allowed_users').notNull().defaultTo([]); + }) + .then(() => { + logger.info('[' + migrate_name + '] proxy_host Table altered'); + }); +}; + +/** + * Undo Migrate + * + * @param {Object} knex + * @param {Promise} Promise + * @returns {Promise} + */ +exports.down = function (knex/*, Promise*/) { + return knex.schema.table('proxy_host', function (proxy_host) { + proxy_host.dropColumn('openidc_restrict_users_enabled'); + proxy_host.dropColumn('openidc_allowed_users'); + }) + .then(() => { + logger.info('[' + migrate_name + '] proxy_host Table altered'); + }); +}; diff --git a/backend/models/proxy_host.js b/backend/models/proxy_host.js index a2c9beee..2a8a9c53 100644 --- a/backend/models/proxy_host.js +++ b/backend/models/proxy_host.js @@ -19,12 +19,18 @@ class ProxyHost extends Model { this.domain_names = []; } + // Default for openidc_allowed_users + if (typeof this.openidc_allowed_users === 'undefined') { + this.openidc_allowed_users = []; + } + // Default for meta if (typeof this.meta === 'undefined') { this.meta = {}; } this.domain_names.sort(); + this.openidc_allowed_users.sort(); } $beforeUpdate () { @@ -34,6 +40,11 @@ class ProxyHost extends Model { if (typeof this.domain_names !== 'undefined') { this.domain_names.sort(); } + + // Sort openidc_allowed_users + if (typeof this.openidc_allowed_users !== 'undefined') { + this.openidc_allowed_users.sort(); + } } static get name () { @@ -45,7 +56,7 @@ class ProxyHost extends Model { } static get jsonAttributes () { - return ['domain_names', 'meta', 'locations']; + return ['domain_names', 'meta', 'locations', 'openidc_allowed_users']; } static get relationMappings () { diff --git a/backend/schema/definitions.json b/backend/schema/definitions.json index 3aec6fa7..999c76ed 100644 --- a/backend/schema/definitions.json +++ b/backend/schema/definitions.json @@ -243,6 +243,22 @@ }, "openidc_client_secret": { "type": "string" + }, + "openidc_restrict_users_enabled": { + "description": "Only allow a specific set of OpenID Connect emails to access the resource", + "example": true, + "type": "boolean" + }, + "openidc_allowed_users": { + "type": "array", + "minItems": 0, + "items": { + "type": "string", + "description": "Email Address", + "example": "john@example.com", + "format": "email", + "minLength": 1 + } } } } diff --git a/backend/schema/endpoints/proxy-hosts.json b/backend/schema/endpoints/proxy-hosts.json index 849a0f76..11862256 100644 --- a/backend/schema/endpoints/proxy-hosts.json +++ b/backend/schema/endpoints/proxy-hosts.json @@ -82,6 +82,12 @@ "openidc_client_secret": { "$ref": "../definitions.json#/definitions/openidc_client_secret" }, + "openidc_restrict_users_enabled": { + "$ref": "../definitions.json#/definitions/openidc_restrict_users_enabled" + }, + "openidc_allowed_users": { + "$ref": "../definitions.json#/definitions/openidc_allowed_users" + }, "enabled": { "$ref": "../definitions.json#/definitions/enabled" }, @@ -197,6 +203,12 @@ "openidc_client_secret": { "$ref": "#/definitions/openidc_client_secret" }, + "openidc_restrict_users_enabled": { + "$ref": "#/definitions/openidc_restrict_users_enabled" + }, + "openidc_allowed_users": { + "$ref": "#/definitions/openidc_allowed_users" + }, "enabled": { "$ref": "#/definitions/enabled" }, @@ -305,6 +317,12 @@ "openidc_client_secret": { "$ref": "#/definitions/openidc_client_secret" }, + "openidc_restrict_users_enabled": { + "$ref": "#/definitions/openidc_restrict_users_enabled" + }, + "openidc_allowed_users": { + "$ref": "#/definitions/openidc_allowed_users" + }, "enabled": { "$ref": "#/definitions/enabled" }, @@ -396,6 +414,12 @@ "openidc_client_secret": { "$ref": "#/definitions/openidc_client_secret" }, + "openidc_restrict_users_enabled": { + "$ref": "#/definitions/openidc_restrict_users_enabled" + }, + "openidc_allowed_users": { + "$ref": "#/definitions/openidc_allowed_users" + }, "enabled": { "$ref": "#/definitions/enabled" }, diff --git a/frontend/js/app/nginx/proxy/form.ejs b/frontend/js/app/nginx/proxy/form.ejs index 9ec1e3f7..4cbaec53 100644 --- a/frontend/js/app/nginx/proxy/form.ejs +++ b/frontend/js/app/nginx/proxy/form.ejs @@ -187,7 +187,7 @@ @@ -224,6 +224,23 @@ +
+
+
+ +
+
+
+
+ + +
+
+
diff --git a/frontend/js/app/nginx/proxy/form.js b/frontend/js/app/nginx/proxy/form.js index e3ae95b8..71a06104 100644 --- a/frontend/js/app/nginx/proxy/form.js +++ b/frontend/js/app/nginx/proxy/form.js @@ -36,7 +36,10 @@ module.exports = Mn.View.extend({ forward_scheme: 'select[name="forward_scheme"]', letsencrypt: '.letsencrypt', openidc_enabled: 'input[name="openidc_enabled"]', - openidc: '.openidc' + openidc_restrict_users_enabled: 'input[name="openidc_restrict_users_enabled"]', + openidc_allowed_users: 'input[name="openidc_allowed_users"]', + openidc: '.openidc', + openidc_users: '.openidc_users' }, regions: { @@ -97,9 +100,18 @@ module.exports = Mn.View.extend({ let checked = this.ui.openidc_enabled.prop('checked'); if (checked) { - this.ui.openidc.show().find('input').prop('required', true); + this.ui.openidc.show().find('input').prop('disabled', false); } else { - this.ui.openidc.hide().find('input').prop('required', false); + this.ui.openidc.hide().find('input').prop('disabled', true); + } + }, + + 'change @ui.openidc_restrict_users_enabled': function () { + let checked = this.ui.openidc_restrict_users_enabled.prop('checked'); + if (checked) { + this.ui.openidc_users.show().find('input').prop('disabled', false); + } else { + this.ui.openidc_users.hide().find('input').prop('disabled', true); } }, @@ -141,11 +153,18 @@ module.exports = Mn.View.extend({ data.hsts_subdomains = !!data.hsts_subdomains; data.ssl_forced = !!data.ssl_forced; data.openidc_enabled = data.openidc_enabled === '1'; + data.openidc_restrict_users_enabled = data.openidc_restrict_users_enabled === '1'; + + if (data.openidc_restrict_users_enabled) { + if (typeof data.openidc_allowed_users === 'string' && data.openidc_allowed_users) { + data.openidc_allowed_users = data.openidc_allowed_users.split(','); + } + } if (typeof data.domain_names === 'string' && data.domain_names) { data.domain_names = data.domain_names.split(','); } - + // Check for any domain names containing wildcards, which are not allowed with letsencrypt if (data.certificate_id === 'new') { let domain_err = false; @@ -287,8 +306,21 @@ module.exports = Mn.View.extend({ }); // OpenID Connect - this.ui.openidc.hide().find('input').prop('required', false); + this.ui.openidc_allowed_users.selectize({ + delimiter: ',', + persist: false, + maxOptions: 15, + create: function (input) { + return { + value: input, + text: input + }; + } + }); + this.ui.openidc.hide().find('input').prop('disabled', true); + this.ui.openidc_users.hide().find('input').prop('disabled', true); this.ui.openidc_enabled.trigger('change'); + this.ui.openidc_restrict_users_enabled.trigger('change'); }, initialize: function (options) { diff --git a/frontend/js/models/proxy-host.js b/frontend/js/models/proxy-host.js index ef1f1f40..85429d18 100644 --- a/frontend/js/models/proxy-host.js +++ b/frontend/js/models/proxy-host.js @@ -28,6 +28,8 @@ const model = Backbone.Model.extend({ openidc_auth_method: 'client_secret_post', openidc_client_id: '', openidc_client_secret: '', + openidc_restrict_users_enabled: false, + openidc_allowed_users: [], enabled: true, meta: {}, // The following are expansions: