Merge branch 'feature/oidc-support' of github.com:Budibase/budibase into feature/oidc-support
This commit is contained in:
commit
238d31e922
|
@ -20,4 +20,6 @@ exports.Configs = {
|
||||||
ACCOUNT: "account",
|
ACCOUNT: "account",
|
||||||
SMTP: "smtp",
|
SMTP: "smtp",
|
||||||
GOOGLE: "google",
|
GOOGLE: "google",
|
||||||
|
OIDC: "oidc",
|
||||||
|
OIDC_LOGOS: "oidc_logos",
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let error = null
|
export let error = null
|
||||||
export let fieldText = ""
|
export let fieldText = ""
|
||||||
|
export let fieldIcon = ""
|
||||||
export let isPlaceholder = false
|
export let isPlaceholder = false
|
||||||
export let placeholderOption = null
|
export let placeholderOption = null
|
||||||
export let options = []
|
export let options = []
|
||||||
|
@ -17,11 +18,11 @@
|
||||||
export let onSelectOption = () => {}
|
export let onSelectOption = () => {}
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
|
export let getOptionIcon = option => option
|
||||||
export let open = false
|
export let open = false
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
dispatch("click")
|
dispatch("click")
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
}
|
}
|
||||||
open = true
|
open = true
|
||||||
}
|
}
|
||||||
|
console.log(fieldIcon)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -42,6 +44,12 @@
|
||||||
aria-haspopup="listbox"
|
aria-haspopup="listbox"
|
||||||
on:mousedown={onClick}
|
on:mousedown={onClick}
|
||||||
>
|
>
|
||||||
|
{#if fieldIcon}
|
||||||
|
<span class="icon-Placeholder-Padding">
|
||||||
|
<img src={fieldIcon} alt="OpenID Icon" width="20" height="15" />
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="spectrum-Picker-label"
|
class="spectrum-Picker-label"
|
||||||
class:is-placeholder={isPlaceholder}
|
class:is-placeholder={isPlaceholder}
|
||||||
|
@ -104,6 +112,16 @@
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
on:click={() => onSelectOption(getOptionValue(option, idx))}
|
on:click={() => onSelectOption(getOptionValue(option, idx))}
|
||||||
>
|
>
|
||||||
|
{#if getOptionIcon(option, idx)}
|
||||||
|
<span class="icon-Padding">
|
||||||
|
<img
|
||||||
|
src={getOptionIcon(option, idx)}
|
||||||
|
alt="test"
|
||||||
|
width="20"
|
||||||
|
height="15"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
<span class="spectrum-Menu-itemLabel"
|
<span class="spectrum-Menu-itemLabel"
|
||||||
>{getOptionLabel(option, idx)}</span
|
>{getOptionLabel(option, idx)}</span
|
||||||
>
|
>
|
||||||
|
@ -148,4 +166,12 @@
|
||||||
.spectrum-Picker-label.auto-width.is-placeholder {
|
.spectrum-Picker-label.auto-width.is-placeholder {
|
||||||
padding-right: 2px;
|
padding-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-Padding {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
.icon-Placeholder-Padding {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,8 +8,10 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let error = null
|
export let error = null
|
||||||
export let options = []
|
export let options = []
|
||||||
|
export let callbackOptionValue = null
|
||||||
export let getOptionLabel = option => option
|
export let getOptionLabel = option => option
|
||||||
export let getOptionValue = option => option
|
export let getOptionValue = option => option
|
||||||
|
export let getOptionIcon = option => option
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
|
@ -17,6 +19,7 @@
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let open = false
|
let open = false
|
||||||
$: fieldText = getFieldText(value, options, placeholder)
|
$: fieldText = getFieldText(value, options, placeholder)
|
||||||
|
$: fieldIcon = getFieldIcon(value, options, placeholder)
|
||||||
|
|
||||||
const getFieldText = (value, options, placeholder) => {
|
const getFieldText = (value, options, placeholder) => {
|
||||||
// Always use placeholder if no value
|
// Always use placeholder if no value
|
||||||
|
@ -36,6 +39,17 @@
|
||||||
return index !== -1 ? getOptionLabel(options[index], index) : value
|
return index !== -1 ? getOptionLabel(options[index], index) : value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFieldIcon = (value, options) => {
|
||||||
|
// Wait for options to load if there is a value but no options
|
||||||
|
if (!options?.length) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
const index = options.findIndex(
|
||||||
|
(option, idx) => getOptionValue(option, idx) === value
|
||||||
|
)
|
||||||
|
return index !== -1 ? getOptionIcon(options[index], index) : value
|
||||||
|
}
|
||||||
|
|
||||||
const selectOption = value => {
|
const selectOption = value => {
|
||||||
dispatch("change", value)
|
dispatch("change", value)
|
||||||
open = false
|
open = false
|
||||||
|
@ -55,6 +69,9 @@
|
||||||
{autoWidth}
|
{autoWidth}
|
||||||
{getOptionLabel}
|
{getOptionLabel}
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
|
{getOptionIcon}
|
||||||
|
{fieldIcon}
|
||||||
|
{callbackOptionValue}
|
||||||
isPlaceholder={value == null || value === ""}
|
isPlaceholder={value == null || value === ""}
|
||||||
placeholderOption={placeholder}
|
placeholderOption={placeholder}
|
||||||
isOptionSelected={option => option === value}
|
isOptionSelected={option => option === value}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
export let options = []
|
export let options = []
|
||||||
export let getOptionLabel = option => extractProperty(option, "label")
|
export let getOptionLabel = option => extractProperty(option, "label")
|
||||||
export let getOptionValue = option => extractProperty(option, "value")
|
export let getOptionValue = option => extractProperty(option, "value")
|
||||||
|
export let getOptionIcon = option => extractObjectProperty(option, "icon")
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
|
|
||||||
|
@ -21,6 +22,13 @@
|
||||||
value = e.detail
|
value = e.detail
|
||||||
dispatch("change", e.detail)
|
dispatch("change", e.detail)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extractObjectProperty = (value, property) => {
|
||||||
|
if (value && typeof value === "object") {
|
||||||
|
return value[property]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const extractProperty = (value, property) => {
|
const extractProperty = (value, property) => {
|
||||||
if (value && typeof value === "object") {
|
if (value && typeof value === "object") {
|
||||||
return value[property]
|
return value[property]
|
||||||
|
@ -41,6 +49,7 @@
|
||||||
{autoWidth}
|
{autoWidth}
|
||||||
{getOptionLabel}
|
{getOptionLabel}
|
||||||
{getOptionValue}
|
{getOptionValue}
|
||||||
|
{getOptionIcon}
|
||||||
on:change={onChange}
|
on:change={onChange}
|
||||||
on:click
|
on:click
|
||||||
/>
|
/>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,39 @@
|
||||||
|
<script>
|
||||||
|
import { ActionButton } from "@budibase/bbui"
|
||||||
|
import OidcLogo from "assets/oidc-logo.png"
|
||||||
|
import { admin } from "stores/portal"
|
||||||
|
|
||||||
|
let show = false
|
||||||
|
|
||||||
|
$: show = $admin.checklist?.oidc
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if show}
|
||||||
|
<ActionButton
|
||||||
|
on:click={() => window.open("/api/admin/auth/oidc", "_blank")}
|
||||||
|
>
|
||||||
|
<div class="inner">
|
||||||
|
<img src={OidcLogo} alt="oidc icon" />
|
||||||
|
<p>Sign in with OIDC</p>
|
||||||
|
</div>
|
||||||
|
</ActionButton>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.inner {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: var(--spacing-xs);
|
||||||
|
padding-bottom: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
.inner img {
|
||||||
|
width: 18px;
|
||||||
|
margin: 3px 10px 3px 3px;
|
||||||
|
}
|
||||||
|
.inner p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<svg
|
||||||
|
width="25"
|
||||||
|
height="25 "
|
||||||
|
viewBox="0 0 100 100"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0)">
|
||||||
|
<path
|
||||||
|
d="M43,90c-88,-16,-21,-86,41,-51l9,-6v17h-26l8,-5c-55,-25,-86,29,-32,36z"
|
||||||
|
fill="#ccc"
|
||||||
|
/>
|
||||||
|
<path d="M43,90v-75l14,-9v75z" fill="#f60" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 319 B |
|
@ -1,5 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import GoogleLogo from "./_logos/Google.svelte"
|
import GoogleLogo from "./_logos/Google.svelte"
|
||||||
|
import OidcLogo from "./_logos/OIDC.svelte"
|
||||||
|
import MicrosoftLogo from "assets/microsoft-logo.png"
|
||||||
|
import OracleLogo from "assets/oracle-logo.png"
|
||||||
|
import Auth0Logo from "assets/auth0-logo.png"
|
||||||
|
import OidcLogoPng from "assets/oidc-logo.png"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Heading,
|
Heading,
|
||||||
|
@ -9,20 +15,25 @@
|
||||||
Layout,
|
Layout,
|
||||||
Input,
|
Input,
|
||||||
Body,
|
Body,
|
||||||
|
Select,
|
||||||
|
Dropzone,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
|
import { writable } from "svelte/store"
|
||||||
|
import { organisation } from "stores/portal"
|
||||||
|
|
||||||
const ConfigTypes = {
|
const ConfigTypes = {
|
||||||
Google: "google",
|
Google: "google",
|
||||||
|
OIDC: "oidc",
|
||||||
// Github: "github",
|
// Github: "github",
|
||||||
// AzureAD: "ad",
|
// AzureAD: "ad",
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConfigFields = {
|
const GoogleConfigFields = {
|
||||||
Google: ["clientID", "clientSecret", "callbackURL"],
|
Google: ["clientID", "clientSecret", "callbackURL"],
|
||||||
}
|
}
|
||||||
const ConfigLabels = {
|
const GoogleConfigLabels = {
|
||||||
Google: {
|
Google: {
|
||||||
clientID: "Client ID",
|
clientID: "Client ID",
|
||||||
clientSecret: "Client secret",
|
clientSecret: "Client secret",
|
||||||
|
@ -30,21 +41,81 @@
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
let google
|
const OIDCConfigFields = {
|
||||||
|
Oidc: ["configUrl", "clientId", "clientSecret"],
|
||||||
async function save(doc) {
|
|
||||||
try {
|
|
||||||
// Save an oauth config
|
|
||||||
const response = await api.post(`/api/admin/configs`, doc)
|
|
||||||
const json = await response.json()
|
|
||||||
if (response.status !== 200) throw new Error(json.message)
|
|
||||||
google._rev = json._rev
|
|
||||||
google._id = json._id
|
|
||||||
|
|
||||||
notifications.success(`Settings saved.`)
|
|
||||||
} catch (err) {
|
|
||||||
notifications.error(`Failed to update OAuth settings. ${err}`)
|
|
||||||
}
|
}
|
||||||
|
const OIDCConfigLabels = {
|
||||||
|
Oidc: {
|
||||||
|
configUrl: "Config URL",
|
||||||
|
clientId: "Client ID",
|
||||||
|
clientSecret: "Client Secret",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconDropdownOptions = [
|
||||||
|
{
|
||||||
|
label: "Azure AD",
|
||||||
|
value: "Active Directory",
|
||||||
|
icon: MicrosoftLogo,
|
||||||
|
},
|
||||||
|
{ label: "Oracle", value: "Oracle", icon: OracleLogo },
|
||||||
|
{ label: "Auth0", value: "Auth0", icon: Auth0Logo },
|
||||||
|
{ label: "OIDC", value: "Oidc", icon: OidcLogoPng },
|
||||||
|
|
||||||
|
{ label: "Upload your own", value: "Upload" },
|
||||||
|
]
|
||||||
|
|
||||||
|
let fileinput
|
||||||
|
let image
|
||||||
|
let google
|
||||||
|
let oidc
|
||||||
|
|
||||||
|
async function uploadLogo(file) {
|
||||||
|
let data = new FormData()
|
||||||
|
data.append("file", file)
|
||||||
|
const res = await api.post(
|
||||||
|
`/api/admin/configs/upload/oidc_logos/${file.name}`,
|
||||||
|
data,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
return await res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFileSelected = e => {
|
||||||
|
let fileName = e.target.files[0].name
|
||||||
|
image = e.target.files[0]
|
||||||
|
providers.oidc.config["iconName"] = fileName
|
||||||
|
iconDropdownOptions.unshift({label: fileName, value: fileName})
|
||||||
|
}
|
||||||
|
|
||||||
|
const providers = { google, oidc }
|
||||||
|
|
||||||
|
async function save(docs) {
|
||||||
|
// only if the user has provided an image, upload it.
|
||||||
|
image && uploadLogo(image)
|
||||||
|
let calls = []
|
||||||
|
docs.forEach(element => {
|
||||||
|
calls.push(api.post(`/api/admin/configs`, element))
|
||||||
|
})
|
||||||
|
Promise.all(calls)
|
||||||
|
.then(responses => {
|
||||||
|
return Promise.all(
|
||||||
|
responses.map(response => {
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
data.forEach(res => {
|
||||||
|
providers[res.type]._rev = res._rev
|
||||||
|
providers[res.type]._id = res._id
|
||||||
|
})
|
||||||
|
notifications.success(`Settings saved.`)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
notifications.error(`Failed to update OAuth settings. ${err}`)
|
||||||
|
throw new Error(err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
@ -55,12 +126,39 @@
|
||||||
const googleDoc = await googleResponse.json()
|
const googleDoc = await googleResponse.json()
|
||||||
|
|
||||||
if (!googleDoc._id) {
|
if (!googleDoc._id) {
|
||||||
google = {
|
providers.google = {
|
||||||
type: ConfigTypes.Google,
|
type: ConfigTypes.Google,
|
||||||
config: {},
|
config: {},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
google = googleDoc
|
providers.google = googleDoc
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the list of user uploaded logos and push it to the dropdown options.
|
||||||
|
//This needs to be done before the config callso they're available when the dropdown renders
|
||||||
|
const res = await api.get(`/api/admin/configs/oidc_logos`)
|
||||||
|
const configSettings = await res.json()
|
||||||
|
const logoKeys = Object.keys(configSettings.config)
|
||||||
|
|
||||||
|
logoKeys.map(logoKey => {
|
||||||
|
const logoUrl = configSettings.config[logoKey]
|
||||||
|
iconDropdownOptions.unshift({
|
||||||
|
label: logoKey,
|
||||||
|
value: logoKey,
|
||||||
|
icon: logoUrl,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const oidcResponse = await api.get(`/api/admin/configs/${ConfigTypes.OIDC}`)
|
||||||
|
const oidcDoc = await oidcResponse.json()
|
||||||
|
|
||||||
|
if (!oidcDoc._id) {
|
||||||
|
providers.oidc = {
|
||||||
|
type: ConfigTypes.OIDC,
|
||||||
|
config: {},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
providers.oidc = oidcDoc
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -74,7 +172,7 @@
|
||||||
below.
|
below.
|
||||||
</Body>
|
</Body>
|
||||||
</Layout>
|
</Layout>
|
||||||
{#if google}
|
{#if providers.google}
|
||||||
<Divider />
|
<Divider />
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="S">
|
<Heading size="S">
|
||||||
|
@ -89,17 +187,65 @@
|
||||||
</Body>
|
</Body>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
{#each ConfigFields.Google as field}
|
{#each GoogleConfigFields.Google as field}
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<Label size="L">{ConfigLabels.Google[field]}</Label>
|
<Label size="L">{GoogleConfigLabels.Google[field]}</Label>
|
||||||
<Input bind:value={google.config[field]} />
|
<Input bind:value={providers.google.config[field]} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</Layout>
|
</Layout>
|
||||||
<div>
|
|
||||||
<Button cta on:click={() => save(google)}>Save</Button>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if providers.oidc}
|
||||||
|
<Divider />
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
|
<Heading size="S">
|
||||||
|
<span>
|
||||||
|
<OidcLogo />
|
||||||
|
OpenID Connect
|
||||||
|
</span>
|
||||||
|
</Heading>
|
||||||
|
<Body size="S">
|
||||||
|
To allow users to authenticate using OIDC, fill out the fields below.
|
||||||
|
</Body>
|
||||||
|
</Layout>
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
|
{#each OIDCConfigFields.Oidc as field}
|
||||||
|
<div class="form-row">
|
||||||
|
<Label size="L">{OIDCConfigLabels.Oidc[field]}</Label>
|
||||||
|
<Input bind:value={providers.oidc.config[field]} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
<br />
|
||||||
|
<Body size="S">
|
||||||
|
To customize your login button, fill out the fields below.
|
||||||
|
</Body>
|
||||||
|
<div class="form-row">
|
||||||
|
<Label size="L">Name</Label>
|
||||||
|
<Input bind:value={providers.oidc.config["name"]} />
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<Label size="L">Icon</Label>
|
||||||
|
<Select
|
||||||
|
label=""
|
||||||
|
bind:value={providers.oidc.config["iconName"]}
|
||||||
|
options={iconDropdownOptions}
|
||||||
|
on:change={e => e.detail === "Upload" && fileinput.click()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
style="display:none"
|
||||||
|
type="file"
|
||||||
|
accept=".jpg, .jpeg, .png"
|
||||||
|
on:change={e => onFileSelected(e)}
|
||||||
|
bind:this={fileinput}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
{/if}
|
||||||
|
<div>
|
||||||
|
<Button cta on:click={() => save([providers.google, providers.oidc])}
|
||||||
|
>Save</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -109,10 +255,13 @@
|
||||||
grid-gap: var(--spacing-l);
|
grid-gap: var(--spacing-l);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-s);
|
gap: var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -146,7 +146,7 @@ exports.upload = async function (ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const url = `/${bucket}/${key}`
|
const url = `/${bucket}/${key}`
|
||||||
cfgStructure.config[`${name}Url`] = url
|
cfgStructure.config[`${name}`] = url
|
||||||
// write back to db with url updated
|
// write back to db with url updated
|
||||||
await db.put(cfgStructure)
|
await db.put(cfgStructure)
|
||||||
|
|
||||||
|
@ -188,6 +188,10 @@ exports.configChecklist = async function (ctx) {
|
||||||
type: Configs.GOOGLE,
|
type: Configs.GOOGLE,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// They have set up OIDC
|
||||||
|
const oidcConfig = await getScopedFullConfig(db, {
|
||||||
|
type: Configs.OIDC,
|
||||||
|
})
|
||||||
// They have set up an admin user
|
// They have set up an admin user
|
||||||
const users = await db.allDocs(
|
const users = await db.allDocs(
|
||||||
getGlobalUserParams(null, {
|
getGlobalUserParams(null, {
|
||||||
|
@ -201,6 +205,7 @@ exports.configChecklist = async function (ctx) {
|
||||||
smtp: !!smtpConfig,
|
smtp: !!smtpConfig,
|
||||||
adminUser,
|
adminUser,
|
||||||
oauth: !!oauthConfig,
|
oauth: !!oauthConfig,
|
||||||
|
oidc: !!oidcConfig,
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(err.status, err)
|
ctx.throw(err.status, err)
|
||||||
|
|
|
@ -41,6 +41,19 @@ function googleValidation() {
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function OidcValidation() {
|
||||||
|
// prettier-ignore
|
||||||
|
return Joi.object({
|
||||||
|
clientID: Joi.string().required(),
|
||||||
|
authUrl: Joi.string().required(),
|
||||||
|
tokenUrl: Joi.string().required(),
|
||||||
|
userInfoUrl: Joi.string().required(),
|
||||||
|
clientId: Joi.string().required(),
|
||||||
|
clientSecret: Joi.string().required(),
|
||||||
|
callbackUrl: Joi.string().required(),
|
||||||
|
}).unknown(true)
|
||||||
|
}
|
||||||
|
|
||||||
function buildConfigSaveValidation() {
|
function buildConfigSaveValidation() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return joiValidator.body(Joi.object({
|
||||||
|
@ -54,7 +67,8 @@ function buildConfigSaveValidation() {
|
||||||
{ is: Configs.SMTP, then: smtpValidation() },
|
{ is: Configs.SMTP, then: smtpValidation() },
|
||||||
{ is: Configs.SETTINGS, then: settingValidation() },
|
{ is: Configs.SETTINGS, then: settingValidation() },
|
||||||
{ is: Configs.ACCOUNT, then: Joi.object().unknown(true) },
|
{ is: Configs.ACCOUNT, then: Joi.object().unknown(true) },
|
||||||
{ is: Configs.GOOGLE, then: googleValidation() }
|
{ is: Configs.GOOGLE, then: googleValidation() },
|
||||||
|
{ is: Configs.OIDC, then: Joi.object().unknown(true) }
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
}).required(),
|
}).required(),
|
||||||
|
@ -65,7 +79,7 @@ function buildUploadValidation() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.params(Joi.object({
|
return joiValidator.params(Joi.object({
|
||||||
type: Joi.string().valid(...Object.values(Configs)).required(),
|
type: Joi.string().valid(...Object.values(Configs)).required(),
|
||||||
name: Joi.string().valid(...Object.values(ConfigUploads)).required(),
|
name: Joi.string().required(),
|
||||||
}).required())
|
}).required())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ exports.Configs = Configs
|
||||||
|
|
||||||
exports.ConfigUploads = {
|
exports.ConfigUploads = {
|
||||||
LOGO: "logo",
|
LOGO: "logo",
|
||||||
|
OIDC_LOGO: "oidc_logo",
|
||||||
}
|
}
|
||||||
|
|
||||||
const TemplateTypes = {
|
const TemplateTypes = {
|
||||||
|
|
Loading…
Reference in New Issue