From 19faffff83b983c662a71580abcdd401539140b9 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 2 Dec 2020 17:08:25 +0000 Subject: [PATCH 01/46] Changing the role system to have permissions integrated rather than the permissions being per user. --- packages/server/src/api/controllers/auth.js | 1 - .../server/src/api/controllers/permission.js | 6 ++ packages/server/src/api/controllers/role.js | 10 ++- packages/server/src/api/controllers/user.js | 22 +---- packages/server/src/api/routes/index.js | 2 + packages/server/src/api/routes/permission.js | 10 +++ packages/server/src/api/routes/role.js | 18 +++- .../src/api/routes/tests/couchTestUtils.js | 85 ++++++------------ .../server/src/api/routes/tests/role.spec.js | 16 ++-- .../server/src/api/routes/tests/user.spec.js | 36 ++++---- packages/server/src/middleware/authorized.js | 7 +- .../src/utilities/builder/setBuilderToken.js | 2 - .../src/utilities/security/permissions.js | 18 ++-- .../server/src/utilities/security/roles.js | 90 +++++++++++++------ 14 files changed, 182 insertions(+), 141 deletions(-) create mode 100644 packages/server/src/api/controllers/permission.js create mode 100644 packages/server/src/api/routes/permission.js diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index de872ebfc4..43cc4317f4 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -34,7 +34,6 @@ exports.authenticate = async ctx => { userId: dbUser._id, roleId: dbUser.roleId, version: app.version, - permissions: dbUser.permissions || [], } // if in cloud add the user api key if (env.CLOUD) { diff --git a/packages/server/src/api/controllers/permission.js b/packages/server/src/api/controllers/permission.js new file mode 100644 index 0000000000..a2715a5363 --- /dev/null +++ b/packages/server/src/api/controllers/permission.js @@ -0,0 +1,6 @@ +const { BUILTIN_PERMISSIONS } = require("../../utilities/security/permissions") + +exports.fetch = async function(ctx) { + // TODO: need to build out custom permissions + ctx.body = Object.values(BUILTIN_PERMISSIONS) +} diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js index ff34a4e954..7889233026 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.js @@ -25,9 +25,13 @@ exports.find = async function(ctx) { exports.save = async function(ctx) { const db = new CouchDB(ctx.user.appId) - - let id = ctx.request.body._id || generateRoleID() - const role = new Role(id, ctx.request.body.name, ctx.request.body.inherits) + let { _id, name, inherits, permissionId } = ctx.request.body + if (!_id) { + _id = generateRoleID() + } + const role = new Role(_id, name) + .addPermission(permissionId) + .addInheritance(inherits) if (ctx.request.body._rev) { role._rev = ctx.request.body._rev } diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index f20ccd3d7e..bea62ecbd5 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -1,10 +1,7 @@ const CouchDB = require("../../db") const bcrypt = require("../../utilities/bcrypt") const { generateUserID, getUserParams, ViewNames } = require("../../db/utils") -const { BUILTIN_ROLE_ID_ARRAY } = require("../../utilities/security/roles") -const { - BUILTIN_PERMISSION_NAMES, -} = require("../../utilities/security/permissions") +const { getRole } = require("../../utilities/security/roles") exports.fetch = async function(ctx) { const database = new CouchDB(ctx.user.appId) @@ -18,13 +15,13 @@ exports.fetch = async function(ctx) { exports.create = async function(ctx) { const db = new CouchDB(ctx.user.appId) - const { username, password, name, roleId, permissions } = ctx.request.body + const { username, password, name, roleId } = ctx.request.body if (!username || !password) { ctx.throw(400, "Username and Password Required.") } - const role = await checkRole(db, roleId) + const role = await getRole(ctx.user.appId, roleId) if (!role) ctx.throw(400, "Invalid Role") @@ -35,7 +32,6 @@ exports.create = async function(ctx) { name: name || username, type: "user", roleId, - permissions: permissions || [BUILTIN_PERMISSION_NAMES.POWER], tableId: ViewNames.USERS, } @@ -88,15 +84,3 @@ exports.find = async function(ctx) { _rev: user._rev, } } - -const checkRole = async (db, roleId) => { - if (!roleId) return - if (BUILTIN_ROLE_ID_ARRAY.indexOf(roleId) !== -1) { - return { - _id: roleId, - name: roleId, - permissions: [], - } - } - return await db.get(roleId) -} diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js index 81c26b366d..c663accea0 100644 --- a/packages/server/src/api/routes/index.js +++ b/packages/server/src/api/routes/index.js @@ -16,6 +16,7 @@ const apiKeysRoutes = require("./apikeys") const templatesRoutes = require("./templates") const analyticsRoutes = require("./analytics") const routingRoutes = require("./routing") +const permissionRoutes = require("./permission") exports.mainRoutes = [ deployRoutes, @@ -32,6 +33,7 @@ exports.mainRoutes = [ analyticsRoutes, webhookRoutes, routingRoutes, + permissionRoutes, // these need to be handled last as they still use /api/:tableId // this could be breaking as koa may recognise other routes as this tableRoutes, diff --git a/packages/server/src/api/routes/permission.js b/packages/server/src/api/routes/permission.js new file mode 100644 index 0000000000..9dcec253b3 --- /dev/null +++ b/packages/server/src/api/routes/permission.js @@ -0,0 +1,10 @@ +const Router = require("@koa/router") +const controller = require("../controllers/permission") +const authorized = require("../../middleware/authorized") +const { BUILDER } = require("../../utilities/security/permissions") + +const router = Router() + +router.get("/api/permissions", authorized(BUILDER), controller.fetch) + +module.exports = router diff --git a/packages/server/src/api/routes/role.js b/packages/server/src/api/routes/role.js index e0a157d8b8..98ac333e17 100644 --- a/packages/server/src/api/routes/role.js +++ b/packages/server/src/api/routes/role.js @@ -2,11 +2,27 @@ const Router = require("@koa/router") const controller = require("../controllers/role") const authorized = require("../../middleware/authorized") const { BUILDER } = require("../../utilities/security/permissions") +const Joi = require("joi") +const joiValidator = require("../../middleware/joi-validator") +const { + BUILTIN_PERMISSION_IDS, +} = require("../../utilities/security/permissions") const router = Router() +function generateValidator() { + // prettier-ignore + return joiValidator.body(Joi.object({ + _id: Joi.string().optional(), + _rev: Joi.string().optional(), + name: Joi.string().required(), + permissionId: Joi.string().valid(...Object.values(BUILTIN_PERMISSION_IDS)).required(), + inherits: Joi.string().optional(), + }).unknown(true)) +} + router - .post("/api/roles", authorized(BUILDER), controller.save) + .post("/api/roles", authorized(BUILDER), generateValidator(), controller.save) .get("/api/roles", authorized(BUILDER), controller.fetch) .get("/api/roles/:roleId", authorized(BUILDER), controller.find) .delete("/api/roles/:roleId/:rev", authorized(BUILDER), controller.destroy) diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js index c7846f85b8..e3a366ac78 100644 --- a/packages/server/src/api/routes/tests/couchTestUtils.js +++ b/packages/server/src/api/routes/tests/couchTestUtils.js @@ -1,9 +1,6 @@ const CouchDB = require("../../../db") const supertest = require("supertest") const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles") -const { - BUILTIN_PERMISSION_NAMES, -} = require("../../../utilities/security/permissions") const packageJson = require("../../../../package") const jwt = require("jsonwebtoken") const env = require("../../../environment") @@ -131,49 +128,7 @@ exports.createUser = async ( return res.body } -const createUserWithOnePermission = async (request, appId, permName) => { - let permissions = [permName] - - return await createUserWithPermissions( - request, - appId, - permissions, - "onePermOnlyUser" - ) -} - -const createUserWithAdminPermissions = async (request, appId) => { - let permissions = [BUILTIN_PERMISSION_NAMES.ADMIN] - - return await createUserWithPermissions( - request, - appId, - permissions, - "adminUser" - ) -} - -const createUserWithAllPermissionExceptOne = async ( - request, - appId, - permName -) => { - let permissions = [permName] - - return await createUserWithPermissions( - request, - appId, - permissions, - "allPermsExceptOneUser" - ) -} - -const createUserWithPermissions = async ( - request, - appId, - permissions, - username -) => { +const createUserWithRole = async (request, appId, roleId, username) => { const password = `password_${username}` await request .post(`/api/users`) @@ -182,8 +137,7 @@ const createUserWithPermissions = async ( name: username, username, password, - roleId: BUILTIN_ROLE_IDS.POWER, - permissions, + roleId, }) const anonUser = { @@ -216,23 +170,29 @@ exports.testPermissionsForEndpoint = async ({ url, body, appId, - permName1, - permName2, + passRole, + failRole, }) => { - const headers = await createUserWithOnePermission(request, appId, permName1) - - await createRequest(request, method, url, body) - .set(headers) - .expect(200) - - const noPermsHeaders = await createUserWithAllPermissionExceptOne( + const passHeader = await createUserWithRole( request, appId, - permName2 + passRole, + "passUser" ) await createRequest(request, method, url, body) - .set(noPermsHeaders) + .set(passHeader) + .expect(200) + + const failHeader = await createUserWithRole( + request, + appId, + failRole, + "failUser" + ) + + await createRequest(request, method, url, body) + .set(failHeader) .expect(403) } @@ -243,7 +203,12 @@ exports.builderEndpointShouldBlockNormalUsers = async ({ body, appId, }) => { - const headers = await createUserWithAdminPermissions(request, appId) + const headers = await createUserWithRole( + request, + appId, + BUILTIN_ROLE_IDS.BASIC, + "basicUser" + ) await createRequest(request, method, url, body) .set(headers) diff --git a/packages/server/src/api/routes/tests/role.spec.js b/packages/server/src/api/routes/tests/role.spec.js index ca451c85c2..5503b7bf5d 100644 --- a/packages/server/src/api/routes/tests/role.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -8,8 +8,9 @@ const { const { BUILTIN_ROLE_IDS, } = require("../../../utilities/security/roles") +const { BUILTIN_PERMISSION_IDS } = require("../../../utilities/security/permissions") -const roleBody = { name: "user", inherits: BUILTIN_ROLE_IDS.BASIC } +const roleBody = { name: "NewRole", inherits: BUILTIN_ROLE_IDS.BASIC, permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY } describe("/roles", () => { let server @@ -43,7 +44,7 @@ describe("/roles", () => { .expect('Content-Type', /json/) .expect(200) - expect(res.res.statusMessage).toEqual("Role 'user' created successfully.") + expect(res.res.statusMessage).toEqual("Role 'NewRole' created successfully.") expect(res.body._id).toBeDefined() expect(res.body._rev).toBeDefined() }) @@ -71,16 +72,19 @@ describe("/roles", () => { expect(res.body.length).toBe(3) const adminRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.ADMIN) - expect(adminRole.inherits).toEqual(BUILTIN_ROLE_IDS.POWER) expect(adminRole).toBeDefined() + expect(adminRole.inherits).toEqual(BUILTIN_ROLE_IDS.POWER) + expect(adminRole.permissionId).toEqual(BUILTIN_PERMISSION_IDS.ADMIN) const powerUserRole = res.body.find(r => r._id === BUILTIN_ROLE_IDS.POWER) - expect(powerUserRole.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) expect(powerUserRole).toBeDefined() + expect(powerUserRole.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) + expect(powerUserRole.permissionId).toEqual(BUILTIN_PERMISSION_IDS.POWER) const customRoleFetched = res.body.find(r => r._id === customRole._id) - expect(customRoleFetched.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) expect(customRoleFetched).toBeDefined() + expect(customRoleFetched.inherits).toEqual(BUILTIN_ROLE_IDS.BASIC) + expect(customRoleFetched.permissionId).toEqual(BUILTIN_PERMISSION_IDS.READ_ONLY) }) }); @@ -89,7 +93,7 @@ describe("/roles", () => { it("should delete custom roles", async () => { const createRes = await request .post(`/api/roles`) - .send({ name: "user" }) + .send({ name: "user", permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY }) .set(defaultHeaders(appId)) .expect('Content-Type', /json/) .expect(200) diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index 96e5923d1d..deac296567 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -5,12 +5,16 @@ const { createUser, testPermissionsForEndpoint, } = require("./couchTestUtils") -const { - BUILTIN_PERMISSION_NAMES, -} = require("../../../utilities/security/permissions") const { BUILTIN_ROLE_IDS, } = require("../../../utilities/security/roles") +const { cloneDeep } = require("lodash/fp") + +const baseBody = { + name: "brandNewUser", + password: "yeeooo", + roleId: BUILTIN_ROLE_IDS.POWER +} describe("/users", () => { let request @@ -20,12 +24,12 @@ describe("/users", () => { beforeAll(async () => { ({ request, server } = await supertest(server)) - }); + }) beforeEach(async () => { app = await createApplication(request) appId = app.instance._id - }); + }) afterAll(() => { server.close() @@ -54,38 +58,40 @@ describe("/users", () => { method: "GET", url: `/api/users`, appId: appId, - permName1: BUILTIN_PERMISSION_NAMES.POWER, - permName2: BUILTIN_PERMISSION_NAMES.WRITE, + passRole: BUILTIN_ROLE_IDS.ADMIN, + failRole: BUILTIN_ROLE_IDS.PUBLIC, }) }) }) describe("create", () => { - it("returns a success message when a user is successfully created", async () => { + const body = cloneDeep(baseBody) + body.username = "bill" const res = await request .post(`/api/users`) .set(defaultHeaders(appId)) - .send({ name: "Bill", username: "bill", password: "bills_password", roleId: BUILTIN_ROLE_IDS.POWER }) + .send(body) .expect(200) .expect('Content-Type', /json/) - expect(res.res.statusMessage).toEqual("User created successfully."); + expect(res.res.statusMessage).toEqual("User created successfully.") expect(res.body._id).toBeUndefined() }) it("should apply authorization to endpoint", async () => { + const body = cloneDeep(baseBody) + body.username = "brandNewUser" await testPermissionsForEndpoint({ request, method: "POST", - body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", roleId: BUILTIN_ROLE_IDS.POWER }, + body, url: `/api/users`, appId: appId, - permName1: BUILTIN_PERMISSION_NAMES.ADMIN, - permName2: BUILTIN_PERMISSION_NAMES.POWER, + passRole: BUILTIN_ROLE_IDS.ADMIN, + failRole: BUILTIN_ROLE_IDS.PUBLIC, }) }) - - }); + }) }) diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index f18cf3b5c8..e959e05f9d 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -1,4 +1,7 @@ -const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") +const { + BUILTIN_ROLE_IDS, + getUserPermissionIds, +} = require("../utilities/security/roles") const { PermissionTypes, doesHavePermission, @@ -48,7 +51,7 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => { } const role = ctx.user.role - const permissions = ctx.user.permissions + const permissions = await getUserPermissionIds(ctx.appId, role._id) if (ADMIN_ROLES.indexOf(role._id) !== -1) { return next() } diff --git a/packages/server/src/utilities/builder/setBuilderToken.js b/packages/server/src/utilities/builder/setBuilderToken.js index ed374a1d8e..93863cee63 100644 --- a/packages/server/src/utilities/builder/setBuilderToken.js +++ b/packages/server/src/utilities/builder/setBuilderToken.js @@ -1,5 +1,4 @@ const { BUILTIN_ROLE_IDS } = require("../security/roles") -const { BUILTIN_PERMISSION_NAMES } = require("../security/permissions") const env = require("../../environment") const CouchDB = require("../../db") const jwt = require("jsonwebtoken") @@ -11,7 +10,6 @@ module.exports = async (ctx, appId, version) => { const builderUser = { userId: "BUILDER", roleId: BUILTIN_ROLE_IDS.BUILDER, - permissions: [BUILTIN_PERMISSION_NAMES.ADMIN], version, } if (env.BUDIBASE_API_KEY) { diff --git a/packages/server/src/utilities/security/permissions.js b/packages/server/src/utilities/security/permissions.js index d19f31e393..0836b29412 100644 --- a/packages/server/src/utilities/security/permissions.js +++ b/packages/server/src/utilities/security/permissions.js @@ -45,7 +45,7 @@ function getAllowedLevels(userPermLevel) { } } -exports.BUILTIN_PERMISSION_NAMES = { +exports.BUILTIN_PERMISSION_IDS = { READ_ONLY: "read_only", WRITE: "write", ADMIN: "admin", @@ -54,21 +54,24 @@ exports.BUILTIN_PERMISSION_NAMES = { exports.BUILTIN_PERMISSIONS = { READ_ONLY: { - name: exports.BUILTIN_PERMISSION_NAMES.READ_ONLY, + _id: exports.BUILTIN_PERMISSION_IDS.READ_ONLY, + name: "Read only", permissions: [ new Permission(PermissionTypes.TABLE, PermissionLevels.READ), new Permission(PermissionTypes.VIEW, PermissionLevels.READ), ], }, WRITE: { - name: exports.BUILTIN_PERMISSION_NAMES.WRITE, + _id: exports.BUILTIN_PERMISSION_IDS.WRITE, + name: "Read/Write", permissions: [ new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE), new Permission(PermissionTypes.VIEW, PermissionLevels.READ), ], }, POWER: { - name: exports.BUILTIN_PERMISSION_NAMES.POWER, + _id: exports.BUILTIN_PERMISSION_IDS.POWER, + name: "Power", permissions: [ new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE), new Permission(PermissionTypes.USER, PermissionLevels.READ), @@ -78,7 +81,8 @@ exports.BUILTIN_PERMISSIONS = { ], }, ADMIN: { - name: exports.BUILTIN_PERMISSION_NAMES.ADMIN, + _id: exports.BUILTIN_PERMISSION_IDS.ADMIN, + name: "Admin", permissions: [ new Permission(PermissionTypes.TABLE, PermissionLevels.ADMIN), new Permission(PermissionTypes.USER, PermissionLevels.ADMIN), @@ -89,11 +93,11 @@ exports.BUILTIN_PERMISSIONS = { }, } -exports.doesHavePermission = (permType, permLevel, userPermissionNames) => { +exports.doesHavePermission = (permType, permLevel, permissionIds) => { const builtins = Object.values(exports.BUILTIN_PERMISSIONS) let permissions = flatten( builtins - .filter(builtin => userPermissionNames.indexOf(builtin.name) !== -1) + .filter(builtin => permissionIds.indexOf(builtin._id) !== -1) .map(builtin => builtin.permissions) ) for (let permission of permissions) { diff --git a/packages/server/src/utilities/security/roles.js b/packages/server/src/utilities/security/roles.js index 09ca2c23fd..e971217d17 100644 --- a/packages/server/src/utilities/security/roles.js +++ b/packages/server/src/utilities/security/roles.js @@ -1,5 +1,6 @@ const CouchDB = require("../../db") const { cloneDeep } = require("lodash/fp") +const { BUILTIN_PERMISSION_IDS } = require("./permissions") const BUILTIN_IDS = { ADMIN: "ADMIN", @@ -9,20 +10,37 @@ const BUILTIN_IDS = { BUILDER: "BUILDER", } -function Role(id, name, inherits) { +function Role(id, name) { this._id = id this.name = name - if (inherits) { - this.inherits = inherits - } +} + +Role.prototype.addPermission = function(permissionId) { + this.permissionId = permissionId + return this +} + +Role.prototype.addInheritance = function(inherits) { + this.inherits = inherits + return this } exports.BUILTIN_ROLES = { - ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin", BUILTIN_IDS.POWER), - POWER: new Role(BUILTIN_IDS.POWER, "Power", BUILTIN_IDS.BASIC), - BASIC: new Role(BUILTIN_IDS.BASIC, "Basic", BUILTIN_IDS.PUBLIC), - PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public"), - BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder"), + ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin") + .addPermission(BUILTIN_PERMISSION_IDS.ADMIN) + .addInheritance(BUILTIN_IDS.POWER), + POWER: new Role(BUILTIN_IDS.POWER, "Power") + .addPermission(BUILTIN_PERMISSION_IDS.POWER) + .addInheritance(BUILTIN_IDS.BASIC), + BASIC: new Role(BUILTIN_IDS.BASIC, "Basic") + .addPermission(BUILTIN_PERMISSION_IDS.WRITE) + .addInheritance(BUILTIN_IDS.PUBLIC), + PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public").addPermission( + BUILTIN_PERMISSION_IDS.READ_ONLY + ), + BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder").addPermission( + BUILTIN_PERMISSION_IDS.ADMIN + ), } exports.BUILTIN_ROLE_ID_ARRAY = Object.values(exports.BUILTIN_ROLES).map( @@ -60,6 +78,29 @@ exports.getRole = async (appId, roleId) => { return role } +/** + * Simple function to get all the roles based on the top level user role ID. + */ +async function getAllUserRoles(appId, userRoleId) { + if (!userRoleId) { + return [BUILTIN_IDS.PUBLIC] + } + let currentRole = await exports.getRole(appId, userRoleId) + let roles = currentRole ? [currentRole] : [] + let roleIds = [userRoleId] + // get all the inherited roles + while ( + currentRole && + currentRole.inherits && + roleIds.indexOf(currentRole.inherits) === -1 + ) { + roleIds.push(currentRole.inherits) + currentRole = await exports.getRole(appId, currentRole.inherits) + roles.push(currentRole) + } + return roles +} + /** * Returns an ordered array of the user's inherited role IDs, this can be used * to determine if a user can access something that requires a specific role. @@ -70,22 +111,21 @@ exports.getRole = async (appId, roleId) => { */ exports.getUserRoleHierarchy = async (appId, userRoleId) => { // special case, if they don't have a role then they are a public user - if (!userRoleId) { - return [BUILTIN_IDS.PUBLIC] - } - let roleIds = [userRoleId] - let userRole = await exports.getRole(appId, userRoleId) - // check if inherited makes it possible - while ( - userRole && - userRole.inherits && - roleIds.indexOf(userRole.inherits) === -1 - ) { - roleIds.push(userRole.inherits) - // go to get the inherited incase it inherits anything - userRole = await exports.getRole(appId, userRole.inherits) - } - return roleIds + return (await getAllUserRoles(appId, userRoleId)).map(role => role._id) +} + +/** + * Get all of the user permissions which could be found across the role hierarchy + * @param appId The ID of the application from which roles should be obtained. + * @param userRoleId The user's role ID, this can be found in their access token. + * @returns {Promise} A list of permission IDs these should all be unique. + */ +exports.getUserPermissionIds = async (appId, userRoleId) => { + return [ + ...new Set( + (await getAllUserRoles(appId, userRoleId)).map(role => role.permissionId) + ), + ] } class AccessController { From 623afd6459aabf087bf993d6d62e8ceacbb67aa5 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 3 Dec 2020 12:28:30 +0000 Subject: [PATCH 02/46] Making public and basic roles accessible. --- packages/server/src/api/controllers/role.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js index 7889233026..3ebda124b0 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.js @@ -15,7 +15,13 @@ exports.fetch = async function(ctx) { ) const customRoles = body.rows.map(row => row.doc) - const staticRoles = [BUILTIN_ROLES.ADMIN, BUILTIN_ROLES.POWER] + // exclude internal roles like builder + const staticRoles = [ + BUILTIN_ROLES.ADMIN, + BUILTIN_ROLES.POWER, + BUILTIN_ROLES.BASIC, + BUILTIN_ROLES.PUBLIC, + ] ctx.body = [...staticRoles, ...customRoles] } From 1bc339679a737c753729a7fb71f65f6ff620b0d7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 08:27:42 +0000 Subject: [PATCH 03/46] Add modal for editing user roles --- .../backend/DataTable/DataTable.svelte | 5 + .../DataTable/buttons/EditRolesButton.svelte | 23 +++ .../backend/DataTable/modals/EditRoles.svelte | 136 ++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 packages/builder/src/components/backend/DataTable/buttons/EditRolesButton.svelte create mode 100644 packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 063ca34633..2869b82e70 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -4,8 +4,10 @@ import CreateColumnButton from "./buttons/CreateColumnButton.svelte" import CreateViewButton from "./buttons/CreateViewButton.svelte" import ExportButton from "./buttons/ExportButton.svelte" + import EditRolesButton from "./buttons/EditRolesButton.svelte" import * as api from "./api" import Table from "./Table.svelte" + import { TableNames } from "constants" let data = [] let loading = false @@ -36,4 +38,7 @@ {/if} + {#if $backendUiStore.selectedTable?._id === TableNames.USERS} + + {/if} diff --git a/packages/builder/src/components/backend/DataTable/buttons/EditRolesButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/EditRolesButton.svelte new file mode 100644 index 0000000000..024905fddc --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/buttons/EditRolesButton.svelte @@ -0,0 +1,23 @@ + + +
+ +
+ + + + + diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte new file mode 100644 index 0000000000..627112d87c --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -0,0 +1,136 @@ + + + + {#if errors.length} + + {/if} + + {#if selectedRole} + + + + {/if} +
+ {#if !isCreating} + + {/if} +
+
From 922256790ce10c1f5ac173c1b932fd4188926455 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 08:27:59 +0000 Subject: [PATCH 04/46] Update errors box to work better in dark theme --- packages/builder/src/components/common/ErrorsBox.svelte | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/common/ErrorsBox.svelte b/packages/builder/src/components/common/ErrorsBox.svelte index 9e29784c66..f05e03f596 100644 --- a/packages/builder/src/components/common/ErrorsBox.svelte +++ b/packages/builder/src/components/common/ErrorsBox.svelte @@ -5,9 +5,9 @@ {#if hasErrors} -
+
{#each errors as error} -
{error.dataPath} {error.message}
+
{error.dataPath || ''} {error.message}
{/each}
{/if} @@ -17,6 +17,8 @@ border-radius: var(--border-radius-m); margin: 0; padding: var(--spacing-m); + background-color: rgba(241, 165, 165, 0.2); + color: var(--red); } .error { From a8a8b60a55120eac8f6eb367e6ea5cc584eb1b86 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 08:28:35 +0000 Subject: [PATCH 05/46] Fix variable name in returned message when deleting a role --- packages/server/src/api/controllers/role.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js index 3ebda124b0..cbe55bc5d6 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.js @@ -50,6 +50,6 @@ exports.save = async function(ctx) { exports.destroy = async function(ctx) { const db = new CouchDB(ctx.user.appId) await db.remove(ctx.params.roleId, ctx.params.rev) - ctx.message = `Role ${ctx.params.id} deleted successfully` + ctx.message = `Role ${ctx.params.roleId} deleted successfully` ctx.status = 200 } From 20c0cd810889b3e6f30911acf37b03033b50907f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 08:28:44 +0000 Subject: [PATCH 06/46] Update lock file --- yarn.lock | 325 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 229 insertions(+), 96 deletions(-) diff --git a/yarn.lock b/yarn.lock index e9f9998edc..f4d4d2114f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,40 +2,64 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" dependencies: "@babel/highlight" "^7.8.3" -"@babel/generator@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e" +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: - "@babel/types" "^7.8.3" + "@babel/highlight" "^7.10.4" + +"@babel/generator@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz#a2c50de5c8b6d708ab95be5e6053936c1884a4de" + integrity sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A== + dependencies: + "@babel/types" "^7.12.5" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" -"@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" "@babel/highlight@^7.8.3": version "7.8.3" @@ -45,38 +69,42 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" +"@babel/parser@^7.12.7", "@babel/parser@^7.7.0": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.7.tgz#fee7b39fe809d0e73e5b25eecaf5780ef3d73056" + integrity sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg== -"@babel/template@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" +"@babel/template@^7.10.4": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" + integrity sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.12.7" + "@babel/types" "^7.12.7" -"@babel/traverse@^7.0.0": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" +"@babel/traverse@^7.7.0": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.9.tgz#fad26c972eabbc11350e0b695978de6cc8e8596f" + integrity sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.4" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.8.4" - "@babel/types" "^7.8.3" + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.12.7" + "@babel/types" "^7.12.7" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.13" + lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" +"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.12.5", "@babel/types@^7.12.7", "@babel/types@^7.7.0": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.7.tgz#6039ff1e242640a29452c9ae572162ec9a8f5d13" + integrity sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ== dependencies: - esutils "^2.0.2" - lodash "^4.17.13" + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" to-fast-properties "^2.0.0" "@fortawesome/fontawesome-common-types@^0.1.7": @@ -825,13 +853,15 @@ abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" -acorn-jsx@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" +acorn-jsx@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== -acorn@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== agent-base@4, agent-base@^4.3.0: version "4.3.0" @@ -865,10 +895,11 @@ ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" ansi-escapes@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== dependencies: - type-fest "^0.8.1" + type-fest "^0.11.0" ansi-regex@^2.0.0: version "2.1.1" @@ -892,6 +923,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -998,13 +1036,14 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" babel-eslint@^10.0.3: - version "10.0.3" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" eslint-visitor-keys "^1.0.0" resolve "^1.12.0" @@ -1202,6 +1241,14 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -1232,6 +1279,7 @@ cli-cursor@^2.1.0: cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" @@ -1239,6 +1287,11 @@ cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" @@ -1275,10 +1328,22 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + columnify@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" @@ -1447,6 +1512,7 @@ cosmiconfig@^5.1.0: cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -1530,6 +1596,7 @@ dedent@^0.7.0: deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= defaults@^1.0.3: version "1.0.3" @@ -1637,6 +1704,7 @@ emoji-regex@^7.0.1: emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== encoding@^0.1.11: version "0.1.12" @@ -1699,14 +1767,16 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" eslint-plugin-cypress@^2.11.1: - version "2.11.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.1.tgz#a945e2774b88211e2c706a059d431e262b5c2862" + version "2.11.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.2.tgz#a8f3fe7ec840f55e4cea37671f93293e6c3e76a0" + integrity sha512-1SergF1sGbVhsf7MYfOLiBhdOg6wqyeV9pXUAIDIffYTGMN3dTBQS9nFAzhLsHhO+Bn0GaVM1Ecm71XUidQ7VA== dependencies: globals "^11.12.0" eslint-plugin-prettier@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.2.0.tgz#af391b2226fa0e15c96f36c733f6e9035dbd952c" + integrity sha512-kOUSJnFjAUFKwVxuzy6sA5yyMx6+o9ino4gCdShzBNx4eyFRudWRYKCFolKjoM40PEiuU6Cn7wBLfq3WsGg7qg== dependencies: prettier-linter-helpers "^1.0.0" @@ -1715,15 +1785,17 @@ eslint-plugin-svelte3@^2.7.3: resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-2.7.3.tgz#e793b646b848e717674fe668c21b909cfa025eb3" eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: - esrecurse "^4.1.0" + esrecurse "^4.3.0" estraverse "^4.1.1" eslint-utils@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: eslint-visitor-keys "^1.1.0" @@ -1734,6 +1806,7 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: eslint@^6.8.0: version "6.8.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -1774,11 +1847,12 @@ eslint@^6.8.0: v8-compile-cache "^2.0.3" espree@^6.1.2: - version "6.1.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== dependencies: - acorn "^7.1.0" - acorn-jsx "^5.1.0" + acorn "^7.1.1" + acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" esprima@^4.0.0: @@ -1786,21 +1860,28 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== dependencies: - estraverse "^4.0.0" + estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: - estraverse "^4.1.0" + estraverse "^5.2.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" @@ -1913,6 +1994,7 @@ fast-json-stable-stringify@^2.0.0: fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.1" @@ -1925,8 +2007,9 @@ figures@^2.0.0: escape-string-regexp "^1.0.5" figures@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" @@ -2235,6 +2318,11 @@ has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -2422,21 +2510,22 @@ inquirer@^6.2.0: through "^2.3.6" inquirer@^7.0.0: - version "7.0.4" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^2.4.2" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-width "^2.0.0" + cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" mute-stream "0.0.8" - run-async "^2.2.0" - rxjs "^6.5.3" + run-async "^2.4.0" + rxjs "^6.6.0" string-width "^4.1.0" - strip-ansi "^5.1.0" + strip-ansi "^6.0.0" through "^2.3.6" invert-kv@^2.0.0: @@ -2546,6 +2635,7 @@ is-fullwidth-code-point@^2.0.0: is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^3.1.0: version "3.1.0" @@ -2776,6 +2866,7 @@ lerna@3.14.1: levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" @@ -2877,10 +2968,15 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.1: +lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -3063,6 +3159,7 @@ mimic-fn@^1.0.0: mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimatch@^3.0.0, minimatch@^3.0.4: version "3.0.4" @@ -3169,6 +3266,7 @@ mute-stream@0.0.7: mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nanomatch@^1.2.9: version "1.2.13" @@ -3404,8 +3502,9 @@ onetime@^2.0.0: mimic-fn "^1.0.0" onetime@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" @@ -3419,6 +3518,7 @@ optimist@^0.6.1: optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" fast-levenshtein "~2.0.6" @@ -3696,6 +3796,7 @@ posix-character-classes@^0.1.0: prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prettier-linter-helpers@^1.0.0: version "1.0.0" @@ -3704,13 +3805,14 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier-plugin-svelte@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-1.4.0.tgz#bb992759fb77ec2c3545d454a7c60f7a258cb745" - integrity sha512-KXO2He7Kql0Lz4DdlzVli1j2JTDUR9jPV/DqyfnJmY1pCeSV1qZkxgdsyYma35W6OLrCAr/G6yKdmzo+75u2Ng== + version "1.4.1" + resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-1.4.1.tgz#2f0f7a149190f476dc9b4ba9da8d482bd196f1e2" + integrity sha512-6y0m37Xw01GRf/WIHau+Kp3uXj2JB1agtEmNVKb9opMy34A6OMOYhfneVpNIlrghQSw/jIV+t3e5Ngt4up2CMA== prettier@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== process-nextick-args@~2.0.0: version "2.0.1" @@ -3907,6 +4009,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== repeat-element@^1.1.2: version "1.1.3" @@ -3995,6 +4098,7 @@ restore-cursor@^2.0.0: restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" signal-exit "^3.0.2" @@ -4045,18 +4149,30 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" dependencies: aproba "^1.1.1" -rxjs@^6.4.0, rxjs@^6.5.3: +rxjs@^6.4.0: version "6.5.4" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" dependencies: tslib "^1.9.0" +rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" @@ -4082,6 +4198,7 @@ safe-regex@^1.1.0: semver@^6.0.0, semver@^6.1.2: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@~5.3.0: version "5.3.0" @@ -4320,6 +4437,7 @@ string-width@^3.0.0: string-width@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" @@ -4400,8 +4518,9 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strong-log-transformer@^2.0.0: version "2.1.0" @@ -4417,6 +4536,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + svelte@^3.30.0: version "3.30.0" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.30.0.tgz#cbde341e96bf34f4ac73c8f14f8a014e03bfb7d6" @@ -4563,9 +4689,15 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -4723,6 +4855,7 @@ windows-release@^3.1.0: word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@~0.0.2: version "0.0.3" From 1c372991070a03fee3c4ff2f29d3a3edbab54d75 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 4 Dec 2020 14:01:02 +0000 Subject: [PATCH 07/46] Fixing a caching bug found by cheeks. --- packages/server/src/utilities/security/roles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/utilities/security/roles.js b/packages/server/src/utilities/security/roles.js index e971217d17..b503446823 100644 --- a/packages/server/src/utilities/security/roles.js +++ b/packages/server/src/utilities/security/roles.js @@ -148,7 +148,7 @@ class AccessController { let roleIds = this.userHierarchies[userRoleId] if (!roleIds) { roleIds = await exports.getUserRoleHierarchy(this.appId, userRoleId) - this.userHierarchies[userRoleId] = userRoleId + this.userHierarchies[userRoleId] = roleIds } return roleIds.indexOf(tryingRoleId) !== -1 From 3339f364b63b2328a1b038f037ff2a9e3044d826 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 7 Dec 2020 15:21:06 +0000 Subject: [PATCH 08/46] Updating role constraints and making sure roles can't be deleted if they are in use. --- packages/server/src/api/controllers/role.js | 57 ++++++++++++++++++++- packages/server/src/api/controllers/user.js | 2 +- packages/server/src/db/utils.js | 6 +-- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js index cbe55bc5d6..f20ca26dd7 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.js @@ -4,7 +4,40 @@ const { Role, getRole, } = require("../../utilities/security/roles") -const { generateRoleID, getRoleParams } = require("../../db/utils") +const { + generateRoleID, + getRoleParams, + getUserParams, + ViewNames, +} = require("../../db/utils") + +const UpdateRolesOptions = { + CREATED: "created", + REMOVED: "removed", +} + +async function updateRolesOnUserTable(db, roleId, updateOption) { + const table = await db.get(ViewNames.USERS) + const schema = table.schema + const remove = updateOption === UpdateRolesOptions.REMOVED + let updated = false + for (let prop of Object.keys(schema)) { + if (prop === "roleId") { + updated = true + const constraints = schema[prop].constraints + const indexOf = constraints.inclusion.indexOf(roleId) + if (remove && indexOf !== -1) { + constraints.inclusion.splice(indexOf, 1) + } else if (!remove && indexOf === -1) { + constraints.inclusion.push(roleId) + } + break + } + } + if (updated) { + await db.put(table) + } +} exports.fetch = async function(ctx) { const db = new CouchDB(ctx.user.appId) @@ -42,6 +75,7 @@ exports.save = async function(ctx) { role._rev = ctx.request.body._rev } const result = await db.put(role) + await updateRolesOnUserTable(db, _id, UpdateRolesOptions.CREATED) role._rev = result.rev ctx.body = role ctx.message = `Role '${role.name}' created successfully.` @@ -49,7 +83,26 @@ exports.save = async function(ctx) { exports.destroy = async function(ctx) { const db = new CouchDB(ctx.user.appId) - await db.remove(ctx.params.roleId, ctx.params.rev) + const roleId = ctx.params.roleId + // first check no users actively attached to role + const users = ( + await db.allDocs( + getUserParams(null, { + include_docs: true, + }) + ) + ).rows.map(row => row.doc) + const usersWithRole = users.filter(user => user.roleId === roleId) + if (usersWithRole.length !== 0) { + ctx.throw("Cannot delete role when it is in use.") + } + + await db.remove(roleId, ctx.params.rev) + await updateRolesOnUserTable( + db, + ctx.params.roleId, + UpdateRolesOptions.REMOVED + ) ctx.message = `Role ${ctx.params.roleId} deleted successfully` ctx.status = 200 } diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index bea62ecbd5..bf56e6b181 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -6,7 +6,7 @@ const { getRole } = require("../../utilities/security/roles") exports.fetch = async function(ctx) { const database = new CouchDB(ctx.user.appId) const data = await database.allDocs( - getUserParams("", { + getUserParams(null, { include_docs: true, }) ) diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index 1989633463..5dc0161228 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -102,11 +102,7 @@ exports.generateRowID = tableId => { * Gets parameters for retrieving users, this is a utility function for the getDocParams function. */ exports.getUserParams = (username = "", otherProps = {}) => { - return getDocParams( - DocumentTypes.ROW, - `${ViewNames.USERS}${SEPARATOR}${DocumentTypes.USER}${SEPARATOR}${username}`, - otherProps - ) + return exports.getRowParams(ViewNames.USERS, username, otherProps) } /** From b70512ada2e300bd5436c855185d8a8fde9fbb16 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 7 Dec 2020 19:05:39 +0000 Subject: [PATCH 09/46] Add custom modal for creating and editing users --- .../backend/DataTable/DataTable.svelte | 16 ++- .../backend/DataTable/RowFieldControl.svelte | 8 +- .../components/backend/DataTable/Table.svelte | 21 +++- .../DataTable/buttons/CreateRowButton.svelte | 6 +- .../backend/DataTable/cells/cellRenderers.js | 18 ++++ ...itRowModal.svelte => CreateEditRow.svelte} | 9 +- .../DataTable/modals/CreateEditUser.svelte | 101 ++++++++++++++++++ .../backend/DataTable/modals/EditRow.svelte | 5 +- 8 files changed, 159 insertions(+), 25 deletions(-) rename packages/builder/src/components/backend/DataTable/modals/{CreateEditRowModal.svelte => CreateEditRow.svelte} (81%) create mode 100644 packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 2869b82e70..5ff7925c5f 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -8,10 +8,13 @@ import * as api from "./api" import Table from "./Table.svelte" import { TableNames } from "constants" + import CreateEditUser from "./modals/CreateEditUser.svelte" + import CreateEditRow from "./modals/CreateEditRow.svelte" let data = [] let loading = false + $: isUsersTable = $backendUiStore.selectedTable?._id === TableNames.USERS $: title = $backendUiStore.selectedTable.name $: schema = $backendUiStore.selectedTable.schema $: tableView = { @@ -31,14 +34,21 @@ } - +
{#if schema && Object.keys(schema).length > 0} - + {/if} - {#if $backendUiStore.selectedTable?._id === TableNames.USERS} + {#if isUsersTable} {/if}
diff --git a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte index 8b1c2e18e6..bddb66e4c9 100644 --- a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte +++ b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte @@ -7,20 +7,16 @@ Toggle, RichText, } from "@budibase/bbui" - import { backendUiStore } from "builderStore" - import { TableNames } from "constants" import Dropzone from "components/common/Dropzone.svelte" import { capitalise } from "../../../helpers" import LinkedRowSelector from "components/common/LinkedRowSelector.svelte" export let meta - export let creating export let value = meta.type === "boolean" ? false : "" + export let readonly $: type = meta.type $: label = capitalise(meta.name) - $: editingUser = - !creating && $backendUiStore.selectedTable?._id === TableNames.USERS {#if type === 'options'} @@ -53,5 +49,5 @@ data-cy="{meta.name}-input" {type} bind:value - disabled={editingUser} /> + disabled={readonly} /> {/if} diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 2599e4f8b0..67fc417191 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -7,10 +7,15 @@ import { notifier } from "builderStore/store/notifications" import Spinner from "components/common/Spinner.svelte" import DeleteRowsButton from "./buttons/DeleteRowsButton.svelte" - import { getRenderer, editRowRenderer } from "./cells/cellRenderers" + import { + getRenderer, + editRowRenderer, + userRowRenderer, + } from "./cells/cellRenderers" import TableLoadingOverlay from "./TableLoadingOverlay" import TableHeader from "./TableHeader" import "@budibase/svelte-ag-grid/dist/index.css" + import { TableNames } from "constants" export let schema = {} export let data = [] @@ -42,6 +47,14 @@ animateRows: true, } + $: isUsersTable = tableId === TableNames.USERS + $: { + if (isUsersTable) { + schema.username.displayFieldName = "Username" + schema.roleId.displayFieldName = "Role" + } + } + $: { let result = [] if (allowEditing) { @@ -57,12 +70,12 @@ suppressMenu: true, minWidth: 114, width: 114, - cellRenderer: editRowRenderer, + cellRenderer: isUsersTable ? userRowRenderer : editRowRenderer, }, ] } - Object.keys(schema || {}).forEach((key, idx) => { + Object.entries(schema || {}).forEach(([key, value]) => { result.push({ headerCheckboxSelection: false, headerComponent: TableHeader, @@ -70,7 +83,7 @@ field: schema[key], editable: allowEditing, }, - headerName: key, + headerName: value.displayFieldName || key, field: key, sortable: true, cellRenderer: getRenderer(schema[key], true), diff --git a/packages/builder/src/components/backend/DataTable/buttons/CreateRowButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/CreateRowButton.svelte index 3c0444881f..dfd8e6f4ac 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/CreateRowButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/CreateRowButton.svelte @@ -1,6 +1,8 @@ @@ -12,5 +14,5 @@
- + diff --git a/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js b/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js index 209f23119f..f3a7b86740 100644 --- a/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js +++ b/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js @@ -1,5 +1,6 @@ import AttachmentList from "./AttachmentCell.svelte" import EditRow from "../modals/EditRow.svelte" +import CreateEditUser from "../modals/CreateEditUser.svelte" import DeleteRow from "../modals/DeleteRow.svelte" import RelationshipDisplay from "./RelationshipCell.svelte" @@ -45,6 +46,23 @@ export function editRowRenderer(params) { return container } +export function userRowRenderer(params) { + const container = document.createElement("div") + container.style.height = "100%" + container.style.display = "flex" + container.style.alignItems = "center" + + new EditRow({ + target: container, + props: { + row: params.data, + modalContentComponent: CreateEditUser, + }, + }) + + return container +} + /* eslint-disable no-unused-vars */ function attachmentRenderer(options, constraints, editable) { return params => { diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditRowModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte similarity index 81% rename from packages/builder/src/components/backend/DataTable/modals/CreateEditRowModal.svelte rename to packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte index e99c5762d8..050c7ce200 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditRowModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte @@ -1,6 +1,5 @@ + + + + + {#if creating} + + {/if} + + {#if rolesLoaded} + + {/if} + {#each customSchemaKeys as [key, meta]} + + {/each} + diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRow.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRow.svelte index a4d2a74fc2..5d858a50f4 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditRow.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditRow.svelte @@ -1,8 +1,9 @@ + + + +
{roleName}
diff --git a/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js b/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js index f3a7b86740..15a7910186 100644 --- a/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js +++ b/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js @@ -3,15 +3,23 @@ import EditRow from "../modals/EditRow.svelte" import CreateEditUser from "../modals/CreateEditUser.svelte" import DeleteRow from "../modals/DeleteRow.svelte" import RelationshipDisplay from "./RelationshipCell.svelte" +import RoleCell from "./RoleCell.svelte" const renderers = { attachment: attachmentRenderer, link: linkedRowRenderer, } -export function getRenderer(schema, editable) { +export function getRenderer({ schema, editable, isUsersTable }) { + const rendererParams = { + options: schema.options, + constraints: schema.constraints, + editable, + } if (renderers[schema.type]) { - return renderers[schema.type](schema.options, schema.constraints, editable) + return renderers[schema.type](rendererParams) + } else if (isUsersTable && schema.name === "roleId") { + return roleRenderer(rendererParams) } else { return false } @@ -63,15 +71,14 @@ export function userRowRenderer(params) { return container } -/* eslint-disable no-unused-vars */ -function attachmentRenderer(options, constraints, editable) { +function attachmentRenderer() { return params => { const container = document.createElement("div") container.style.height = "100%" container.style.display = "flex" container.style.alignItems = "center" - const attachmentInstance = new AttachmentList({ + new AttachmentList({ target: container, props: { files: params.value || [], @@ -82,7 +89,6 @@ function attachmentRenderer(options, constraints, editable) { } } -/* eslint-disable no-unused-vars */ function linkedRowRenderer() { return params => { let container = document.createElement("div") @@ -102,3 +108,21 @@ function linkedRowRenderer() { return container } } + +function roleRenderer() { + return params => { + let container = document.createElement("div") + container.style.display = "grid" + container.style.height = "100%" + container.style.alignItems = "center" + + new RoleCell({ + target: container, + props: { + roleId: params.value, + }, + }) + + return container + } +} diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte index 39ac8a1fdc..30a0568e8e 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte @@ -2,7 +2,6 @@ import { goto } from "@sveltech/routify" import { backendUiStore } from "builderStore" import { TableNames } from "constants" - import ListItem from "./ListItem.svelte" import CreateTableModal from "./modals/CreateTableModal.svelte" import EditTablePopover from "./popovers/EditTablePopover.svelte" import EditViewPopover from "./popovers/EditViewPopover.svelte" @@ -47,7 +46,9 @@ text={table.name} selected={selectedView === `all_${table._id}`} on:click={() => selectTable(table)}> - + {#if table._id !== TableNames.USERS} + + {/if} {#each Object.keys(table.views || {}) as viewName} Date: Tue, 8 Dec 2020 15:11:26 +0000 Subject: [PATCH 13/46] Fix indentation level in screen and layout trees --- .../userInterface/ComponentNavigationTree/ComponentTree.svelte | 2 +- .../userInterface/ComponentNavigationTree/PathTree.svelte | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/ComponentTree.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/ComponentTree.svelte index 6a2019aa69..485af84e74 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/ComponentTree.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/ComponentTree.svelte @@ -71,7 +71,7 @@ on:drop={dragDropStore.actions.drop} text={isScreenslot(component._component) ? 'Screenslot' : component._instanceName} withArrow - indentLevel={level + 3} + indentLevel={level + 1} selected={$store.selectedComponentId === component._id}> diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/PathTree.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/PathTree.svelte index 6fefd92cd8..765041b184 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/PathTree.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/PathTree.svelte @@ -50,6 +50,7 @@ {#if selectedScreen?._id === screenId} From ff3e79696d163a606281130b3838bd8a0dd6e2a1 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 8 Dec 2020 15:21:01 +0000 Subject: [PATCH 14/46] Fix bug which caused autolinks to not be generated --- packages/builder/src/builderStore/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index 503d9b08a7..887ef733e4 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -70,7 +70,7 @@ export const allScreens = derived(store, $store => { export const mainLayout = derived(store, $store => { return $store.layouts?.find( - layout => layout.props?._id === LAYOUT_NAMES.MASTER.PRIVATE + layout => layout._id === LAYOUT_NAMES.MASTER.PRIVATE ) }) From 3c6be9d48149e5c9fef3c0373c9182dbfd00b9a2 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 8 Dec 2020 15:34:15 +0000 Subject: [PATCH 15/46] Sort path groupings --- .../userInterface/ComponentNavigationTree/index.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/index.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/index.svelte index d1ed4b687e..9528b2844c 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/index.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/index.svelte @@ -1,11 +1,12 @@
- {#each Object.keys($store.routes || {}) as path} + {#each paths as path} {/each}
From a926b23720bd9d8c97e8484aa9a180afff0ac3e0 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 8 Dec 2020 15:44:35 +0000 Subject: [PATCH 16/46] Sort routes to ensure the client router always matches correctly --- packages/client/src/store/routes.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/client/src/store/routes.js b/packages/client/src/store/routes.js index c69bb83ef0..88ca90df73 100644 --- a/packages/client/src/store/routes.js +++ b/packages/client/src/store/routes.js @@ -21,6 +21,12 @@ const createRouteStore = () => { }) }) }) + + // Sort route by paths so that the router matches correctly + routes.sort((a, b) => { + return a.path > b.path ? -1 : 1 + }) + store.update(state => { state.routes = routes return state From 4d5ac0ee9edf26692f9635602d2139b0d28ba194 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 8 Dec 2020 17:33:08 +0000 Subject: [PATCH 17/46] Updating builder/server in a few ways, to allow creating users with extra columns attached, allowing password to be updated in the builder and making sure that all row endpoints correctly pass through the user controller so that we can still have customised functionality for users (such as making sure password is never returned). --- .../backend/DataTable/cells/RoleCell.svelte | 1 - .../DataTable/modals/CreateEditUser.svelte | 8 +- packages/builder/yarn.lock | 181 +----------------- packages/server/src/api/controllers/row.js | 100 +++++----- packages/server/src/api/controllers/user.js | 34 +++- packages/server/src/api/routes/row.js | 1 - .../server/src/api/routes/tests/row.spec.js | 26 +-- 7 files changed, 84 insertions(+), 267 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte b/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte index f3b90aaf98..c6f6cbbcdf 100644 --- a/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte +++ b/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte @@ -9,7 +9,6 @@ return await cachedRoles } cachedRoles = new Promise(resolve => { - console.log("HIT API") builderApi .get("/api/roles") .then(response => response.json()) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte index c703e00b23..bde6514f8f 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte @@ -75,11 +75,9 @@ meta={{ ...tableSchema.email, name: 'Email' }} bind:value={row.email} readonly={!creating} /> - {#if creating} - - {/if} + {#if rolesLoaded} diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock index 837b7dbe13..7f650a83bc 100644 --- a/packages/builder/yarn.lock +++ b/packages/builder/yarn.lock @@ -854,15 +854,6 @@ svelte-portal "^1.0.0" turndown "^7.0.0" -"@budibase/client@^0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.3.8.tgz#75df7e97e8f0d9b58c00e2bb0d3b4a55f8d04735" - integrity sha512-tnFdmCdXKS+uZGoipr69Wa0oVoFHmyoV0ydihI6q0gKQH0KutypVHAaul2qPB8t5a/mTZopC//2WdmCeX1GKVg== - dependencies: - deep-equal "^2.0.1" - mustache "^4.0.1" - regexparam "^1.3.0" - "@budibase/colorpicker@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@budibase/colorpicker/-/colorpicker-1.0.1.tgz#940c180e7ebba0cb0756c4c8ef13f5dfab58e810" @@ -1655,11 +1646,6 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= -array-filter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" - integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -1727,13 +1713,6 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -available-typed-arrays@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" - integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== - dependencies: - array-filter "^1.0.0" - aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -2897,27 +2876,6 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-equal@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.5.tgz#55cd2fe326d83f9cbf7261ef0e060b3f724c5cb9" - integrity sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw== - dependencies: - call-bind "^1.0.0" - es-get-iterator "^1.1.1" - get-intrinsic "^1.0.1" - is-arguments "^1.0.4" - is-date-object "^1.0.2" - is-regex "^1.1.1" - isarray "^2.0.5" - object-is "^1.1.4" - object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.3.0" - side-channel "^1.0.3" - which-boxed-primitive "^1.0.1" - which-collection "^1.0.1" - which-typed-array "^1.1.2" - deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -3126,7 +3084,7 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" -es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: +es-abstract@^1.18.0-next.1: version "1.18.0-next.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== @@ -3144,20 +3102,6 @@ es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" -es-get-iterator@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.1.tgz#b93ddd867af16d5118e00881396533c1c6647ad9" - integrity sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.1" - has-symbols "^1.0.1" - is-arguments "^1.0.4" - is-map "^2.0.1" - is-set "^2.0.1" - is-string "^1.0.5" - isarray "^2.0.5" - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -3541,7 +3485,7 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -foreach@^2.0.5, foreach@~2.0.1: +foreach@~2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= @@ -3631,7 +3575,7 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.0, get-intrinsic@^1.0.1: +get-intrinsic@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be" integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== @@ -4007,11 +3951,6 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-bigint@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" - integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -4019,13 +3958,6 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-boolean-object@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" - integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== - dependencies: - call-bind "^1.0.0" - is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -4064,7 +3996,7 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1, is-date-object@^1.0.2: +is-date-object@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== @@ -4141,11 +4073,6 @@ is-installed-globally@^0.3.2: global-dirs "^2.0.1" is-path-inside "^3.0.1" -is-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" - integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== - is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -4156,11 +4083,6 @@ is-negative-zero@^2.0.0: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= -is-number-object@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -4226,11 +4148,6 @@ is-regex@^1.0.4, is-regex@^1.1.1: dependencies: has-symbols "^1.0.1" -is-set@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" - integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4241,11 +4158,6 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.4, is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== - is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -4253,32 +4165,11 @@ is-symbol@^1.0.2: dependencies: has-symbols "^1.0.1" -is-typed-array@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.4.tgz#1f66f34a283a3c94a4335434661ca53fff801120" - integrity sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA== - dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.0" - es-abstract "^1.18.0-next.1" - foreach "^2.0.5" - has-symbols "^1.0.1" - is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - -is-weakset@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" - integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4304,11 +4195,6 @@ isarray@1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isbuffer@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/isbuffer/-/isbuffer-0.0.0.tgz#38c146d9df528b8bf9b0701c3d43cf12df3fc39b" @@ -5606,14 +5492,6 @@ object-is@^1.0.1: define-properties "^1.1.3" es-abstract "^1.18.0-next.1" -object-is@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068" - integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5640,7 +5518,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2: +object.assign@^4.1.0, object.assign@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== @@ -6220,7 +6098,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.0: +regexp.prototype.flags@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== @@ -6228,11 +6106,6 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.0: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -regexparam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" - integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g== - regexpu-core@^4.7.1: version "4.7.1" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" @@ -6717,14 +6590,6 @@ shortid@^2.2.15: dependencies: nanoid "^2.1.0" -side-channel@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3" - integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g== - dependencies: - es-abstract "^1.18.0-next.0" - object-inspect "^1.8.0" - signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -7570,45 +7435,11 @@ whatwg-url@^8.0.0: tr46 "^2.0.2" webidl-conversions "^6.1.0" -which-boxed-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" - integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== - dependencies: - is-bigint "^1.0.0" - is-boolean-object "^1.0.0" - is-number-object "^1.0.3" - is-string "^1.0.4" - is-symbol "^1.0.2" - -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-typed-array@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" - integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== - dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.0" - es-abstract "^1.18.0-next.1" - foreach "^2.0.5" - function-bind "^1.1.1" - has-symbols "^1.0.1" - is-typed-array "^1.1.3" - which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" diff --git a/packages/server/src/api/controllers/row.js b/packages/server/src/api/controllers/row.js index 344a9753a1..61dd666f3e 100644 --- a/packages/server/src/api/controllers/row.js +++ b/packages/server/src/api/controllers/row.js @@ -29,6 +29,15 @@ validateJs.extend(validateJs.validators.datetime, { }, }) +// lots of row functionality too specific to pass to user controller, simply handle the +// password deletion here +function removePassword(tableId, row) { + if (tableId === ViewNames.USERS) { + delete row.password + } + return row +} + exports.patch = async function(ctx) { const appId = ctx.user.appId const db = new CouchDB(appId) @@ -64,6 +73,13 @@ exports.patch = async function(ctx) { tableId: row.tableId, table, }) + + // Creation of a new user goes to the user controller + if (row.tableId === ViewNames.USERS) { + await usersController.update(ctx) + return + } + const response = await db.put(row) row._rev = response.rev row.type = "row" @@ -80,19 +96,25 @@ exports.save = async function(ctx) { let row = ctx.request.body row.tableId = ctx.params.tableId + // TODO: find usage of this and break out into own endpoint if (ctx.request.body.type === "delete") { await bulkDelete(ctx) ctx.body = ctx.request.body.rows return } + // if the row obj had an _id then it will have been retrieved + const existingRow = ctx.preExisting + if (existingRow) { + ctx.params.id = row._id + await exports.patch(ctx) + return + } + if (!row._rev && !row._id) { row._id = generateRowID(row.tableId) } - // if the row obj had an _id then it will have been retrieved - const existingRow = ctx.preExisting - const table = await db.get(row.tableId) row = coerceRowValues(row, table) @@ -121,39 +143,22 @@ exports.save = async function(ctx) { }) // Creation of a new user goes to the user controller - if (!existingRow && row.tableId === ViewNames.USERS) { - try { - await usersController.create(ctx) - } catch (err) { - ctx.body = { errors: [err.message] } - } - return - } - - if (existingRow) { - const response = await db.put(row) - row._rev = response.rev - row.type = "row" - ctx.body = row - ctx.status = 200 - ctx.message = `${table.name} updated successfully.` + if (row.tableId === ViewNames.USERS) { + await usersController.create(ctx) return } row.type = "row" - const response = await db.post(row) + const response = await db.put(row) row._rev = response.rev - ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table) ctx.body = row ctx.status = 200 - ctx.message = `${table.name} created successfully` + ctx.message = `${table.name} saved successfully` } exports.fetchView = async function(ctx) { const appId = ctx.user.appId - const db = new CouchDB(appId) - const { calculation, group, field } = ctx.query const viewName = ctx.params.viewName // if this is a table view being looked for just transfer to that @@ -163,6 +168,8 @@ exports.fetchView = async function(ctx) { return } + const db = new CouchDB(appId) + const { calculation, group, field } = ctx.query const response = await db.query(`database/${viewName}`, { include_docs: !calculation, group, @@ -197,40 +204,32 @@ exports.fetchView = async function(ctx) { exports.fetchTableRows = async function(ctx) { const appId = ctx.user.appId - const db = new CouchDB(appId) - const response = await db.allDocs( - getRowParams(ctx.params.tableId, null, { - include_docs: true, - }) - ) - ctx.body = response.rows.map(row => row.doc) - ctx.body = await linkRows.attachLinkInfo( - appId, - response.rows.map(row => row.doc) - ) -} - -exports.search = async function(ctx) { - const appId = ctx.user.appId - const db = new CouchDB(appId) - const response = await db.allDocs({ - include_docs: true, - ...ctx.request.body, - }) - ctx.body = await linkRows.attachLinkInfo( - appId, - response.rows.map(row => row.doc) - ) + // special case for users, fetch through the user controller + let rows + if (ctx.params.tableId === ViewNames.USERS) { + await usersController.fetch(ctx) + rows = ctx.body + } else { + const db = new CouchDB(appId) + const response = await db.allDocs( + getRowParams(ctx.params.tableId, null, { + include_docs: true, + }) + ) + rows = response.rows.map(row => row.doc) + } + ctx.body = await linkRows.attachLinkInfo(appId, rows) } exports.find = async function(ctx) { const appId = ctx.user.appId const db = new CouchDB(appId) - const row = await db.get(ctx.params.rowId) + let row = await db.get(ctx.params.rowId) if (row.tableId !== ctx.params.tableId) { ctx.throw(400, "Supplied tableId does not match the rows tableId") return } + row = removePassword(ctx.params.tableId, row) ctx.body = await linkRows.attachLinkInfo(appId, row) } @@ -297,7 +296,8 @@ exports.fetchEnrichedRow = async function(ctx) { return } // need table to work out where links go in row - const [table, row] = await Promise.all([db.get(tableId), db.get(rowId)]) + let [table, row] = await Promise.all([db.get(tableId), db.get(rowId)]) + row = removePassword(tableId, row) // get the link docs const linkVals = await linkRows.getLinkDocuments({ appId, diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index b906f9648a..7ca2e05015 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -5,12 +5,18 @@ const { getRole } = require("../../utilities/security/roles") exports.fetch = async function(ctx) { const database = new CouchDB(ctx.user.appId) - const data = await database.allDocs( - getUserParams(null, { - include_docs: true, - }) - ) - ctx.body = data.rows.map(row => row.doc) + const users = ( + await database.allDocs( + getUserParams(null, { + include_docs: true, + }) + ) + ).rows.map(row => row.doc) + // user hashed password shouldn't ever be returned + for (let user of users) { + delete user.password + } + ctx.body = users } exports.create = async function(ctx) { @@ -25,12 +31,15 @@ exports.create = async function(ctx) { if (!role) ctx.throw(400, "Invalid Role") + const hashedPassword = await bcrypt.hash(password) const user = { + ...ctx.request.body, + // these must all be after the object spread, make sure + // any values are overwritten, generateUserID will always + // generate the same ID for the user as it is not UUID based _id: generateUserID(email), - email, - password: await bcrypt.hash(password), type: "user", - roleId, + password: hashedPassword, tableId: ViewNames.USERS, } @@ -55,7 +64,12 @@ exports.create = async function(ctx) { exports.update = async function(ctx) { const db = new CouchDB(ctx.user.appId) const user = ctx.request.body - const dbUser = db.get(ctx.request.body._id) + const dbUser = await db.get(ctx.request.body._id) + if (user.password) { + user.password = await bcrypt.hash(user.password) + } else { + delete user.password + } const newData = { ...dbUser, ...user } const response = await db.put(newData) diff --git a/packages/server/src/api/routes/row.js b/packages/server/src/api/routes/row.js index 4409f7f27b..63964e3066 100644 --- a/packages/server/src/api/routes/row.js +++ b/packages/server/src/api/routes/row.js @@ -25,7 +25,6 @@ router authorized(PermissionTypes.TABLE, PermissionLevels.READ), rowController.find ) - .post("/api/rows/search", rowController.search) .post( "/api/:tableId/rows", authorized(PermissionTypes.TABLE, PermissionLevels.WRITE), diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index 507f05cf6c..5972e90ca9 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -55,7 +55,7 @@ describe("/rows", () => { it("returns a success message when the row is created", async () => { const res = await createRow() - expect(res.res.statusMessage).toEqual(`${table.name} created successfully`) + expect(res.res.statusMessage).toEqual(`${table.name} saved successfully`) expect(res.body.name).toEqual("Test Contact") expect(res.body._rev).toBeDefined() }) @@ -118,30 +118,6 @@ describe("/rows", () => { expect(res.body.find(r => r.name === row.name)).toBeDefined() }) - it("lists rows when queried by their ID", async () => { - const newRow = { - tableId: table._id, - name: "Second Contact", - status: "new" - } - const row = await createRow() - const secondRow = await createRow(newRow) - - const rowIds = [row.body._id, secondRow.body._id] - - const res = await request - .post(`/api/rows/search`) - .set(defaultHeaders(appId)) - .send({ - keys: rowIds - }) - .expect('Content-Type', /json/) - .expect(200) - - expect(res.body.length).toBe(2) - expect(res.body.map(response => response._id)).toEqual(expect.arrayContaining(rowIds)) - }) - it("load should return 404 when row does not exist", async () => { await createRow() await request From 36432a490f08c53f6b5ccfca607c41247dcd53be Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 9 Dec 2020 10:52:18 +0000 Subject: [PATCH 18/46] Updating row controller to make sure that all user requests (bar deletion) are passed through correctly to the user controller so that any logic such as removing user password can be correctly held in the user controller logic. --- packages/server/src/api/controllers/row.js | 102 +++++------------- packages/server/src/api/controllers/user.js | 11 +- .../server/src/api/routes/tests/row.spec.js | 2 - packages/server/src/utilities/index.js | 94 ++++++++++++++++ 4 files changed, 128 insertions(+), 81 deletions(-) diff --git a/packages/server/src/api/controllers/row.js b/packages/server/src/api/controllers/row.js index 61dd666f3e..41d2ae15b1 100644 --- a/packages/server/src/api/controllers/row.js +++ b/packages/server/src/api/controllers/row.js @@ -9,7 +9,7 @@ const { ViewNames, } = require("../../db/utils") const usersController = require("./user") -const { cloneDeep } = require("lodash") +const { coerceRowValues } = require("../../utilities") const TABLE_VIEW_BEGINS_WITH = `all${SEPARATOR}${DocumentTypes.TABLE}${SEPARATOR}` @@ -29,11 +29,24 @@ validateJs.extend(validateJs.validators.datetime, { }, }) -// lots of row functionality too specific to pass to user controller, simply handle the -// password deletion here -function removePassword(tableId, row) { +async function findRow(db, appId, tableId, rowId) { + let row if (tableId === ViewNames.USERS) { - delete row.password + let ctx = { + params: { + userId: rowId, + }, + user: { + appId, + }, + } + await usersController.find(ctx) + row = ctx.body + } else { + row = await db.get(rowId) + } + if (row.tableId !== tableId) { + throw "Supplied tableId does not match the rows tableId" } return row } @@ -224,13 +237,12 @@ exports.fetchTableRows = async function(ctx) { exports.find = async function(ctx) { const appId = ctx.user.appId const db = new CouchDB(appId) - let row = await db.get(ctx.params.rowId) - if (row.tableId !== ctx.params.tableId) { - ctx.throw(400, "Supplied tableId does not match the rows tableId") - return + try { + const row = await findRow(db, appId, ctx.params.tableId, ctx.params.rowId) + ctx.body = await linkRows.attachLinkInfo(appId, row) + } catch (err) { + ctx.throw(400, err) } - row = removePassword(ctx.params.tableId, row) - ctx.body = await linkRows.attachLinkInfo(appId, row) } exports.destroy = async function(ctx) { @@ -296,8 +308,10 @@ exports.fetchEnrichedRow = async function(ctx) { return } // need table to work out where links go in row - let [table, row] = await Promise.all([db.get(tableId), db.get(rowId)]) - row = removePassword(tableId, row) + let [table, row] = await Promise.all([ + db.get(tableId), + findRow(db, appId, tableId, rowId), + ]) // get the link docs const linkVals = await linkRows.getLinkDocuments({ appId, @@ -327,68 +341,6 @@ exports.fetchEnrichedRow = async function(ctx) { ctx.status = 200 } -function coerceRowValues(record, table) { - const row = cloneDeep(record) - for (let [key, value] of Object.entries(row)) { - const field = table.schema[key] - if (!field) continue - - // eslint-disable-next-line no-prototype-builtins - if (TYPE_TRANSFORM_MAP[field.type].hasOwnProperty(value)) { - row[key] = TYPE_TRANSFORM_MAP[field.type][value] - } else if (TYPE_TRANSFORM_MAP[field.type].parse) { - row[key] = TYPE_TRANSFORM_MAP[field.type].parse(value) - } - } - return row -} - -const TYPE_TRANSFORM_MAP = { - link: { - "": [], - [null]: [], - [undefined]: undefined, - }, - options: { - "": "", - [null]: "", - [undefined]: undefined, - }, - string: { - "": "", - [null]: "", - [undefined]: undefined, - }, - longform: { - "": "", - [null]: "", - [undefined]: undefined, - }, - number: { - "": null, - [null]: null, - [undefined]: undefined, - parse: n => parseFloat(n), - }, - datetime: { - "": null, - [undefined]: undefined, - [null]: null, - }, - attachment: { - "": [], - [null]: [], - [undefined]: undefined, - }, - boolean: { - "": null, - [null]: null, - [undefined]: undefined, - true: true, - false: false, - }, -} - async function bulkDelete(ctx) { const appId = ctx.user.appId const { rows } = ctx.request.body diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 7ca2e05015..9ef7c2281f 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -89,9 +89,12 @@ exports.destroy = async function(ctx) { exports.find = async function(ctx) { const database = new CouchDB(ctx.user.appId) - const user = await database.get(generateUserID(ctx.params.email)) - ctx.body = { - email: user.email, - _rev: user._rev, + let lookup = ctx.params.email + ? generateUserID(ctx.params.email) + : ctx.params.userId + const user = await database.get(lookup) + if (user) { + delete user.password } + ctx.body = user } diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index 5972e90ca9..0698250050 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -51,8 +51,6 @@ describe("/rows", () => { describe("save, load, update, delete", () => { - - it("returns a success message when the row is created", async () => { const res = await createRow() expect(res.res.statusMessage).toEqual(`${table.name} saved successfully`) diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index cde10b6b62..8a43e9d27a 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -1,8 +1,59 @@ const env = require("../environment") const { DocumentTypes, SEPARATOR } = require("../db/utils") +const fs = require("fs") +const { cloneDeep } = require("lodash/fp") const APP_PREFIX = DocumentTypes.APP + SEPARATOR +/** + * A map of how we convert various properties in rows to each other based on the row type. + */ +const TYPE_TRANSFORM_MAP = { + link: { + "": [], + [null]: [], + [undefined]: undefined, + }, + options: { + "": "", + [null]: "", + [undefined]: undefined, + }, + string: { + "": "", + [null]: "", + [undefined]: undefined, + }, + longform: { + "": "", + [null]: "", + [undefined]: undefined, + }, + number: { + "": null, + [null]: null, + [undefined]: undefined, + parse: n => parseFloat(n), + }, + datetime: { + "": null, + [undefined]: undefined, + [null]: null, + }, + attachment: { + "": [], + [null]: [], + [undefined]: undefined, + }, + boolean: { + "": null, + [null]: null, + [undefined]: undefined, + true: true, + false: false, + }, +} + function confirmAppId(possibleAppId) { return possibleAppId && possibleAppId.startsWith(APP_PREFIX) ? possibleAppId @@ -74,3 +125,46 @@ exports.setCookie = (ctx, name, value) => { exports.isClient = ctx => { return ctx.headers["x-budibase-type"] === "client" } + +/** + * Recursively walk a directory tree and execute a callback on all files. + * @param {String} dirPath - Directory to traverse + * @param {Function} callback - callback to execute on files + */ +exports.walkDir = (dirPath, callback) => { + for (let filename of fs.readdirSync(dirPath)) { + const filePath = `${dirPath}/${filename}` + const stat = fs.lstatSync(filePath) + + if (stat.isFile()) { + callback(filePath) + } else { + exports.walkDir(filePath, callback) + } + } +} + +/** + * This will coerce the values in a row to the correct types based on the type transform map and the + * table schema. + * @param {object} row The row which is to be coerced to correct values based on schema, this input + * row will not be updated. + * @param {object} table The table that has been retrieved from DB, this must contain the expected + * schema for the rows. + * @returns {object} The updated row will be returned with all values coerced. + */ +exports.coerceRowValues = (row, table) => { + const clonedRow = cloneDeep(row) + for (let [key, value] of Object.entries(clonedRow)) { + const field = table.schema[key] + if (!field) continue + + // eslint-disable-next-line no-prototype-builtins + if (TYPE_TRANSFORM_MAP[field.type].hasOwnProperty(value)) { + clonedRow[key] = TYPE_TRANSFORM_MAP[field.type][value] + } else if (TYPE_TRANSFORM_MAP[field.type].parse) { + clonedRow[key] = TYPE_TRANSFORM_MAP[field.type].parse(value) + } + } + return clonedRow +} From 05b9c3fa68bdf39794b3931d2590f84c4cc9a54a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 11:31:50 +0000 Subject: [PATCH 19/46] Fix selected row state not resetting properly --- packages/builder/src/components/backend/DataTable/Table.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 3787c05a1b..e43a4f7408 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -56,6 +56,9 @@ } $: { + // Reset selection every time data changes + selectedRows = [] + let result = [] if (allowEditing) { result = [ From 31b3fac6590790fd07274db3b79d5679587b4556 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 11:37:09 +0000 Subject: [PATCH 20/46] Fix crash when having a screen selected that was deleted --- packages/builder/src/builderStore/store/frontend.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 7be4f68f25..ea00a461fb 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -86,13 +86,16 @@ export const getFrontendStore = () => { select: async screenId => { let promise store.update(state => { - const screen = get(allScreens).find(screen => screen._id === screenId) + const screens = get(allScreens) + let selectedScreen = screens.find(screen => screen._id === screenId) + if (!selectedScreen) { + selectedScreen = screens[0] + } state.currentFrontEndType = FrontendTypes.SCREEN - state.currentAssetId = screenId + state.currentAssetId = selectedScreen._id state.currentView = "detail" - - promise = store.actions.screens.regenerateCss(screen) - state.selectedComponentId = screen.props._id + promise = store.actions.screens.regenerateCss(selectedScreen) + state.selectedComponentId = selectedScreen.props._id return state }) await promise From 22932d7b52b4cc0025e980407eba1536f9df6f65 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 12:16:00 +0000 Subject: [PATCH 21/46] Fix routing setting and add initial role ID setting to screens for testing --- .../userInterface/ComponentPropertiesPanel.svelte | 3 ++- .../components/userInterface/SettingsView.svelte | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte index b4ac25bdd5..4699a8e415 100644 --- a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte +++ b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte @@ -6,6 +6,7 @@ import CategoryTab from "./CategoryTab.svelte" import DesignView from "./DesignView.svelte" import SettingsView from "./SettingsView.svelte" + import { setWith } from "lodash" let flattenedPanel = flattenComponents(panelStructure.categories) let categories = [ @@ -69,7 +70,7 @@ ) { selectedAsset.props._instanceName = value } else { - selectedAsset[name] = value + setWith(selectedAsset, name.split("."), value, Object) } return state }) diff --git a/packages/builder/src/components/userInterface/SettingsView.svelte b/packages/builder/src/components/userInterface/SettingsView.svelte index dd4644882c..f90e071b08 100644 --- a/packages/builder/src/components/userInterface/SettingsView.svelte +++ b/packages/builder/src/components/userInterface/SettingsView.svelte @@ -1,4 +1,5 @@ - + {text} From 4e748dad2b81309c23eed24c38f59a887f2da05d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 14:51:42 +0000 Subject: [PATCH 26/46] Fix bugs around selecting screens and layouts when creating them --- .../src/builderStore/store/frontend.js | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 87a46e4a91..f70e5f4d47 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -120,17 +120,13 @@ export const getFrontendStore = () => { state.screens.splice(foundScreen, 1) } state.screens.push(screen) - - if (creatingNewScreen) { - const safeProps = makePropsSafe( - state.components[screen.props._component], - screen.props - ) - state.selectedComponentId = safeProps._id - screen.props = safeProps - } return state }) + + if (creatingNewScreen) { + store.actions.screens.select(screen._id) + } + return screen }, delete: async screens => { @@ -178,28 +174,30 @@ export const getFrontendStore = () => { }, save: async layout => { const layoutToSave = cloneDeep(layout) - + const creatingNewLayout = layoutToSave._id === undefined const response = await api.post(`/api/layouts`, layoutToSave) - - const json = await response.json() + const savedLayout = await response.json() store.update(state => { const layoutIdx = state.layouts.findIndex( - stateLayout => stateLayout._id === json._id + stateLayout => stateLayout._id === savedLayout._id ) - if (layoutIdx >= 0) { // update existing layout - state.layouts.splice(layoutIdx, 1, json) + state.layouts.splice(layoutIdx, 1, savedLayout) } else { // save new layout - state.layouts.push(json) + state.layouts.push(savedLayout) } - - state.currentAssetId = json._id - state.selectedComponentId = json.props._id return state }) + + // Select layout if creating a new one + if (creatingNewLayout) { + store.actions.layouts.select(savedLayout._id) + } + + return savedLayout }, find: layoutId => { if (!layoutId) { @@ -450,7 +448,6 @@ export const getFrontendStore = () => { // Save layout nav._children = [...nav._children, newLink] - state.currentAssetId = layout._id promises.push(store.actions.layouts.save(layout)) } return state From 660345313c9d6ef8e69b791e5431ec340b585a96 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 14:53:03 +0000 Subject: [PATCH 27/46] Navigate to new layout after creating it --- .../src/components/userInterface/NewLayoutModal.svelte | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/builder/src/components/userInterface/NewLayoutModal.svelte b/packages/builder/src/components/userInterface/NewLayoutModal.svelte index e1cdc4af29..dbc9e3dc7a 100644 --- a/packages/builder/src/components/userInterface/NewLayoutModal.svelte +++ b/packages/builder/src/components/userInterface/NewLayoutModal.svelte @@ -1,19 +1,15 @@ @@ -113,14 +112,18 @@ {/each} {/if} - - - + {#if roles.length} + + {/if} From b842a2addc4972390d993466ca254a5ec59ca29b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 15:35:57 +0000 Subject: [PATCH 29/46] Cache roles in store and add role selector for screens --- .../builder/src/builderStore/store/backend.js | 21 ++++++++++ .../backend/DataTable/cells/RoleCell.svelte | 40 ++----------------- .../DataTable/modals/CreateEditUser.svelte | 36 +++++------------ .../backend/DataTable/modals/EditRoles.svelte | 26 +++++------- .../userInterface/NewScreenModal.svelte | 20 +++------- .../userInterface/RoleSelect.svelte | 15 +++++++ .../userInterface/SettingsView.svelte | 3 +- .../src/pages/[application]/_reset.svelte | 1 + 8 files changed, 70 insertions(+), 92 deletions(-) create mode 100644 packages/builder/src/components/userInterface/RoleSelect.svelte diff --git a/packages/builder/src/builderStore/store/backend.js b/packages/builder/src/builderStore/store/backend.js index 3cdabf1ed5..5592488590 100644 --- a/packages/builder/src/builderStore/store/backend.js +++ b/packages/builder/src/builderStore/store/backend.js @@ -6,6 +6,7 @@ const INITIAL_BACKEND_UI_STATE = { tables: [], views: [], users: [], + roles: [], selectedDatabase: {}, selectedTable: {}, draftTable: {}, @@ -177,6 +178,26 @@ export const getBackendUiStore = () => { return state }), }, + roles: { + fetch: async () => { + const response = await api.get("/api/roles") + const roles = await response.json() + store.update(state => { + state.roles = roles + return state + }) + }, + delete: async role => { + const response = await api.delete(`/api/roles/${role._id}/${role._rev}`) + await store.actions.roles.fetch() + return response + }, + save: async role => { + const response = await api.post("/api/roles", role) + await store.actions.roles.fetch() + return response + }, + }, } return store diff --git a/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte b/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte index c6f6cbbcdf..a45f376a3e 100644 --- a/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte +++ b/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte @@ -1,42 +1,10 @@ - - -
{roleName}
diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte index bde6514f8f..aac515e74b 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte @@ -1,18 +1,14 @@ - {#if rolesLoaded} - - {/if} + {#each customSchemaKeys as [key, meta]} {/each} diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte index 627112d87c..3b97f6d61b 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -4,27 +4,26 @@ import api from "builderStore/api" import { notifier } from "builderStore/store/notifications" import ErrorsBox from "components/common/ErrorsBox.svelte" + import { backendUiStore } from "builderStore" - let roles = [] let permissions = [] let selectedRole = {} let errors = [] $: selectedRoleId = selectedRole._id - $: otherRoles = roles.filter(role => role._id !== selectedRoleId) + $: otherRoles = $backendUiStore.roles.filter( + role => role._id !== selectedRoleId + ) $: isCreating = selectedRoleId == null || selectedRoleId === "" - // Loads available roles and permissions from the server - const fetchRoles = async () => { - const rolesResponse = await api.get("/api/roles") - roles = await rolesResponse.json() + const fetchPermissions = async () => { const permissionsResponse = await api.get("/api/permissions") permissions = await permissionsResponse.json() } - // Changes the seleced role + // Changes the selected role const changeRole = event => { const id = event?.target?.value - const role = roles.find(role => role._id === id) + const role = $backendUiStore.roles.find(role => role._id === id) if (role) { selectedRole = { ...role, @@ -61,7 +60,7 @@ } // Save/create the role - const response = await api.post("/api/roles", selectedRole) + const response = await backendUiStore.actions.roles.save(selectedRole) if (response.status === 200) { notifier.success("Role saved successfully.") } else { @@ -72,11 +71,8 @@ // Deletes the selected role const deleteRole = async () => { - const response = await api.delete( - `/api/roles/${selectedRole._id}/${selectedRole._rev}` - ) + const response = await backendUiStore.actions.roles.delete(selectedRole) if (response.status === 200) { - await fetchRoles() changeRole() notifier.success("Role deleted successfully.") } else { @@ -84,7 +80,7 @@ } } - onMount(fetchRoles) + onMount(fetchPermissions) - {#each roles as role} + {#each $backendUiStore.roles as role} {/each} diff --git a/packages/builder/src/components/userInterface/NewScreenModal.svelte b/packages/builder/src/components/userInterface/NewScreenModal.svelte index 4a5186adc0..8d7271601a 100644 --- a/packages/builder/src/components/userInterface/NewScreenModal.svelte +++ b/packages/builder/src/components/userInterface/NewScreenModal.svelte @@ -15,7 +15,6 @@ let templateIndex let draftScreen let createLink = true - let roles = [] let roleId = "BASIC" $: templates = getTemplates($store, $backendUiStore.tables) @@ -30,11 +29,6 @@ } } - const fetchRoles = async () => { - const response = await api.get("/api/roles") - roles = await response.json() - } - const templateChanged = newTemplateIndex => { if (newTemplateIndex === undefined) return const template = templates[newTemplateIndex] @@ -96,8 +90,6 @@ route = "/" + event.target.value } } - - onMount(fetchRoles) @@ -118,12 +110,10 @@ error={routeError} bind:value={route} on:change={routeChanged} /> - {#if roles.length} - - {/if} + diff --git a/packages/builder/src/components/userInterface/RoleSelect.svelte b/packages/builder/src/components/userInterface/RoleSelect.svelte new file mode 100644 index 0000000000..e49222d2c0 --- /dev/null +++ b/packages/builder/src/components/userInterface/RoleSelect.svelte @@ -0,0 +1,15 @@ + + + diff --git a/packages/builder/src/components/userInterface/SettingsView.svelte b/packages/builder/src/components/userInterface/SettingsView.svelte index f90e071b08..eee50d8cf6 100644 --- a/packages/builder/src/components/userInterface/SettingsView.svelte +++ b/packages/builder/src/components/userInterface/SettingsView.svelte @@ -4,6 +4,7 @@ import { FrontendTypes } from "constants" import PropertyControl from "./PropertyControl.svelte" import LayoutSelect from "./LayoutSelect.svelte" + import RoleSelect from "./RoleSelect.svelte" import Input from "./PropertyPanelControls/Input.svelte" import { excludeProps } from "./propertyCategories.js" import { store, allScreens, currentAsset } from "builderStore" @@ -36,8 +37,8 @@ const screenDefinition = [ { key: "description", label: "Description", control: Input }, { key: "routing.route", label: "Route", control: Input }, + { key: "routing.roleId", label: "Access", control: RoleSelect }, { key: "layoutId", label: "Layout", control: LayoutSelect }, - { key: "routing.roleId", label: "Role", control: Input }, ] const layoutDefinition = [{ key: "title", label: "Title", control: Input }] diff --git a/packages/builder/src/pages/[application]/_reset.svelte b/packages/builder/src/pages/[application]/_reset.svelte index a6f7e86454..e1daf7da1d 100644 --- a/packages/builder/src/pages/[application]/_reset.svelte +++ b/packages/builder/src/pages/[application]/_reset.svelte @@ -20,6 +20,7 @@ backendUiStore.actions.reset() await store.actions.initialise(pkg) await automationStore.actions.fetch() + await backendUiStore.actions.roles.fetch() return pkg } else { throw new Error(pkg) From 80e1c4f97887e861f879fc22bac14aa3d772a968 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 15:42:44 +0000 Subject: [PATCH 30/46] Fix z index in beta warning --- packages/builder/src/pages/[application]/_reset.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/pages/[application]/_reset.svelte b/packages/builder/src/pages/[application]/_reset.svelte index e1daf7da1d..74367c794d 100644 --- a/packages/builder/src/pages/[application]/_reset.svelte +++ b/packages/builder/src/pages/[application]/_reset.svelte @@ -218,5 +218,6 @@ position: absolute; bottom: var(--spacing-m); left: var(--spacing-m); + z-index: 1; } From c0c2583e093102603bd1410327ecf0e4dbe34f36 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 15:43:06 +0000 Subject: [PATCH 31/46] Force white background behind prevrew iframe regardless of theme to better reflect what the real app will look like --- .../userInterface/AppPreview/CurrentItemPreview.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte index ccb0153e45..0c0fa6156f 100644 --- a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte @@ -84,6 +84,7 @@ overflow: hidden; margin: auto; height: 100%; + background-color: white; } .component-container iframe { border: 0; From e0d902d27927eb4d929968c65e06c82dcfd1114d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 15:58:34 +0000 Subject: [PATCH 32/46] Fix merge bug --- packages/builder/src/builderStore/store/frontend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index e857689eba..c4d9ed6503 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -88,7 +88,7 @@ export const getFrontendStore = () => { const screen = get(allScreens).find(screen => screen._id === screenId) if (!screen) return state state.currentFrontEndType = FrontendTypes.SCREEN - state.currentAssetId = selectedScreen._id + state.currentAssetId = screen._id state.currentView = "detail" state.selectedComponentId = screen.props?._id return state From 248a1f456f49c72558d51c00ad1d99221a015850 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 16:01:16 +0000 Subject: [PATCH 33/46] Fix empty screen list when deleting a table backing an autoscreen which is currently selected --- packages/builder/src/builderStore/store/frontend.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index c4d9ed6503..a853a98dce 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -85,7 +85,9 @@ export const getFrontendStore = () => { screens: { select: screenId => { store.update(state => { - const screen = get(allScreens).find(screen => screen._id === screenId) + let screens = get(allScreens) + let screen = + screens.find(screen => screen._id === screenId) || screens[0] if (!screen) return state state.currentFrontEndType = FrontendTypes.SCREEN state.currentAssetId = screen._id From e5a34871c7eb1fe01a226a0d50c75f83af7be497 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 18:18:47 +0000 Subject: [PATCH 34/46] Add role selection for viewing screens and smart handling of preview screen --- packages/builder/src/builderStore/index.js | 4 +- .../src/builderStore/store/frontend.js | 1 + .../ComponentNavigationTree/PathTree.svelte | 2 + .../ComponentNavigationTree/index.svelte | 65 +++++++++++++++++-- .../userInterface/FrontendNavigatePane.svelte | 30 +++++++-- .../components/userInterface/Layout.svelte | 3 +- .../userInterface/NewScreenModal.svelte | 2 +- 7 files changed, 96 insertions(+), 11 deletions(-) diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index 887ef733e4..f1914efab2 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -2,7 +2,7 @@ import { getFrontendStore } from "./store/frontend" import { getBackendUiStore } from "./store/backend" import { getAutomationStore } from "./store/automation/" import { getThemeStore } from "./store/theme" -import { derived } from "svelte/store" +import { derived, writable } from "svelte/store" import analytics from "analytics" import { LAYOUT_NAMES } from "../constants" import { makePropsSafe } from "components/userInterface/assetParsing/createProps" @@ -74,6 +74,8 @@ export const mainLayout = derived(store, $store => { ) }) +export const selectedAccessRole = writable("BASIC") + export const initialise = async () => { try { await analytics.activate() diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index a853a98dce..754cd6e127 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -110,6 +110,7 @@ export const getFrontendStore = () => { const creatingNewScreen = screen._id === undefined const response = await api.post(`/api/screens`, screen) screen = await response.json() + await store.actions.routing.fetch() store.update(state => { const foundScreen = state.screens.findIndex( diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/PathTree.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/PathTree.svelte index 765041b184..5dd4ed9a7c 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/PathTree.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/PathTree.svelte @@ -20,6 +20,7 @@ export let route export let path export let indent + export let border $: selectedScreen = $currentAsset @@ -34,6 +35,7 @@ icon="ri-folder-line" text={path} opened={true} + {border} withArrow={route.subpaths} /> {#each Object.entries(route.subpaths) as [url, subpath]} diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/index.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/index.svelte index 9528b2844c..a88c6feb34 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/index.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/index.svelte @@ -1,12 +1,69 @@
- {#each paths as path} - + {#each paths as path, idx} + 0} {path} route={routes[path]} /> {/each} + + {#if !paths.length} +
+ There aren't any screens configured with this access role. +
+ {/if}
+ + diff --git a/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte b/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte index 23d2e6cf1a..25f939d2f1 100644 --- a/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte +++ b/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte @@ -1,13 +1,18 @@ From 7768b9ace8fe9baf60daf00bb24e44e29949f8bf Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 18:39:49 +0000 Subject: [PATCH 35/46] Allow multiple screens to have the same URL as long as role is different --- .../components/userInterface/NewScreenModal.svelte | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/userInterface/NewScreenModal.svelte b/packages/builder/src/components/userInterface/NewScreenModal.svelte index d7e65a84b2..db9181b1bd 100644 --- a/packages/builder/src/components/userInterface/NewScreenModal.svelte +++ b/packages/builder/src/components/userInterface/NewScreenModal.svelte @@ -48,10 +48,10 @@ const save = async () => { if (!route) { - routeError = "Url is required" + routeError = "URL is required" } else { - if (routeNameExists(route)) { - routeError = "This url is already taken" + if (routeExists(route, roleId)) { + routeError = "This URL is already taken for this access role" } else { routeError = "" } @@ -79,9 +79,11 @@ $goto(`./${createdScreen._id}`) } - const routeNameExists = route => { + const routeExists = (route, roleId) => { return $allScreens.some( - screen => screen.routing.route.toLowerCase() === route.toLowerCase() + screen => + screen.routing.route.toLowerCase() === route.toLowerCase() && + screen.routing.roleId === roleId ) } From 01ee1a1145cf50369970681e09fb8c974339d53a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 18:41:18 +0000 Subject: [PATCH 36/46] Reduce nav item selected state border radius slightly --- packages/builder/src/components/common/NavItem.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index 5b1ede4394..8eca07b1b0 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -61,7 +61,7 @@ } .nav-item:hover, .nav-item.selected { - border-radius: var(--border-radius-m); + border-radius: var(--border-radius-s); } .content { From 48a1932bc37a6d7b9dbaf621729aa5d7d1f76e0a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 18:56:54 +0000 Subject: [PATCH 37/46] Lint --- packages/builder/src/builderStore/store/frontend.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 754cd6e127..280af804cb 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -3,7 +3,6 @@ import { cloneDeep } from "lodash/fp" import { createProps, getBuiltin, - makePropsSafe, } from "components/userInterface/assetParsing/createProps" import { allScreens, From 8022fc12300c86bbf45bf78374e84bcf6e09ba6b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 9 Dec 2020 19:04:46 +0000 Subject: [PATCH 38/46] Select new role when creating screen with a different role than the selected one --- packages/builder/src/builderStore/store/frontend.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 280af804cb..dc6706373a 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -10,6 +10,7 @@ import { currentAsset, mainLayout, selectedComponent, + selectedAccessRole, } from "builderStore" import { fetchComponentLibDefinitions } from "../loadComponentLibraries" import api from "../api" @@ -88,6 +89,11 @@ export const getFrontendStore = () => { let screen = screens.find(screen => screen._id === screenId) || screens[0] if (!screen) return state + + // Update role to the screen's role setting so that it will always + // be visible + selectedAccessRole.set(screen.routing.roleId) + state.currentFrontEndType = FrontendTypes.SCREEN state.currentAssetId = screen._id state.currentView = "detail" From 84e1f54fa05d1272bf075ebdbfd08a69cca1ee95 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 11 Dec 2020 14:24:19 +0000 Subject: [PATCH 39/46] Improve navigation when logging in and out, remove need to hard reload the page and replace with smooth transitions --- .../client/src/components/ClientApp.svelte | 6 ++---- packages/client/src/components/Router.svelte | 16 ++++++++++++---- packages/client/src/store/auth.js | 18 +++++++++++++++--- packages/client/src/store/index.js | 6 ++++++ packages/client/src/store/initialise.js | 7 +++++++ packages/client/src/store/routes.js | 2 ++ .../standard-components/src/Navigation.svelte | 4 ++-- 7 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 packages/client/src/store/initialise.js diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 75061ac327..a7a972a435 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -3,7 +3,7 @@ import { setContext, onMount } from "svelte" import Component from "./Component.svelte" import SDK from "../sdk" - import { createDataStore, routeStore, screenStore } from "../store" + import { createDataStore, initialise, screenStore } from "../store" // Provide contexts setContext("sdk", SDK) @@ -14,13 +14,11 @@ // Load app config onMount(async () => { - await routeStore.actions.fetchRoutes() - await screenStore.actions.fetchScreens() + await initialise() loaded = true }) {#if loaded && $screenStore.activeLayout} - {/if} diff --git a/packages/client/src/components/Router.svelte b/packages/client/src/components/Router.svelte index 5684b189eb..3c2d71c422 100644 --- a/packages/client/src/components/Router.svelte +++ b/packages/client/src/components/Router.svelte @@ -7,7 +7,15 @@ const { styleable } = getContext("sdk") const component = getContext("component") - $: routerConfig = getRouterConfig($routeStore.routes) + // Only wrap this as an array to take advantage of svelte keying, + // to ensure the svelte-spa-router is fully remounted when route config + // changes + $: configs = [ + { + routes: getRouterConfig($routeStore.routes), + id: $routeStore.routeSessionId, + }, + ] const getRouterConfig = routes => { let config = {} @@ -25,11 +33,11 @@ } -{#if routerConfig} +{#each configs as config (config.id)}
- +
-{/if} +{/each}