Allow user uploaded icons in oidc config

This commit is contained in:
Peter Clement 2021-07-07 13:41:09 +01:00
parent 0e6fb73b9e
commit 37b1200051
13 changed files with 154 additions and 53 deletions

View File

@ -20,5 +20,6 @@ exports.Configs = {
ACCOUNT: "account",
SMTP: "smtp",
GOOGLE: "google",
OIDC: "oidc"
OIDC: "oidc",
OIDC_LOGOS:"oidc_logos"
}

View File

@ -10,18 +10,20 @@
export let disabled = false
export let error = null
export let fieldText = ""
export let fieldIcon = ""
export let isPlaceholder = false
export let placeholderOption = null
export let options = []
export let callbackOptionValue = null
export let isOptionSelected = () => false
export let onSelectOption = () => {}
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let getOptionIcon = option => option
export let open = false
export let readonly = false
export let quiet = false
export let autoWidth = false
const dispatch = createEventDispatcher()
const onClick = () => {
dispatch("click")
@ -30,6 +32,7 @@
}
open = true
}
console.log(fieldIcon)
</script>
<button
@ -42,6 +45,12 @@
aria-haspopup="listbox"
on:mousedown={onClick}
>
{#if fieldIcon}
<span class="icon-Placeholder-Padding">
<img src={fieldIcon} alt="test" width="20" height="15" />
</span>
{/if}
<span
class="spectrum-Picker-label"
class:is-placeholder={isPlaceholder}
@ -104,6 +113,16 @@
tabindex="0"
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"
>{getOptionLabel(option, idx)}</span
>
@ -148,4 +167,12 @@
.spectrum-Picker-label.auto-width.is-placeholder {
padding-right: 2px;
}
.icon-Padding {
padding-right: 10px;
}
.icon-Placeholder-Padding {
padding-top: 5px;
padding-right: 10px;
}
</style>

View File

@ -8,8 +8,10 @@
export let disabled = false
export let error = null
export let options = []
export let callbackOptionValue = null
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let getOptionIcon = option => option
export let readonly = false
export let quiet = false
export let autoWidth = false
@ -17,6 +19,7 @@
const dispatch = createEventDispatcher()
let open = false
$: fieldText = getFieldText(value, options, placeholder)
$: fieldIcon = getFieldIcon(value, options, placeholder)
const getFieldText = (value, options, placeholder) => {
// Always use placeholder if no value
@ -36,6 +39,17 @@
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 => {
dispatch("change", value)
open = false
@ -55,6 +69,9 @@
{autoWidth}
{getOptionLabel}
{getOptionValue}
{getOptionIcon}
{fieldIcon}
{callbackOptionValue}
isPlaceholder={value == null || value === ""}
placeholderOption={placeholder}
isOptionSelected={option => option === value}

View File

@ -13,6 +13,7 @@
export let options = []
export let getOptionLabel = option => extractProperty(option, "label")
export let getOptionValue = option => extractProperty(option, "value")
export let getOptionIcon = option => extractObjectProperty(option, "icon")
export let quiet = false
export let autoWidth = false
@ -21,6 +22,13 @@
value = e.detail
dispatch("change", e.detail)
}
const extractObjectProperty = (value, property) => {
if (value && typeof value === "object") {
return value[property]
}
}
const extractProperty = (value, property) => {
if (value && typeof value === "object") {
return value[property]
@ -41,6 +49,7 @@
{autoWidth}
{getOptionLabel}
{getOptionValue}
{getOptionIcon}
on:change={onChange}
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

View File

@ -1,7 +1,7 @@
<svg
width="18"
height="18"
viewBox="0 0 268 268"
width="25"
height="25 "
viewBox="0 0 100 100"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
@ -12,9 +12,4 @@
/>
<path d="M43,90v-75l14,-9v75z" fill="#f60" />
</g>
<defs>
<clipPath id="clip0">
<rect width="268" height="268" fill="white" />
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 432 B

After

Width:  |  Height:  |  Size: 319 B

View File

@ -1,6 +1,11 @@
<script>
import GoogleLogo from "./_logos/Google.svelte"
import OIDCLogo from "./_logos/OIDC.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 {
Button,
Heading,
@ -10,10 +15,13 @@
Layout,
Input,
Body,
Select
Select,
Dropzone,
} from "@budibase/bbui"
import { onMount } from "svelte"
import api from "builderStore/api"
import { writable } from "svelte/store"
import { organisation } from "stores/portal"
const ConfigTypes = {
Google: "google",
@ -34,51 +42,70 @@
}
const OIDCConfigFields = {
Oidc: [
"issuer",
"authUrl",
"tokenUrl",
"userInfoUrl",
"clientId",
"clientSecret",
"callbackUrl",
"name"
],
Oidc: ["configUrl", "clientId", "clientSecret"],
}
const OIDCConfigLabels = {
Oidc: {
issuer: "Issuer",
authUrl: "Authorization URL",
tokenUrl: "Token URL",
userInfoUrl: "User Info URL",
configUrl: "Config URL",
clientId: "Client ID",
clientSecret: "Client Secret",
callbackUrl: "Callback URL",
name: "Name"
},
}
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: "Auth0", icon: OidcLogoPng },
{ label: "Upload your own", value: "Upload" },
]
let fileinput
let image
let google
let oidc
const providers = {google, 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 => {
image = e.target.files[0]
}
const providers = { google, oidc }
async function save(docs) {
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 => {
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
})
//res.json()._rev = res.json()._rev
//res.json().id = res.json().id
notifications.success(`Settings saved.`)
})
.catch(err => {
@ -114,8 +141,15 @@
} else {
providers.oidc = oidcDoc
}
})
let fileInput
const res = await api.get(`/api/admin/configs/oidc_logos`)
const configSettings = await res.json()
console.log(configSettings)
const logoKeys = Object.keys(configSettings.config)
logoKeys.map(logoKey => {
const logoUrl = configSettings.config[logoKey]
iconDropdownOptions.unshift({label: logoKey, value: logoUrl, icon: logoUrl})
})
})
</script>
@ -156,7 +190,7 @@
<Layout gap="XS" noPadding>
<Heading size="S">
<span>
<GoogleLogo />
<OidcLogo />
OpenID Connect
</span>
</Heading>
@ -171,21 +205,37 @@
<Input bind:value={providers.oidc.config[field]} />
</div>
{/each}
<br />
<Body size="S">
To customize your login button, fill out the fields below.
To customize your login button, fill out the fields below.
</Body>
<div class="form-row">
<Label size="L">{OIDCConfigLabels.Oidc['name']}</Label>
<Select
options={["Upload File"]}
placeholder={null}
/>
<Label size="L">Name</Label>
<Input bind:value={providers.oidc.config["name"]} />
</div>
<div class="form-row">
<Label size="L">Icon</Label>
</div>
<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>
<Button cta on:click={() => save([providers.google, providers.oidc])}
>Save</Button
>
</div>
</Layout>
@ -201,4 +251,8 @@
align-items: center;
gap: var(--spacing-s);
}
input {
display: none;
}
</style>

View File

@ -146,7 +146,7 @@ exports.upload = async function (ctx) {
}
}
const url = `/${bucket}/${key}`
cfgStructure.config[`${name}Url`] = url
cfgStructure.config[`${name}`] = url
// write back to db with url updated
await db.put(cfgStructure)
@ -192,8 +192,6 @@ exports.configChecklist = async function (ctx) {
const oidcConfig = await getScopedFullConfig(db, {
type: Configs.OIDC,
})
// They have set up an admin user
const users = await db.allDocs(
getGlobalUserParams(null, {

View File

@ -79,7 +79,7 @@ function buildUploadValidation() {
// prettier-ignore
return joiValidator.params(Joi.object({
type: Joi.string().valid(...Object.values(Configs)).required(),
name: Joi.string().valid(...Object.values(ConfigUploads)).required(),
name: Joi.string().required(),
}).required())
}

View File

@ -16,7 +16,7 @@ exports.Configs = Configs
exports.ConfigUploads = {
LOGO: "logo",
OIDC_LOGO: "oidc_logo"
OIDC_LOGO: "oidc_logo",
}
const TemplateTypes = {