-
Notifications
You must be signed in to change notification settings - Fork 54
Discord slash command code to grant AWS access #276
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
Changes from all commits
fff804c
0421825
f3d33e3
50579bf
87a08d7
b9e8d03
9a958f6
7433c40
90f46f4
4b218f0
725e491
0d5a3fd
7130af6
49f45f0
75875f6
fa37aa3
fb649cf
fd71a85
89a8350
fddc63a
2bc33d0
a9f8156
ef03bd0
1ebb5f2
7b72896
865d751
a5c84a8
7016a87
dd153f3
7cc0e36
cb6770b
b70a6b6
dab6d0f
e04b977
82f1af8
3239471
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
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"; | ||
import { DevFlag } from "../typeDefinitions/filterUsersByRole"; | ||
|
||
export async function grantAWSAccessCommand( | ||
transformedArgument: { | ||
member: messageRequestMember; | ||
userDetails: messageRequestDataOptions; | ||
awsGroupDetails: messageRequestDataOptions; | ||
channelId: number; | ||
dev?: DevFlag; | ||
}, | ||
env: env, | ||
ctx: ExecutionContext | ||
) { | ||
const dev = transformedArgument?.dev?.value || false; | ||
if (!dev) { | ||
return discordTextResponse("Please enable feature flag to make this work"); | ||
} | ||
const isUserSuperUser = [SUPER_USER_ONE, SUPER_USER_TWO].includes( | ||
samarpan1738 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import jwt from "@tsndr/cloudflare-worker-jwt"; | ||
|
||
export async function generateDiscordAuthToken( | ||
name: string, | ||
expiry: number, | ||
privateKey: string, | ||
algorithm: string | ||
) { | ||
return await jwt.sign({ name: name, exp: expiry }, privateKey, { | ||
algorithm: algorithm, | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import jwt from "@tsndr/cloudflare-worker-jwt"; | ||
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"; | ||
import { generateDiscordAuthToken } from "./authTokenGenerator"; | ||
|
||
export async function processAWSAccessRequest( | ||
discordUserId: string, | ||
awsGroupId: string, | ||
env: env, | ||
channelId: number | ||
): Promise<void> { | ||
const authToken = await generateDiscordAuthToken( | ||
"Cloudflare Worker", | ||
Math.floor(Date.now() / 1000) + 2, | ||
env.BOT_PRIVATE_KEY, | ||
"RS256" | ||
); | ||
const discordReplyUrl = `${DISCORD_BASE_URL}/channels/${channelId}/messages`; | ||
const base_url = config(env).RDS_BASE_API_URL; | ||
const grantAWSAccessAPIUrl = `${base_url}/aws/groups/access?dev=true`; | ||
|
||
try { | ||
const requestData = { | ||
groupId: awsGroupId, | ||
userId: discordUserId, | ||
}; | ||
|
||
/** | ||
* Grant AWS access is the API in website backend, | ||
* which takes the discordId and AWS groupId, it fetches the | ||
* user based on the discordId, checks if the user is part of AWS account | ||
* if not creates a new user and adds user to the AWS group. | ||
*/ | ||
|
||
const response = await fetch(grantAWSAccessAPIUrl, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${authToken}`, | ||
}, | ||
body: JSON.stringify(requestData), | ||
}); | ||
|
||
let content = ""; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why we can't directly use content:= in line 50 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We had two values of content, so declared the variable before and assigning the values depending on the condition. |
||
if (!response.ok) { | ||
const responseText = await response.text(); | ||
const errorData = JSON.parse(responseText); | ||
content = `<@${discordUserId}> Error occurred while granting AWS access: ${errorData.error}`; | ||
} else { | ||
content = `AWS access granted successfully <@${discordUserId}>! Please head over to AWS - ${AWS_IAM_SIGNIN_URL}.`; | ||
} | ||
await fetch(discordReplyUrl, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
Authorization: `Bot ${env.DISCORD_TOKEN}`, | ||
}, | ||
body: JSON.stringify({ | ||
content: content, | ||
}), | ||
}); | ||
} catch (err) { | ||
const content = `<@${discordUserId}> Error occurred while granting AWS access.`; | ||
await fetch(discordReplyUrl, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
Authorization: `Bot ${env.DISCORD_TOKEN}`, | ||
}, | ||
body: JSON.stringify({ | ||
content: content, | ||
}), | ||
vikhyat187 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
} | ||
} | ||
|
||
export async function grantAWSAccess( | ||
discordUserId: string, | ||
awsGroupId: string, | ||
env: env, | ||
ctx: ExecutionContext, | ||
channelId: number | ||
) { | ||
// Immediately send a Discord response to acknowledge the command, as the cloudfare workers have a limit of response time equals to 3s | ||
const initialResponse = discordTextResponse( | ||
`<@${discordUserId}> Processing your request to grant AWS access.` | ||
); | ||
|
||
ctx.waitUntil( | ||
// Asynchronously call the function to grant AWS access | ||
vikhyat187 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
processAWSAccessRequest(discordUserId, awsGroupId, env, channelId) | ||
); | ||
|
||
// Return the immediate response within 3 seconds | ||
vikhyat187 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return initialResponse; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.