Compare commits

...

11 Commits

Author SHA1 Message Date
Jamie Curnow
a91dcb144d Use model for db defaults as sqlite doesn't support them 2021-09-09 08:12:50 +10:00
Subv
e7f7be2a2b OpenIDC: Trigger the change event of the "restrict users" toggle when enabling/disabling oidc.
If this is not triggered and the OIDC toggle is enabled, the "disabled" property will be removed from the restricted user list input, causing an error when trying to submit the form without it.
2021-09-09 08:12:50 +10:00
Subv
076d89b5b5 Use localized strings for the OpenID Connect texts. 2021-09-09 08:12:50 +10:00
Subv
8539930f89 Updated the docs to add a section about OpenID Connect 2021-09-09 08:12:50 +10:00
Subv
87d9babbd3 Fix conditionals in the liquid template for OpenID Connect conf. 2021-09-09 08:12:50 +10:00
Subv
9f2d3a1737 Manually set the default values for the OpenID Connect columns.
There is a Knex issue ( https://github.com/knex/knex/issues/2649 ) that prevents .defaultTo from working for text columns.
2021-09-09 08:12:50 +10:00
Subv
daf399163c Allow limiting OpenID Connect auth to a list of users. 2021-09-09 08:12:50 +10:00
Subv
cdf702e545 Add a field to specify a list of allowed emails when using OpenID Connect auth. 2021-09-09 08:12:50 +10:00
Subv
5811345050 Use OpenResty instead of plain nginx to support OpenID Connect authorization. 2021-09-09 08:12:48 +10:00
Subv
53792a5cf7 Add database columns to store OpenID Connect information for Proxy Hosts. 2021-09-09 08:12:19 +10:00
Subv
8e10b7da37 Add UI tab for specifying OpenID Connect options for proxy hosts. 2021-09-09 08:12:19 +10:00
13 changed files with 491 additions and 33 deletions

View File

@ -0,0 +1,48 @@
const migrate_name = 'openid_connect';
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_enabled').notNull().unsigned().defaultTo(0);
proxy_host.text('openidc_redirect_uri').notNull().defaultTo('');
proxy_host.text('openidc_discovery').notNull().defaultTo('');
proxy_host.text('openidc_auth_method').notNull().defaultTo('');
proxy_host.text('openidc_client_id').notNull().defaultTo('');
proxy_host.text('openidc_client_secret').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_enabled');
proxy_host.dropColumn('openidc_redirect_uri');
proxy_host.dropColumn('openidc_discovery');
proxy_host.dropColumn('openidc_auth_method');
proxy_host.dropColumn('openidc_client_id');
proxy_host.dropColumn('openidc_client_secret');
})
.then(() => {
logger.info('[' + migrate_name + '] proxy_host Table altered');
});
};

View File

@ -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');
});
};

View File

@ -20,12 +20,23 @@ 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 = {};
}
// Openidc defaults
if (typeof this.openidc_auth_method === 'undefined') {
this.openidc_auth_method = 'client_secret_post';
}
this.domain_names.sort();
this.openidc_allowed_users.sort();
}
$beforeUpdate () {
@ -35,6 +46,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 () {
@ -46,7 +62,7 @@ class ProxyHost extends Model {
}
static get jsonAttributes () {
return ['domain_names', 'meta', 'locations'];
return ['domain_names', 'meta', 'locations', 'openidc_allowed_users'];
}
static get relationMappings () {

View File

@ -235,6 +235,43 @@
"description": "Should we cache assets",
"example": true,
"type": "boolean"
},
"openidc_enabled": {
"description": "Is OpenID Connect authentication enabled",
"example": true,
"type": "boolean"
},
"openidc_redirect_uri": {
"type": "string"
},
"openidc_discovery": {
"type": "string"
},
"openidc_auth_method": {
"type": "string",
"pattern": "^(client_secret_basic|client_secret_post)$"
},
"openidc_client_id": {
"type": "string"
},
"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
}
}
}
}

View File

@ -64,6 +64,30 @@
"advanced_config": {
"type": "string"
},
"openidc_enabled": {
"$ref": "../definitions.json#/definitions/openidc_enabled"
},
"openidc_redirect_uri": {
"$ref": "../definitions.json#/definitions/openidc_redirect_uri"
},
"openidc_discovery": {
"$ref": "../definitions.json#/definitions/openidc_discovery"
},
"openidc_auth_method": {
"$ref": "../definitions.json#/definitions/openidc_auth_method"
},
"openidc_client_id": {
"$ref": "../definitions.json#/definitions/openidc_client_id"
},
"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"
},
@ -161,6 +185,30 @@
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"openidc_enabled": {
"$ref": "#/definitions/openidc_enabled"
},
"openidc_redirect_uri": {
"$ref": "#/definitions/openidc_redirect_uri"
},
"openidc_discovery": {
"$ref": "#/definitions/openidc_discovery"
},
"openidc_auth_method": {
"$ref": "#/definitions/openidc_auth_method"
},
"openidc_client_id": {
"$ref": "#/definitions/openidc_client_id"
},
"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"
},
@ -251,6 +299,30 @@
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"openidc_enabled": {
"$ref": "#/definitions/openidc_enabled"
},
"openidc_redirect_uri": {
"$ref": "#/definitions/openidc_redirect_uri"
},
"openidc_discovery": {
"$ref": "#/definitions/openidc_discovery"
},
"openidc_auth_method": {
"$ref": "#/definitions/openidc_auth_method"
},
"openidc_client_id": {
"$ref": "#/definitions/openidc_client_id"
},
"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"
},
@ -324,6 +396,30 @@
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"openidc_enabled": {
"$ref": "#/definitions/openidc_enabled"
},
"openidc_redirect_uri": {
"$ref": "#/definitions/openidc_redirect_uri"
},
"openidc_discovery": {
"$ref": "#/definitions/openidc_discovery"
},
"openidc_auth_method": {
"$ref": "#/definitions/openidc_auth_method"
},
"openidc_client_id": {
"$ref": "#/definitions/openidc_client_id"
},
"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"
},

View File

@ -0,0 +1,47 @@
{% if openidc_enabled == 1 or openidc_enabled == true -%}
access_by_lua_block {
local openidc = require("resty.openidc")
local opts = {
redirect_uri = "{{- openidc_redirect_uri -}}",
discovery = "{{- openidc_discovery -}}",
token_endpoint_auth_method = "{{- openidc_auth_method -}}",
client_id = "{{- openidc_client_id -}}",
client_secret = "{{- openidc_client_secret -}}",
scope = "openid email profile"
}
local res, err = openidc.authenticate(opts)
if err then
ngx.status = 500
ngx.say(err)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
{% if openidc_restrict_users_enabled == 1 or openidc_restrict_users_enabled == true -%}
local function contains(table, val)
for i=1,#table do
if table[i] == val then
return true
end
end
return false
end
local allowed_users = {
{% for user in openidc_allowed_users %}
"{{ user }}",
{% endfor %}
}
if not contains(allowed_users, res.id_token.email) then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
{% endif -%}
ngx.req.set_header("X-OIDC-SUB", res.id_token.sub)
ngx.req.set_header("X-OIDC-EMAIL", res.id_token.email)
ngx.req.set_header("X-OIDC-NAME", res.id_token.name)
}
{% endif %}

View File

@ -51,7 +51,8 @@ proxy_http_version 1.1;
{% endif %}
{% include "_hsts.conf" %}
{% include "_openid_connect.conf" %}
{% include "_hsts.conf" %}
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
proxy_set_header Upgrade $http_upgrade;

View File

@ -43,6 +43,16 @@ http {
proxy_cache_path /var/lib/nginx/cache/public levels=1:2 keys_zone=public-cache:30m max_size=192m;
proxy_cache_path /var/lib/nginx/cache/private levels=1:2 keys_zone=private-cache:5m max_size=1024m;
lua_package_path '~/lua/?.lua;;';
lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
lua_ssl_verify_depth 5;
# cache for discovery metadata documents
lua_shared_dict discovery 1m;
# cache for JWKs
lua_shared_dict jwks 1m;
log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"';
log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"';

View File

@ -172,3 +172,26 @@ value by specifying it as a Docker environment variable. The default if not spec
X_FRAME_OPTIONS: "sameorigin"
...
```
## OpenID Connect SSO
You can secure any of your proxy hosts with OpenID Connect authentication, providing SSO support from an identity provider like Azure AD or KeyCloak. OpenID Connect support is provided through the [`lua-resty-openidc`](https://github.com/zmartzone/lua-resty-openidc) library of [`OpenResty`](https://github.com/openresty/openresty).
You will need a few things to get started with OpenID Connect:
- A registered application with your identity provider, they will provide you with a `Client ID` and a `Client Secret`. Public OpenID Connect applications (without a client secret) are not yet supported.
- A redirect URL to send the users to after they login with the identity provider, this can be any unused URL under the proxy host, like `https://<proxy host url>/private/callback`, the server will take care of capturing that URL and redirecting you to the proxy host root. You will need to add this URL to the list of allowed redirect URLs for the application you registered with your identity provider.
- The well-known discovery endpoint of the identity provider you want to use, this is an URL usually with the form `https://<provider URL>/.well-known/openid-configuration`.
After you have all this you can proceed to configure the proxy host with OpenID Connect authentication.
You can also add some rudimentary access control through a list of allowed emails in case your identity provider doesn't let you do that, if this option is enabled, any email not on that list will be denied access to the proxied host.
The proxy adds some headers based on the authentication result from the identity provider:
- `X-OIDC-SUB`: The subject identifier, according to the OpenID Coonect spec: `A locally unique and never reassigned identifier within the Issuer for the End-User`.
- `X-OIDC-EMAIL`: The email of the user that logged in, as specified in the `id_token` returned from the identity provider. The same value that will be checked for the email whitelist.
- `X-OIDC-NAME`: The user's name claim from the `id_token`, please note that not all id tokens necessarily contain this claim.

View File

@ -11,6 +11,7 @@
<li role="presentation" class="nav-item"><a href="#locations" aria-controls="tab4" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-layers"></i> <%- i18n('all-hosts', 'locations') %></a></li>
<li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li>
<li role="presentation" class="nav-item"><a href="#advanced" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('all-hosts', 'advanced') %></a></li>
<li role="presentation" class="nav-item"><a href="#openidc" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i><%- i18n('proxy-hosts', 'oidc') %></a></li>
</ul>
<div class="tab-content">
@ -270,6 +271,71 @@
</div>
</div>
</div>
<!-- OpenID Connect -->
<div role="tabpanel" class="tab-pane" id="openidc">
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="openidc_enabled" value="1"<%- openidc_enabled ? ' checked' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('proxy-hosts', 'oidc-enabled') %></span>
</label>
</div>
</div>
<div class="col-sm-12 col-md-12 openidc">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'oidc-redirect-uri') %><span class="form-required">*</span></label>
<input type="text" name="openidc_redirect_uri" class="form-control text-monospace" placeholder="" value="<%- openidc_redirect_uri %>" autocomplete="off" maxlength="255" required>
</div>
</div>
<div class="col-sm-12 col-md-12 openidc">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'oidc-discovery-endpoint') %><span class="form-required">*</span></label>
<input type="text" name="openidc_discovery" class="form-control text-monospace" placeholder="" value="<%- openidc_discovery %>" autocomplete="off" maxlength="255" required>
</div>
</div>
<div class="col-sm-12 col-md-12 openidc">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'oidc-token-auth-method') %><span class="form-required">*</span></label>
<select name="openidc_auth_method" class="form-control custom-select" placeholder="client_secret_post">
<option value="client_secret_post" <%- openidc_auth_method === 'client_secret_post' ? 'selected' : '' %>>client_secret_post</option>
<option value="client_secret_basic" <%- openidc_auth_method === 'client_secret_basic' ? 'selected' : '' %>>client_secret_basic</option>
</select>
</div>
</div>
<div class="col-sm-12 col-md-12 openidc">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'oidc-client-id') %><span class="form-required">*</span></label>
<input type="text" name="openidc_client_id" class="form-control text-monospace" placeholder="" value="<%- openidc_client_id %>" autocomplete="off" maxlength="255" required>
</div>
</div>
<div class="col-sm-12 col-md-12 openidc">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'oidc-client-secret') %><span class="form-required">*</span></label>
<input type="text" name="openidc_client_secret" class="form-control text-monospace" placeholder="" value="<%- openidc_client_secret %>" autocomplete="off" maxlength="255" required>
</div>
</div>
<div class="openidc">
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="openidc_restrict_users_enabled" value="1"<%- openidc_restrict_users_enabled ? ' checked' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('proxy-hosts', 'oidc-allow-only-emails') %></span>
</label>
</div>
</div>
<div class="col-sm-12 col-md-12 openidc_users">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'oidc-allowed-emails') %><span class="form-required">*</span></label>
<input type="text" name="openidc_allowed_users" class="form-control" id="openidc_allowed_users" value="<%- openidc_allowed_users.join(',') %>" required>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>

View File

@ -21,29 +21,34 @@ module.exports = Mn.View.extend({
locationsCollection: new ProxyLocationModel.Collection(),
ui: {
form: 'form',
domain_names: 'input[name="domain_names"]',
forward_host: 'input[name="forward_host"]',
buttons: '.modal-footer button',
cancel: 'button.cancel',
save: 'button.save',
add_location_btn: 'button.add_location',
locations_container: '.locations_container',
le_error_info: '#le-error-info',
certificate_select: 'select[name="certificate_id"]',
access_list_select: 'select[name="access_list_id"]',
ssl_forced: 'input[name="ssl_forced"]',
hsts_enabled: 'input[name="hsts_enabled"]',
hsts_subdomains: 'input[name="hsts_subdomains"]',
http2_support: 'input[name="http2_support"]',
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
dns_challenge_content: '.dns-challenge',
dns_provider: 'select[name="meta[dns_provider]"]',
credentials_file_content: '.credentials-file-content',
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
forward_scheme: 'select[name="forward_scheme"]',
letsencrypt: '.letsencrypt'
form: 'form',
domain_names: 'input[name="domain_names"]',
forward_host: 'input[name="forward_host"]',
buttons: '.modal-footer button',
cancel: 'button.cancel',
save: 'button.save',
add_location_btn: 'button.add_location',
locations_container: '.locations_container',
le_error_info: '#le-error-info',
certificate_select: 'select[name="certificate_id"]',
access_list_select: 'select[name="access_list_id"]',
ssl_forced: 'input[name="ssl_forced"]',
hsts_enabled: 'input[name="hsts_enabled"]',
hsts_subdomains: 'input[name="hsts_subdomains"]',
http2_support: 'input[name="http2_support"]',
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
dns_challenge_content: '.dns-challenge',
dns_provider: 'select[name="meta[dns_provider]"]',
credentials_file_content: '.credentials-file-content',
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
forward_scheme: 'select[name="forward_scheme"]',
letsencrypt: '.letsencrypt',
openidc_enabled: 'input[name="openidc_enabled"]',
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: {
@ -113,7 +118,7 @@ module.exports = Mn.View.extend({
} else {
this.ui.dns_provider.prop('required', false);
this.ui.dns_provider_credentials.prop('required', false);
this.ui.dns_challenge_content.hide();
this.ui.dns_challenge_content.hide();
}
},
@ -125,13 +130,34 @@ module.exports = Mn.View.extend({
this.ui.credentials_file_content.show();
} else {
this.ui.dns_provider_credentials.prop('required', false);
this.ui.credentials_file_content.hide();
this.ui.credentials_file_content.hide();
}
},
'change @ui.openidc_enabled': function () {
let checked = this.ui.openidc_enabled.prop('checked');
if (checked) {
this.ui.openidc.show().find('input').prop('disabled', false);
} else {
this.ui.openidc.hide().find('input').prop('disabled', true);
}
this.ui.openidc_restrict_users_enabled.trigger('change');
},
'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);
}
},
'click @ui.add_location_btn': function (e) {
e.preventDefault();
const model = new ProxyLocationModel.Model();
this.locationsCollection.add(model);
},
@ -167,17 +193,25 @@ module.exports = Mn.View.extend({
data.hsts_enabled = !!data.hsts_enabled;
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.meta === 'undefined') data.meta = {};
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
data.meta.dns_challenge = data.meta.dns_challenge == 1;
if(!data.meta.dns_challenge){
data.meta.dns_provider = undefined;
data.meta.dns_provider_credentials = undefined;
data.meta.propagation_seconds = undefined;
} else {
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
}
if (typeof data.domain_names === 'string' && data.domain_names) {
@ -185,7 +219,7 @@ module.exports = Mn.View.extend({
}
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
if (data.certificate_id === 'new') {
if (data.certificate_id === 'new') {
let domain_err = false;
if (!data.meta.dns_challenge) {
data.domain_names.map(function (name) {
@ -203,6 +237,12 @@ module.exports = Mn.View.extend({
data.certificate_id = parseInt(data.certificate_id, 10);
}
// OpenID Connect won't work with multiple domain names because the redirect URL has to point to a specific one
if (data.openidc_enabled && data.domain_names.length > 1) {
alert('Cannot use mutliple domain names when OpenID Connect is enabled');
return;
}
let method = App.Api.Nginx.ProxyHosts.create;
let is_new = true;
@ -344,6 +384,23 @@ module.exports = Mn.View.extend({
view.ui.certificate_select[0].selectize.setValue(view.model.get('certificate_id'));
}
});
// OpenID Connect
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) {

View File

@ -130,7 +130,16 @@
"access-list": "Access List",
"allow-websocket-upgrade": "Websockets Support",
"ignore-invalid-upstream-ssl": "Ignore Invalid SSL",
"custom-forward-host-help": "Use 1.1.1.1/path for sub-folder forwarding"
"custom-forward-host-help": "Use 1.1.1.1/path for sub-folder forwarding",
"oidc": "OpenID Connect",
"oidc-enabled": "Use OpenID Connect authentication",
"oidc-redirect-uri": "Redirect URI",
"oidc-discovery-endpoint": "Well-known discovery endpoint",
"oidc-token-auth-method": "Token endpoint auth method",
"oidc-client-id": "Client ID",
"oidc-client-secret": "Client secret",
"oidc-allow-only-emails": "Allow only these user emails",
"oidc-allowed-emails": "Allowed email addresses"
},
"redirection-hosts": {
"title": "Redirection Hosts",

View File

@ -22,6 +22,14 @@ const model = Backbone.Model.extend({
block_exploits: false,
http2_support: false,
advanced_config: '',
openidc_enabled: false,
openidc_redirect_uri: '',
openidc_discovery: '',
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: