Skip to content

Commit

Permalink
feat: add StaticTeamProvider to default config with some sample teams
Browse files Browse the repository at this point in the history
  • Loading branch information
Tethik committed Oct 11, 2023
1 parent 9e71ebe commit 93839d8
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 11 deletions.
4 changes: 4 additions & 0 deletions api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import threatsV1 from "./resources/gram/v1/threats/index.js";
import tokenV1 from "./resources/gram/v1/token/index.js";
import userV1 from "./resources/gram/v1/user/index.js";
import errorHandler from "./middlewares/errorHandler.js";
import { getTeam } from "./resources/gram/v1/team/get.js";
import { initSentry } from "./util/sentry.js";
import { retryReviewApproval } from "./resources/gram/v1/admin/retryReviewApproval.js";
import { config } from "@gram/core/dist/config/index.js";
Expand Down Expand Up @@ -220,6 +221,9 @@ export async function createApp(dal: DataAccessLayer) {
errorWrap(systemPropertyRoutes.properties)
);

// Team
authenticatedRoutes.get("/teams/:id", cache, errorWrap(getTeam(dal)));

// Component Classes
authenticatedRoutes.get(
"/component-class",
Expand Down
36 changes: 36 additions & 0 deletions api/src/resources/gram/v1/team/get.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import request from "supertest";
import { createTestApp } from "../../../../test-util/app.js";
import { sampleTeam } from "../../../../test-util/sampleTeam.js";
import { sampleUserToken } from "../../../../test-util/sampleTokens.js";

const token = await sampleUserToken();

describe("team.get", () => {
let app: any;

beforeAll(async () => {
({ app } = await createTestApp());
});

it("should return 401 on un-authenticated request", async () => {
const res = await request(app).get("/api/v1/teams/123");
expect(res.status).toBe(401);
});

it("should return 404 on invalid team id", async () => {
const res = await request(app)
.get("/api/v1/teams/234")
.set("Authorization", token);

expect(res.status).toBe(404);
});

it("should return 200 with dummy data", async () => {
const res = await request(app)
.get("/api/v1/teams/" + sampleTeam.id)
.set("Authorization", token);

expect(res.status).toBe(200);
expect(res.body.team).toEqual(sampleTeam);
});
});
16 changes: 16 additions & 0 deletions api/src/resources/gram/v1/team/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { DataAccessLayer } from "@gram/core/dist/data/dal.js";
import { Request, Response } from "express";

export function getTeam(dal: DataAccessLayer) {
return async (req: Request, res: Response) => {
const id = req.params.id;

const team = await dal.teamHandler.getTeam({ currentRequest: req }, id);

if (team === null) {
return res.sendStatus(404);
}

return res.json({ team });
};
}
32 changes: 32 additions & 0 deletions api/src/test-util/TestTeamProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { TeamProvider } from "@gram/core/dist/auth/TeamProvider.js";
import { Team } from "@gram/core/dist/auth/models/Team.js";
import { RequestContext } from "@gram/core/dist/data/providers/RequestContext.js";
import {
sampleAdmin,
sampleOtherUser,
sampleReviewer,
sampleUser,
} from "./sampleUser.js";
import { sampleTeam, sampleOtherTeam, teams } from "./sampleTeam.js";

export class TestTeamProvider implements TeamProvider {
private teamMap: Map<string, string[]>;

constructor() {
this.teamMap = new Map([
[sampleUser.sub, [sampleTeam.id]],
[sampleOtherUser.sub, [sampleOtherTeam.id]],
[sampleReviewer.sub, [sampleOtherTeam.id]],
[sampleAdmin.sub, [sampleTeam.id, sampleOtherTeam.id]],
]);
}
async lookup(ctx: RequestContext, teamIds: string[]): Promise<Team[]> {
return teamIds
.map((tid) => teams.find((team) => team.id === tid))
.filter((t) => t) as Team[];
}
async getTeamsForUser(ctx: RequestContext, userId: string): Promise<Team[]> {
return this.lookup(ctx, this.teamMap.get(userId) || []);
}
key: string = "test";
}
2 changes: 2 additions & 0 deletions api/src/test-util/sampleTeam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export const sampleOtherTeam: Team = {
name: "other team",
email: "bb",
};

export const teams = [sampleTeam, sampleOtherTeam];
6 changes: 4 additions & 2 deletions api/src/test-util/testConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import type {
GramConfiguration,
Providers,
} from "@gram/core/dist/config/GramConfiguration.js";
import { ComponentClass } from "@gram/core/dist/data/component-classes/index.js";
import type { DataAccessLayer } from "@gram/core/dist/data/dal.js";
import classes from "./classes.js";
import { testReviewerProvider } from "./sampleReviewer.js";
import { TestTeamProvider } from "./TestTeamProvider.js";
import { testUserProvider } from "./sampleUser.js";
import { testSystemProvider } from "./system.js";
import { ComponentClass } from "@gram/core/dist/data/component-classes/index.js";
import classes from "./classes.js";

export const testConfig: GramConfiguration = {
appPort: 8080,
Expand Down Expand Up @@ -98,6 +99,7 @@ export const testConfig: GramConfiguration = {
userProvider: testUserProvider,
systemProvider: testSystemProvider,
suggestionSources: [],
teamProvider: new TestTeamProvider(),
};
},
};
1 change: 1 addition & 0 deletions app/src/api/gram/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const api = createApi({
"Suggestions",
"System",
"Templates",
"Team",
"Threats",
"User",
],
Expand Down
14 changes: 14 additions & 0 deletions app/src/api/gram/team.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { api } from "./api";

const teamApi = api.injectEndpoints({
endpoints: (build) => ({
getTeam: build.query({
query: ({ teamId }) => ({
url: `/teams/${teamId}`,
}),
transformResponse: (response, meta, arg) => response.team,
}),
}),
});

export const { useGetTeamQuery } = teamApi;
5 changes: 5 additions & 0 deletions app/src/components/systems/Systems.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,8 @@ div#systems .visualize:focus {
div#systems .visualize:hover:after {
content: "Visualize All Systems";
}

h5 a {
color: #eee;
text-decoration: none;
}
12 changes: 5 additions & 7 deletions app/src/components/systems/TeamSystems/TeamSystemsPageList.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useListSystemsQuery } from "../../../api/gram/system";
import { SystemComplianceBadge } from "../../elements/SystemComplianceBadge";
import Loading from "../../loading";
import "../Systems.css";
import { useGetTeamQuery } from "../../../api/gram/team";

export function TeamSystemsPageList({
teamName,
Expand All @@ -26,17 +27,14 @@ export function TeamSystemsPageList({

const opts = { filter: "team", teamId, pagesize, page };
const { data, isLoading, isFetching } = useListSystemsQuery(opts);

// Because too lazy to write a new endpoint to fetch team name.
const dynamicTeamName =
data && data.systems.length > 0 && data.systems[0].owners.length > 0
? data.systems[0].owners[0].name
: "Team";
const { data: team } = useGetTeamQuery({ teamId });

return (
<Card sx={{ width, marginTop: "25px" }}>
<CardContent>
<Typography variant="h5">{teamName || dynamicTeamName}</Typography>
<Typography variant="h5">
<Link to={`/team/${teamId}`}>{team?.name}</Link>
</Typography>

<List sx={{ height: listHeight, overflow: "auto" }}>
{isLoading || isFetching ? (
Expand Down
26 changes: 24 additions & 2 deletions config/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { StaticAuthzProvider } from "./providers/static/StaticAuthzProvider.js";
import { StaticReviewerProvider } from "./providers/static/StaticReviewerProvider.js";
import { StaticSystemProvider } from "./providers/static/StaticSystemProvider.js";
import { StaticUserProvider } from "./providers/static/StaticUserProvider.js";
import { Team } from "@gram/core/dist/auth/models/Team.js";
import { StaticTeamProvider } from "./providers/static/StaticTeamProvider.js";

export const defaultConfig: GramConfiguration = {
appPort: 8080,
Expand Down Expand Up @@ -132,19 +134,38 @@ export const defaultConfig: GramConfiguration = {
slackUrl: "",
};

const sampleTeams: Team[] = [
{
id: "frontend",
name: "Frontend Team",
email: "frontend@localhost",
},
{
id: "backend",
name: "Backend Team",
email: "backend@localhost",
},
];

const teamMap: Map<string, string[]> = new Map([
["user@localhost", ["frontend"]],
["reviewer@localhost", ["backend"]],
["admin@localhost", ["backend", "frontend"]],
]);

const sampleSystems: System[] = [
new System(
"web",
"Website",
"Website",
[],
[sampleTeams[0]],
"The main website of the org"
),
new System(
"order-api",
"Order API",
"Order API",
[],
[sampleTeams[1]],
"Backend API for receiving orders"
),
];
Expand Down Expand Up @@ -178,6 +199,7 @@ export const defaultConfig: GramConfiguration = {
userProvider: new StaticUserProvider(sampleUsers),
systemProvider: new StaticSystemProvider(sampleSystems),
suggestionSources: [new ThreatLibSuggestionProvider()],
teamProvider: new StaticTeamProvider(sampleTeams, teamMap),
};
},
};
24 changes: 24 additions & 0 deletions config/providers/static/StaticTeamProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { TeamProvider } from "@gram/core/dist/auth/TeamProvider.js";
import { Team } from "@gram/core/dist/auth/models/Team.js";

import { RequestContext } from "@gram/core/dist/data/providers/RequestContext.js";

export class StaticTeamProvider implements TeamProvider {
constructor(
public teams: Team[],
public UserIdToTeamIds: Map<string, string[]>
) {}

async lookup(ctx: RequestContext, teamIds: string[]): Promise<Team[]> {
return teamIds
.map((tid) => this.teams.find((team) => team.id === tid))
.filter((t) => t) as Team[];
}

async getTeamsForUser(ctx: RequestContext, userId: string): Promise<Team[]> {
const teamIds = this.UserIdToTeamIds.get(userId) || [];
return this.lookup(ctx, teamIds);
}

key: string = "static";
}

0 comments on commit 93839d8

Please sign in to comment.