Skip to content

Commit 2bce592

Browse files
committed
adds roles and updates the can() method in authz.ts to account for role permissions when making an action (proper unit tests included)
1 parent a1f4a8a commit 2bce592

File tree

7 files changed

+121
-27
lines changed

7 files changed

+121
-27
lines changed

data/githubDiscordMap.json

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,76 +2,97 @@
22
"AJaccP": {
33
"githubUsername": "AJaccP",
44
"githubId": "U_kgDOBrIU2w",
5-
"discordId": "693093284998021141"
5+
"discordId": "693093284998021141",
6+
"roles": ["admin"]
67
},
78
"eros-mcguire": {
89
"githubUsername": "eros-mcguire",
910
"githubId": "U_kgDOCLs4uA",
10-
"discordId": "981895462992891935"
11+
"discordId": "981895462992891935",
12+
"roles": ["readonly"]
1113
},
1214
"exkellybur": {
1315
"githubUsername": "exkellybur",
1416
"githubId": "U_kgDOBx7TuA",
15-
"discordId": "398923451311587338"
17+
"discordId": "398923451311587338",
18+
"roles": ["readonly"]
1619
},
1720
"JohnLu2004": {
1821
"githubUsername": "JohnLu2004",
1922
"githubId": "MDQ6VXNlcjg3NjczMDY4",
20-
"discordId": "358054341179080704"
23+
"discordId": "358054341179080704",
24+
"roles": ["readonly"]
2125
},
2226
"kimiaKR": {
2327
"githubUsername": "kimiaKR",
2428
"githubId": "U_kgDOC0aBdw",
25-
"discordId": "706977821502865578"
29+
"discordId": "706977821502865578",
30+
"roles": ["readonly"]
2631
},
2732
"LandonJMM": {
2833
"githubUsername": "LandonJMM",
2934
"githubId": "U_kgDOBs5OWw",
30-
"discordId": "657607140835328059"
35+
"discordId": "657607140835328059",
36+
"roles": ["readonly"]
3137
},
3238
"MathyouMB": {
3339
"githubUsername": "MathyouMB",
3440
"githubId": "MDQ6VXNlcjQzMjIzNjgy",
35-
"discordId": "147881865548791808"
41+
"discordId": "147881865548791808",
42+
"roles": ["admin"]
3643
},
3744
"MrRibcage": {
3845
"githubUsername": "MrRibcage",
3946
"githubId": "MDQ6VXNlcjQzNjU2MTM3",
40-
"discordId": "142782738615762944"
47+
"discordId": "142782738615762944",
48+
"roles": ["readonly"]
4149
},
4250
"Nguyen-HanhNong": {
4351
"githubUsername": "Nguyen-HanhNong",
4452
"githubId": "MDQ6VXNlcjgxOTc3MzUw",
45-
"discordId": "929100662082531398"
53+
"discordId": "929100662082531398",
54+
"roles": ["readonly"]
4655
},
4756
"rebeccakempe12": {
4857
"githubUsername": "rebeccakempe12",
4958
"githubId": "MDQ6VXNlcjc3MzY4MTky",
50-
"discordId": "730876980861599744"
59+
"discordId": "730876980861599744",
60+
"roles": ["readonly"]
5161
},
5262
"richard-dh-kim": {
5363
"githubUsername": "richard-dh-kim",
5464
"githubId": "MDQ6VXNlcjU4OTU5NjQ5",
55-
"discordId": "241421629543022592"
65+
"discordId": "241421629543022592",
66+
"roles": ["readonly"]
5667
},
5768
"rj-sci": {
5869
"githubUsername": "rj-sci",
59-
"githubId": "tbd",
60-
"discordId": "327557300497809422"
70+
"githubId": "MDQ6VXNlcjcxNTM2MjYy",
71+
"discordId": "327557300497809422",
72+
"roles": ["readonly"]
6173
},
6274
"ryangchung": {
6375
"githubUsername": "ryangchung",
6476
"githubId": "MDQ6VXNlcjg3MDI3OTgx",
65-
"discordId": "365948481946517504"
77+
"discordId": "365948481946517504",
78+
"roles": ["readonly"]
6679
},
6780
"VictorLi5611": {
6881
"githubUsername": "VictorLi5611",
6982
"githubId": "MDQ6VXNlcjczMzA1Mjg3",
70-
"discordId": "247472197902270465"
83+
"discordId": "247472197902270465",
84+
"roles": ["readonly"]
7185
},
7286
"VMordvinova": {
7387
"githubUsername": "VMordvinova",
7488
"githubId": "U_kgDOCFXXvw",
75-
"discordId": "759902786107473932"
89+
"discordId": "759902786107473932",
90+
"roles": ["readonly"]
91+
},
92+
"marceljc3": {
93+
"githubUsername": "marceljc3",
94+
"githubId": "MDQ6VXNlcjQ2NjkzMzU2",
95+
"discordId": "1180337219400118393",
96+
"roles": ["readonly"]
7697
}
7798
}

data/roles.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"admin": {
3+
"permissions": ["githubIssue:write", "githubIssue:read"]
4+
},
5+
"readonly": {
6+
"permissions": ["githubIssue:read"]
7+
}
8+
}
Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
11
import githubDiscordMapJson from "../../../data/githubDiscordMap.json";
2+
import rolesJson from "../../../data/roles.json";
23

34
// New structure: map from GitHub username to object with discordId
45
const githubDiscordMap: {
56
[key: string]: {
67
githubUsername: string;
78
githubId: string;
89
discordId: string;
10+
roles: string[];
911
};
1012
} = githubDiscordMapJson;
1113

12-
// TODO: this any should be the generalized discord.js interaction type so that all interactions can leverage this method
13-
export const can = (interaction: any): boolean => {
14-
const userId = interaction.user?.id;
14+
const roles: {
15+
[role: string]: {
16+
permissions: string[];
17+
};
18+
} = rolesJson;
19+
20+
export const can = (discordUserID: string, perms: string[]): boolean => {
21+
const userEntry = Object.values(githubDiscordMap).find(
22+
(entry: any) => entry.discordId === discordUserID
23+
);
24+
25+
if (!userEntry || !userEntry.roles) {
26+
return false;
27+
}
1528

16-
const discordIds = Object.values(githubDiscordMap).map(
17-
(entry) => entry.discordId,
29+
const userPermissions = new Set(
30+
userEntry.roles.flatMap((role: string) => roles[role]?.permissions || [])
1831
);
19-
const isAuthorized = discordIds.includes(userId);
2032

21-
return isAuthorized;
22-
};
33+
return perms.every(perm => userPermissions.has(perm));
34+
};

src/infrastructure/discord/commands/createIssue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const data = new SlashCommandBuilder()
1818
);
1919

2020
export async function execute(interaction: CommandInteraction) {
21-
if (!can(interaction)) {
21+
if (!can(interaction.user.id, ["githubissue:write"])) {
2222
await interaction.reply({
2323
content: "You do not have permission to create an issue.",
2424
ephemeral: true,

src/infrastructure/discord/commands/myIssues.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const data = new SlashCommandBuilder()
2626
);
2727

2828
export async function execute(interaction: CommandInteraction) {
29-
if (!can(interaction)) {
29+
if (!can(interaction.user.id, ["githubissue:read"])) {
3030
await interaction.reply({
3131
content: "You do not have permission to create an issue.",
3232
ephemeral: true,

src/infrastructure/discord/commands/unassignedIssues.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const data = new SlashCommandBuilder()
2727
);
2828

2929
export async function execute(interaction: CommandInteraction) {
30-
if (!can(interaction)) {
30+
if (!can(interaction.user.id, ["githubissue:read"])) {
3131
await interaction.reply({
3232
content: "You do not have permission to view issues.",
3333
ephemeral: true,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { can } from "@infrastructure/discord/authz";
2+
3+
describe("auth tests", () => {
4+
it("will accept one role with all permissions", () =>{
5+
const discordID = "693093284998021141";
6+
jest.mock(
7+
"../../../../data/githubDiscordMap.json",
8+
() => ({
9+
User1: {
10+
discordID: discordID,
11+
roles: ["admin"],
12+
},
13+
}),
14+
{ virtual: true },
15+
);
16+
expect(can(discordID, ["githubIssue:write", "githubIssue:read"])).toBe(true);
17+
});
18+
19+
it("will throw an error if user has no roles", () =>{
20+
const discordID = "12345";
21+
jest.mock(
22+
"../../../../data/githubDiscordMap.json",
23+
() => ({
24+
User1: {
25+
discordID: discordID,
26+
roles: [],
27+
},
28+
}),
29+
{ virtual: true },
30+
);
31+
expect(can(discordID, ["githubIssue:read", "githubIssue:write"])).toBe(false);
32+
});
33+
34+
it("will throw an error if user does not have all of the required permissions", () =>{
35+
const discordID = "142782738615762944";
36+
jest.mock(
37+
"../../../../data/githubDiscordMap.json",
38+
() => ({
39+
User1: {
40+
discordID: discordID,
41+
roles: ["readonly"],
42+
},
43+
}),
44+
{ virtual: true },
45+
);
46+
expect(can(discordID, ["githubIssue:read", "githubIssue:write"])).toBe(false);
47+
});
48+
49+
it('will throw an error if the user is not in the map', () => {
50+
const id = "12345";
51+
expect(can(id, ["githubIssue:write", "githubIssue:read"])).toBe(false);
52+
});
53+
});

0 commit comments

Comments
 (0)