From a2158cc836cf6d7a6c7e40eabce58acf83734497 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sun, 9 Mar 2025 17:31:45 -0500 Subject: [PATCH 1/4] cleanup events UI --- src/ui/pages/events/ViewEvents.page.tsx | 33 +++++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/ui/pages/events/ViewEvents.page.tsx b/src/ui/pages/events/ViewEvents.page.tsx index a44ef44e..003ac354 100644 --- a/src/ui/pages/events/ViewEvents.page.tsx +++ b/src/ui/pages/events/ViewEvents.page.tsx @@ -1,4 +1,15 @@ -import { Text, Button, Table, Modal, Group, Transition, ButtonGroup, Title } from '@mantine/core'; +import { + Text, + Button, + Table, + Modal, + Group, + Transition, + ButtonGroup, + Title, + Badge, + Anchor, +} from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { notifications } from '@mantine/notifications'; import { IconPlus, IconTrash } from '@tabler/icons-react'; @@ -65,13 +76,21 @@ export const ViewEventsPage: React.FC = () => { style={{ ...styles, display: shouldShow ? 'table-row' : 'none' }} key={`${event.id}-tr`} > - {event.title} + + {event.title} {event.featured ? Featured : null} + {dayjs(event.start).format('MMM D YYYY hh:mm')} {event.end ? dayjs(event.end).format('MMM D YYYY hh:mm') : 'N/A'} - {event.location} - {event.description} + + {event.locationLink ? ( + + {event.location} + + ) : ( + event.location + )} + {event.host} - {event.featured ? 'Yes' : 'No'} {capitalizeFirstLetter(event.repeats || 'Never')} @@ -98,7 +117,7 @@ export const ViewEventsPage: React.FC = () => { useEffect(() => { const getEvents = async () => { const response = await api.get('/api/v1/events'); - const upcomingEvents = await api.get('/api/v1/events?upcomingOnly=true'); + const upcomingEvents = await api.get(`/api/v1/events?upcomingOnly=true&ts=${Date.now()}`); const upcomingEventsSet = new Set(upcomingEvents.data.map((x: EventGetResponse) => x.id)); const events = response.data; events.sort((a: EventGetResponse, b: EventGetResponse) => { @@ -188,9 +207,7 @@ export const ViewEventsPage: React.FC = () => { Start End Location - Description Host - Featured Repeats Actions From 1df672a0eedaf3a56666e8472eeafc3ae31d83aa Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sun, 9 Mar 2025 17:32:01 -0500 Subject: [PATCH 2/4] cleanup IAM permissions --- cloudformation/iam.yml | 169 +++++++++++++++++++++++++++++----------- cloudformation/main.yml | 6 +- 2 files changed, 129 insertions(+), 46 deletions(-) diff --git a/cloudformation/iam.yml b/cloudformation/iam.yml index fbeb9e3a..cfccea49 100644 --- a/cloudformation/iam.yml +++ b/cloudformation/iam.yml @@ -15,7 +15,7 @@ Parameters: SqsQueueArn: Type: String Resources: - ApiLambdaIAMRole: + SqsLambdaIAMRole: Type: AWS::IAM::Role Properties: ManagedPolicyArns: @@ -30,28 +30,89 @@ Resources: Service: - lambda.amazonaws.com Policies: + - PolicyDocument: + Version: "2012-10-17" + Statement: + - Action: + - ses:SendEmail + - ses:SendRawEmail + Effect: Allow + Resource: "*" + Condition: + StringEquals: + ses:FromAddress: + Fn::Sub: "membership@${SesEmailDomain}" + ForAllValues:StringLike: + ses:Recipients: + - "*@illinois.edu" + PolicyName: ses-membership - PolicyDocument: Version: "2012-10-17" Statement: - Action: - - ses:SendEmail - - ses:SendRawEmail + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Effect: Allow + Resource: + - Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:* + - PolicyDocument: + Version: 2012-10-17 + Statement: + - Action: + - secretsmanager:GetSecretValue Effect: Allow + Resource: + - Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config* + PolicyName: lambda-db-secrets + + - PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: DynamoDBTableAccess + Effect: Allow + Action: + - dynamodb:BatchGetItem + - dynamodb:BatchWriteItem + - dynamodb:ConditionCheckItem + - dynamodb:PutItem + - dynamodb:DescribeTable + - dynamodb:DeleteItem + - dynamodb:GetItem + - dynamodb:Scan + - dynamodb:Query + - dynamodb:UpdateItem + Resource: + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-cache + + - Sid: DynamoDBDescribeLimitsAccess + Effect: Allow + Action: + - dynamodb:DescribeLimits Resource: "*" - Condition: - StringEquals: - ses:FromAddress: !Sub "membership@${SesEmailDomain}" - ForAllValues:StringLike: - ses:Recipients: - - "*@illinois.edu" - PolicyName: ses-membership + PolicyName: lambda-dynamo + + ApiLambdaIAMRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Action: + - sts:AssumeRole + Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Policies: - PolicyDocument: Version: "2012-10-17" Statement: - Action: - sqs:SendMessage Effect: Allow - Resource: !Ref SqsQueueArn + Resource: + Fn::Ref: SqsQueueArn PolicyName: lambda-sqs - PolicyDocument: Version: "2012-10-17" @@ -63,16 +124,6 @@ Resources: Effect: Allow Resource: - Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:* - - Effect: Allow - Action: - - ec2:CreateNetworkInterface - - ec2:DescribeNetworkInterfaces - - ec2:DeleteNetworkInterface - - ec2:DescribeSubnets - - ec2:DeleteNetworkInterface - - ec2:AssignPrivateIpAddresses - - ec2:UnassignPrivateIpAddresses - Resource: "*" PolicyName: lambda - PolicyDocument: Version: 2012-10-17 @@ -81,36 +132,60 @@ Resources: - secretsmanager:GetSecretValue Effect: Allow Resource: - - !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config* + - Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config* PolicyName: lambda-db-secrets - PolicyDocument: Version: 2012-10-17 Statement: - - Action: - - dynamodb:* + - Sid: DynamoDBIndexAccess Effect: Allow + Action: + - dynamodb:Query + Resource: + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links/index/* + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events/index/* + + - Sid: DynamoDBStreamAccess + Effect: Allow + Action: + - dynamodb:GetShardIterator + - dynamodb:DescribeStream + - dynamodb:GetRecords + - dynamodb:ListStreams Resource: - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-cache - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-cache/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-purchase-history/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-purchase-history - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-tickets - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-tickets/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-ticketing-metadata/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-ticketing-metadata - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-metadata/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-metadata - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-userroles - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-userroles/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links/* - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-membership-provisioning - - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-membership-provisioning/* + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links/stream/* + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events/stream/* + - Sid: DynamoDBTableAccess + Effect: Allow + Action: + - dynamodb:BatchGetItem + - dynamodb:BatchWriteItem + - dynamodb:ConditionCheckItem + - dynamodb:PutItem + - dynamodb:DescribeTable + - dynamodb:DeleteItem + - dynamodb:GetItem + - dynamodb:Scan + - dynamodb:Query + - dynamodb:UpdateItem + Resource: + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-cache + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-purchase-history + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-tickets + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-ticketing-metadata + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-metadata + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-userroles + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links + - Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-membership-provisioning + + - Sid: DynamoDBDescribeLimitsAccess + Effect: Allow + Action: + - dynamodb:DescribeLimits + Resource: "*" PolicyName: lambda-dynamo Outputs: MainFunctionRoleArn: @@ -119,3 +194,9 @@ Outputs: Fn::GetAtt: - ApiLambdaIAMRole - Arn + SqsFunctionRoleArn: + Description: Sqs IAM role ARN + Value: + Fn::GetAtt: + - SqsLambdaIAMRole + - Arn diff --git a/cloudformation/main.yml b/cloudformation/main.yml index cd7eface..7b97281b 100644 --- a/cloudformation/main.yml +++ b/cloudformation/main.yml @@ -216,8 +216,10 @@ Resources: FunctionName: !Sub ${ApplicationPrefix}-sqs-lambda Handler: index.handler MemorySize: 512 - Role: !GetAtt AppSecurityRoles.Outputs.MainFunctionRoleArn - Timeout: !Ref SqsLambdaTimeout + Role: + Fn::GetAtt: AppSecurityRoles.Outputs.SqsFunctionRoleArn + Timeout: + Fn::Ref: SqsLambdaTimeout LoggingConfig: LogGroup: !Sub /aws/lambda/${ApplicationPrefix}-lambda Environment: From 45f02221aaa5bbc2ea4114a736797c42ea164bf6 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sun, 9 Mar 2025 17:37:51 -0500 Subject: [PATCH 3/4] fix cfn --- cloudformation/iam.yml | 78 +++++++++++++++++++++++------------------ cloudformation/main.yml | 3 +- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/cloudformation/iam.yml b/cloudformation/iam.yml index cfccea49..4d33f98a 100644 --- a/cloudformation/iam.yml +++ b/cloudformation/iam.yml @@ -1,6 +1,7 @@ AWSTemplateFormatVersion: "2010-09-09" Description: Stack IAM Roles Transform: AWS::Serverless-2016-10-31 + Parameters: RunEnvironment: Type: String @@ -14,6 +15,7 @@ Parameters: Type: String SqsQueueArn: Type: String + Resources: SqsLambdaIAMRole: Type: AWS::IAM::Role @@ -30,23 +32,25 @@ Resources: Service: - lambda.amazonaws.com Policies: - - PolicyDocument: - Version: "2012-10-17" - Statement: - - Action: - - ses:SendEmail - - ses:SendRawEmail - Effect: Allow - Resource: "*" - Condition: - StringEquals: - ses:FromAddress: - Fn::Sub: "membership@${SesEmailDomain}" - ForAllValues:StringLike: - ses:Recipients: - - "*@illinois.edu" - PolicyName: ses-membership - - PolicyDocument: + - PolicyName: ses-membership + PolicyDocument: + Version: "2012-10-17" + Statement: + - Action: + - ses:SendEmail + - ses:SendRawEmail + Effect: Allow + Resource: "*" + Condition: + StringEquals: + ses:FromAddress: + Fn::Sub: "membership@${SesEmailDomain}" + ForAllValues:StringLike: + ses:Recipients: + - "*@illinois.edu" + + - PolicyName: lambda-logs + PolicyDocument: Version: "2012-10-17" Statement: - Action: @@ -56,18 +60,20 @@ Resources: Effect: Allow Resource: - Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:* - - PolicyDocument: - Version: 2012-10-17 + + - PolicyName: lambda-db-secrets + PolicyDocument: + Version: "2012-10-17" Statement: - Action: - secretsmanager:GetSecretValue Effect: Allow Resource: - Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config* - PolicyName: lambda-db-secrets - - PolicyDocument: - Version: 2012-10-17 + - PolicyName: lambda-dynamo + PolicyDocument: + Version: "2012-10-17" Statement: - Sid: DynamoDBTableAccess Effect: Allow @@ -90,7 +96,6 @@ Resources: Action: - dynamodb:DescribeLimits Resource: "*" - PolicyName: lambda-dynamo ApiLambdaIAMRole: Type: AWS::IAM::Role @@ -105,16 +110,18 @@ Resources: Service: - lambda.amazonaws.com Policies: - - PolicyDocument: + - PolicyName: lambda-sqs + PolicyDocument: Version: "2012-10-17" Statement: - Action: - sqs:SendMessage Effect: Allow Resource: - Fn::Ref: SqsQueueArn - PolicyName: lambda-sqs - - PolicyDocument: + - Fn::Sub: "${SqsQueueArn}" + + - PolicyName: lambda-logs + PolicyDocument: Version: "2012-10-17" Statement: - Action: @@ -124,18 +131,20 @@ Resources: Effect: Allow Resource: - Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:* - PolicyName: lambda - - PolicyDocument: - Version: 2012-10-17 + + - PolicyName: lambda-db-secrets + PolicyDocument: + Version: "2012-10-17" Statement: - Action: - secretsmanager:GetSecretValue Effect: Allow Resource: - Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config* - PolicyName: lambda-db-secrets - - PolicyDocument: - Version: 2012-10-17 + + - PolicyName: lambda-dynamo + PolicyDocument: + Version: "2012-10-17" Statement: - Sid: DynamoDBIndexAccess Effect: Allow @@ -186,7 +195,7 @@ Resources: Action: - dynamodb:DescribeLimits Resource: "*" - PolicyName: lambda-dynamo + Outputs: MainFunctionRoleArn: Description: Main API IAM role ARN @@ -194,6 +203,7 @@ Outputs: Fn::GetAtt: - ApiLambdaIAMRole - Arn + SqsFunctionRoleArn: Description: Sqs IAM role ARN Value: diff --git a/cloudformation/main.yml b/cloudformation/main.yml index 7b97281b..d39d9333 100644 --- a/cloudformation/main.yml +++ b/cloudformation/main.yml @@ -218,8 +218,7 @@ Resources: MemorySize: 512 Role: Fn::GetAtt: AppSecurityRoles.Outputs.SqsFunctionRoleArn - Timeout: - Fn::Ref: SqsLambdaTimeout + Timeout: !Ref SqsLambdaTimeout LoggingConfig: LogGroup: !Sub /aws/lambda/${ApplicationPrefix}-lambda Environment: From aac7ca0a9a0a7d825111e6f4d89ef06f0d024f31 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sun, 9 Mar 2025 18:02:14 -0500 Subject: [PATCH 4/4] fix e2e test --- tests/e2e/events.spec.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/e2e/events.spec.ts b/tests/e2e/events.spec.ts index 356860a3..cd9d3661 100644 --- a/tests/e2e/events.spec.ts +++ b/tests/e2e/events.spec.ts @@ -26,19 +26,25 @@ describe("Events tests", () => { for (let i = 0; i < rows.length; i++) { const row = rows[i]; const expectedData = expectedTableData[i]; - const title = await row.locator("td:nth-child(1)").innerText(); - const location = await row.locator("td:nth-child(4)").innerText(); - const description = await row.locator("td:nth-child(5)").innerText(); - const host = await row.locator("td:nth-child(6)").innerText(); - const featured = await row.locator("td:nth-child(7)").innerText(); - const repeats = await row.locator("td:nth-child(8)").innerText(); - - expect(title).toEqual(expectedData.title); - expect(location).toEqual(expectedData.location); - expect(description).toEqual(expectedData.description); - expect(host).toEqual(expectedData.host); - expect(featured).toEqual(expectedData.featured ? "Yes" : "No"); - expect(repeats).toEqual(capitalizeFirstLetter(expectedData.repeats)); + + const title = (await row.locator("td:nth-child(1)").innerText()).trim(); + const location = ( + await row.locator("td:nth-child(4)").innerText() + ).trim(); + const host = (await row.locator("td:nth-child(5)").innerText()).trim(); + const repeats = (await row.locator("td:nth-child(6)").innerText()).trim(); + + let expectedTitle = expectedData.title; + if (expectedData.featured) { + expectedTitle = `${expectedData.title} \nFEATURED`; + } + + expect(title.trim()).toEqual(expectedTitle.trim()); + expect(location).toEqual(expectedData.location.trim()); + expect(host).toEqual(expectedData.host.trim()); + expect(repeats).toEqual( + capitalizeFirstLetter(expectedData.repeats).trim(), + ); } expect(page.url()).toEqual("https://core.aws.qa.acmuiuc.org/events/manage");