Bypass password onboarding for enforced sso (#9851)

This commit is contained in:
Rory Powell 2023-03-01 21:56:30 +00:00 committed by GitHub
parent 2127717987
commit 4c43ba2943
7 changed files with 159 additions and 130 deletions

View File

@ -62,3 +62,8 @@ export const PluginSource = {
GITHUB: "Github",
FILE: "File Upload",
}
export const OnboardingType = {
EMAIL: "email",
PASSWORD: "password",
}

View File

@ -13,6 +13,7 @@
let formData = {}
let onboarding = false
let errors = {}
let loaded = false
$: company = $organisation.company || "Budibase"
@ -39,6 +40,11 @@
if (invite?.email) {
formData.email = invite?.email
}
if ($organisation.isSSOEnforced) {
// auto accept invite and redirect to login
await users.acceptInvite(inviteCode)
$goto("../auth")
}
} catch (error) {
notifications.error(error.message)
}
@ -61,130 +67,135 @@
try {
await organisation.init()
await getInvite()
loaded = true
} catch (error) {
notifications.error("Error getting invite config")
}
})
</script>
<TestimonialPage>
<Layout gap="M" noPadding>
<img alt="logo" src={$organisation.logoUrl || Logo} />
<Layout gap="XS" noPadding>
<Heading size="M">Join {company}</Heading>
<Body size="M">Create your account to access your budibase apps!</Body>
</Layout>
{#if loaded}
<TestimonialPage>
<Layout gap="M" noPadding>
<img alt="logo" src={$organisation.logoUrl || Logo} />
<Layout gap="XS" noPadding>
<Heading size="M">Join {company}</Heading>
<Body size="M">Create your account to access your budibase apps!</Body>
</Layout>
<Layout gap="S" noPadding>
<FancyForm bind:this={form}>
<FancyInput
label="Email"
value={formData.email}
disabled={true}
error={errors.email}
/>
<FancyInput
label="First name"
value={formData.firstName}
on:change={e => {
formData = {
...formData,
firstName: e.detail,
}
}}
validate={() => {
let fieldError = {
firstName: !formData.firstName
? "Please enter your first name"
: undefined,
}
errors = handleError({ ...errors, ...fieldError })
}}
error={errors.firstName}
disabled={onboarding}
/>
<FancyInput
label="Last name (optional)"
value={formData.lastName}
on:change={e => {
formData = {
...formData,
lastName: e.detail,
}
}}
disabled={onboarding}
/>
<FancyInput
label="Password"
value={formData.password}
type="password"
on:change={e => {
formData = {
...formData,
password: e.detail,
}
}}
validate={() => {
let fieldError = {}
fieldError["password"] = !formData.password
? "Please enter a password"
: undefined
fieldError["confirmationPassword"] =
!passwordsMatch(
formData.password,
formData.confirmationPassword
) && formData.confirmationPassword
? "Passwords must match"
: undefined
errors = handleError({ ...errors, ...fieldError })
}}
error={errors.password}
disabled={onboarding}
/>
<FancyInput
label="Repeat password"
value={formData.confirmationPassword}
type="password"
on:change={e => {
formData = {
...formData,
confirmationPassword: e.detail,
}
}}
validate={() => {
let fieldError = {
confirmationPassword:
!passwordsMatch(
formData.password,
formData.confirmationPassword
) && formData.password
? "Passwords must match"
<Layout gap="S" noPadding>
<FancyForm bind:this={form}>
<FancyInput
label="Email"
value={formData.email}
disabled={true}
error={errors.email}
/>
<FancyInput
label="First name"
value={formData.firstName}
on:change={e => {
formData = {
...formData,
firstName: e.detail,
}
}}
validate={() => {
let fieldError = {
firstName: !formData.firstName
? "Please enter your first name"
: undefined,
}
}
errors = handleError({ ...errors, ...fieldError })
}}
error={errors.confirmationPassword}
disabled={onboarding}
/>
</FancyForm>
errors = handleError({ ...errors, ...fieldError })
}}
error={errors.firstName}
disabled={onboarding}
/>
<FancyInput
label="Last name (optional)"
value={formData.lastName}
on:change={e => {
formData = {
...formData,
lastName: e.detail,
}
}}
disabled={onboarding}
/>
{#if !$organisation.isSSOEnforced}
<FancyInput
label="Password"
value={formData.password}
type="password"
on:change={e => {
formData = {
...formData,
password: e.detail,
}
}}
validate={() => {
let fieldError = {}
fieldError["password"] = !formData.password
? "Please enter a password"
: undefined
fieldError["confirmationPassword"] =
!passwordsMatch(
formData.password,
formData.confirmationPassword
) && formData.confirmationPassword
? "Passwords must match"
: undefined
errors = handleError({ ...errors, ...fieldError })
}}
error={errors.password}
disabled={onboarding}
/>
<FancyInput
label="Repeat password"
value={formData.confirmationPassword}
type="password"
on:change={e => {
formData = {
...formData,
confirmationPassword: e.detail,
}
}}
validate={() => {
let fieldError = {
confirmationPassword:
!passwordsMatch(
formData.password,
formData.confirmationPassword
) && formData.password
? "Passwords must match"
: undefined,
}
errors = handleError({ ...errors, ...fieldError })
}}
error={errors.confirmationPassword}
disabled={onboarding}
/>
{/if}
</FancyForm>
</Layout>
<div>
<Button
size="L"
disabled={Object.keys(errors).length > 0 || onboarding}
cta
on:click={acceptInvite}
>
Create account
</Button>
</div>
</Layout>
<div>
<Button
size="L"
disabled={Object.keys(errors).length > 0 || onboarding}
cta
on:click={acceptInvite}
>
Create account
</Button>
</div>
</Layout>
</TestimonialPage>
</TestimonialPage>
{/if}
<style>
img {

View File

@ -1,9 +1,8 @@
<script>
import { ModalContent, Body, Layout, Icon } from "@budibase/bbui"
import { OnboardingType } from "../../../../../../constants"
export let chooseCreationType
let emailOnboardingKey = "emailOnboarding"
let basicOnboaridngKey = "basicOnboarding"
let selectedOnboardingType
</script>
@ -20,9 +19,9 @@
<Layout noPadding gap="S">
<div
class="onboarding-type item"
class:selected={selectedOnboardingType == emailOnboardingKey}
class:selected={selectedOnboardingType == OnboardingType.EMAIL}
on:click={() => {
selectedOnboardingType = emailOnboardingKey
selectedOnboardingType = OnboardingType.EMAIL
}}
>
<div class="content onboarding-type-wrap">
@ -32,7 +31,7 @@
</div>
</div>
<div style="color: var(--spectrum-global-color-green-600); float: right">
{#if selectedOnboardingType == emailOnboardingKey}
{#if selectedOnboardingType == OnboardingType.EMAIL}
<div class="checkmark-spacing">
<Icon size="S" name="CheckmarkCircle" />
</div>
@ -42,9 +41,9 @@
<div
class="onboarding-type item"
class:selected={selectedOnboardingType == basicOnboaridngKey}
class:selected={selectedOnboardingType == OnboardingType.PASSWORD}
on:click={() => {
selectedOnboardingType = basicOnboaridngKey
selectedOnboardingType = OnboardingType.PASSWORD
}}
>
<div class="content onboarding-type-wrap">
@ -54,7 +53,7 @@
</div>
</div>
<div style="color: var(--spectrum-global-color-green-600); float: right">
{#if selectedOnboardingType == basicOnboaridngKey}
{#if selectedOnboardingType == OnboardingType.PASSWORD}
<div class="checkmark-spacing">
<Icon size="S" name="CheckmarkCircle" />
</div>

View File

@ -13,7 +13,7 @@
Divider,
} from "@budibase/bbui"
import AddUserModal from "./_components/AddUserModal.svelte"
import { users, groups, auth, licensing } from "stores/portal"
import { users, groups, auth, licensing, organisation } from "stores/portal"
import { onMount } from "svelte"
import DeleteRowsButton from "components/backend/DataTable/buttons/DeleteRowsButton.svelte"
import GroupsTableRenderer from "./_components/GroupsTableRenderer.svelte"
@ -27,6 +27,7 @@
import { get } from "svelte/store"
import { Constants, Utils, fetchData } from "@budibase/frontend-core"
import { API } from "api"
import { OnboardingType } from "../../../../../constants"
const fetch = fetchData({
API,
@ -105,10 +106,18 @@
const debouncedUpdateFetch = Utils.debounce(updateFetch, 250)
const showOnboardingTypeModal = async addUsersData => {
// no-op if users already exist
userData = await removingDuplicities(addUsersData)
if (!userData?.users?.length) return
if (!userData?.users?.length) {
return
}
onboardingTypeModal.show()
if ($organisation.isSSOEnforced) {
// bypass the onboarding type selection of sso is enforced
await chooseCreationType(OnboardingType.EMAIL)
} else {
onboardingTypeModal.show()
}
}
async function createUserFlow() {
@ -181,7 +190,7 @@
}
async function chooseCreationType(onboardingType) {
if (onboardingType === "emailOnboarding") {
if (onboardingType === OnboardingType.EMAIL) {
await createUserFlow()
} else {
await createUsers()

View File

@ -50,8 +50,8 @@ function buildInviteAcceptValidation() {
// prettier-ignore
return auth.joiValidator.body(Joi.object({
inviteCode: Joi.string().required(),
password: Joi.string().required(),
firstName: Joi.string().required(),
password: Joi.string().optional(),
firstName: Joi.string().optional(),
lastName: Joi.string().optional(),
}).required().unknown(true))
}

View File

@ -140,7 +140,12 @@ const buildUser = async (
hashedPassword = opts.hashPassword ? await utils.hash(password) : password
} else if (dbUser) {
hashedPassword = dbUser.password
} else if (opts.requirePassword) {
}
// passwords are never required if sso is enforced
const requirePasswords =
opts.requirePassword && !(await pro.features.isSSOEnforced())
if (!hashedPassword && requirePasswords) {
throw "Password must be specified."
}

View File

@ -47,7 +47,7 @@ async function getACode(db: string, code: string, deleteCode = true) {
const client = await getClient(db)
const value = await client.get(code)
if (!value) {
throw "Invalid code."
throw new Error("Invalid code.")
}
if (deleteCode) {
await client.delete(code)