diff --git a/nodejs/routes/host.js b/nodejs/routes/host.js index 424b7d4..ccfe75d 100755 --- a/nodejs/routes/host.js +++ b/nodejs/routes/host.js @@ -1,7 +1,10 @@ 'use strict'; const router = require('express').Router(); -const {Host, Domain} = require('../models').models; +const conf = require('@simpleworkjs/conf'); +const {Host, Domain, User} = require('../models').models; +const {LocalGroup} = require('../models/local_group'); +const {Permission} = require('../models/permission'); const authz = require('../middleware/authz'); const {normalizeHostFeatures} = require('../utils/host_features'); const {collectHostFieldErrors} = require('../utils/hostname_validate'); @@ -25,6 +28,31 @@ function hashHostSecrets(body){ } } +// Autocomplete source for the per-host auth allow-lists (SSO users/groups). +// Available to any authenticated host editor (not just global admins). Groups +// are derived from local groups, existing permission group-subjects, and the +// conf.auth admin/role-map groups. +router.get('/auth-suggestions', async function(req, res, next){ + try{ + let users = []; + try{ users = (await User.list()) || []; }catch(error){ /* none */ } + + let groups = new Set(); + try{ for(let g of await LocalGroup.list()) groups.add(g); }catch(error){ /* none */ } + try{ + for(let p of await Permission.listDetail()){ + if(p.subjectType === 'group' && p.subject) groups.add(p.subject); + } + }catch(error){ /* none */ } + for(let g of (conf.auth && conf.auth.adminGroups) || []) groups.add(g); + for(let g of Object.keys((conf.auth && conf.auth.groupRoleMap) || {})) groups.add(g); + + return res.json({users, groups: [...groups].sort()}); + }catch(error){ + return next(error); + } +}); + router.get('/', async function(req, res, next){ try{ let results = await Model[req.query.detail ? "listDetail" : "list"](); diff --git a/nodejs/views/hosts.ejs b/nodejs/views/hosts.ejs index 3953c96..918bbfa 100755 --- a/nodejs/views/hosts.ejs +++ b/nodejs/views/hosts.ejs @@ -98,6 +98,29 @@ bootstrap.Tab.getOrCreateInstance(document.getElementById(id)).show(); } + // Append a picked/typed value to one of the SSO allow-list textareas (deduped). + function allowListAdd(input, name){ + let val = (input.value || '').trim(); + if(!val) return; + let $ta = $('#hostForm textarea[name="' + name + '"]'); + let lines = ($ta.val() || '').split(/\r?\n/).map(s => s.trim()).filter(Boolean); + if(lines.indexOf(val) === -1) lines.push(val); + $ta.val(lines.join('\n')); + input.value = ''; + input.focus(); + } + + // Fill the user/group datalists that back the allow-list autocomplete. + function hostLoadAuthSuggestions(){ + app.api.get('host/auth-suggestions', function(error, data){ + if(error || !data) return; + let $u = $('#hostSsoUsers').empty(); + for(let u of (data.users || [])) $u.append($('').val(u)); + let $g = $('#hostSsoGroups').empty(); + for(let g of (data.groups || [])) $g.append($('').val(g)); + }); + } + // Return the form to a clean "add" state. function hostFormReset(){ let form = document.getElementById('hostForm'); @@ -215,6 +238,7 @@ $(document).ready(function(){ // Populate the host UI table hostPopulate(); + hostLoadAuthSuggestions(); // Determine what Let's Encrypt challenge type the given host name can use. let $hostField = $('#hostForm [name=host]'); @@ -233,11 +257,13 @@ return; } - // Check if a wildcard cert is available for the given host. + // Check if a wildcard cert is available for the given host. When it is, + // make "Parent Wildcard" the default choice (it reuses an existing cert). let wildcardParent = await hostMatchWildcard(host); if(wildcardParent){ $('#challengeType-child-container').removeClass('challengeType-container'); $('#challengeType-child-relatedHost').text(wildcardParent.host); + $('#challengeType-wildcardChild').prop('checked', true); return; } @@ -427,6 +453,7 @@ Traffic Headers Access + Authentication @@ -605,15 +632,18 @@ These sources are always blocked (deny wins over allow). + - - - Authentication - — basic auth and SSO are OR'd; either one grants access. - + + + + Basic auth and SSO are OR'd — if either is enabled, a request + is allowed when it passes either one. Leave both off for a + public host. + + Basic authentication - Basic authentication Off @@ -638,9 +668,8 @@ - + Single sign-on (SSO) - Single sign-on (SSO) Off @@ -649,19 +678,36 @@ Require login via the configured OIDC provider - Gates the site behind the same identity provider the admin app uses. + Gates the site behind the same identity provider the admin app uses. Empty allow-lists below mean any authenticated user is allowed. + Allowed users + + + + Add + + Allowed groups + + + + Add + + + +
+ Basic auth and SSO are OR'd — if either is enabled, a request + is allowed when it passes either one. Leave both off for a + public host. +