Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bringing realm roles and permissions up to date on a fresh branch #156

Merged
merged 3 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions db/init/020_roles_and_permissions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ CREATE TYPE role_permissions_type AS ENUM (
'post_on_realm',
'comment_on_realm',
'create_thread_on_realm',
'access_locked_boards_on_realm'
'access_locked_boards_on_realm',
'view_roles_on_realm',
'view_roles_on_board'
);

CREATE TABLE IF NOT EXISTS roles
Expand All @@ -37,14 +39,18 @@ CREATE UNIQUE INDEX roles_string_id on roles(string_id);
CREATE TABLE IF NOT EXISTS board_user_roles(
user_id BIGINT REFERENCES users(id) ON DELETE RESTRICT NOT NULL,
board_id BIGINT REFERENCES boards(id) ON DELETE RESTRICT NOT NULL,
role_id BIGINT REFERENCES roles(id) ON DELETE RESTRICT NOT NULL
role_id BIGINT REFERENCES roles(id) ON DELETE RESTRICT NOT NULL,
/*This is a note admins may add to the role assignment for reference*/
label TEXT
);
CREATE UNIQUE INDEX board_user_roles_entry on board_user_roles(user_id, board_id, role_id);

CREATE TABLE IF NOT EXISTS realm_user_roles(
realm_id BIGINT REFERENCES realms(id) ON DELETE RESTRICT NOT NULL,
user_id BIGINT REFERENCES users(id) ON DELETE RESTRICT NOT NULL,
role_id BIGINT REFERENCES roles(id) ON DELETE RESTRICT NOT NULL
role_id BIGINT REFERENCES roles(id) ON DELETE RESTRICT NOT NULL,
/*This is a note admins may add to the role assignment for reference*/
label TEXT
);
CREATE UNIQUE INDEX realm_user_roles_entry on realm_user_roles(user_id, realm_id, role_id);

Expand Down
32 changes: 21 additions & 11 deletions db/test_db_init/070_roles_insert.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,47 @@ VALUES
ARRAY['edit_board_details'::role_permissions_type,
'post_as_role'::role_permissions_type,
'move_thread'::role_permissions_type,
'create_realm_invite'::role_permissions_type]);
'create_realm_invite'::role_permissions_type,
'view_roles_on_realm'::role_permissions_type,
'view_roles_on_board'::role_permissions_type]);

INSERT INTO board_user_roles(user_id, board_id, role_id)
INSERT INTO board_user_roles(user_id, board_id, role_id, label)
VALUES
((SELECT id FROM users WHERE username = 'bobatan'),
(SELECT id FROM boards WHERE slug = 'gore'),
(SELECT id FROM roles WHERE name = 'GoreMaster5000')),
(SELECT id FROM roles WHERE name = 'GoreMaster5000'),
'Test Label'),
((SELECT id FROM users WHERE username = 'bobatan'),
(SELECT id FROM boards WHERE slug = 'memes'),
(SELECT id FROM roles WHERE name = 'The Memester'));
(SELECT id FROM roles WHERE name = 'The Memester'),
'Test Label');

INSERT INTO realm_user_roles(realm_id, user_id, role_id)
INSERT INTO realm_user_roles(realm_id, user_id, role_id, label)
VALUES
((SELECT id FROM realms WHERE slug = 'twisted-minds'),
(SELECT id FROM users WHERE username = 'bobatan'),
(SELECT id FROM roles WHERE name = 'The Owner')),
(SELECT id FROM roles WHERE name = 'The Owner'),
'Look ma, a label'),
((SELECT id FROM realms WHERE slug = 'uwu'),
(SELECT id FROM users WHERE username = 'bobatan'),
(SELECT id FROM roles WHERE name = 'The Memester')),
(SELECT id FROM roles WHERE name = 'The Memester'),
'meme machine'),
((SELECT id FROM realms WHERE slug = 'twisted-minds'),
(SELECT id FROM users WHERE username = 'bobatan'),
(SELECT id FROM roles WHERE name = 'GoreMaster5000')),
(SELECT id FROM roles WHERE name = 'GoreMaster5000'),
'we have fun here'),
((SELECT id FROM realms WHERE slug = 'twisted-minds'),
(SELECT id FROM users WHERE username = 'SexyDaddy69'),
(SELECT id FROM roles WHERE name = 'The Owner')),
(SELECT id FROM roles WHERE name = 'The Owner'),
'well earned'),
((SELECT id FROM realms WHERE slug = 'uwu'),
(SELECT id FROM users WHERE username = 'The Zodiac Killer'),
(SELECT id FROM roles WHERE name = 'The Owner')),
(SELECT id FROM roles WHERE name = 'The Owner'),
'hello world'),
((SELECT id FROM realms WHERE slug = 'twisted-minds'),
(SELECT id FROM users WHERE username = 'oncest5evah'),
(SELECT id FROM roles WHERE name = 'GoreMaster5000'));
(SELECT id FROM roles WHERE name = 'GoreMaster5000'),
'');

INSERT INTO content_warnings(warning)
VALUES
Expand Down
20 changes: 20 additions & 0 deletions server/boards/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,23 @@ export const createThread = async ({
return newThreadExternalId;
});
};

export const getBoardRoles = async ({
boardExternalId,
}: {
boardExternalId: string;
}): Promise<
| {
user_id: string;
username:string
role_id: string;
role_name: string;
label: string | null;
}[]
| null
> => {
const boardRoles = await pool.manyOrNone(sql.fetchRolesInBoard, {
board_external_id: boardExternalId,
});
return boardRoles;
};
79 changes: 78 additions & 1 deletion server/boards/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import {
} from "types/open-api/generated/schemas";
import { BoardPermissions, RealmPermissions } from "types/permissions";
import { CacheKeys, cache } from "server/cache";
import {
Internal500Error,
NotFound404Error,
} from "types/errors/api";
import {
createThread,
dismissBoardNotifications,
getBoardRoles,
markBoardVisit,
muteBoard,
pinBoard,
Expand All @@ -27,7 +32,6 @@ import {
makeServerThread,
} from "utils/response-utils";

import { NotFound404Error } from "types/errors/api";
import debug from "debug";
import { ensureLoggedIn } from "handlers/auth";
import express from "express";
Expand All @@ -36,6 +40,7 @@ import { getThreadByExternalId } from "server/threads/queries";

const info = debug("bobaserver:board:routes-info");
const log = debug("bobaserver:board:routes");
const error = debug("bobaserver:board:routes-error");

const router = express.Router();

Expand Down Expand Up @@ -704,4 +709,76 @@ router.delete(
}
);

/**
* @openapi
* /boards/{board_id}/roles:
* get:
* summary: Fetches latest roles summary for the board.
* operationId: getBoardRolesByExternalId
* tags:
* - /boards/
* security:
* - firebase: []
* parameters:
* - name: board_id
* in: path
* description: The id of the board.
* required: true
* schema:
* type: string
* format: uuid
* responses:
* 200:
* description: The board roles summary.
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/RealmRoles"
* examples:
* twisted_minds:
* value:
* roles:
* - user_firebase_id: "a90b0809-2c57-4ff1-be7c-4b7ab1b7edcc"
* username: "bobatan"
* role_string_id: "3df1d417-c36a-43dd-aaba-9590316ffc32"
* role_name: "The Owner"
* label: "Look ma, a label"
* 401:
* $ref: "#/components/responses/ensureLoggedIn401"
* 403:
* $ref: "#/components/responses/ensurePermission403"
* 404:
* description: The board was not found.
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/genericResponse"
* 500:
* description: There was an error fetching board roles.
*/

router.get(
"/:board_id/roles",
ensureBoardAccess, ensureLoggedIn,
ensureBoardPermission(BoardPermissions.viewRolesOnBoard),
async (req, res) => {
try {
const { board_id } = req.params;
const boardRoles = await getBoardRoles({
boardExternalId: board_id,
});
if (!boardRoles?.length){
res.status(200).json({roles:[]});
return;
}
res.status(200).json({
roles: boardRoles || [],
});
} catch (e) {
error(e);
throw new Internal500Error("There was an error fetching board roles.");
}
}
);

export default router;
17 changes: 17 additions & 0 deletions server/boards/sql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,22 @@ const updateBoardSettings = `
settings = $/settings/
WHERE boards.string_id = $/board_id/`;


const fetchRolesInBoard = `
SELECT
users.firebase_id as user_firebase_id,
users.username as username,
roles.string_id as role_string_id,
roles.name as role_name,
board_user_roles.label as label
FROM board_user_roles
INNER JOIN boards ON board_user_roles.board_id = boards.id
INNER JOIN roles ON board_user_roles.role_id=roles.id
INNER JOIN users ON users.id=board_user_roles.user_id
WHERE boards.string_id = $/board_external_id/`;



export default {
getAllBoards: new QueryFile(path.join(__dirname, "all-boards.sql")),
getBoardByExternalId: new QueryFile(
Expand All @@ -133,4 +149,5 @@ export default {
pinBoardByExternalId,
unpinBoardByExternalId,
dismissNotificationsByExternalId,
fetchRolesInBoard,
};
2 changes: 2 additions & 0 deletions server/boards/sql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export const BoardPermissionsEnumSchema = z.enum([
"comment_on_realm",
"create_thread_on_realm",
"access_locked_boards_on_realm",
"view_roles_on_realm",
"view_roles_on_board",
]);
export const BoardRestrictionsEnumSchema = z.enum(["lock_access", "delist"]);
export type BoardRestrictionsEnum = z.infer<typeof BoardRestrictionsEnumSchema>;
Expand Down
2 changes: 2 additions & 0 deletions server/boards/tests/by-external-id.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ const GORE_BOARD_LOGGED_IN: BoardByExternalId = {
"edit_content_notices",
"move_thread",
"create_realm_invite",
"view_roles_on_realm",
"view_roles_on_board",
],
pinned_order: 1,
posting_identities: [
Expand Down
48 changes: 48 additions & 0 deletions server/boards/tests/routes/board-roles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
BOBATAN_USER_ID,
JERSEY_DEVIL_USER_ID,
} from "test/data/auth";
import { setLoggedInUser, startTestServer } from "utils/test-utils";

import {
GORE_BOARD_ID,
} from "test/data/boards";
import debug from "debug";
import { mocked } from "ts-jest/utils";
import request from "supertest";
import router from "../../routes";
import stringify from "fast-json-stable-stringify";

const log = debug("bobaserver:board:routes");
jest.mock("handlers/auth");


describe("Tests board role queries", () => {
const server = startTestServer(router);

test("fetches board roles with permissions", async () => {
setLoggedInUser(BOBATAN_USER_ID);
const res = await request(server.app).get(`/${GORE_BOARD_ID}/roles`);
expect(res.status).toBe(200);
expect(res.body).toEqual({
roles: [
{
user_firebase_id: 'c6HimTlg2RhVH3fC1psXZORdLcx2',
username: 'bobatan',
role_string_id: "e5f86f53-6dcd-4f15-b6ea-6ca1f088e62d",
role_name: 'GoreMaster5000',
label: 'Test Label'
}
]
});
});

test("does not fetch board roles without permissions", async () => {
setLoggedInUser(JERSEY_DEVIL_USER_ID);
const res = await request(server.app).get(`/${GORE_BOARD_ID}/roles`);
expect(res.status).toBe(403);
expect(res.body).toEqual({
message: 'User does not have required permissions for board operation.'
});
});
});
20 changes: 20 additions & 0 deletions server/realms/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,23 @@ export const checkUserOnRealm = async ({
return null;
}
};

export const getRealmRoles = async ({
realmExternalId,
}: {
realmExternalId: string;
}): Promise<
| {
user_id: string;
username:string
role_id: string;
role_name: string;
label: string | null;
}[]
| null
> => {
const realmRoles = await pool.manyOrNone(sql.fetchRolesInRealm, {
realm_external_id: realmExternalId,
});
return realmRoles;
};
Loading
Loading