Skip to content

Commit b2d9eb6

Browse files
committed
feat: route to get IAM role assignments
1 parent 874a882 commit b2d9eb6

File tree

3 files changed

+77
-18
lines changed

3 files changed

+77
-18
lines changed

src/api/routes/iam.ts

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ import {
1616
InternalServerError,
1717
NotFoundError,
1818
} from "../../common/errors/index.js";
19-
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
19+
import {
20+
DynamoDBClient,
21+
PutItemCommand,
22+
ScanCommand,
23+
} from "@aws-sdk/client-dynamodb";
2024
import { genericConfig, roleArns } from "../../common/config.js";
21-
import { marshall } from "@aws-sdk/util-dynamodb";
25+
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
2226
import {
2327
invitePostRequestSchema,
2428
groupMappingCreatePostSchema,
@@ -27,11 +31,9 @@ import {
2731
EntraGroupActions,
2832
entraGroupMembershipListResponse,
2933
entraProfilePatchRequest,
34+
iamGetAllAssignedRolesResponse,
3035
} from "../../common/types/iam.js";
31-
import {
32-
AUTH_DECISION_CACHE_SECONDS,
33-
getGroupRoles,
34-
} from "../functions/authorization.js";
36+
import { AUTH_DECISION_CACHE_SECONDS } from "../functions/authorization.js";
3537
import { getRoleCredentials } from "api/functions/sts.js";
3638
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
3739
import { createAuditLogEntry } from "api/functions/auditLog.js";
@@ -105,32 +107,55 @@ const iamRoutes: FastifyPluginAsync = async (fastify, _options) => {
105107
},
106108
);
107109
fastify.withTypeProvider<FastifyZodOpenApiTypeProvider>().get(
108-
"/groups/:groupId/roles",
110+
"/assignments",
109111
{
110112
schema: withRoles(
111113
[AppRoles.IAM_ADMIN],
112114
withTags(["IAM"], {
113-
params: z.object({
114-
groupId,
115-
}),
116-
summary: "Get a group's application role mappings.",
115+
response: {
116+
200: iamGetAllAssignedRolesResponse,
117+
},
118+
summary: "Get all user and group role assignments.",
117119
}),
118120
),
119-
onRequest: fastify.authorizeFromSchema,
120121
},
121122
async (request, reply) => {
123+
const allUserRoleAssignments = fastify.dynamoClient.send(
124+
new ScanCommand({
125+
TableName: `${genericConfig.IAMTablePrefix}-userroles`,
126+
}),
127+
);
128+
const allGroupRoleAssignments = fastify.dynamoClient.send(
129+
new ScanCommand({
130+
TableName: `${genericConfig.IAMTablePrefix}-grouproles`,
131+
}),
132+
);
122133
try {
123-
const groupId = request.params.groupId;
124-
const roles = await getGroupRoles(fastify.dynamoClient, groupId);
125-
return reply.send(roles);
126-
} catch (e: unknown) {
134+
const [userResponse, groupResponse] = await Promise.all([
135+
allUserRoleAssignments,
136+
allGroupRoleAssignments,
137+
]);
138+
const userUnmarshalled = userResponse.Items?.map((x) => unmarshall(x));
139+
const groupUnmarshalled = groupResponse.Items?.map((x) =>
140+
unmarshall(x),
141+
);
142+
return reply.send({
143+
user:
144+
(userUnmarshalled as z.infer<
145+
typeof iamGetAllAssignedRolesResponse
146+
>["user"]) || [],
147+
group:
148+
(groupUnmarshalled as z.infer<
149+
typeof iamGetAllAssignedRolesResponse
150+
>["group"]) || [],
151+
});
152+
} catch (e) {
127153
if (e instanceof BaseError) {
128154
throw e;
129155
}
130-
131156
request.log.error(e);
132157
throw new DatabaseFetchError({
133-
message: "An error occurred finding the group role mapping.",
158+
message: "Failed to get application mappings.",
134159
});
135160
}
136161
},

src/common/types/iam.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,16 @@ export const entraProfilePatchRequest = z.object({
7474
});
7575

7676
export type ProfilePatchRequest = z.infer<typeof entraProfilePatchRequest>;
77+
78+
export const iamGetAllAssignedRolesResponse = z.object({
79+
user: z.array(z.object({
80+
userEmail: z.string(),
81+
createdAt: z.string().datetime(),
82+
roles: z.array(z.string())
83+
})),
84+
group: z.array(z.object({
85+
groupUuid: z.string(),
86+
createdAt: z.string().datetime(),
87+
roles: z.array(z.string())
88+
}))
89+
})

tests/live/iam.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,32 @@ import { createJwt } from "./utils.js";
33
import {
44
EntraActionResponse,
55
GroupMemberGetResponse,
6+
iamGetAllAssignedRolesResponse,
67
} from "../../src/common/types/iam.js";
78
import { allAppRoles, AppRoles } from "../../src/common/roles.js";
89
import { getBaseEndpoint } from "./utils.js";
10+
import { z } from "zod";
911

1012
const baseEndpoint = getBaseEndpoint();
13+
test("getting all role assignments", async () => {
14+
const token = await createJwt();
15+
const response = await fetch(`${baseEndpoint}/api/v1/iam/assignments`, {
16+
method: "GET",
17+
headers: {
18+
Authorization: `Bearer ${token}`,
19+
"Content-Type": "application/json",
20+
},
21+
});
22+
expect(response.status).toBe(200);
23+
const responseJson = (await response.json()) as z.infer<
24+
typeof iamGetAllAssignedRolesResponse
25+
>;
26+
expect(responseJson["user"]).toBeDefined();
27+
expect(responseJson["group"]).toBeDefined();
28+
expect(responseJson["user"].length).greaterThan(0);
29+
expect(responseJson["group"].length).greaterThan(0);
30+
});
31+
1132
test("getting members of a group", async () => {
1233
const token = await createJwt();
1334
const response = await fetch(

0 commit comments

Comments
 (0)