mirror of https://github.com/Budibase/budibase.git
99 changed files with 2558 additions and 1249 deletions
@ -0,0 +1,58 @@ |
|||||
|
<script> |
||||
|
import { Button, Select, Label, Heading, Input } from "@budibase/bbui" |
||||
|
|
||||
|
export let value |
||||
|
|
||||
|
let presets = false |
||||
|
|
||||
|
const CRON_EXPRESSIONS = [ |
||||
|
{ |
||||
|
label: "Every Minute", |
||||
|
value: "* * * * *", |
||||
|
}, |
||||
|
{ |
||||
|
label: "Every Hour", |
||||
|
value: "0 * * * *", |
||||
|
}, |
||||
|
{ |
||||
|
label: "Every Morning at 8AM", |
||||
|
value: "0 8 * * *", |
||||
|
}, |
||||
|
{ |
||||
|
label: "Every Night at Midnight", |
||||
|
value: "0 0 * * *", |
||||
|
}, |
||||
|
{ |
||||
|
label: "Every Budibase Reboot", |
||||
|
value: "@reboot", |
||||
|
}, |
||||
|
] |
||||
|
</script> |
||||
|
|
||||
|
<div class="block-field"> |
||||
|
<Input bind:value /> |
||||
|
|
||||
|
<div class="presets"> |
||||
|
<Button on:click={() => (presets = !presets)} |
||||
|
>{presets ? "Hide" : "Show"} Presets</Button |
||||
|
> |
||||
|
{#if presets} |
||||
|
<Select |
||||
|
bind:value |
||||
|
secondary |
||||
|
extraThin |
||||
|
label="Presets" |
||||
|
options={CRON_EXPRESSIONS} |
||||
|
/> |
||||
|
{/if} |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.presets { |
||||
|
margin-top: var(--spacing-m); |
||||
|
} |
||||
|
.block-field { |
||||
|
padding-top: var(--spacing-s); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,19 @@ |
|||||
|
<script> |
||||
|
import { admin } from "stores/portal" |
||||
|
import { onMount } from "svelte" |
||||
|
import { redirect } from "@roxi/routify" |
||||
|
|
||||
|
let loaded = false |
||||
|
|
||||
|
onMount(() => { |
||||
|
if ($admin?.checklist?.adminUser) { |
||||
|
$redirect("../") |
||||
|
} else { |
||||
|
loaded = true |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
{#if loaded} |
||||
|
<slot /> |
||||
|
{/if} |
||||
@ -0,0 +1,7 @@ |
|||||
|
<script> |
||||
|
import { auth } from "stores/portal" |
||||
|
</script> |
||||
|
|
||||
|
{#if $auth.user} |
||||
|
<slot /> |
||||
|
{/if} |
||||
@ -0,0 +1,27 @@ |
|||||
|
<script> |
||||
|
import { ModalContent, Body, notifications } from "@budibase/bbui" |
||||
|
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte" |
||||
|
import { auth } from "stores/portal" |
||||
|
|
||||
|
let password |
||||
|
let error |
||||
|
|
||||
|
const updatePassword = async () => { |
||||
|
try { |
||||
|
await auth.updateSelf({ ...$auth.user, password }) |
||||
|
notifications.success("Information updated successfully") |
||||
|
} catch (error) { |
||||
|
notifications.error("Failed to update password") |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<ModalContent |
||||
|
title="Update password" |
||||
|
confirmText="Update password" |
||||
|
onConfirm={updatePassword} |
||||
|
disabled={error || !password} |
||||
|
> |
||||
|
<Body size="S">Enter your new password below.</Body> |
||||
|
<PasswordRepeatInput bind:password bind:error /> |
||||
|
</ModalContent> |
||||
@ -0,0 +1,31 @@ |
|||||
|
<script> |
||||
|
import { ModalContent, Body, Input, notifications } from "@budibase/bbui" |
||||
|
import { writable } from "svelte/store" |
||||
|
import { auth } from "stores/portal" |
||||
|
|
||||
|
const values = writable({ |
||||
|
firstName: $auth.user.firstName, |
||||
|
lastName: $auth.user.lastName, |
||||
|
}) |
||||
|
|
||||
|
const updateInfo = async () => { |
||||
|
try { |
||||
|
await auth.updateSelf({ ...$auth.user, ...$values }) |
||||
|
notifications.success("Information updated successfully") |
||||
|
} catch (error) { |
||||
|
notifications.error("Failed to update information") |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<ModalContent |
||||
|
title="Update user information" |
||||
|
confirmText="Update information" |
||||
|
onConfirm={updateInfo} |
||||
|
> |
||||
|
<Body size="S"> |
||||
|
Personalise the platform by adding your first name and last name. |
||||
|
</Body> |
||||
|
<Input bind:value={$values.firstName} label="First name" /> |
||||
|
<Input bind:value={$values.lastName} label="Last name" /> |
||||
|
</ModalContent> |
||||
@ -0,0 +1,7 @@ |
|||||
|
<script> |
||||
|
import { auth } from "stores/portal" |
||||
|
</script> |
||||
|
|
||||
|
{#if $auth.user} |
||||
|
<slot /> |
||||
|
{/if} |
||||
@ -0,0 +1,163 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
Heading, |
||||
|
Layout, |
||||
|
Select, |
||||
|
Divider, |
||||
|
ActionMenu, |
||||
|
MenuItem, |
||||
|
Avatar, |
||||
|
Page, |
||||
|
Icon, |
||||
|
Body, |
||||
|
Modal, |
||||
|
} from "@budibase/bbui" |
||||
|
import { onMount } from "svelte" |
||||
|
import { apps, organisation, auth } from "stores/portal" |
||||
|
import { goto } from "@roxi/routify" |
||||
|
import { AppStatus } from "constants" |
||||
|
import { gradient } from "actions" |
||||
|
import UpdateUserInfoModal from "./_components/UpdateUserInfoModal.svelte" |
||||
|
import ChangePasswordModal from "./_components/ChangePasswordModal.svelte" |
||||
|
|
||||
|
let loaded = false |
||||
|
let userInfoModal |
||||
|
let changePasswordModal |
||||
|
|
||||
|
onMount(async () => { |
||||
|
await organisation.init() |
||||
|
await apps.load(AppStatus.DEV) |
||||
|
loaded = true |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
{#if loaded} |
||||
|
<div class="container"> |
||||
|
<Page> |
||||
|
<div class="content"> |
||||
|
<Layout noPadding> |
||||
|
<img src={$organisation.logoUrl} /> |
||||
|
<div class="info-title"> |
||||
|
<Layout noPadding gap="XS"> |
||||
|
<Heading size="L"> |
||||
|
Hey {$auth.user.firstName || $auth.user.email} |
||||
|
</Heading> |
||||
|
<Body> |
||||
|
Welcome to the {$organisation.company} portal. Below you'll find |
||||
|
the list of apps that you have access to. |
||||
|
</Body> |
||||
|
</Layout> |
||||
|
<ActionMenu align="right"> |
||||
|
<div slot="control" class="avatar"> |
||||
|
<Avatar size="M" name="John Doe" /> |
||||
|
<Icon size="XL" name="ChevronDown" /> |
||||
|
</div> |
||||
|
<MenuItem icon="UserEdit" on:click={() => userInfoModal.show()}> |
||||
|
Update user information |
||||
|
</MenuItem> |
||||
|
<MenuItem |
||||
|
icon="LockClosed" |
||||
|
on:click={() => changePasswordModal.show()} |
||||
|
> |
||||
|
Update password |
||||
|
</MenuItem> |
||||
|
<MenuItem |
||||
|
icon="UserDeveloper" |
||||
|
on:click={() => $goto("../portal")} |
||||
|
> |
||||
|
Open developer mode |
||||
|
</MenuItem> |
||||
|
<MenuItem icon="LogOut" on:click={auth.logout}>Log out</MenuItem> |
||||
|
</ActionMenu> |
||||
|
</div> |
||||
|
<Divider /> |
||||
|
{#if $apps.length} |
||||
|
<Heading>Apps</Heading> |
||||
|
<div class="group"> |
||||
|
<Layout gap="S" noPadding> |
||||
|
{#each $apps as app, idx (app.appId)} |
||||
|
<a class="app" target="_blank" href={`/${app.appId}`}> |
||||
|
<div class="preview" use:gradient={{ seed: app.name }} /> |
||||
|
<div class="app-info"> |
||||
|
<Heading size="XS">{app.name}</Heading> |
||||
|
<Body size="S"> |
||||
|
Edited {Math.round(Math.random() * 10 + 1)} months ago |
||||
|
</Body> |
||||
|
</div> |
||||
|
<Icon name="ChevronRight" /> |
||||
|
</a> |
||||
|
{/each} |
||||
|
</Layout> |
||||
|
</div> |
||||
|
{:else} |
||||
|
<Layout gap="XS" noPadding> |
||||
|
<Heading size="S">You don't have access to any apps yet.</Heading> |
||||
|
<Body size="S"> |
||||
|
The apps you have access to will be listed here. |
||||
|
</Body> |
||||
|
</Layout> |
||||
|
{/if} |
||||
|
</Layout> |
||||
|
</div> |
||||
|
</Page> |
||||
|
</div> |
||||
|
<Modal bind:this={userInfoModal}> |
||||
|
<UpdateUserInfoModal /> |
||||
|
</Modal> |
||||
|
<Modal bind:this={changePasswordModal}> |
||||
|
<ChangePasswordModal /> |
||||
|
</Modal> |
||||
|
{/if} |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
height: 100%; |
||||
|
overflow: auto; |
||||
|
} |
||||
|
.content { |
||||
|
padding: 60px 0; |
||||
|
width: 100%; |
||||
|
} |
||||
|
img { |
||||
|
width: 40px; |
||||
|
margin-bottom: -12px; |
||||
|
} |
||||
|
.info-title { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr auto; |
||||
|
grid-gap: var(--spacing-xl); |
||||
|
} |
||||
|
.avatar { |
||||
|
display: grid; |
||||
|
grid-template-columns: auto auto; |
||||
|
place-items: center; |
||||
|
grid-gap: var(--spacing-xs); |
||||
|
} |
||||
|
.avatar:hover { |
||||
|
cursor: pointer; |
||||
|
filter: brightness(110%); |
||||
|
} |
||||
|
.group { |
||||
|
margin-top: var(--spacing-s); |
||||
|
} |
||||
|
.app { |
||||
|
display: grid; |
||||
|
grid-template-columns: auto 1fr auto; |
||||
|
background-color: var(--background); |
||||
|
padding: var(--spacing-xl); |
||||
|
border-radius: var(--border-radius-s); |
||||
|
align-items: center; |
||||
|
grid-gap: var(--spacing-xl); |
||||
|
color: inherit; |
||||
|
} |
||||
|
.app:hover { |
||||
|
cursor: pointer; |
||||
|
background: var(--spectrum-global-color-gray-200); |
||||
|
transition: background-color 130ms ease-in-out; |
||||
|
} |
||||
|
.preview { |
||||
|
height: 40px; |
||||
|
width: 60px; |
||||
|
border-radius: var(--border-radius-s); |
||||
|
} |
||||
|
</style> |
||||
@ -1,4 +1,4 @@ |
|||||
<script> |
<script> |
||||
import { goto } from "@roxi/routify" |
import { redirect } from "@roxi/routify" |
||||
$goto("./login") |
$redirect("./login") |
||||
</script> |
</script> |
||||
|
|||||
@ -1,4 +1,14 @@ |
|||||
<script> |
<script> |
||||
import { goto } from "@roxi/routify" |
import { redirect } from "@roxi/routify" |
||||
$goto("./portal") |
import { auth } from "stores/portal" |
||||
|
|
||||
|
$: { |
||||
|
if (!$auth.user) { |
||||
|
$redirect("./auth/login") |
||||
|
} else if ($auth.user.builder?.global) { |
||||
|
$redirect("./portal") |
||||
|
} else { |
||||
|
$redirect("./apps") |
||||
|
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|||||
@ -1,4 +1,4 @@ |
|||||
<script> |
<script> |
||||
import { goto } from "@roxi/routify" |
import { redirect } from "@roxi/routify" |
||||
$goto("./apps") |
$redirect("./apps") |
||||
</script> |
</script> |
||||
|
|||||
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1,115 +0,0 @@ |
|||||
<script> |
|
||||
import GoogleLogo from "./_logos/Google.svelte" |
|
||||
import { |
|
||||
Button, |
|
||||
Heading, |
|
||||
Divider, |
|
||||
Label, |
|
||||
notifications, |
|
||||
Layout, |
|
||||
Input, |
|
||||
Body, |
|
||||
Page, |
|
||||
} from "@budibase/bbui" |
|
||||
import { onMount } from "svelte" |
|
||||
import api from "builderStore/api" |
|
||||
|
|
||||
const ConfigTypes = { |
|
||||
Google: "google", |
|
||||
// Github: "github", |
|
||||
// AzureAD: "ad", |
|
||||
} |
|
||||
|
|
||||
const ConfigFields = { |
|
||||
Google: ["clientID", "clientSecret", "callbackURL"], |
|
||||
} |
|
||||
|
|
||||
let google |
|
||||
|
|
||||
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}`) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
onMount(async () => { |
|
||||
// fetch the configs for oauth |
|
||||
const googleResponse = await api.get( |
|
||||
`/api/admin/configs/${ConfigTypes.Google}` |
|
||||
) |
|
||||
const googleDoc = await googleResponse.json() |
|
||||
|
|
||||
if (!googleDoc._id) { |
|
||||
google = { |
|
||||
type: ConfigTypes.Google, |
|
||||
config: {}, |
|
||||
} |
|
||||
} else { |
|
||||
google = googleDoc |
|
||||
} |
|
||||
}) |
|
||||
</script> |
|
||||
|
|
||||
<Page> |
|
||||
<header> |
|
||||
<Heading size="M">OAuth</Heading> |
|
||||
<Body size="S"> |
|
||||
Every budibase app comes with basic authentication (email/password) |
|
||||
included. You can add additional authentication methods from the options |
|
||||
below. |
|
||||
</Body> |
|
||||
</header> |
|
||||
<Divider /> |
|
||||
{#if google} |
|
||||
<div class="config-form"> |
|
||||
<Layout gap="S"> |
|
||||
<Heading size="S"> |
|
||||
<span> |
|
||||
<GoogleLogo /> |
|
||||
Google |
|
||||
</span> |
|
||||
</Heading> |
|
||||
{#each ConfigFields.Google as field} |
|
||||
<div class="form-row"> |
|
||||
<Label>{field}</Label> |
|
||||
<Input bind:value={google.config[field]} /> |
|
||||
</div> |
|
||||
{/each} |
|
||||
</Layout> |
|
||||
<Button primary on:click={() => save(google)}>Save</Button> |
|
||||
</div> |
|
||||
<Divider /> |
|
||||
{/if} |
|
||||
</Page> |
|
||||
|
|
||||
<style> |
|
||||
.config-form { |
|
||||
margin-top: 42px; |
|
||||
margin-bottom: 42px; |
|
||||
} |
|
||||
|
|
||||
.form-row { |
|
||||
display: grid; |
|
||||
grid-template-columns: 20% 1fr; |
|
||||
grid-gap: var(--spacing-l); |
|
||||
} |
|
||||
|
|
||||
span { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: var(--spacing-s); |
|
||||
} |
|
||||
|
|
||||
header { |
|
||||
margin-bottom: 42px; |
|
||||
} |
|
||||
</style> |
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1,114 +0,0 @@ |
|||||
<script> |
|
||||
import GoogleLogo from "./_logos/Google.svelte" |
|
||||
import { |
|
||||
Button, |
|
||||
Page, |
|
||||
Heading, |
|
||||
Divider, |
|
||||
Label, |
|
||||
notifications, |
|
||||
Layout, |
|
||||
Input, |
|
||||
Body, |
|
||||
} from "@budibase/bbui" |
|
||||
import { onMount } from "svelte" |
|
||||
import api from "builderStore/api" |
|
||||
|
|
||||
const ConfigTypes = { |
|
||||
Google: "google", |
|
||||
// Github: "github", |
|
||||
// AzureAD: "ad", |
|
||||
} |
|
||||
|
|
||||
const ConfigFields = { |
|
||||
Google: ["clientID", "clientSecret", "callbackURL"], |
|
||||
} |
|
||||
|
|
||||
let google |
|
||||
|
|
||||
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}`) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
onMount(async () => { |
|
||||
// fetch the configs for oauth |
|
||||
const googleResponse = await api.get( |
|
||||
`/api/admin/configs/${ConfigTypes.Google}` |
|
||||
) |
|
||||
const googleDoc = await googleResponse.json() |
|
||||
|
|
||||
if (!googleDoc._id) { |
|
||||
google = { |
|
||||
type: ConfigTypes.Google, |
|
||||
config: {}, |
|
||||
} |
|
||||
} else { |
|
||||
google = googleDoc |
|
||||
} |
|
||||
}) |
|
||||
</script> |
|
||||
|
|
||||
<Page> |
|
||||
<Layout noPadding> |
|
||||
<div> |
|
||||
<Heading size="M">OAuth</Heading> |
|
||||
<Body> |
|
||||
Every budibase app comes with basic authentication (email/password) |
|
||||
included. You can add additional authentication methods from the options |
|
||||
below. |
|
||||
</Body> |
|
||||
</div> |
|
||||
<Divider /> |
|
||||
{#if google} |
|
||||
<div> |
|
||||
<Heading size="S"> |
|
||||
<span> |
|
||||
<GoogleLogo /> |
|
||||
Google |
|
||||
</span> |
|
||||
</Heading> |
|
||||
<Body> |
|
||||
To allow users to authenticate using their Google accounts, fill out |
|
||||
the fields below. |
|
||||
</Body> |
|
||||
</div> |
|
||||
|
|
||||
{#each ConfigFields.Google as field} |
|
||||
<div class="form-row"> |
|
||||
<Label size="L">{field}</Label> |
|
||||
<Input bind:value={google.config[field]} /> |
|
||||
</div> |
|
||||
{/each} |
|
||||
<div> |
|
||||
<Button cta on:click={() => save(google)}>Save</Button> |
|
||||
</div> |
|
||||
<Divider /> |
|
||||
{/if} |
|
||||
</Layout> |
|
||||
</Page> |
|
||||
|
|
||||
<style> |
|
||||
.form-row { |
|
||||
display: grid; |
|
||||
grid-template-columns: 20% 1fr; |
|
||||
grid-gap: var(--spacing-l); |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
span { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: var(--spacing-s); |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,4 +1,4 @@ |
|||||
<script> |
<script> |
||||
import { goto } from "@roxi/routify" |
import { redirect } from "@roxi/routify" |
||||
$goto("./builder") |
$redirect("./builder") |
||||
</script> |
</script> |
||||
|
|||||
@ -1,90 +0,0 @@ |
|||||
const roles = require("@budibase/auth/roles") |
|
||||
const userController = require("../../api/controllers/user") |
|
||||
const env = require("../../environment") |
|
||||
const usage = require("../../utilities/usageQuota") |
|
||||
|
|
||||
module.exports.definition = { |
|
||||
description: "Create a new user", |
|
||||
tagline: "Create user {{inputs.email}}", |
|
||||
icon: "ri-user-add-line", |
|
||||
name: "Create User", |
|
||||
type: "ACTION", |
|
||||
stepId: "CREATE_USER", |
|
||||
inputs: { |
|
||||
roleId: roles.BUILTIN_ROLE_IDS.POWER, |
|
||||
}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
email: { |
|
||||
type: "string", |
|
||||
customType: "email", |
|
||||
title: "Email", |
|
||||
}, |
|
||||
password: { |
|
||||
type: "string", |
|
||||
title: "Password", |
|
||||
}, |
|
||||
roleId: { |
|
||||
type: "string", |
|
||||
title: "Role", |
|
||||
enum: roles.BUILTIN_ROLE_ID_ARRAY, |
|
||||
pretty: roles.BUILTIN_ROLE_NAME_ARRAY, |
|
||||
}, |
|
||||
}, |
|
||||
required: ["email", "password", "roleId"], |
|
||||
}, |
|
||||
outputs: { |
|
||||
properties: { |
|
||||
id: { |
|
||||
type: "string", |
|
||||
description: "The identifier of the new user", |
|
||||
}, |
|
||||
revision: { |
|
||||
type: "string", |
|
||||
description: "The revision of the new user", |
|
||||
}, |
|
||||
response: { |
|
||||
type: "object", |
|
||||
description: "The response from the user table", |
|
||||
}, |
|
||||
success: { |
|
||||
type: "boolean", |
|
||||
description: "Whether the action was successful", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["id", "revision", "success"], |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
module.exports.run = async function ({ inputs, appId, apiKey, emitter }) { |
|
||||
const { email, password, roleId } = inputs |
|
||||
const ctx = { |
|
||||
appId, |
|
||||
request: { |
|
||||
body: { email, password, roleId }, |
|
||||
}, |
|
||||
eventEmitter: emitter, |
|
||||
} |
|
||||
|
|
||||
try { |
|
||||
if (env.isProd()) { |
|
||||
await usage.update(apiKey, usage.Properties.USER, 1) |
|
||||
} |
|
||||
await userController.createMetadata(ctx) |
|
||||
return { |
|
||||
response: ctx.body, |
|
||||
// internal property not returned through the API
|
|
||||
id: ctx.body._id, |
|
||||
revision: ctx.body._rev, |
|
||||
success: ctx.status === 200, |
|
||||
} |
|
||||
} catch (err) { |
|
||||
console.error(err) |
|
||||
return { |
|
||||
success: false, |
|
||||
response: err, |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,42 +0,0 @@ |
|||||
const usageQuota = require("../../utilities/usageQuota") |
|
||||
const setup = require("./utilities") |
|
||||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") |
|
||||
const { InternalTables } = require("../../db/utils") |
|
||||
|
|
||||
jest.mock("../../utilities/usageQuota") |
|
||||
|
|
||||
describe("test the create user action", () => { |
|
||||
let config = setup.getConfig() |
|
||||
let user |
|
||||
|
|
||||
beforeEach(async () => { |
|
||||
await config.init() |
|
||||
user = { |
|
||||
email: "test@test.com", |
|
||||
password: "password", |
|
||||
roleId: BUILTIN_ROLE_IDS.POWER |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
afterAll(setup.afterAll) |
|
||||
|
|
||||
it("should be able to run the action", async () => { |
|
||||
const res = await setup.runStep(setup.actions.CREATE_USER.stepId, user) |
|
||||
expect(res.id).toBeDefined() |
|
||||
expect(res.revision).toBeDefined() |
|
||||
const userDoc = await config.getRow(InternalTables.USER_METADATA, res.id) |
|
||||
expect(userDoc).toBeDefined() |
|
||||
}) |
|
||||
|
|
||||
it("should return an error if no inputs provided", async () => { |
|
||||
const res = await setup.runStep(setup.actions.CREATE_USER.stepId, {}) |
|
||||
expect(res.success).toEqual(false) |
|
||||
}) |
|
||||
|
|
||||
it("check usage quota attempts", async () => { |
|
||||
await setup.runInProd(async () => { |
|
||||
await setup.runStep(setup.actions.CREATE_USER.stepId, user) |
|
||||
expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "users", 1) |
|
||||
}) |
|
||||
}) |
|
||||
}) |
|
||||
File diff suppressed because it is too large
@ -1,10 +1,11 @@ |
|||||
const Router = require("@koa/router") |
const Router = require("@koa/router") |
||||
const controller = require("../../controllers/admin/roles") |
const controller = require("../../controllers/admin/roles") |
||||
|
const adminOnly = require("../../../middleware/adminOnly") |
||||
|
|
||||
const router = Router() |
const router = Router() |
||||
|
|
||||
router |
router |
||||
.get("/api/admin/roles", controller.fetch) |
.get("/api/admin/roles", adminOnly, controller.fetch) |
||||
.get("/api/admin/roles/:appId", controller.find) |
.get("/api/admin/roles/:appId", adminOnly, controller.find) |
||||
|
|
||||
module.exports = router |
module.exports = router |
||||
|
|||||
@ -0,0 +1,6 @@ |
|||||
|
module.exports = async (ctx, next) => { |
||||
|
if (!ctx.user || !ctx.user.admin || !ctx.user.admin.global) { |
||||
|
ctx.throw(403, "Admin user only endpoint.") |
||||
|
} |
||||
|
return next() |
||||
|
} |
||||
Loading…
Reference in new issue