diff --git a/packages/server/src/api/routes/tests/application.spec.ts b/packages/server/src/api/routes/tests/application.spec.ts index dbe4eb51ae..78f021ac5d 100644 --- a/packages/server/src/api/routes/tests/application.spec.ts +++ b/packages/server/src/api/routes/tests/application.spec.ts @@ -248,4 +248,10 @@ describe("/applications", () => { expect(devLogs.data.length).toBe(0) }) }) + + describe("permissions", () => { + it("should return the list of apps the user has access to", async () => { + const user = config.user + }) + }) }) diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 4e84422dec..aa96a30b00 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -29,6 +29,6 @@ start().catch(err => { throw err }) -export function getServer() { +export function getServer(): Server { return server } diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 22bb66b130..5333f1ebf2 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -49,6 +49,7 @@ import { AuthToken, Automation, CreateViewRequest, + Ctx, Datasource, FieldType, INTERNAL_TABLE_SOURCE_ID, @@ -68,6 +69,8 @@ import { import API from "./api" import { cloneDeep } from "lodash" import jwt, { Secret } from "jsonwebtoken" +import { Server } from "http" +import { userDetailListType } from "aws-sdk/clients/iam" mocks.licenses.init(pro) @@ -82,16 +85,16 @@ export interface TableToBuild extends Omit { } export default class TestConfiguration { - server: any - request: supertest.SuperTest | undefined + server?: Server + request?: supertest.SuperTest started: boolean - appId: string | null + appId?: string allApps: any[] app?: App - prodApp: any - prodAppId: any - user: any - userMetadataId: any + prodApp?: App + prodAppId?: string + user?: User + userMetadataId?: string table?: Table automation: any datasource?: Datasource @@ -99,10 +102,6 @@ export default class TestConfiguration { api: API csrfToken?: string - private get globalUserId() { - return this.user._id - } - constructor(openServer = true) { if (openServer) { // use a random port because it doesn't matter @@ -114,7 +113,7 @@ export default class TestConfiguration { } else { this.started = false } - this.appId = null + this.appId = undefined this.allApps = [] this.api = new API(this) @@ -134,37 +133,49 @@ export default class TestConfiguration { getAppId() { if (!this.appId) { - throw "appId has not been initialised properly" + throw new Error("appId has not been initialised properly") } - return this.appId } getProdAppId() { + if (!this.prodAppId) { + throw new Error( + "prodAppId has not been initialised, call config.init() first" + ) + } return this.prodAppId } + getUser(): User { + if (!this.user) { + throw new Error("User has not been initialised, call config.init() first") + } + return this.user + } + getUserDetails() { + const user = this.getUser() return { - globalId: this.globalUserId, - email: this.user.email, - firstName: this.user.firstName, - lastName: this.user.lastName, + globalId: user._id!, + email: user.email, + firstName: user.firstName, + lastName: user.lastName, } } async doInContext( - appId: string | null, + appId: string | undefined, task: () => Promise ): Promise { - if (!appId) { - appId = this.appId - } - const tenant = this.getTenantId() return tenancy.doInTenant(tenant, () => { + if (!appId) { + appId = this.appId + } + // check if already in a context - if (context.getAppId() == null && appId !== null) { + if (context.getAppId() == null && appId) { return context.doInAppContext(appId, async () => { return task() }) @@ -259,7 +270,11 @@ export default class TestConfiguration { // UTILS - _req(body: any, params: any, controlFunc: any) { + _req, Res, Context extends Ctx>( + handler: (ctx: Context) => Promise, + body?: Req, + params?: Record + ) { // create a fake request ctx const request: any = {} const appId = this.appId @@ -278,29 +293,19 @@ export default class TestConfiguration { throw new Error(`Error ${status} - ${message}`) } return this.doInContext(appId, async () => { - await controlFunc(request) + await handler(request) return request.body }) } // USER / AUTH - async globalUser( - config: { - id?: string - firstName?: string - lastName?: string - builder?: boolean - admin?: boolean - email?: string - roles?: any - } = {} - ): Promise { + async globalUser(config: Partial = {}): Promise { const { - id = `us_${newid()}`, + _id = `us_${newid()}`, firstName = generator.first(), lastName = generator.last(), - builder = true, - admin = false, + builder = { global: true }, + admin = { global: false }, email = generator.email(), roles, } = config @@ -308,72 +313,30 @@ export default class TestConfiguration { const db = tenancy.getTenantDB(this.getTenantId()) let existing try { - existing = await db.get(id) + existing = await db.get(_id) } catch (err) { existing = { email } } const user: User = { - _id: id, + _id: _id, ...existing, roles: roles || {}, tenantId: this.getTenantId(), firstName, lastName, } - await sessions.createASession(id, { + await sessions.createASession(_id, { sessionId: "sessionid", tenantId: this.getTenantId(), csrfToken: this.csrfToken, }) - if (builder) { - user.builder = { global: true } - } else { - user.builder = { global: false } - } - if (admin) { - user.admin = { global: true } - } else { - user.admin = { global: false } - } const resp = await db.put(user) - return { - _rev: resp.rev, - ...user, - } + return { _rev: resp.rev, ...user } } - async createUser( - user: { - id?: string - firstName?: string - lastName?: string - email?: string - builder?: boolean - admin?: boolean - roles?: UserRoles - } = {} - ): Promise { - const { - id, - firstName = generator.first(), - lastName = generator.last(), - email = generator.email(), - builder = true, - admin, - roles, - } = user - - const globalId = !id ? `us_${Math.random()}` : `us_${id}` - const resp = await this.globalUser({ - id: globalId, - firstName, - lastName, - email, - builder, - admin, - roles: roles || {}, - }) - await cache.user.invalidateUser(globalId) + async createUser(user: Partial = {}): Promise { + const resp = await this.globalUser(user) + await cache.user.invalidateUser(resp._id!) return resp } @@ -381,7 +344,7 @@ export default class TestConfiguration { return context.doInTenant(this.tenantId!, async () => { const baseGroup = structures.userGroups.userGroup() baseGroup.roles = { - [this.prodAppId]: roleId, + [this.getProdAppId()]: roleId, } const { id, rev } = await pro.sdk.groups.save(baseGroup) return { @@ -404,8 +367,18 @@ export default class TestConfiguration { }) } - async login({ roleId, userId, builder, prodApp = false }: any = {}) { - const appId = prodApp ? this.prodAppId : this.appId + async login({ + roleId, + userId, + builder, + prodApp, + }: { + roleId: string + userId: string + builder: boolean + prodApp: boolean + }) { + const appId = prodApp ? this.getProdAppId() : this.getAppId() return context.doInAppContext(appId, async () => { userId = !userId ? `us_uuid1` : userId if (!this.request) { @@ -414,9 +387,9 @@ export default class TestConfiguration { // make sure the user exists in the global DB if (roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) { await this.globalUser({ - id: userId, - builder, - roles: { [this.prodAppId]: roleId }, + _id: userId, + builder: { global: builder }, + roles: { [appId]: roleId }, }) } await sessions.createASession(userId, { @@ -445,8 +418,9 @@ export default class TestConfiguration { defaultHeaders(extras = {}, prodApp = false) { const tenantId = this.getTenantId() + const user = this.getUser() const authObj: AuthToken = { - userId: this.globalUserId, + userId: user._id!, sessionId: "sessionid", tenantId, } @@ -498,7 +472,7 @@ export default class TestConfiguration { builder = false, prodApp = true, } = {}) { - return this.login({ email, roleId, builder, prodApp }) + return this.login({ userId: email, roleId, builder, prodApp }) } // TENANCY @@ -521,7 +495,7 @@ export default class TestConfiguration { this.tenantId = structures.tenant.id() this.user = await this.globalUser() - this.userMetadataId = generateUserMetadataID(this.user._id) + this.userMetadataId = generateUserMetadataID(this.user._id!) return this.createApp(appName) } @@ -532,7 +506,11 @@ export default class TestConfiguration { // API - async generateApiKey(userId = this.user._id) { + async generateApiKey(userId?: string) { + const user = this.getUser() + if (!userId) { + userId = user._id! + } const db = tenancy.getTenantDB(this.getTenantId()) const id = dbCore.generateDevInfoID(userId) let devInfo: any @@ -552,13 +530,15 @@ export default class TestConfiguration { async createApp(appName: string): Promise { // create dev app // clear any old app - this.appId = null + this.appId = undefined this.app = await context.doInTenant(this.tenantId!, async () => { - const app = await this._req({ name: appName }, null, appController.create) + const app = (await this._req(appController.create, { + name: appName, + })) as App this.appId = app.appId! return app }) - return await context.doInAppContext(this.getAppId(), async () => { + return await context.doInAppContext(this.app.appId!, async () => { // create production app this.prodApp = await this.publish() @@ -570,7 +550,7 @@ export default class TestConfiguration { } async publish() { - await this._req(null, null, deployController.publishApp) + await this._req(deployController.publishApp) // @ts-ignore const prodAppId = this.getAppId().replace("_dev", "") this.prodAppId = prodAppId @@ -582,13 +562,11 @@ export default class TestConfiguration { } async unpublish() { - const response = await this._req( - null, - { appId: this.appId }, - appController.unpublish - ) - this.prodAppId = null - this.prodApp = null + const response = await this._req(appController.unpublish, { + appId: this.appId, + }) + this.prodAppId = undefined + this.prodApp = undefined return response } @@ -716,8 +694,7 @@ export default class TestConfiguration { // ROLE async createRole(config?: any) { - config = config || basicRole() - return this._req(config, null, roleController.save) + return this._req(roleController.save, config || basicRole()) } // VIEW @@ -730,7 +707,7 @@ export default class TestConfiguration { tableId: this.table!._id, name: generator.guid(), } - return this._req(view, null, viewController.v1.save) + return this._req(viewController.v1.save, view) } async createView( @@ -760,13 +737,13 @@ export default class TestConfiguration { delete config._rev } this.automation = ( - await this._req(config, null, automationController.create) + await this._req(automationController.create, config) ).automation return this.automation } async getAllAutomations() { - return this._req(null, null, automationController.fetch) + return this._req(automationController.fetch) } async deleteAutomation(automation?: any) { @@ -774,11 +751,10 @@ export default class TestConfiguration { if (!automation) { return } - return this._req( - null, - { id: automation._id, rev: automation._rev }, - automationController.destroy - ) + return this._req(automationController.destroy, { + id: automation._id, + rev: automation._rev, + }) } async createWebhook(config?: any) { @@ -787,7 +763,7 @@ export default class TestConfiguration { } config = config || basicWebhook(this.automation._id) - return (await this._req(config, null, webhookController.save)).webhook + return (await this._req(webhookController.save, config)).webhook } // DATASOURCE @@ -871,21 +847,21 @@ export default class TestConfiguration { throw "No datasource created for query." } config = config || basicQuery(this.datasource!._id!) - return this._req(config, null, queryController.save) + return this._req(queryController.save, config) } // SCREEN async createScreen(config?: any) { config = config || basicScreen() - return this._req(config, null, screenController.save) + return this._req(screenController.save, config) } // LAYOUT async createLayout(config?: any) { config = config || basicLayout() - return await this._req(config, null, layoutController.save) + return await this._req(layoutController.save, config) } }