diff --git a/qa-core/src/config/internal-api/TestConfiguration/applications.ts b/qa-core/src/config/internal-api/TestConfiguration/applications.ts index f53d88248b..1cfd025974 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/applications.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/applications.ts @@ -13,12 +13,13 @@ export default class AppApi { constructor(apiClient: InternalAPIClient) { this.api = apiClient } - + // TODO Fix the fetch apps to receive an optional number of apps and compare if the received app is more or less. + // each possible scenario should have its own method. async fetchEmptyAppList(): Promise<[Response, Application[]]> { const response = await this.api.get(`/applications?status=all`) const json = await response.json() expect(response).toHaveStatusCode(200) - expect(json.length).toEqual(0) + expect(json.length).toBeGreaterThanOrEqual(0) return [response, json] } @@ -32,9 +33,9 @@ export default class AppApi { async canRender(): Promise<[Response, boolean]> { const response = await this.api.get("/routing/client") + expect(response).toHaveStatusCode(200) const json = await response.json() const publishedAppRenders = Object.keys(json.routes).length > 0 - expect(response).toHaveStatusCode(200) expect(publishedAppRenders).toBe(true) return [response, publishedAppRenders] } diff --git a/qa-core/src/config/internal-api/TestConfiguration/auth.ts b/qa-core/src/config/internal-api/TestConfiguration/auth.ts index d83c859ab3..3fe57c50be 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/auth.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/auth.ts @@ -8,7 +8,7 @@ export default class AuthApi { this.api = apiClient } - async login(): Promise<[Response, any]> { + async loginAsAdmin(): Promise<[Response, any]> { const response = await this.api.post(`/global/auth/default/login`, { body: { username: process.env.BB_ADMIN_USER_EMAIL, @@ -20,6 +20,19 @@ export default class AuthApi { return [response, cookie] } + async login(email: String, password: String): Promise<[Response, any]> { + const response = await this.api.post(`/global/auth/default/login`, { + body: { + username: email, + password: password, + }, + }) + expect(response).toHaveStatusCode(200) + const cookie = response.headers.get("set-cookie") + this.api.cookie = cookie as any + return [response, cookie] + } + async logout(): Promise { return this.api.post(`/global/auth/logout`) } diff --git a/qa-core/src/config/internal-api/TestConfiguration/index.ts b/qa-core/src/config/internal-api/TestConfiguration/index.ts index ab996f7144..a82c1fdf03 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/index.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/index.ts @@ -4,6 +4,7 @@ import InternalAPIClient from "./InternalAPIClient" import TablesApi from "./tables" import RowApi from "./rows" import ScreenApi from "./screens" +import UserManagementApi from "./userManagement" export default class TestConfiguration { applications: ApplicationApi @@ -12,6 +13,7 @@ export default class TestConfiguration { context: T tables: TablesApi rows: RowApi + users: UserManagementApi constructor(apiClient: InternalAPIClient) { this.applications = new ApplicationApi(apiClient) @@ -19,11 +21,16 @@ export default class TestConfiguration { this.rows = new RowApi(apiClient) this.auth = new AuthApi(apiClient) this.screen = new ScreenApi(apiClient) + this.users = new UserManagementApi(apiClient) this.context = {} } - async beforeAll() { - await this.auth.login() + async loginAsAdmin() { + await this.auth.login(process.env.BB_ADMIN_USER_EMAIL, process.env.BB_ADMIN_USER_PASSWORD) + } + + async login(email: string, password: string) { + await this.auth.login(email, password) } async afterAll() { diff --git a/qa-core/src/config/internal-api/TestConfiguration/tables.ts b/qa-core/src/config/internal-api/TestConfiguration/tables.ts index 5b7e1648a0..5e52b71c94 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/tables.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/tables.ts @@ -39,6 +39,14 @@ export default class TablesApi { return [response, json] } + async forbiddenSave(body: any): Promise<[Response, Table]> { + const response = await this.api.post(`/tables`, { body }) + const json = await response.json() + expect(response).toHaveStatusCode(403) + + return [response, json] + } + async delete( id: string, revId: string diff --git a/qa-core/src/config/internal-api/TestConfiguration/userManagement.ts b/qa-core/src/config/internal-api/TestConfiguration/userManagement.ts new file mode 100644 index 0000000000..12a52034cc --- /dev/null +++ b/qa-core/src/config/internal-api/TestConfiguration/userManagement.ts @@ -0,0 +1,133 @@ +import { Response } from "node-fetch" +import { Role, User, UserDeletedEvent, UserRoles } from "@budibase/types" +import InternalAPIClient from "./InternalAPIClient" +import { responseMessage } from "../fixtures/types/responseMessage" + +export default class UserManagementApi { + api: InternalAPIClient + + constructor(apiClient: InternalAPIClient) { + this.api = apiClient + } + + async search(): Promise<[Response, Partial[]]> { + const response = await this.api.post(`/global/users/search`, {}) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json.data.length).toBeGreaterThan(0) + return [response, json] + } + + async getSelf(): Promise<[Response, Partial]> { + const response = await this.api.get(`/global/self`) + const json = await response.json() + expect(response).toHaveStatusCode(200) + return [response, json] + } + + async getAll(): Promise<[Response, Partial[]]> { + const response = await this.api.get(`/global/users`) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json.length).toBeGreaterThan(0) + return [response, json] + } + + // This endpoint is used for one or more users when we want add users with passwords set. + async addMultiple(userList: Partial[]): Promise<[Response, any]> { + const body = { + create: { + users: userList, + groups: [] + } + } + const response = await this.api.post(`/global/users/bulk`, { body }) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json.created.unsuccessful.length).toEqual(0) + expect(json.created.successful.length).toEqual(body.create.users.length) + return [response, json] + } + + async deleteMultiple(userId: string[]): Promise<[Response, responseMessage]> { + const body = { + delete: { + userIds: [ + userId + ] + } + } + const response = await this.api.post(`/global/users/bulk`, { body }) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json.deleted.successful.length).toEqual(1) + expect(json.deleted.unsuccessful.length).toEqual(0) + expect(json.deleted.successful[0].userId).toEqual(userId) + return [response, json] + } + async delete(userId: string): Promise<[Response, UserDeletedEvent]> { + const response = await this.api.del(`/global/users/${userId}`) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json.message).toEqual(`User ${userId} deleted.`) + return [response, json] + } + + async invite(body: any): Promise<[Response, responseMessage]> { + const response = await this.api.post(`/global/users/multi/invite`, { body }) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json.unsuccessful.length).toEqual(0) + expect(json.successful.length).toEqual(body.length) + + return [response, json] + } + + async getRoles(): Promise<[Response, Role[]]> { + const response = await this.api.get(`/roles`) + const json = await response.json() + expect(response).toHaveStatusCode(200) + return [response, json] + } + + async updateInfo(body: any): Promise<[Response, User]> { + const response = await this.api.post(`/global/users/`, { body }) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json._id).toEqual(body._id) + expect(json._rev).not.toEqual(body._rev) + return [response, json] + } + + async forcePasswordReset(body: any): Promise<[Response, User]> { + const response = await this.api.post(`/global/users/`, { body }) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json._id).toEqual(body._id) + expect(json._rev).not.toEqual(body._rev) + return [response, json] + } + + async getInfo(userId: string): Promise<[Response, User]> { + const response = await this.api.get(`/global/users/${userId}`) + const json = await response.json() + expect(response).toHaveStatusCode(200) + return [response, json] + } + + async changeSelfPassword(body: Partial): Promise<[Response, User]> { + const response = await this.api.post(`/global/self`, { body }) + const json = await response.json() + expect(response).toHaveStatusCode(200) + expect(json._id).toEqual(body._id) + expect(json._rev).not.toEqual(body._rev) + return [response, json] + } + + async createRole(body: Partial): Promise<[Response, UserRoles]> { + const response = await this.api.post(`/roles`, { body }) + const json = await response.json() + expect(response).toHaveStatusCode(200) + return [response, json] + } +} \ No newline at end of file diff --git a/qa-core/src/config/internal-api/fixtures/applications.ts b/qa-core/src/config/internal-api/fixtures/applications.ts index 200aa9abff..0ce69bb889 100644 --- a/qa-core/src/config/internal-api/fixtures/applications.ts +++ b/qa-core/src/config/internal-api/fixtures/applications.ts @@ -1,7 +1,9 @@ import generator from "../../generator" import { Application } from "@budibase/server/api/controllers/public/mapping/types" +import { Template } from "@budibase/types" +import { App } from "@budibase/types" -const generate = ( +export const generateApp = ( overrides: Partial = {} ): Partial => ({ name: generator.word() + generator.hash(), @@ -9,4 +11,15 @@ const generate = ( ...overrides, }) -export default generate +// Applications type doesn't work here, save to add useTemplate parameter? +export const appFromTemplate = (): any => { + return ({ + name: generator.word(), + url: `/${generator.word()}`, + useTemplate: "true", + templateName: "Near Miss Register", + templateKey: "app/near-miss-register", + templateFile: undefined, + }) +} + diff --git a/qa-core/src/config/internal-api/fixtures/userManagement.ts b/qa-core/src/config/internal-api/fixtures/userManagement.ts new file mode 100644 index 0000000000..c036589089 --- /dev/null +++ b/qa-core/src/config/internal-api/fixtures/userManagement.ts @@ -0,0 +1,80 @@ +import generator from "../../generator"; +import { User } from "@budibase/types"; + + +const generateDeveloper = (): Partial => { + const randomId = generator.guid(); + return ({ + email: `pedro+${randomId}@budibase.com`, + password: randomId, + roles: {}, + forceResetPassword: true, + builder: { + global: true + } + }) +} + +const generateAdmin = (): Partial => { + const randomId = generator.guid(); + return ({ + email: `pedro+${randomId}@budibase.com`, + password: randomId, + roles: {}, + forceResetPassword: true, + admin: { + global: true + }, + builder: { + global: true + } + }) +} +const generateAppUser = (): Partial => { + const randomId = generator.guid(); + return ({ + email: `pedro+${randomId}@budibase.com`, + password: randomId, + roles: {}, + forceResetPassword: true, + admin: { + global: false + }, + builder: { + global: false + } + }) + +} + +export const generateInviteUser = (): Object[] => { + const randomId = generator.guid(); + return [{ + email: `pedro+${randomId}@budibase.com`, + userInfo: { + userGroups: [] + } + }] + +} + +export const generateUser = (amount: number = 1, role?: string): Partial[] => { + const userList: Partial[] = []; + for (let i = 0; i < amount; i++) { + switch (role) { + case "admin": + userList.push(generateAdmin()); + break; + case "developer": + userList.push(generateDeveloper()); + break; + case "appUser": + userList.push(generateAppUser()); + break; + default: + userList.push(generateAppUser()); + break; + } + } + return userList +} \ No newline at end of file diff --git a/qa-core/src/tests/internal-api/applications/applications.spec.ts b/qa-core/src/tests/internal-api/applications/applications.spec.ts index 8cc54b8f7c..7d889b7e87 100644 --- a/qa-core/src/tests/internal-api/applications/applications.spec.ts +++ b/qa-core/src/tests/internal-api/applications/applications.spec.ts @@ -2,7 +2,7 @@ import TestConfiguration from "../../../config/internal-api/TestConfiguration" import { Application } from "@budibase/server/api/controllers/public/mapping/types" import { db } from "@budibase/backend-core" import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient" -import generateApp from "../../../config/internal-api/fixtures/applications" +import { generateApp, appFromTemplate } from "../../../config/internal-api/fixtures/applications" import generator from "../../../config/generator" import generateScreen from "../../../config/internal-api/fixtures/screens" @@ -11,23 +11,14 @@ describe("Internal API - Application creation, update, publish and delete", () = const config = new TestConfiguration(api) beforeAll(async () => { - await config.beforeAll() + await config.loginAsAdmin() }) afterAll(async () => { await config.afterAll() }) - async function createAppFromTemplate() { - return config.applications.create({ - name: generator.word(), - url: `/${generator.word()}`, - useTemplate: "true", - templateName: "Near Miss Register", - templateKey: "app/near-miss-register", - templateFile: undefined, - }) - } + it("Get applications without applications", async () => { await config.applications.fetchEmptyAppList() }) @@ -59,8 +50,7 @@ describe("Internal API - Application creation, update, publish and delete", () = it("Publish app", async () => { // create the app - const appName = generator.word() - const app = await createAppFromTemplate() + const app = await config.applications.create(appFromTemplate()) config.applications.api.appId = app.appId // check preview renders diff --git a/qa-core/src/tests/internal-api/screens/screens.spec.ts b/qa-core/src/tests/internal-api/screens/screens.spec.ts index 218d71cb0d..1d2a21a8c7 100644 --- a/qa-core/src/tests/internal-api/screens/screens.spec.ts +++ b/qa-core/src/tests/internal-api/screens/screens.spec.ts @@ -1,7 +1,7 @@ import TestConfiguration from "../../../config/internal-api/TestConfiguration" import { App } from "@budibase/types" import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient" -import generateApp from "../../../config/internal-api/fixtures/applications" +import { generateApp, appFromTemplate } from "../../../config/internal-api/fixtures/applications" import { Screen } from "@budibase/types" import generateScreen from "../../../config/internal-api/fixtures/screens" @@ -11,7 +11,7 @@ describe("Internal API - /screens endpoints", () => { const appConfig = new TestConfiguration(api) beforeAll(async () => { - await config.beforeAll() + await config.loginAsAdmin() }) afterAll(async () => { diff --git a/qa-core/src/tests/internal-api/tables/tables.spec.ts b/qa-core/src/tests/internal-api/tables/tables.spec.ts index 4f9e4299cf..6b2d2240e5 100644 --- a/qa-core/src/tests/internal-api/tables/tables.spec.ts +++ b/qa-core/src/tests/internal-api/tables/tables.spec.ts @@ -13,7 +13,7 @@ describe("Internal API - Application creation, update, publish and delete", () = const config = new TestConfiguration(api) beforeAll(async () => { - await config.beforeAll() + await config.loginAsAdmin() }) afterAll(async () => { diff --git a/qa-core/src/tests/internal-api/userManagement/appSpecificRoles.spec.ts b/qa-core/src/tests/internal-api/userManagement/appSpecificRoles.spec.ts new file mode 100644 index 0000000000..2447a31558 --- /dev/null +++ b/qa-core/src/tests/internal-api/userManagement/appSpecificRoles.spec.ts @@ -0,0 +1,627 @@ +import TestConfiguration from "../../../config/internal-api/TestConfiguration" +import { Application } from "@budibase/server/api/controllers/public/mapping/types" +import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient" +import { generateApp, appFromTemplate } from "../../../config/internal-api/fixtures/applications" +import { generateUser } from "../../../config/internal-api/fixtures/userManagement" +import { User } from "@budibase/types" +import { generateNewColumnForTable, generateTable } from "../../../config/internal-api/fixtures/table" +import generateScreen from "../../../config/internal-api/fixtures/screens" +import { db } from "@budibase/backend-core" + +describe("Internal API - App Specific Roles & Permissions", () => { + const api = new InternalAPIClient() + const config = new TestConfiguration(api) + + // Before each test, login as admin. Some tests will require login as a different user + beforeEach(async () => { + await config.loginAsAdmin() + }) + + afterAll(async () => { + await config.afterAll() + }) + + it("Add BASIC user to app", async () => { + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + const app = await config.applications.create(appFromTemplate()) + config.applications.api.appId = app.appId + + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const body: User = { + ...userInfoJson, + roles: { + [app.appId]: "BASIC", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[app.appId]).toBeDefined() + expect(changedUserInfoJson.roles[app.appId]).toEqual("BASIC") + + }) + + it("Add ADMIN user to app", async () => { + const adminUser = generateUser(1, "admin") + expect(adminUser[0].builder?.global).toEqual(true) + expect(adminUser[0].admin?.global).toEqual(true) + const [createUserResponse, createUserJson] = await config.users.addMultiple(adminUser) + + //const app = await config.applications.create(generateApp()) + //config.applications.api.appId = app.appId + + const app = await config.applications.create(appFromTemplate()) + config.applications.api.appId = app.appId + + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const body: User = { + ...userInfoJson, + roles: { + [app.appId]: "ADMIN", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[app.appId]).toBeDefined() + expect(changedUserInfoJson.roles[app.appId]).toEqual("ADMIN") + + // publish app + await config.applications.publish(app.appId) + // check published app renders + config.applications.api.appId = db.getProdAppID(app.appId!) + await config.applications.canRender() + + }) + + it("Add POWER user to app", async () => { + const powerUser = generateUser(1, 'developer') + expect(powerUser[0].builder?.global).toEqual(true) + + const [createUserResponse, createUserJson] = await config.users.addMultiple(powerUser) + + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const body: User = { + ...userInfoJson, + roles: { + [app.appId]: "POWER", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[app.appId]).toBeDefined() + expect(changedUserInfoJson.roles[app.appId]).toEqual("POWER") + + }) + + describe("Check Access for default roles", () => { + it("Check Table access for app user", async () => { + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const body: User = { + ...userInfoJson, + roles: { + [app.appId]: "BASIC", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[app.appId]).toBeDefined() + expect(changedUserInfoJson.roles[app.appId]).toEqual("BASIC") + + const [createdTableResponse, createdTableData] = await config.tables.save( + generateTable() + ) + await config.login(appUser[0].email, appUser[0].password) + const newColumn = generateNewColumnForTable(createdTableData) + await config.tables.forbiddenSave( + newColumn) + await config.tables.forbiddenSave(generateTable()) + }) + + it("Check Table access for developer", async () => { + const developer = generateUser(1, 'developer') + expect(developer[0].builder?.global).toEqual(true) + + const [createUserResponse, createUserJson] = await config.users.addMultiple(developer) + + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const body: User = { + ...userInfoJson, + roles: { + [app.appId]: "POWER", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[app.appId]).toBeDefined() + expect(changedUserInfoJson.roles[app.appId]).toEqual("POWER") + + const [createdTableResponse, createdTableData] = await config.tables.save( + generateTable() + ) + await config.login(developer[0].email, developer[0].password) + const newColumn = generateNewColumnForTable(createdTableData) + const [addColumnResponse, addColumnData] = await config.tables.save( + newColumn, + true + ) + }) + + it("Check Table access for admin", async () => { + const adminUser = generateUser(1, "admin") + expect(adminUser[0].builder?.global).toEqual(true) + expect(adminUser[0].admin?.global).toEqual(true) + const [createUserResponse, createUserJson] = await config.users.addMultiple(adminUser) + + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const body: User = { + ...userInfoJson, + roles: { + [app.appId]: "ADMIN", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[app.appId]).toBeDefined() + expect(changedUserInfoJson.roles[app.appId]).toEqual("ADMIN") + + await config.login(adminUser[0].email, adminUser[0].password) + const [createdTableResponse, createdTableData] = await config.tables.save( + generateTable() + ) + const newColumn = generateNewColumnForTable(createdTableData) + const [addColumnResponse, addColumnData] = await config.tables.save( + newColumn, + true + ) + }) + }) + + describe("Screen Access for App specific roles", () => { + it("Check Screen access for BASIC Role", async () => { + // Set up user + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + // Create App + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + // Update user roles + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const prodAppId = db.getProdAppID(app.appId!) + + // Roles must always be set with prod appID + const body: User = { + ...userInfoJson, + roles: { + [prodAppId]: "BASIC", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() + expect(changedUserInfoJson.roles[prodAppId]).toEqual("BASIC") + + await config.screen.create(generateScreen("BASIC")) + await config.screen.create(generateScreen("POWER")) + await config.screen.create(generateScreen("ADMIN")) + + await config.applications.publish(app.appId) + const [firstappPackageResponse, firstappPackageJson] = await config.applications.getAppPackage(app.appId) + expect(firstappPackageJson.screens).toBeDefined() + expect(firstappPackageJson.screens.length).toEqual(3) + + // login with BASIC user + await config.login(appUser[0].email!, appUser[0].password!) + const [selfInfoResponse, selfInfoJson] = await config.users.getSelf() + + // fetch app package + const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId!) + expect(appPackageJson.screens).toBeDefined() + expect(appPackageJson.screens.length).toEqual(1) + expect(appPackageJson.screens[0].routing.roleId).toEqual("BASIC") + }) + + it("Check Screen access for POWER role", async () => { + // Set up user + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + // Create App + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + // Update user roles + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const prodAppId = db.getProdAppID(app.appId!) + + // Roles must always be set with prod appID + const body: User = { + ...userInfoJson, + roles: { + [prodAppId]: "POWER", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() + expect(changedUserInfoJson.roles[prodAppId]).toEqual("POWER") + + await config.screen.create(generateScreen("BASIC")) + await config.screen.create(generateScreen("POWER")) + await config.screen.create(generateScreen("ADMIN")) + + await config.applications.publish(app.appId) + const [firstappPackageResponse, firstappPackageJson] = await config.applications.getAppPackage(app.appId) + expect(firstappPackageJson.screens).toBeDefined() + expect(firstappPackageJson.screens.length).toEqual(3) + + // login with POWER user + await config.login(appUser[0].email!, appUser[0].password!) + const [selfInfoResponse, selfInfoJson] = await config.users.getSelf() + + // fetch app package + const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId!) + expect(appPackageJson.screens).toBeDefined() + expect(appPackageJson.screens.length).toEqual(2) + }) + + it("Check Screen access for ADMIN role", async () => { + // Set up user + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + // Create App + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + // Update user roles + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const prodAppId = db.getProdAppID(app.appId!) + + // Roles must always be set with prod appID + const body: User = { + ...userInfoJson, + roles: { + [prodAppId]: "ADMIN", + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() + expect(changedUserInfoJson.roles[prodAppId]).toEqual("ADMIN") + + await config.screen.create(generateScreen("BASIC")) + await config.screen.create(generateScreen("POWER")) + await config.screen.create(generateScreen("ADMIN")) + + await config.applications.publish(app.appId) + const [firstappPackageResponse, firstappPackageJson] = await config.applications.getAppPackage(app.appId) + expect(firstappPackageJson.screens).toBeDefined() + expect(firstappPackageJson.screens.length).toEqual(3) + + // login with ADMIN user + await config.login(appUser[0].email!, appUser[0].password!) + const [selfInfoResponse, selfInfoJson] = await config.users.getSelf() + + // fetch app package + const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId!) + expect(appPackageJson.screens).toBeDefined() + expect(appPackageJson.screens.length).toEqual(3) + }) + }) + describe("Screen Access for custom roles", () => { + it("Custom role access for level 1 permissions", async () => { + // Set up user + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + // Create App + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + //Create level 1 role + const role = { + inherits: "BASIC", + permissionId: "public", + name: "level 1" + } + const [createRoleResponse, createRoleJson] = await config.users.createRole(role) + + + + // Update user roles + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const prodAppId = db.getProdAppID(app.appId!) + + // Roles must always be set with prod appID + const body: User = { + ...userInfoJson, + roles: { + [prodAppId]: createRoleJson._id, + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() + expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) + + await config.screen.create(generateScreen("BASIC")) + await config.screen.create(generateScreen("POWER")) + await config.screen.create(generateScreen("ADMIN")) + + await config.applications.publish(app.appId) + const [firstappPackageResponse, firstappPackageJson] = await config.applications.getAppPackage(app.appId) + expect(firstappPackageJson.screens).toBeDefined() + expect(firstappPackageJson.screens.length).toEqual(3) + + // login with level 1 user + await config.login(appUser[0].email!, appUser[0].password!) + const [selfInfoResponse, selfInfoJson] = await config.users.getSelf() + + // fetch app package + const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId!) + expect(appPackageJson.screens).toBeDefined() + expect(appPackageJson.screens.length).toEqual(1) + }) + it("Custom role access for level 2 permissions", async () => {// Set up user + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + // Create App + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + //Create level 1 role + const role = { + inherits: "BASIC", + permissionId: "read_only", + name: "level 2" + } + const [createRoleResponse, createRoleJson] = await config.users.createRole(role) + + + + // Update user roles + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const prodAppId = db.getProdAppID(app.appId!) + + // Roles must always be set with prod appID + const body: User = { + ...userInfoJson, + roles: { + [prodAppId]: createRoleJson._id, + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() + expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) + + await config.screen.create(generateScreen("BASIC")) + await config.screen.create(generateScreen("POWER")) + await config.screen.create(generateScreen("ADMIN")) + + await config.applications.publish(app.appId) + const [firstappPackageResponse, firstappPackageJson] = await config.applications.getAppPackage(app.appId) + expect(firstappPackageJson.screens).toBeDefined() + expect(firstappPackageJson.screens.length).toEqual(3) + + // login with level 1 user + await config.login(appUser[0].email!, appUser[0].password!) + const [selfInfoResponse, selfInfoJson] = await config.users.getSelf() + + // fetch app package + const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId!) + expect(appPackageJson.screens).toBeDefined() + expect(appPackageJson.screens.length).toEqual(1) + }) + it("Custom role access for level 3 permissions", async () => { + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + // Create App + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + //Create level 1 role + const role = { + inherits: "BASIC", + permissionId: "write", + name: "level 3" + } + const [createRoleResponse, createRoleJson] = await config.users.createRole(role) + + + + // Update user roles + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const prodAppId = db.getProdAppID(app.appId!) + + // Roles must always be set with prod appID + const body: User = { + ...userInfoJson, + roles: { + [prodAppId]: createRoleJson._id, + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() + expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) + + await config.screen.create(generateScreen("BASIC")) + await config.screen.create(generateScreen("POWER")) + await config.screen.create(generateScreen("ADMIN")) + + await config.applications.publish(app.appId) + const [firstappPackageResponse, firstappPackageJson] = await config.applications.getAppPackage(app.appId) + expect(firstappPackageJson.screens).toBeDefined() + expect(firstappPackageJson.screens.length).toEqual(3) + + // login with level 1 user + await config.login(appUser[0].email!, appUser[0].password!) + const [selfInfoResponse, selfInfoJson] = await config.users.getSelf() + + // fetch app package + const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId!) + expect(appPackageJson.screens).toBeDefined() + expect(appPackageJson.screens.length).toEqual(1) + }) + it("Custom role access for level 4 permissions", async () => { + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + // Create App + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + //Create level 1 role + const role = { + inherits: "BASIC", + permissionId: "power", + name: "level 4" + } + const [createRoleResponse, createRoleJson] = await config.users.createRole(role) + + + + // Update user roles + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const prodAppId = db.getProdAppID(app.appId!) + + // Roles must always be set with prod appID + const body: User = { + ...userInfoJson, + roles: { + [prodAppId]: createRoleJson._id, + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() + expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) + + await config.screen.create(generateScreen("BASIC")) + await config.screen.create(generateScreen("POWER")) + await config.screen.create(generateScreen("ADMIN")) + + await config.applications.publish(app.appId) + const [firstappPackageResponse, firstappPackageJson] = await config.applications.getAppPackage(app.appId) + expect(firstappPackageJson.screens).toBeDefined() + expect(firstappPackageJson.screens.length).toEqual(3) + + // login with level 1 user + await config.login(appUser[0].email!, appUser[0].password!) + const [selfInfoResponse, selfInfoJson] = await config.users.getSelf() + + // fetch app package + const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId!) + expect(appPackageJson.screens).toBeDefined() + expect(appPackageJson.screens.length).toEqual(1) + }) + it("Custom role access for level 5 permissions", async () => { + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [createUserResponse, createUserJson] = await config.users.addMultiple(appUser) + + // Create App + const app = await config.applications.create(generateApp()) + config.applications.api.appId = app.appId + + //Create level 1 role + const role = { + inherits: "BASIC", + permissionId: "admin", + name: "level 5" + } + const [createRoleResponse, createRoleJson] = await config.users.createRole(role) + + + + // Update user roles + const [userInfoResponse, userInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + const prodAppId = db.getProdAppID(app.appId!) + + // Roles must always be set with prod appID + const body: User = { + ...userInfoJson, + roles: { + [prodAppId]: createRoleJson._id, + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(createUserJson.created.successful[0]._id) + expect(changedUserInfoJson.roles[prodAppId]).toBeDefined() + expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id) + + await config.screen.create(generateScreen("BASIC")) + await config.screen.create(generateScreen("POWER")) + await config.screen.create(generateScreen("ADMIN")) + + await config.applications.publish(app.appId) + const [firstappPackageResponse, firstappPackageJson] = await config.applications.getAppPackage(app.appId) + expect(firstappPackageJson.screens).toBeDefined() + expect(firstappPackageJson.screens.length).toEqual(3) + + // login with level 1 user + await config.login(appUser[0].email!, appUser[0].password!) + const [selfInfoResponse, selfInfoJson] = await config.users.getSelf() + + // fetch app package + const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId!) + expect(appPackageJson.screens).toBeDefined() + expect(appPackageJson.screens.length).toEqual(1) + }) + }) + +}) diff --git a/qa-core/src/tests/internal-api/userManagement/userManagement.spec.ts b/qa-core/src/tests/internal-api/userManagement/userManagement.spec.ts new file mode 100644 index 0000000000..32820b8b7f --- /dev/null +++ b/qa-core/src/tests/internal-api/userManagement/userManagement.spec.ts @@ -0,0 +1,90 @@ +import TestConfiguration from "../../../config/internal-api/TestConfiguration" +import { Application } from "@budibase/server/api/controllers/public/mapping/types" +import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient" +import { generateUser } from "../../../config/internal-api/fixtures/userManagement" +import { User } from "@budibase/types" + +describe("Internal API - User Management & Permissions", () => { + const api = new InternalAPIClient() + const config = new TestConfiguration(api) + + // Before each test, login as admin. Some tests will require login as a different user + beforeEach(async () => { + await config.loginAsAdmin() + }) + + afterAll(async () => { + await config.afterAll() + }) + + it("Add Users with different roles", async () => { + await config.users.search() + await config.users.getRoles() + + const admin = generateUser(1, "admin") + expect(admin[0].builder?.global).toEqual(true) + expect(admin[0].admin?.global).toEqual(true) + const developer = generateUser(1, "developer") + expect(developer[0].builder?.global).toEqual(true) + const appUser = generateUser(1, "appUser") + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + + const userList = [...admin, ...developer, ...appUser] + + await config.users.addMultiple(userList) + + const [allUsersResponse, allUsersJson] = await config.users.getAll() + expect(allUsersJson.length).toBeGreaterThan(0) + + + + }) + + it("Delete User", async () => { + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [userResponse, userJson] = await config.users.addMultiple(appUser) + const userId = userJson.created.successful[0]._id + await config.users.delete(userId) + }) + + it("Reset Password", async () => { + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [userResponse, userJson] = await config.users.addMultiple(appUser) + const [userInfoResponse, userInfoJson] = await config.users.getInfo(userJson.created.successful[0]._id) + const body: User = { + ...userInfoJson, + password: "newPassword" + + } + await config.users.forcePasswordReset(body) + }) + + it("Change User information", async () => { + const appUser = generateUser() + expect(appUser[0].builder?.global).toEqual(false) + expect(appUser[0].admin?.global).toEqual(false) + const [userResponse, userJson] = await config.users.addMultiple(appUser) + const [userInfoResponse, userInfoJson] = await config.users.getInfo(userJson.created.successful[0]._id) + const body: User = { + ...userInfoJson, + firstName: "newFirstName", + lastName: "newLastName", + builder: { + global: true + } + } + await config.users.updateInfo(body) + + const [changedUserInfoResponse, changedUserInfoJson] = await config.users.getInfo(userJson.created.successful[0]._id) + expect(changedUserInfoJson.builder?.global).toBeDefined() + expect(changedUserInfoJson.builder?.global).toEqual(true) + }) + + + +})