Skip to content

Commit

Permalink
Discord slash command code to grant AWS access
Browse files Browse the repository at this point in the history
  • Loading branch information
vikhyat187 committed Oct 19, 2024
1 parent bf79d4c commit fff804c
Show file tree
Hide file tree
Showing 8 changed files with 2,467 additions and 7,073 deletions.
9,350 changes: 2,279 additions & 7,071 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"format-fix": "prettier --write .",
"fix": "npm run lint-fix && npm run format-fix",
"ngrok": "ngrok http 8787",
"register": "ts-node-esm src/register.ts"
"register": "npx ts-node src/register.ts"
},
"keywords": [],
"author": "",
Expand All @@ -42,7 +42,8 @@
"discord-interactions": "^3.2.0",
"dotenv": "^16.0.3",
"itty-router": "^3.0.11",
"node-fetch": "^3.3.0"
"node-fetch": "^3.3.0",
"uuid": "^10.0.0"
},
"pre-commit": [
"lint-check",
Expand Down
36 changes: 36 additions & 0 deletions src/constants/commands.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { config } from "dotenv";

config();

export const HELLO = {
name: "hello",
description: "Replies with hello in the channel",
Expand Down Expand Up @@ -27,6 +31,38 @@ export const GROUP_INVITE = {
},
],
};
export const GRANT_AWS_ACCESS = {
name: "grant-aws-access",
description: "This command is to grant AWS access to the discord users.",
options: [
{
name: "user-name",
description: "User to be granted the AWS access",
type: 6, //user Id to be grant the access
required: true,
},
{
name: "aws-group-name",
description: "AWS group name",
type: 3,
required: true,
choices: [
{
name: "S3 read only access",
value: process.env.S3_READ_ONLY_ACCESS_AWS_GROUP_ID,
},
{
name: "EC2 deployment access",
value: process.env.EC2_DEPLOYMENT_ACCESS_AWS_GROUP_ID,
},
{
name: "DDB read only access",
value: process.env.DDB_READ_ONLY_ACCESS_AWS_GROUP_ID,
},
],
},
],
};

export const MENTION_EACH = {
name: "mention-each",
Expand Down
1 change: 1 addition & 0 deletions src/constants/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const RDS_BASE_STAGING_API_URL = "https://staging-api.realdevsquad.com";
export const RDS_BASE_DEVELOPMENT_API_URL = "http://localhost:3000"; // If needed, modify the URL to your local API server run through ngrok

export const DISCORD_BASE_URL = "https://discord.com/api/v10";
export const AWS_IAM_SIGNIN_URL = "https://realdevsquad.awsapps.com/start#/";
export const DISCORD_AVATAR_BASE_URL = "https://cdn.discordapp.com/avatars";

export const VERIFICATION_SITE_URL = "https://my.realdevsquad.com";
Expand Down
14 changes: 14 additions & 0 deletions src/controllers/baseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
USER,
REMOVE,
GROUP_INVITE,
GRANT_AWS_ACCESS,
} from "../constants/commands";
import { updateNickName } from "../utils/updateNickname";
import { discordEphemeralResponse } from "../utils/discordEphemeralResponse";
Expand All @@ -44,6 +45,7 @@ import {
import { DevFlag } from "../typeDefinitions/filterUsersByRole";
import { kickEachUser } from "./kickEachUser";
import { groupInvite } from "./groupInvite";
import { grantAWSAccessCommand } from "./grantAWSAccessCommand";

export async function baseHandler(
message: discordMessageRequest,
Expand Down Expand Up @@ -79,6 +81,18 @@ export async function baseHandler(
return await mentionEachUser(transformedArgument, env, ctx);
}

case getCommandName(GRANT_AWS_ACCESS): {
const data = message.data?.options as Array<messageRequestDataOptions>;
const transformedArgument = {
member: message.member,
userDetails: data[0],
awsGroupDetails: data[1],
channelId: message.channel_id,
};

return await grantAWSAccessCommand(transformedArgument, env, ctx);
}

case getCommandName(REMOVE): {
const data = message.data?.options as Array<messageRequestDataOptions>;
const transformedArgument = {
Expand Down
32 changes: 32 additions & 0 deletions src/controllers/grantAWSAccessCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { discordTextResponse } from "../utils/discordResponse";
import { SUPER_USER_ONE, SUPER_USER_TWO } from "../constants/variables";
import { env } from "../typeDefinitions/default.types";
import {
messageRequestMember,
messageRequestDataOptions,
} from "../typeDefinitions/discordMessage.types";
import { grantAWSAccess } from "../utils/awsAccess";

export async function grantAWSAccessCommand(
transformedArgument: {
member: messageRequestMember;
userDetails: messageRequestDataOptions;
awsGroupDetails: messageRequestDataOptions;
channelId: number;
},
env: env,
ctx: ExecutionContext
) {
const isUserSuperUser = [SUPER_USER_ONE, SUPER_USER_TWO].includes(
transformedArgument.member.user.id.toString()
);
if (!isUserSuperUser) {
const responseText = `You're not authorized to make this request.`;
return discordTextResponse(responseText);
}
const roleId = transformedArgument.userDetails.value;
const groupId = transformedArgument.awsGroupDetails.value;
const channelId = transformedArgument.channelId;

return grantAWSAccess(roleId, groupId, env, ctx, channelId);
}
2 changes: 2 additions & 0 deletions src/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
USER,
REMOVE,
GROUP_INVITE,
GRANT_AWS_ACCESS,
} from "./constants/commands";
import { config } from "dotenv";
import { DISCORD_BASE_URL } from "./constants/urls";
Expand Down Expand Up @@ -41,6 +42,7 @@ async function registerGuildCommands(
NOTIFY_ONBOARDING,
REMOVE,
GROUP_INVITE,
GRANT_AWS_ACCESS,
];

try {
Expand Down
100 changes: 100 additions & 0 deletions src/utils/awsAccess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import jwt from "@tsndr/cloudflare-worker-jwt";
import { v4 as uuidv4 } from "uuid";
import { env } from "../typeDefinitions/default.types";
import config from "../../config/config";
import { discordTextResponse } from "./discordResponse";
import { DISCORD_BASE_URL, AWS_IAM_SIGNIN_URL } from "../constants/urls";

async function processAWSAccessRequest(
discordUserId: string,
awsGroupId: string,
env: env,
TraceId: uuidv4,
channelId: number
) {
const authToken = await jwt.sign(
{ name: "Cloudflare Worker", exp: Math.floor(Date.now() / 1000) + 2 },
env.BOT_PRIVATE_KEY,
{ algorithm: "RS256" }
);

try {
const base_url = config(env).RDS_BASE_API_URL;
const requestData = {
groupId: awsGroupId,
userId: discordUserId,
};

const url = `${base_url}/aws-access/`;

const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
body: JSON.stringify(requestData),
});

if (!response.ok) {
return fetch(`${DISCORD_BASE_URL}/channels/${channelId}/messages`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bot ${env.DISCORD_TOKEN}`,
},
body: JSON.stringify({
content: `<@${discordUserId}> Error occurred while granting AWS access: ${response.status} ${response.statusText}`,
}),
});
} else {
return fetch(`${DISCORD_BASE_URL}/channels/${channelId}/messages`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bot ${env.DISCORD_TOKEN}`,
},
body: JSON.stringify({
content: `AWS access granted successfully <@${discordUserId}>! Please head over to AWS - ${AWS_IAM_SIGNIN_URL}.`,
}),
});
}
} catch (err) {
console.log(
`[TraceId: ${TraceId}] Failed to grant AWS Access, error - `,
err
);
return fetch(`${DISCORD_BASE_URL}/channels/${channelId}/messages`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bot ${env.DISCORD_TOKEN}`,
},
body: JSON.stringify({
content: `[TraceId: ${TraceId}] <@${discordUserId}> Error occurred while granting AWS access.`,
}),
});
}
}

export async function grantAWSAccess(
discordUserId: string,
awsGroupId: string,
env: env,
ctx: ExecutionContext,
channelId: number
) {
const TraceId = uuidv4();
// Immediately send a Discord response to acknowledge the command
const initialResponse = discordTextResponse(
`[TraceId: ${TraceId}] <@${discordUserId}> Processing your request to grant AWS access.`
);

ctx.waitUntil(
// Asynchronously call the function to grant AWS access
processAWSAccessRequest(discordUserId, awsGroupId, env, TraceId, channelId)
);

// Return the immediate response within 3 seconds
return initialResponse;
}

0 comments on commit fff804c

Please sign in to comment.