Frontend: domain terminology, object-keyed origins, 🔑 auth host in table, display-time ordering
This commit is contained in:
@@ -15,11 +15,11 @@ const NAME_EDIT_TYPES = new Set(['org-update', 'role-update', 'user-update-name'
|
||||
const NO_SUBMIT_TYPES = new Set([])
|
||||
const discoveryUrl = computed(() => `${window.location.origin}/.well-known/openid-configuration`)
|
||||
|
||||
// The rp-id of the realm being edited in the 'realm-edit' dialog
|
||||
const realmRpId = computed(() => props.dialog?.data?.rp_id || '')
|
||||
// The rp-id of the domain being edited in the 'domain-edit' dialog
|
||||
const dialogRpId = computed(() => props.dialog?.data?.rp_id || '')
|
||||
|
||||
// Initialize validation properties
|
||||
if (props.dialog?.data && props.dialog.type === 'realm-edit') {
|
||||
if (props.dialog?.data && props.dialog.type === 'domain-edit') {
|
||||
if (!('originValidation' in props.dialog.data)) {
|
||||
props.dialog.data.originValidation = (props.dialog.data.origins || []).map(() => null)
|
||||
}
|
||||
@@ -33,7 +33,7 @@ if (props.dialog?.data && props.dialog.type === 'realm-edit') {
|
||||
// (entries may be hosted elsewhere, or a new domain whose DNS is not
|
||||
// routed to this instance yet).
|
||||
const isValidationInvalid = computed(() => {
|
||||
if (props.dialog?.type !== 'realm-edit') return false
|
||||
if (props.dialog?.type !== 'domain-edit') return false
|
||||
const d = props.dialog.data
|
||||
const bad = v => v === 'invalid' || v === 'validating'
|
||||
if (d.originValidation?.some(bad)) return true
|
||||
@@ -47,7 +47,7 @@ const isValidationInvalid = computed(() => {
|
||||
// from the hostname — the submit handler splits the two lists apart.
|
||||
function isRelatedEntry(origin) {
|
||||
const h = originHostname(origin)
|
||||
return !!(h && realmRpId.value && !isWithinDomain(origin, realmRpId.value))
|
||||
return !!(h && dialogRpId.value && !isWithinDomain(origin, dialogRpId.value))
|
||||
}
|
||||
const relatedEntries = computed(() => {
|
||||
const d = props.dialog?.data
|
||||
@@ -86,7 +86,7 @@ function addOrigin() {
|
||||
// very domain (so saving never locks them out), else with the rp-id
|
||||
// (https default).
|
||||
const onThisDomain = authStore.settings?.rp_id && d.rp_id === authStore.settings.rp_id
|
||||
d.origins.push(onThisDomain ? window.location.origin : realmRpId.value)
|
||||
d.origins.push(onThisDomain ? window.location.origin : dialogRpId.value)
|
||||
d.originValidation.push(null)
|
||||
validateOrigin(d.origins.length - 1)
|
||||
}
|
||||
@@ -148,7 +148,7 @@ async function validateOriginConnectivity(i) {
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
// Valid when the entry is served by this instance for the edited domain
|
||||
d.originValidation[i] = (data.rp_id && data.rp_id === realmRpId.value) ? 'valid' : 'mismatch'
|
||||
d.originValidation[i] = (data.rp_id && data.rp_id === dialogRpId.value) ? 'valid' : 'mismatch'
|
||||
} else {
|
||||
d.originValidation[i] = 'unreachable'
|
||||
}
|
||||
@@ -207,9 +207,9 @@ watch(() => relatedEntries.value.map(asHttpsOrigin).join('|'), testWellKnown, {
|
||||
// so the list always shows what is allowed ('*.example.com' = the domain and
|
||||
// all its subdomains). Removing the last in-domain entry is blocked in the
|
||||
// row menu, so the list never becomes empty afterwards.
|
||||
watch(realmRpId, rp => {
|
||||
watch(dialogRpId, rp => {
|
||||
const d = props.dialog?.data
|
||||
if (props.dialog?.type !== 'realm-edit' || !d?.isNew) return
|
||||
if (props.dialog?.type !== 'domain-edit' || !d?.isNew) return
|
||||
if (!d.origins.length && isWellFormedDomain(rp)) {
|
||||
d.origins.push('*.' + rp)
|
||||
d.originValidation.push(null)
|
||||
@@ -220,33 +220,32 @@ watch(realmRpId, rp => {
|
||||
|
||||
const openMenu = ref(null)
|
||||
|
||||
// Host[:port] of an entry or the auth_host value, for comparison.
|
||||
function hostWithPort(value) {
|
||||
if (!value?.trim()) return null
|
||||
if (value.trim().startsWith('*.')) return null
|
||||
try { return new URL(value.startsWith('http') ? value : 'https://' + value).host } catch { return null }
|
||||
// Origins-dict key form of an entry (https:// omitted), also used for the
|
||||
// auth_host value.
|
||||
function entryKey(value) {
|
||||
return value?.trim().replace(/^https:\/\//, '').replace(/\/+$/, '') || ''
|
||||
}
|
||||
|
||||
function isAuthHostEntry(origin) {
|
||||
const d = props.dialog?.data
|
||||
const host = hostWithPort(origin)
|
||||
return !!(host && d?.auth_host && host === hostWithPort(d.auth_host))
|
||||
const key = entryKey(origin)
|
||||
return !!(key && d?.auth_host && key === d.auth_host)
|
||||
}
|
||||
|
||||
function setAuthHost(i) {
|
||||
const d = props.dialog?.data
|
||||
if (!d) return
|
||||
let entry = d.origins[i].trim()
|
||||
if (entry.startsWith('*.')) {
|
||||
let key = entryKey(d.origins[i])
|
||||
if (key.startsWith('*.')) {
|
||||
// A wildcard cannot be the auth host — create a concrete auth.<base> entry
|
||||
entry = 'auth.' + entry.slice(2)
|
||||
if (!d.origins.some(o => hostWithPort(o) === entry)) {
|
||||
d.origins.push(entry)
|
||||
key = 'auth.' + key.slice(2)
|
||||
if (!d.origins.some(o => entryKey(o) === key)) {
|
||||
d.origins.push(key)
|
||||
d.originValidation.push(null)
|
||||
validateOrigin(d.origins.length - 1)
|
||||
}
|
||||
}
|
||||
d.auth_host = hostWithPort(entry) || entry
|
||||
d.auth_host = key
|
||||
openMenu.value = null
|
||||
}
|
||||
|
||||
@@ -288,7 +287,7 @@ function onRemoveOrigin(i) {
|
||||
<template v-else-if="dialog.type==='user-update-name'">Edit User Name</template>
|
||||
<template v-else-if="dialog.type==='perm-create' || dialog.type==='perm-display'">{{ dialog.type === 'perm-create' ? 'Create Permission' : 'Edit Permission' }}</template>
|
||||
<template v-else-if="dialog.type==='oidc-edit'">{{ dialog.data?.isNew ? 'New OIDC Client' : 'OIDC Client' }}</template>
|
||||
<template v-else-if="dialog.type==='realm-edit'">{{ dialog.data?.isNew ? 'Add Domain' : 'Edit Domain' }}</template>
|
||||
<template v-else-if="dialog.type==='domain-edit'">{{ dialog.data?.isNew ? 'Add Domain' : 'Edit Domain' }}</template>
|
||||
<template v-else-if="dialog.type==='confirm'">Confirm</template>
|
||||
</h3>
|
||||
<form @submit.prevent="$emit('submitDialog')" class="modal-form">
|
||||
@@ -346,9 +345,9 @@ function onRemoveOrigin(i) {
|
||||
<label>Domain Scope
|
||||
<input v-model="dialog.data.domain" data-form-type="other" />
|
||||
</label>
|
||||
<p class="small muted">A domain restricts this permission to that host (any configured realm's rp-id or a subdomain of it). An OIDC client UUID sends it as a <em>groups</em> claim to that client.</p>
|
||||
<p class="small muted">A domain restricts this permission to that host (any configured domain's rp-id or a subdomain of it). An OIDC client UUID sends it as a <em>groups</em> claim to that client.</p>
|
||||
</template>
|
||||
<template v-else-if="dialog.type==='realm-edit'">
|
||||
<template v-else-if="dialog.type==='domain-edit'">
|
||||
<template v-if="dialog.data.isNew">
|
||||
<label>Domain (rp-id)
|
||||
<input v-model="dialog.data.rp_id" placeholder="example.com" data-form-type="other" required />
|
||||
|
||||
Reference in New Issue
Block a user