Fixing issues with the user table within the apps.

This commit is contained in:
mike12345567 2021-05-19 15:55:00 +01:00
parent 6a51e58f73
commit c6f7f115b5
7 changed files with 70 additions and 57 deletions

View File

@ -190,7 +190,7 @@ exports.getAllApps = async ({ dev, all } = {}) => {
} else { } else {
return apps.map(app => ({ return apps.map(app => ({
...app, ...app,
status: isDevApp(app) ? "development" : "published" status: isDevApp(app) ? "development" : "published",
})) }))
} }
} }

View File

@ -112,6 +112,9 @@ exports.isClient = ctx => {
* @return {Promise<object|null>} * @return {Promise<object|null>}
*/ */
exports.getGlobalUserByEmail = async email => { exports.getGlobalUserByEmail = async email => {
if (email == null) {
throw "Must supply an email address to view"
}
const db = getDB(StaticDatabases.GLOBAL.name) const db = getDB(StaticDatabases.GLOBAL.name)
try { try {
let users = ( let users = (

View File

@ -85,10 +85,6 @@
bind:value={row.email} bind:value={row.email}
readonly={!creating} readonly={!creating}
/> />
<RowFieldControl
meta={{ name: "password", type: "password" }}
bind:value={row.password}
/>
<RowFieldControl <RowFieldControl
meta={{ ...tableSchema.firstName, name: "First Name" }} meta={{ ...tableSchema.firstName, name: "First Name" }}
bind:value={row.firstName} bind:value={row.firstName}
@ -107,16 +103,6 @@
getOptionLabel={role => role.name} getOptionLabel={role => role.name}
getOptionValue={role => role._id} getOptionValue={role => role._id}
/> />
<Select
label="Status"
bind:value={row.status}
options={[
{ label: "Active", value: "active" },
{ label: "Inactive", value: "inactive" },
]}
getOptionLabel={status => status.label}
getOptionValue={status => status.value}
/>
{#each customSchemaKeys as [key, meta]} {#each customSchemaKeys as [key, meta]}
{#if !meta.autocolumn} {#if !meta.autocolumn}
<RowFieldControl {meta} bind:value={row[key]} {creating} /> <RowFieldControl {meta} bind:value={row[key]} {creating} />

View File

@ -56,13 +56,28 @@ async function findRow(ctx, db, tableId, rowId) {
exports.patch = async function (ctx) { exports.patch = async function (ctx) {
const appId = ctx.appId const appId = ctx.appId
const db = new CouchDB(appId) const db = new CouchDB(appId)
let dbRow = await db.get(ctx.params.rowId) const inputs = ctx.request.body
let dbTable = await db.get(dbRow.tableId) const tableId = inputs.tableId
const patchfields = ctx.request.body const isUserTable = tableId === InternalTables.USER_METADATA
let dbRow
try {
dbRow = await db.get(ctx.params.rowId)
} catch (err) {
if (isUserTable) {
// don't include the rev, it'll be the global rev
// this time
dbRow = {
_id: inputs._id,
}
} else {
ctx.throw(400, "Row does not exist")
}
}
let dbTable = await db.get(tableId)
// need to build up full patch fields before coerce // need to build up full patch fields before coerce
for (let key of Object.keys(patchfields)) { for (let key of Object.keys(inputs)) {
if (!dbTable.schema[key]) continue if (!dbTable.schema[key]) continue
dbRow[key] = patchfields[key] dbRow[key] = inputs[key]
} }
// this returns the table and row incase they have been updated // this returns the table and row incase they have been updated
@ -90,13 +105,9 @@ exports.patch = async function (ctx) {
table, table,
}) })
// TODO remove special user case in future if (isUserTable) {
if (row.tableId === InternalTables.USER_METADATA) {
// the row has been updated, need to put it into the ctx // the row has been updated, need to put it into the ctx
ctx.request.body = { ctx.request.body = row
...row,
password: ctx.request.body.password,
}
await userController.updateMetadata(ctx) await userController.updateMetadata(ctx)
return return
} }
@ -129,13 +140,10 @@ exports.save = async function (ctx) {
// if the row obj had an _id then it will have been retrieved // if the row obj had an _id then it will have been retrieved
if (inputs._id && inputs._rev) { if (inputs._id && inputs._rev) {
const existingRow = await db.get(inputs._id)
if (existingRow) {
ctx.params.rowId = inputs._id ctx.params.rowId = inputs._id
await exports.patch(ctx) await exports.patch(ctx)
return return
} }
}
if (!inputs._rev && !inputs._id) { if (!inputs._rev && !inputs._id) {
inputs._id = generateRowID(inputs.tableId) inputs._id = generateRowID(inputs.tableId)
@ -167,13 +175,6 @@ exports.save = async function (ctx) {
table, table,
}) })
if (row.tableId === InternalTables.USER_METADATA && row._id) {
// the row has been updated, need to put it into the ctx
ctx.request.body = row
await userController.updateMetadata(ctx)
return
}
row.type = "row" row.type = "row"
const response = await db.put(row) const response = await db.put(row)
// don't worry about rev, tables handle rev/lastID updates // don't worry about rev, tables handle rev/lastID updates

View File

@ -5,7 +5,10 @@ const {
} = require("../../db/utils") } = require("../../db/utils")
const { InternalTables } = require("../../db/utils") const { InternalTables } = require("../../db/utils")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { getGlobalUsers, addAppRoleToSelf } = require("../../utilities/workerRequests") const {
getGlobalUsers,
addAppRoleToUser,
} = require("../../utilities/workerRequests")
const { getFullUser } = require("../../utilities/users") const { getFullUser } = require("../../utilities/users")
function removeGlobalProps(user) { function removeGlobalProps(user) {
@ -46,7 +49,7 @@ exports.updateSelfMetadata = async function (ctx) {
ctx.request.body._id = ctx.user._id ctx.request.body._id = ctx.user._id
if (ctx.user.builder && ctx.user.builder.global) { if (ctx.user.builder && ctx.user.builder.global) {
// specific case, update self role in global user // specific case, update self role in global user
await addAppRoleToSelf(ctx, ctx.appId, BUILTIN_ROLE_IDS.ADMIN) await addAppRoleToUser(ctx, ctx.appId, BUILTIN_ROLE_IDS.ADMIN)
} }
// make sure no stale rev // make sure no stale rev
delete ctx.request.body._rev delete ctx.request.body._rev
@ -57,10 +60,12 @@ exports.updateMetadata = async function (ctx) {
const appId = ctx.appId const appId = ctx.appId
const db = new CouchDB(appId) const db = new CouchDB(appId)
const user = removeGlobalProps(ctx.request.body) const user = removeGlobalProps(ctx.request.body)
if (user.roleId) {
await addAppRoleToUser(ctx, appId, user.roleId, user._id)
}
const metadata = { const metadata = {
tableId: InternalTables.USER_METADATA, tableId: InternalTables.USER_METADATA,
_id: user._id, ...user,
_rev: user._rev,
} }
ctx.body = await db.put(metadata) ctx.body = await db.put(metadata)
} }

View File

@ -3,6 +3,7 @@ const env = require("../environment")
const { checkSlashesInUrl } = require("./index") const { checkSlashesInUrl } = require("./index")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { getDeployedAppID } = require("@budibase/auth/db") const { getDeployedAppID } = require("@budibase/auth/db")
const { getGlobalIDFromUserMetadataID } = require("../db/utils")
function getAppRole(appId, user) { function getAppRole(appId, user) {
if (!user.roles) { if (!user.roles) {
@ -131,21 +132,33 @@ exports.getGlobalSelf = async ctx => {
return json return json
} }
exports.addAppRoleToSelf = async (ctx, appId, roleId) => { exports.addAppRoleToUser = async (ctx, appId, roleId, userId = null) => {
const self = await exports.getGlobalSelf(ctx) appId = getDeployedAppID(appId)
const endpoint = `/api/admin/users/self` let user,
const reqCfg = { endpoint,
method: "POST", body = {}
body: { if (!userId) {
roles: { user = await exports.getGlobalSelf(ctx)
...self.roles, endpoint = `/api/admin/users/self`
[appId]: roleId, } else {
userId = getGlobalIDFromUserMetadataID(userId)
user = await exports.getGlobalUsers(ctx, appId, userId)
body._id = userId
endpoint = `/api/admin/users`
} }
body = {
...body,
roles: {
...user.roles,
[appId]: roleId,
}, },
} }
const response = await fetch( const response = await fetch(
checkSlashesInUrl(env.WORKER_URL + endpoint), checkSlashesInUrl(env.WORKER_URL + endpoint),
request(ctx, reqCfg) request(ctx, {
method: "POST",
body,
})
) )
const json = await response.json() const json = await response.json()
if (json.status !== 200 && response.status !== 200) { if (json.status !== 200 && response.status !== 200) {

View File

@ -16,10 +16,15 @@ exports.save = async ctx => {
const { email, password, _id } = ctx.request.body const { email, password, _id } = ctx.request.body
// make sure another user isn't using the same email // make sure another user isn't using the same email
const dbUser = await getGlobalUserByEmail(email) let dbUser
if (email) {
dbUser = await getGlobalUserByEmail(email)
if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) { if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) {
ctx.throw(400, "Email address already in use.") ctx.throw(400, "Email address already in use.")
} }
} else {
dbUser = await db.get(_id)
}
// get the password, make sure one is defined // get the password, make sure one is defined
let hashedPassword let hashedPassword