Merge branch 'master' of github.com:Budibase/budibase into fix/lockdown-admin

This commit is contained in:
mike12345567 2021-05-21 16:54:52 +01:00
commit 348c61a8c5
18 changed files with 108 additions and 66 deletions

View File

@ -12,15 +12,7 @@
export let size = "M" export let size = "M"
export let url = "" export let url = ""
export let disabled = false export let disabled = false
export let name = "John Doe" export let initials = "JD"
function getInitials(name) {
let parts = name.split(" ")
if (parts.length > 0) {
return parts.map(name => name[0]).join("")
}
return name
}
</script> </script>
{#if url} {#if url}
@ -38,7 +30,7 @@
size size
)}); font-size: calc(var({sizes.get(size)}) / 2)" )}); font-size: calc(var({sizes.get(size)}) / 2)"
> >
{getInitials(name)} {initials || ""}
</div> </div>
{/if} {/if}
@ -52,5 +44,6 @@
border-radius: 50%; border-radius: 50%;
overflow: hidden; overflow: hidden;
user-select: none; user-select: none;
text-transform: uppercase;
} }
</style> </style>

View File

@ -82,6 +82,7 @@
on:blur on:blur
on:focus on:focus
on:input on:input
on:keyup
on:blur={onBlur} on:blur={onBlur}
on:focus={onFocus} on:focus={onFocus}
on:input={onInput} on:input={onInput}

View File

@ -34,5 +34,6 @@
on:input on:input
on:blur on:blur
on:focus on:focus
on:keyup
/> />
</Field> </Field>

View File

@ -8,7 +8,7 @@
const updatePassword = async () => { const updatePassword = async () => {
try { try {
await auth.updateSelf({ ...$auth.user, password }) await auth.updateSelf({ password })
notifications.success("Password changed successfully") notifications.success("Password changed successfully")
} catch (error) { } catch (error) {
notifications.error("Failed to update password") notifications.error("Failed to update password")

View File

@ -10,7 +10,7 @@
const updateInfo = async () => { const updateInfo = async () => {
try { try {
await auth.updateSelf({ ...$auth.user, ...$values }) await auth.updateSelf($values)
notifications.success("Information updated successfully") notifications.success("Information updated successfully")
} catch (error) { } catch (error) {
notifications.error("Failed to update information") notifications.error("Failed to update information")

View File

@ -10,8 +10,10 @@
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import api from "builderStore/api" import api from "builderStore/api"
import { admin, organisation } from "stores/portal" import { admin, organisation } from "stores/portal"
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte"
let adminUser = {} let adminUser = {}
let error
async function save() { async function save() {
try { try {
@ -42,13 +44,11 @@
</Layout> </Layout>
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<Input label="Email" bind:value={adminUser.email} /> <Input label="Email" bind:value={adminUser.email} />
<Input <PasswordRepeatInput bind:password={adminUser.password} bind:error />
label="Password"
type="password"
bind:value={adminUser.password}
/>
</Layout> </Layout>
<Button cta on:click={save}>Create super admin user</Button> <Button cta disabled={error} on:click={save}>
Create super admin user
</Button>
</Layout> </Layout>
</div> </div>
</section> </section>

View File

@ -51,7 +51,7 @@
</Layout> </Layout>
<ActionMenu align="right"> <ActionMenu align="right">
<div slot="control" class="avatar"> <div slot="control" class="avatar">
<Avatar size="M" name="John Doe" /> <Avatar size="M" initials={$auth.initials} />
<Icon size="XL" name="ChevronDown" /> <Icon size="XL" name="ChevronDown" />
</div> </div>
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}> <MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}>

View File

@ -1,24 +1,26 @@
<script> <script>
import { onMount } from "svelte"
import { ActionButton } from "@budibase/bbui" import { ActionButton } from "@budibase/bbui"
import GoogleLogo from "/assets/google-logo.png" import GoogleLogo from "/assets/google-logo.png"
import { admin } from "stores/portal"
let show = false
$: show = $admin.checklist?.oauth
</script> </script>
<ActionButton> {#if show}
<a target="_blank" href="/api/admin/auth/google"> <ActionButton>
<div class="inner"> <a target="_blank" href="/api/admin/auth/google">
<img src={GoogleLogo} alt="google icon" /> <div class="inner">
<p>Sign in with Google</p> <img src={GoogleLogo} alt="google icon" />
</div> <p>Sign in with Google</p>
</a> </div>
</ActionButton> </a>
</ActionButton>
{/if}
<style> <style>
.outer {
border: 1px solid #494949;
border-radius: 4px;
width: 100%;
background-color: var(--background-alt);
}
.inner { .inner {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -38,8 +38,13 @@
notifications.error("Invalid credentials") notifications.error("Invalid credentials")
} }
} }
function handleKeydown(evt) {
if (evt.key === "Enter") login()
}
</script> </script>
<svelte:window on:keydown={handleKeydown} />
<div class="login"> <div class="login">
<div class="main"> <div class="main">
<Layout> <Layout>

View File

@ -13,7 +13,6 @@
try { try {
if (forceResetPassword) { if (forceResetPassword) {
await auth.updateSelf({ await auth.updateSelf({
...$auth.user,
password, password,
forceResetPassword: false, forceResetPassword: false,
}) })

View File

@ -3,7 +3,6 @@
import { import {
Icon, Icon,
Avatar, Avatar,
Search,
Layout, Layout,
SideNavigation as Navigation, SideNavigation as Navigation,
SideNavigationItem as Item, SideNavigationItem as Item,
@ -77,7 +76,7 @@
<div /> <div />
<ActionMenu align="right"> <ActionMenu align="right">
<div slot="control" class="avatar"> <div slot="control" class="avatar">
<Avatar size="M" name="John Doe" /> <Avatar size="M" initials={$auth.initials} />
<Icon size="XL" name="ChevronDown" /> <Icon size="XL" name="ChevronDown" />
</div> </div>
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}> <MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}>

View File

@ -41,8 +41,8 @@
const enrichedApps = apps.map(app => ({ const enrichedApps = apps.map(app => ({
...app, ...app,
deployed: app.status === AppStatus.DEPLOYED, deployed: app.status === AppStatus.DEPLOYED,
lockedYou: app.lockedBy?.email === user.email, lockedYou: app.lockedBy && app.lockedBy.email === user?.email,
lockedOther: app.lockedBy && app.lockedBy.email !== user.email, lockedOther: app.lockedBy && app.lockedBy.email !== user?.email,
})) }))
if (sortBy === "status") { if (sortBy === "status") {
return enrichedApps.sort((a, b) => { return enrichedApps.sort((a, b) => {

View File

@ -4,7 +4,6 @@
Button, Button,
Heading, Heading,
Divider, Divider,
Page,
Label, Label,
notifications, notifications,
Layout, Layout,
@ -23,6 +22,13 @@
const ConfigFields = { const ConfigFields = {
Google: ["clientID", "clientSecret", "callbackURL"], Google: ["clientID", "clientSecret", "callbackURL"],
} }
const ConfigLabels = {
Google: {
clientID: "Client ID",
clientSecret: "Client secret",
callbackURL: "Callback URL",
},
}
let google let google
@ -85,7 +91,7 @@
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
{#each ConfigFields.Google as field} {#each ConfigFields.Google as field}
<div class="form-row"> <div class="form-row">
<Label size="L">{field}</Label> <Label size="L">{ConfigLabels.Google[field]}</Label>
<Input bind:value={google.config[field]} /> <Input bind:value={google.config[field]} />
</div> </div>
{/each} {/each}

View File

@ -92,7 +92,7 @@
<Heading>User: {$userFetch?.data?.email}</Heading> <Heading>User: {$userFetch?.data?.email}</Heading>
<Body> <Body>
Change user settings and update their app roles. Also contains the ability Change user settings and update their app roles. Also contains the ability
to delete the user as well as force reset their password.. to delete the user as well as force reset their password.
</Body> </Body>
</Layout> </Layout>
<Divider size="S" /> <Divider size="S" />
@ -118,7 +118,7 @@
<!-- don't let a user remove the privileges that let them be here --> <!-- don't let a user remove the privileges that let them be here -->
{#if userId !== $auth.user._id} {#if userId !== $auth.user._id}
<div class="field"> <div class="field">
<Label size="L">Development access?</Label> <Label size="L">Development access</Label>
<Toggle <Toggle
text="" text=""
value={$userFetch?.data?.builder?.global} value={$userFetch?.data?.builder?.global}
@ -127,7 +127,7 @@
/> />
</div> </div>
<div class="field"> <div class="field">
<Label size="L">Administration access?</Label> <Label size="L">Administration access</Label>
<Toggle <Toggle
text="" text=""
value={$userFetch?.data?.admin?.global} value={$userFetch?.data?.admin?.global}

View File

@ -9,13 +9,26 @@
$: leftover = roles.length - tags.length $: leftover = roles.length - tags.length
</script> </script>
<Tags> <div class="tag-renderer">
{#each tags as tag} <Tags>
<Tag disabled> {#each tags as tag}
{tag} <Tag>
</Tag> {tag}
{/each} </Tag>
{#if leftover} {/each}
<Tag>+{leftover} more</Tag> {#if leftover}
{/if} <Tag>+{leftover} more</Tag>
</Tags> {/if}
</Tags>
</div>
<style>
.tag-renderer :global(.spectrum-Tags-item:hover) {
color: var(--spectrum-alias-label-text-color);
border-color: var(--spectrum-alias-border-color-darker-default);
cursor: pointer;
}
.tag-renderer :global(.spectrum-Tags-itemLabel) {
cursor: pointer;
}
</style>

View File

@ -53,9 +53,8 @@
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<Heading>Users</Heading> <Heading>Users</Heading>
<Body> <Body>
Users are the common denominator in Budibase. Each user is assigned to a Each user is assigned to a group that contains apps and permissions. In
group that contains apps and permissions. In this section, you can add this section, you can add users, or edit and delete an existing user.
users, or edit and delete an existing user.
</Body> </Body>
</Layout> </Layout>
<Divider size="S" /> <Divider size="S" />

View File

@ -1,25 +1,42 @@
import { writable } from "svelte/store" import { derived, writable, get } from "svelte/store"
import api from "../../builderStore/api" import api from "../../builderStore/api"
export function createAuthStore() { export function createAuthStore() {
const store = writable({ user: null }) const user = writable(null)
const store = derived(user, $user => {
let initials = null
if ($user) {
if ($user.firstName) {
initials = $user.firstName[0]
if ($user.lastName) {
initials += $user.lastName[0]
}
} else {
initials = $user.email[0]
}
}
return {
user: $user,
initials,
}
})
return { return {
subscribe: store.subscribe, subscribe: store.subscribe,
checkAuth: async () => { checkAuth: async () => {
const response = await api.get("/api/admin/users/self") const response = await api.get("/api/admin/users/self")
if (response.status !== 200) { if (response.status !== 200) {
store.update(state => ({ ...state, user: null })) user.set(null)
} else { } else {
const user = await response.json() const json = await response.json()
store.update(state => ({ ...state, user })) user.set(json)
} }
}, },
login: async creds => { login: async creds => {
const response = await api.post(`/api/admin/auth`, creds) const response = await api.post(`/api/admin/auth`, creds)
const json = await response.json() const json = await response.json()
if (response.status === 200) { if (response.status === 200) {
store.update(state => ({ ...state, user: json.user })) user.set(json.user)
} else { } else {
throw "Invalid credentials" throw "Invalid credentials"
} }
@ -31,12 +48,13 @@ export function createAuthStore() {
throw "Unable to create logout" throw "Unable to create logout"
} }
await response.json() await response.json()
store.update(state => ({ ...state, user: null })) user.set(null)
}, },
updateSelf: async user => { updateSelf: async fields => {
const response = await api.post("/api/admin/users/self", user) const newUser = { ...get(user), ...fields }
const response = await api.post("/api/admin/users/self", newUser)
if (response.status === 200) { if (response.status === 200) {
store.update(state => ({ ...state, user: { ...state.user, ...user } })) user.set(newUser)
} else { } else {
throw "Unable to update user details" throw "Unable to update user details"
} }

View File

@ -168,6 +168,11 @@ exports.configChecklist = async function (ctx) {
type: Configs.SMTP, type: Configs.SMTP,
}) })
// They have set up Google Auth
const oauthConfig = await getScopedFullConfig(db, {
type: Configs.GOOGLE,
})
// 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, {
@ -180,6 +185,7 @@ exports.configChecklist = async function (ctx) {
apps: appDbNames.length, apps: appDbNames.length,
smtp: !!smtpConfig, smtp: !!smtpConfig,
adminUser, adminUser,
oauth: !!oauthConfig,
} }
} catch (err) { } catch (err) {
ctx.throw(err.status, err) ctx.throw(err.status, err)