Skip to content

Commit 1b1375a

Browse files
authored
Cleanup IAM roles and web UI (#71)
* cleanup events UI * cleanup IAM permissions * fix cfn * fix e2e test
1 parent a1e036e commit 1b1375a

File tree

4 files changed

+185
-70
lines changed

4 files changed

+185
-70
lines changed

cloudformation/iam.yml

Lines changed: 139 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
AWSTemplateFormatVersion: "2010-09-09"
22
Description: Stack IAM Roles
33
Transform: AWS::Serverless-2016-10-31
4+
45
Parameters:
56
RunEnvironment:
67
Type: String
@@ -14,8 +15,9 @@ Parameters:
1415
Type: String
1516
SqsQueueArn:
1617
Type: String
18+
1719
Resources:
18-
ApiLambdaIAMRole:
20+
SqsLambdaIAMRole:
1921
Type: AWS::IAM::Role
2022
Properties:
2123
ManagedPolicyArns:
@@ -30,7 +32,8 @@ Resources:
3032
Service:
3133
- lambda.amazonaws.com
3234
Policies:
33-
- PolicyDocument:
35+
- PolicyName: ses-membership
36+
PolicyDocument:
3437
Version: "2012-10-17"
3538
Statement:
3639
- Action:
@@ -40,20 +43,85 @@ Resources:
4043
Resource: "*"
4144
Condition:
4245
StringEquals:
43-
ses:FromAddress: !Sub "membership@${SesEmailDomain}"
46+
ses:FromAddress:
47+
Fn::Sub: "membership@${SesEmailDomain}"
4448
ForAllValues:StringLike:
4549
ses:Recipients:
4650
- "*@illinois.edu"
47-
PolicyName: ses-membership
48-
- PolicyDocument:
51+
52+
- PolicyName: lambda-logs
53+
PolicyDocument:
54+
Version: "2012-10-17"
55+
Statement:
56+
- Action:
57+
- logs:CreateLogGroup
58+
- logs:CreateLogStream
59+
- logs:PutLogEvents
60+
Effect: Allow
61+
Resource:
62+
- Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:*
63+
64+
- PolicyName: lambda-db-secrets
65+
PolicyDocument:
66+
Version: "2012-10-17"
67+
Statement:
68+
- Action:
69+
- secretsmanager:GetSecretValue
70+
Effect: Allow
71+
Resource:
72+
- Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config*
73+
74+
- PolicyName: lambda-dynamo
75+
PolicyDocument:
76+
Version: "2012-10-17"
77+
Statement:
78+
- Sid: DynamoDBTableAccess
79+
Effect: Allow
80+
Action:
81+
- dynamodb:BatchGetItem
82+
- dynamodb:BatchWriteItem
83+
- dynamodb:ConditionCheckItem
84+
- dynamodb:PutItem
85+
- dynamodb:DescribeTable
86+
- dynamodb:DeleteItem
87+
- dynamodb:GetItem
88+
- dynamodb:Scan
89+
- dynamodb:Query
90+
- dynamodb:UpdateItem
91+
Resource:
92+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-cache
93+
94+
- Sid: DynamoDBDescribeLimitsAccess
95+
Effect: Allow
96+
Action:
97+
- dynamodb:DescribeLimits
98+
Resource: "*"
99+
100+
ApiLambdaIAMRole:
101+
Type: AWS::IAM::Role
102+
Properties:
103+
AssumeRolePolicyDocument:
104+
Version: "2012-10-17"
105+
Statement:
106+
- Action:
107+
- sts:AssumeRole
108+
Effect: Allow
109+
Principal:
110+
Service:
111+
- lambda.amazonaws.com
112+
Policies:
113+
- PolicyName: lambda-sqs
114+
PolicyDocument:
49115
Version: "2012-10-17"
50116
Statement:
51117
- Action:
52118
- sqs:SendMessage
53119
Effect: Allow
54-
Resource: !Ref SqsQueueArn
55-
PolicyName: lambda-sqs
56-
- PolicyDocument:
120+
Resource:
121+
- Fn::Sub: "${SqsQueueArn}"
122+
123+
- PolicyName: lambda-logs
124+
PolicyDocument:
57125
Version: "2012-10-17"
58126
Statement:
59127
- Action:
@@ -63,59 +131,82 @@ Resources:
63131
Effect: Allow
64132
Resource:
65133
- Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:*
66-
- Effect: Allow
67-
Action:
68-
- ec2:CreateNetworkInterface
69-
- ec2:DescribeNetworkInterfaces
70-
- ec2:DeleteNetworkInterface
71-
- ec2:DescribeSubnets
72-
- ec2:DeleteNetworkInterface
73-
- ec2:AssignPrivateIpAddresses
74-
- ec2:UnassignPrivateIpAddresses
75-
Resource: "*"
76-
PolicyName: lambda
77-
- PolicyDocument:
78-
Version: 2012-10-17
134+
135+
- PolicyName: lambda-db-secrets
136+
PolicyDocument:
137+
Version: "2012-10-17"
79138
Statement:
80139
- Action:
81140
- secretsmanager:GetSecretValue
82141
Effect: Allow
83142
Resource:
84-
- !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config*
85-
PolicyName: lambda-db-secrets
86-
- PolicyDocument:
87-
Version: 2012-10-17
143+
- Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config*
144+
145+
- PolicyName: lambda-dynamo
146+
PolicyDocument:
147+
Version: "2012-10-17"
88148
Statement:
89-
- Action:
90-
- dynamodb:*
149+
- Sid: DynamoDBIndexAccess
91150
Effect: Allow
151+
Action:
152+
- dynamodb:Query
153+
Resource:
154+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links/index/*
155+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events/index/*
156+
157+
- Sid: DynamoDBStreamAccess
158+
Effect: Allow
159+
Action:
160+
- dynamodb:GetShardIterator
161+
- dynamodb:DescribeStream
162+
- dynamodb:GetRecords
163+
- dynamodb:ListStreams
164+
Resource:
165+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links/stream/*
166+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events/stream/*
167+
168+
- Sid: DynamoDBTableAccess
169+
Effect: Allow
170+
Action:
171+
- dynamodb:BatchGetItem
172+
- dynamodb:BatchWriteItem
173+
- dynamodb:ConditionCheckItem
174+
- dynamodb:PutItem
175+
- dynamodb:DescribeTable
176+
- dynamodb:DeleteItem
177+
- dynamodb:GetItem
178+
- dynamodb:Scan
179+
- dynamodb:Query
180+
- dynamodb:UpdateItem
92181
Resource:
93-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events/*
94-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events
95-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-cache
96-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-cache/*
97-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-purchase-history/*
98-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-purchase-history
99-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-tickets
100-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-tickets/*
101-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-ticketing-metadata/*
102-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-ticketing-metadata
103-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-metadata/*
104-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-metadata
105-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-userroles
106-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-userroles/*
107-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles
108-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles/*
109-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links
110-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links/*
111-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-membership-provisioning
112-
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-membership-provisioning/*
182+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-events
183+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-cache
184+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-purchase-history
185+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-tickets
186+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-events-ticketing-metadata
187+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-merchstore-metadata
188+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-userroles
189+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-iam-grouproles
190+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-stripe-links
191+
- Fn::Sub: arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/infra-core-api-membership-provisioning
192+
193+
- Sid: DynamoDBDescribeLimitsAccess
194+
Effect: Allow
195+
Action:
196+
- dynamodb:DescribeLimits
197+
Resource: "*"
113198

114-
PolicyName: lambda-dynamo
115199
Outputs:
116200
MainFunctionRoleArn:
117201
Description: Main API IAM role ARN
118202
Value:
119203
Fn::GetAtt:
120204
- ApiLambdaIAMRole
121205
- Arn
206+
207+
SqsFunctionRoleArn:
208+
Description: Sqs IAM role ARN
209+
Value:
210+
Fn::GetAtt:
211+
- SqsLambdaIAMRole
212+
- Arn

cloudformation/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ Resources:
216216
FunctionName: !Sub ${ApplicationPrefix}-sqs-lambda
217217
Handler: index.handler
218218
MemorySize: 512
219-
Role: !GetAtt AppSecurityRoles.Outputs.MainFunctionRoleArn
219+
Role:
220+
Fn::GetAtt: AppSecurityRoles.Outputs.SqsFunctionRoleArn
220221
Timeout: !Ref SqsLambdaTimeout
221222
LoggingConfig:
222223
LogGroup: !Sub /aws/lambda/${ApplicationPrefix}-lambda

src/ui/pages/events/ViewEvents.page.tsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
import { Text, Button, Table, Modal, Group, Transition, ButtonGroup, Title } from '@mantine/core';
1+
import {
2+
Text,
3+
Button,
4+
Table,
5+
Modal,
6+
Group,
7+
Transition,
8+
ButtonGroup,
9+
Title,
10+
Badge,
11+
Anchor,
12+
} from '@mantine/core';
213
import { useDisclosure } from '@mantine/hooks';
314
import { notifications } from '@mantine/notifications';
415
import { IconPlus, IconTrash } from '@tabler/icons-react';
@@ -65,13 +76,21 @@ export const ViewEventsPage: React.FC = () => {
6576
style={{ ...styles, display: shouldShow ? 'table-row' : 'none' }}
6677
key={`${event.id}-tr`}
6778
>
68-
<Table.Td>{event.title}</Table.Td>
79+
<Table.Td>
80+
{event.title} {event.featured ? <Badge color="green">Featured</Badge> : null}
81+
</Table.Td>
6982
<Table.Td>{dayjs(event.start).format('MMM D YYYY hh:mm')}</Table.Td>
7083
<Table.Td>{event.end ? dayjs(event.end).format('MMM D YYYY hh:mm') : 'N/A'}</Table.Td>
71-
<Table.Td>{event.location}</Table.Td>
72-
<Table.Td>{event.description}</Table.Td>
84+
<Table.Td>
85+
{event.locationLink ? (
86+
<Anchor target="_blank" size="sm" href={event.locationLink}>
87+
{event.location}
88+
</Anchor>
89+
) : (
90+
event.location
91+
)}
92+
</Table.Td>
7393
<Table.Td>{event.host}</Table.Td>
74-
<Table.Td>{event.featured ? 'Yes' : 'No'}</Table.Td>
7594
<Table.Td>{capitalizeFirstLetter(event.repeats || 'Never')}</Table.Td>
7695
<Table.Td>
7796
<ButtonGroup>
@@ -98,7 +117,7 @@ export const ViewEventsPage: React.FC = () => {
98117
useEffect(() => {
99118
const getEvents = async () => {
100119
const response = await api.get('/api/v1/events');
101-
const upcomingEvents = await api.get('/api/v1/events?upcomingOnly=true');
120+
const upcomingEvents = await api.get(`/api/v1/events?upcomingOnly=true&ts=${Date.now()}`);
102121
const upcomingEventsSet = new Set(upcomingEvents.data.map((x: EventGetResponse) => x.id));
103122
const events = response.data;
104123
events.sort((a: EventGetResponse, b: EventGetResponse) => {
@@ -188,9 +207,7 @@ export const ViewEventsPage: React.FC = () => {
188207
<Table.Th>Start</Table.Th>
189208
<Table.Th>End</Table.Th>
190209
<Table.Th>Location</Table.Th>
191-
<Table.Th>Description</Table.Th>
192210
<Table.Th>Host</Table.Th>
193-
<Table.Th>Featured</Table.Th>
194211
<Table.Th>Repeats</Table.Th>
195212
<Table.Th>Actions</Table.Th>
196213
</Table.Tr>

tests/e2e/events.spec.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,25 @@ describe("Events tests", () => {
2626
for (let i = 0; i < rows.length; i++) {
2727
const row = rows[i];
2828
const expectedData = expectedTableData[i];
29-
const title = await row.locator("td:nth-child(1)").innerText();
30-
const location = await row.locator("td:nth-child(4)").innerText();
31-
const description = await row.locator("td:nth-child(5)").innerText();
32-
const host = await row.locator("td:nth-child(6)").innerText();
33-
const featured = await row.locator("td:nth-child(7)").innerText();
34-
const repeats = await row.locator("td:nth-child(8)").innerText();
35-
36-
expect(title).toEqual(expectedData.title);
37-
expect(location).toEqual(expectedData.location);
38-
expect(description).toEqual(expectedData.description);
39-
expect(host).toEqual(expectedData.host);
40-
expect(featured).toEqual(expectedData.featured ? "Yes" : "No");
41-
expect(repeats).toEqual(capitalizeFirstLetter(expectedData.repeats));
29+
30+
const title = (await row.locator("td:nth-child(1)").innerText()).trim();
31+
const location = (
32+
await row.locator("td:nth-child(4)").innerText()
33+
).trim();
34+
const host = (await row.locator("td:nth-child(5)").innerText()).trim();
35+
const repeats = (await row.locator("td:nth-child(6)").innerText()).trim();
36+
37+
let expectedTitle = expectedData.title;
38+
if (expectedData.featured) {
39+
expectedTitle = `${expectedData.title} \nFEATURED`;
40+
}
41+
42+
expect(title.trim()).toEqual(expectedTitle.trim());
43+
expect(location).toEqual(expectedData.location.trim());
44+
expect(host).toEqual(expectedData.host.trim());
45+
expect(repeats).toEqual(
46+
capitalizeFirstLetter(expectedData.repeats).trim(),
47+
);
4248
}
4349

4450
expect(page.url()).toEqual("https://core.aws.qa.acmuiuc.org/events/manage");

0 commit comments

Comments
 (0)