Skip to content

Commit

Permalink
Dev to main (#204)
Browse files Browse the repository at this point in the history
* Added a route to send Profile Blocked Messages (#198)

* Added a route to sent Profile Blocked Messages

* Modification to the message bot is sending

* Authentication Added

* Fixed Lint issues

* Code Cleaning

* Update variables.ts

* Code Cleaning

* Added a re-usable function for Auth (#199)

* Added Auth for Identity Service (#200)

* Added a route to sent Profile Blocked Messages

* Modification to the message bot is sending

* Authentication Added

* Fixed Lint issues

* Code Cleaning

* Update variables.ts

* Code Cleaning

* Added a re-usable function for auth token

* Added Auth for Identity Service

* Set IDENTITY_SERVICE_PUBLIC_KEY environment while deploying (#201)
  • Loading branch information
lakshayman authored Mar 2, 2024
1 parent 515256d commit f4c056c
Show file tree
Hide file tree
Showing 18 changed files with 276 additions and 50 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/register-commands-production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
BOT_PRIVATE_KEY
RDS_SERVERLESS_PUBLIC_KEY
CRON_JOBS_PUBLIC_KEY
IDENTITY_SERVICE_PUBLIC_KEY
env:
CURRENT_ENVIRONMENT: production
CLOUDFLARE_API_TOKEN: ${{secrets.CLOUDFLARE_API_TOKEN}}
Expand All @@ -45,3 +46,4 @@ jobs:
DISCORD_GUILD_ID: ${{secrets.DISCORD_GUILD_ID}}
RDS_SERVERLESS_PUBLIC_KEY: ${{secrets.RDS_SERVERLESS_PUBLIC_KEY}}
CRON_JOBS_PUBLIC_KEY: ${{secrets.CRON_JOBS_PUBLIC_KEY}}
IDENTITY_SERVICE_PUBLIC_KEY: ${{secrets.IDENTITY_SERVICE_PUBLIC_KEY}}
2 changes: 2 additions & 0 deletions .github/workflows/register-commands-staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
CURRENT_ENVIRONMENT
BOT_PRIVATE_KEY
CRON_JOBS_PUBLIC_KEY
IDENTITY_SERVICE_PUBLIC_KEY
env:
CURRENT_ENVIRONMENT: staging
CLOUDFLARE_API_TOKEN: ${{secrets.CLOUDFLARE_API_TOKEN}}
Expand All @@ -43,3 +44,4 @@ jobs:
BOT_PRIVATE_KEY: ${{secrets.BOT_PRIVATE_KEY}}
DISCORD_GUILD_ID: ${{secrets.DISCORD_GUILD_ID}}
CRON_JOBS_PUBLIC_KEY: ${{secrets.CRON_JOBS_PUBLIC_KEY}}
IDENTITY_SERVICE_PUBLIC_KEY: ${{secrets.IDENTITY_SERVICE_PUBLIC_KEY}}
9 changes: 9 additions & 0 deletions config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,32 @@ import {
DEVELOPMENT_RDS_TRACKING_CHANNEL_URL,
STAGING_RDS_TRACKING_CHANNEL_URL,
} from "../src/constants/urls";
import {
DISCORD_PROFILE_SERVICE_HELP_GROUP,
DISCORD_PROFILE_SERVICE_STAGING_HELP_GROUP,
DISCORD_PROFILE_SERVICE_DEVELOPMENT_HELP_GROUP,
} from "../src/constants/variables";

const config = (env: env) => {
const environment: environment = {
production: {
RDS_BASE_API_URL: RDS_BASE_API_URL,
VERIFICATION_SITE_URL: VERIFICATION_SITE_URL,
TRACKING_CHANNEL_URL: RDS_TRACKING_CHANNEL_URL,
PROFILE_SERVICE_HELP_GROUP_ID: DISCORD_PROFILE_SERVICE_HELP_GROUP,
},
staging: {
RDS_BASE_API_URL: RDS_BASE_STAGING_API_URL,
VERIFICATION_SITE_URL: STAGING_VERIFICATION_SITE_URL,
TRACKING_CHANNEL_URL: STAGING_RDS_TRACKING_CHANNEL_URL,
PROFILE_SERVICE_HELP_GROUP_ID: DISCORD_PROFILE_SERVICE_STAGING_HELP_GROUP,
},
default: {
RDS_BASE_API_URL: RDS_BASE_DEVELOPMENT_API_URL,
VERIFICATION_SITE_URL: DEVELOPMENT_VERIFICATION_SITE_URL,
TRACKING_CHANNEL_URL: DEVELOPMENT_RDS_TRACKING_CHANNEL_URL,
PROFILE_SERVICE_HELP_GROUP_ID:
DISCORD_PROFILE_SERVICE_DEVELOPMENT_HELP_GROUP,
},
};

Expand Down
5 changes: 5 additions & 0 deletions src/constants/variables.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export const SUPER_USER_ONE = "154585730465660929";
export const SUPER_USER_TWO = "1040700289348542566";

export const DISCORD_PROFILE_SERVICE_HELP_GROUP = "1209244798557360138";
export const DISCORD_PROFILE_SERVICE_STAGING_HELP_GROUP = "1209248671170953236";
export const DISCORD_PROFILE_SERVICE_DEVELOPMENT_HELP_GROUP =
"1209237447083303014"; //Change this for your local environment
4 changes: 2 additions & 2 deletions src/controllers/changeNickname.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { IRequest } from "itty-router";
import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import * as response from "../constants/responses";
import { verifyAuthToken } from "../utils/verifyAuthToken";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { updateNickName } from "../utils/updateNickname";

export async function changeNickname(request: IRequest, env: env) {
Expand All @@ -14,7 +14,7 @@ export async function changeNickname(request: IRequest, env: env) {
}

try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);
const { discordId, userName } = await request.json();
const res = await updateNickName(discordId, userName, env, reason);
return new JSONResponse(res);
Expand Down
4 changes: 2 additions & 2 deletions src/controllers/generateDiscordInvite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { IRequest } from "itty-router";
import * as response from "../constants/responses";
import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import { verifyAuthToken } from "../utils/verifyAuthToken";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { generateDiscordLink } from "../utils/generateDiscordInvite";
import { inviteLinkBody } from "../typeDefinitions/discordLink.types";

Expand All @@ -12,7 +12,7 @@ export async function generateInviteLink(request: IRequest, env: env) {
return new JSONResponse(response.BAD_SIGNATURE);
}
try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);
const reason = request.headers.get("X-Audit-Log-Reason");

const body: inviteLinkBody = await request.json();
Expand Down
4 changes: 2 additions & 2 deletions src/controllers/getGuildMemberDetailsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as response from "../constants/responses";
import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import { IRequest } from "itty-router";
import { verifyAuthToken } from "../utils/verifyAuthToken";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { getGuildMemberDetails } from "../utils/getGuildMemberDetails";

export async function getGuildMemberDetailsHandler(
Expand All @@ -14,7 +14,7 @@ export async function getGuildMemberDetailsHandler(
return new JSONResponse(response.BAD_SIGNATURE);
}
try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);

const { id: discordId } = request.params;

Expand Down
4 changes: 2 additions & 2 deletions src/controllers/getMembersInServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import { User } from "../typeDefinitions/user.types";
import { getMembersInServer } from "../utils/getMembersInServer";
import { verifyAuthToken } from "../utils/verifyAuthToken";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";

export const getMembersInServerHandler = async (
request: IRequest,
Expand All @@ -17,7 +17,7 @@ export const getMembersInServerHandler = async (
return new JSONResponse(response.BAD_SIGNATURE);
}
try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);

const users = (await getMembersInServer(env)) as User[];

Expand Down
17 changes: 10 additions & 7 deletions src/controllers/guildRoleHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
createNewRole,
memberGroupRole,
} from "../typeDefinitions/discordMessage.types";
import { verifyAuthToken, verifyCronJobsToken } from "../utils/verifyAuthToken";
import {
verifyNodejsBackendAuthToken,
verifyCronJobsToken,
} from "../utils/verifyAuthToken";
import { batchDiscordRequests } from "../utils/batchDiscordRequests";
import { DISCORD_BASE_URL } from "../constants/urls";
import { GROUP_ROLE_ADD } from "../constants/requestsActions";
Expand All @@ -25,7 +28,7 @@ export async function createGuildRoleHandler(request: IRequest, env: env) {
return new JSONResponse(response.BAD_SIGNATURE);
}
try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);
const body: createNewRole = await request.json();
const reason = request.headers.get("X-Audit-Log-Reason");
const res = await createGuildRole(body, env, reason);
Expand All @@ -40,7 +43,7 @@ export async function addGroupRoleHandler(request: IRequest, env: env) {
return new JSONResponse(response.BAD_SIGNATURE);
}
try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);
const body: memberGroupRole = await request.json();
const reason = request.headers.get("X-Audit-Log-Reason");

Expand All @@ -64,7 +67,7 @@ export async function getGuildRolesPostHandler(request: IRequest, env: env) {
if (dev === "true") {
await verifyCronJobsToken(authHeader, env);
} else {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);
}

switch (action) {
Expand Down Expand Up @@ -161,7 +164,7 @@ export async function removeGuildRoleHandler(request: IRequest, env: env) {
return new JSONResponse(response.BAD_SIGNATURE, { status: 401 });
}
try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);
const body: memberGroupRole = await request.json();
const res = await removeGuildRole(body, env, reason);
return new JSONResponse(res, {
Expand All @@ -186,7 +189,7 @@ export async function getGuildRolesHandler(request: IRequest, env: env) {
return new JSONResponse(response.BAD_SIGNATURE, { status: 401 });
}
try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);
const roles = await getGuildRoles(env);
return new JSONResponse({ roles });
} catch (err: any) {
Expand Down Expand Up @@ -223,7 +226,7 @@ export async function getGuildRoleByRoleNameHandler(
return new JSONResponse(response.BAD_REQUEST, { status: 404 });
}
try {
await verifyAuthToken(authHeader, env);
await verifyNodejsBackendAuthToken(authHeader, env);
const role = await getGuildRoleByName(roleName, env);
if (!role) {
return new JSONResponse(response.NOT_FOUND, {
Expand Down
22 changes: 22 additions & 0 deletions src/controllers/profileHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { env } from "../typeDefinitions/default.types";
import { sendProfileServiceBlockedMessage } from "../utils/sendProfileServiceBlockedMessage";
import JSONResponse from "../utils/JsonResponse";
import * as response from "../constants/responses";
import { verifyIdentityServiceAuthToken } from "../utils/verifyAuthToken";

export const sendProfileBlockedMessage = async (request: any, env: env) => {
const authHeader = request.headers.get("Authorization");
if (!authHeader) {
return new JSONResponse(response.BAD_SIGNATURE);
}
try {
await verifyIdentityServiceAuthToken(authHeader, env);
const messageRequest: { userId: string; reason: string } =
await request.json();
const { userId, reason } = messageRequest;
await sendProfileServiceBlockedMessage(userId, reason, env);
return new JSONResponse("Message sent in tracking channel on discord");
} catch (e) {
return new JSONResponse(response.BAD_SIGNATURE);
}
};
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { changeNickname } from "./controllers/changeNickname";
import { getGuildMemberDetailsHandler } from "./controllers/getGuildMemberDetailsHandler";
import { send } from "./handlers/scheduledEventHandler";
import { generateInviteLink } from "./controllers/generateDiscordInvite";
import { sendProfileBlockedMessage } from "./controllers/profileHandler";

const router = Router();

Expand Down Expand Up @@ -54,6 +55,8 @@ router.put("/roles/add", addGroupRoleHandler);

router.delete("/roles", removeGuildRoleHandler);

router.post("/profile/blocked", sendProfileBlockedMessage);

router.post("/", async (request, env) => {
const message: discordMessageRequest = await request.json();

Expand All @@ -76,7 +79,7 @@ router.all("*", async () => {

export default {
async fetch(request: Request, env: env): Promise<Response> {
const apiUrls = ["/invite", "/roles"];
const apiUrls = ["/invite", "/roles", "/profile/blocked"];
const url = new URL(request.url);
if (request.method === "POST" && !apiUrls.includes(url.pathname)) {
const isVerifiedRequest = await verifyBot(request, env);
Expand Down
1 change: 1 addition & 0 deletions src/typeDefinitions/default.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface variables {
RDS_BASE_API_URL: string;
VERIFICATION_SITE_URL: string;
TRACKING_CHANNEL_URL: string;
PROFILE_SERVICE_HELP_GROUP_ID: string;
}

export interface discordCommand {
Expand Down
40 changes: 40 additions & 0 deletions src/utils/sendProfileServiceBlockedMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { env } from "../typeDefinitions/default.types";
import config from "../../config/config";

export const generateStringToBeSent = (
userId: string,
reason: string,
env: env
) => {
const helpGroupRoleId = config(env).PROFILE_SERVICE_HELP_GROUP_ID;
return `Hello${userId ? ` <@${userId}>` : ""},\n${
userId ? "Your" : "Someone's"
} Profile Service is **BLOCKED** because of the below-mentioned reason.${
userId
? ` Please visit the [MY SITE](https://my.realdevsquad.com/identity) to fix this.\nIf you have any issue related to profile service, you can tag <@&${helpGroupRoleId}> and ask for help.`
: ""
}\n\n**Reason:** \`${reason ? reason : "No reason provided"}\``;
};

export async function sendProfileServiceBlockedMessage(
userId: string,
reason: string,
env: env
): Promise<void> {
const stringToBeSent = generateStringToBeSent(userId, reason, env);

const bodyObj = {
content: stringToBeSent,
};

const url = config(env).TRACKING_CHANNEL_URL;

await fetch(url, {
method: "POST",
body: JSON.stringify(bodyObj),
headers: {
"Content-Type": "application/json",
Authorization: `Bot ${env.DISCORD_TOKEN}`,
},
});
}
62 changes: 42 additions & 20 deletions src/utils/verifyAuthToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@ import {
import { env } from "../typeDefinitions/default.types";
import jwt from "@tsndr/cloudflare-worker-jwt";

/**
*
* @param authHeader { string } : the auth header of request
* @param env { env }: the ctx (context) which contains the secrets put in as wrangler secrets.
*/

export async function verifyAuthToken(authHeader: string, env: env) {
async function verifyAuthToken(authHeader: string, public_key: string) {
if (!authHeader) {
throw new Error(INVALID_TOKEN_FORMAT);
}
const parts = authHeader.split(" ");
if (parts.length !== 2 || parts[0] !== "Bearer") {
throw new Error(INVALID_TOKEN_FORMAT);
}
const authToken = parts[1];
const isValid = await jwt.verify(authToken, env.RDS_SERVERLESS_PUBLIC_KEY, {
const isValid = await jwt.verify(authToken, public_key, {
algorithm: "RS256",
});
if (!isValid) {
Expand All @@ -30,19 +27,44 @@ export async function verifyAuthToken(authHeader: string, env: env) {
* @param authHeader { string } : the auth header of request
* @param env { env }: the ctx (context) which contains the secrets put in as wrangler secrets.
*/
export async function verifyCronJobsToken(authHeader: string, env: env) {
if (!authHeader) {
throw new Error(INVALID_TOKEN_FORMAT);

export async function verifyNodejsBackendAuthToken(
authHeader: string,
env: env
) {
try {
await verifyAuthToken(authHeader, env.RDS_SERVERLESS_PUBLIC_KEY);
} catch (err: any) {
throw new Error(err.message);
}
const authHeaderParts = authHeader.split(" ");
if (authHeaderParts.length !== 2 || authHeaderParts[0] !== "Bearer") {
throw new Error(INVALID_TOKEN_FORMAT);
}

/**
*
* @param authHeader { string } : the auth header of request
* @param env { env }: the ctx (context) which contains the secrets put in as wrangler secrets.
*/

export async function verifyIdentityServiceAuthToken(
authHeader: string,
env: env
) {
try {
await verifyAuthToken(authHeader, env.IDENTITY_SERVICE_PUBLIC_KEY);
} catch (err: any) {
throw new Error(err.message);
}
const authToken = authHeaderParts[1];
const isValid = await jwt.verify(authToken, env.CRON_JOBS_PUBLIC_KEY, {
algorithm: "RS256",
});
if (!isValid) {
throw new Error(AUTHENTICATION_ERROR);
}

/**
*
* @param authHeader { string } : the auth header of request
* @param env { env }: the ctx (context) which contains the secrets put in as wrangler secrets.
*/
export async function verifyCronJobsToken(authHeader: string, env: env) {
try {
await verifyAuthToken(authHeader, env.CRON_JOBS_PUBLIC_KEY);
} catch (err: any) {
throw new Error(err.message);
}
}
2 changes: 1 addition & 1 deletion tests/unit/handlers/generateDiscordInvite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as responseConstants from "../../../src/constants/responses";
import { inviteResponseType } from "../../../src/typeDefinitions/discordLink.types";

jest.mock("../../../src/utils/verifyAuthToken", () => ({
verifyAuthToken: jest.fn().mockReturnValue(true),
verifyNodejsBackendAuthToken: jest.fn().mockReturnValue(true),
}));

describe("generate discord link", () => {
Expand Down
Loading

0 comments on commit f4c056c

Please sign in to comment.