Skip to content

Commit 75e9b5d

Browse files
authored
add onboarding (#155)
1 parent 9f864b7 commit 75e9b5d

File tree

18 files changed

+559
-47
lines changed

18 files changed

+559
-47
lines changed

backend/src/api/keys/index.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { createApiKey } from "./service"
77
import Error400BadRequest from "errors/error-400-bad-request"
88
import { createQB, getRepository } from "services/database/utils"
99
import { MetloRequest } from "types"
10+
import { API_KEY_TYPE } from "@common/enums"
1011

1112
export const listKeys = async (
1213
req: MetloRequest,
@@ -28,7 +29,7 @@ export const createKey = async (
2829
req: MetloRequest,
2930
res: Response,
3031
): Promise<void> => {
31-
const { name: keyName } = req.body
32+
const { name: keyName, keyFor } = req.body
3233
const key_exists = await getRepository(req.ctx, ApiKey).countBy({
3334
name: keyName,
3435
})
@@ -44,7 +45,20 @@ export const createKey = async (
4445
new Error400BadRequest(`Key name is required.`),
4546
)
4647
}
48+
if (keyFor && !API_KEY_TYPE[keyFor]) {
49+
return ApiResponseHandler.error(
50+
res,
51+
new Error400BadRequest(
52+
`The key must be for one of the following: ${Object.keys(
53+
API_KEY_TYPE,
54+
).join(", ")}`,
55+
),
56+
)
57+
}
4758
const [key, rawKey] = createApiKey(keyName)
59+
if (keyFor) {
60+
key.for = API_KEY_TYPE[keyFor]
61+
}
4862
await getRepository(req.ctx, ApiKey).save(key)
4963
return ApiResponseHandler.success(res, {
5064
apiKey: rawKey,
@@ -78,3 +92,28 @@ export const deleteKey = async (
7892
)
7993
}
8094
}
95+
96+
export const getOnboardingKeys = async (
97+
req: MetloRequest,
98+
res: Response,
99+
): Promise<void> => {
100+
try {
101+
const keys = await getRepository(req.ctx, ApiKey).find({
102+
where: { for: API_KEY_TYPE.ONBOARDING },
103+
order: {
104+
createdAt: "DESC",
105+
},
106+
})
107+
const payload = keys
108+
? keys.map<ApiKeyType>(v => ({
109+
name: v.name,
110+
identifier: `metlo.${v.keyIdentifier}`,
111+
created: v.createdAt.toISOString(),
112+
for: v.for,
113+
}))
114+
: null
115+
await ApiResponseHandler.success(res, payload)
116+
} catch (err) {
117+
await ApiResponseHandler.error(res, err)
118+
}
119+
}

backend/src/api/summary/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Response } from "express"
2-
import { getSummaryData } from "services/summary"
2+
import { getEndpointExists, getSummaryData } from "services/summary"
33
import ApiResponseHandler from "api-response-handler"
44
import { MetloRequest } from "types"
55

@@ -14,3 +14,15 @@ export const getSummaryHandler = async (
1414
await ApiResponseHandler.error(res, err)
1515
}
1616
}
17+
18+
export const getEndpointTrackedHandler = async (
19+
req: MetloRequest,
20+
res: Response,
21+
): Promise<void> => {
22+
try {
23+
const exists = await getEndpointExists(req.ctx)
24+
await ApiResponseHandler.success(res, { exists })
25+
} catch (err) {
26+
await ApiResponseHandler.error(res, err)
27+
}
28+
}

backend/src/data-source.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { addApiEndpointUuidIndexForAlert1667259254414 } from "migrations/1667259
2626
import { MetloConfig } from "models/metlo-config"
2727
import { addMetloConfigTable1667599667595 } from "migrations/1667599667595-add-metlo-config-table"
2828
import { updateDisabledPathsColumnBlockFieldsTable1667606447208 } from "migrations/1667606447208-update-disabledPaths-column-blockFields-table"
29+
import { removeApiKeyTypeEnum1669778297643 } from "migrations/1669778297643-remove-apiKeyType-enum"
2930

3031
export const AppDataSource: DataSource = new DataSource({
3132
type: "postgres",
@@ -57,6 +58,7 @@ export const AppDataSource: DataSource = new DataSource({
5758
addApiEndpointUuidIndexForAlert1667259254414,
5859
addMetloConfigTable1667599667595,
5960
updateDisabledPathsColumnBlockFieldsTable1667606447208,
61+
removeApiKeyTypeEnum1669778297643,
6062
],
6163
migrationsRun: runMigration,
6264
logging: false,

backend/src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
} from "api/spec"
2323
import { getAlertsHandler, updateAlertHandler } from "api/alert"
2424
import { deleteDataFieldHandler, updateDataFieldClasses } from "api/data-field"
25-
import { getSummaryHandler } from "api/summary"
25+
import { getEndpointTrackedHandler, getSummaryHandler } from "api/summary"
2626
import { MetloRequest } from "types"
2727
import { AppDataSource } from "data-source"
2828
import { MulterSource } from "multer-source"
@@ -37,7 +37,7 @@ import { RedisClient } from "utils/redis"
3737
import { getSensitiveDataSummaryHandler } from "api/data-field/sensitive-data"
3838
import { getVulnerabilitySummaryHandler } from "api/alert/vulnerability"
3939
import { inSandboxMode } from "utils"
40-
import { createKey, deleteKey, listKeys } from "api/keys"
40+
import { createKey, deleteKey, getOnboardingKeys, listKeys } from "api/keys"
4141
import {
4242
getInstanceSettingsHandler,
4343
putInstanceSettingsHandler,
@@ -74,6 +74,7 @@ app.get("/api/v1", (req: MetloRequest, res: Response) => {
7474

7575
const apiRouter = express.Router()
7676
apiRouter.get("/api/v1/summary", getSummaryHandler)
77+
apiRouter.get("/api/v1/summary/endpoint-tracked", getEndpointTrackedHandler)
7778
apiRouter.get("/api/v1/instance-settings", getInstanceSettingsHandler)
7879
apiRouter.put("/api/v1/instance-settings", putInstanceSettingsHandler)
7980
apiRouter.get("/api/v1/sensitive-data-summary", getSensitiveDataSummaryHandler)
@@ -122,6 +123,7 @@ apiRouter.delete("/api/v1/test/:uuid/delete", deleteTest)
122123
apiRouter.get("/api/v1/keys/list", listKeys)
123124
apiRouter.post("/api/v1/keys/create", createKey)
124125
apiRouter.delete("/api/v1/keys/:name/delete", deleteKey)
126+
apiRouter.get("/api/v1/keys/onboarding", getOnboardingKeys)
125127

126128
apiRouter.put("/api/v1/metlo-config", updateMetloConfigHandler)
127129
apiRouter.get("/api/v1/metlo-config", getMetloConfigHandler)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm"
2+
3+
export class removeApiKeyTypeEnum1669778297643 implements MigrationInterface {
4+
public async up(queryRunner: QueryRunner): Promise<void> {
5+
await queryRunner.query(
6+
`ALTER TABLE "api_key" ALTER COLUMN "for" TYPE text`,
7+
)
8+
await queryRunner.query(
9+
`ALTER TABLE "api_key" ALTER COLUMN "for" SET NOT NULL`,
10+
)
11+
await queryRunner.query(
12+
`ALTER TABLE "api_key" ALTER COLUMN "for" SET DEFAULT 'GENERIC'`,
13+
)
14+
const existingApiKeyForEnum = await queryRunner.query(
15+
"SELECT 1 FROM pg_type WHERE typname = 'api_key_for_enum'",
16+
)
17+
const existingApiKeyTypeEnum = await queryRunner.query(
18+
"SELECT 1 FROM pg_type WHERE typname = 'api_key_type_enum'",
19+
)
20+
if (existingApiKeyForEnum) {
21+
await queryRunner.query(`DROP TYPE IF EXISTS "public"."api_key_for_enum"`)
22+
}
23+
if (existingApiKeyTypeEnum) {
24+
await queryRunner.query(
25+
`DROP TYPE IF EXISTS "public"."api_key_type_enum"`,
26+
)
27+
}
28+
}
29+
30+
public async down(queryRunner: QueryRunner): Promise<void> {
31+
const existingApiKeyTypeEnum = await queryRunner.query(
32+
"SELECT 1 FROM pg_type WHERE typname = 'api_key_type_enum'",
33+
)
34+
if (!existingApiKeyTypeEnum[0]) {
35+
await queryRunner.query(
36+
`CREATE TYPE "public"."api_key_type_enum" AS ENUM('GCP', 'AWS', 'GENERIC')`,
37+
)
38+
}
39+
await queryRunner.query(
40+
`ALTER TABLE "api_key" ALTER COLUMN "for" "public"."api_key_type_enum" NOT NULL DEFAULT 'GENERIC'`,
41+
)
42+
}
43+
}

backend/src/services/summary/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Summary as SummaryResponse } from "@common/types"
2+
import { ApiEndpoint } from "models"
23
import { ConnectionsService } from "services/connections"
4+
import { createQB } from "services/database/utils"
35
import { MetloContext } from "types"
46
import { getAlertTypeAggCached, getTopAlertsCached } from "./alerts"
57
import { getTopEndpointsCached } from "./endpoints"
@@ -26,3 +28,17 @@ export const getSummaryData = async (
2628
...counts,
2729
}
2830
}
31+
32+
export const getEndpointExists = async (
33+
ctx: MetloContext,
34+
): Promise<boolean> => {
35+
const existingEndpoint = await createQB(ctx)
36+
.select(["uuid"])
37+
.from(ApiEndpoint, "endpoint")
38+
.limit(1)
39+
.getRawMany()
40+
if (existingEndpoint?.length > 0) {
41+
return true
42+
}
43+
return false
44+
}

backend/src/services/summary/usageStats.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ export const getCountsCached = async (ctx: MetloContext) => {
113113
return cacheRes
114114
}
115115
const realRes = await getCounts(ctx)
116-
await RedisClient.addToRedis(ctx, "usageCounts", realRes, 60)
116+
if (realRes.hostCount > 0) {
117+
await RedisClient.addToRedis(ctx, "usageCounts", realRes, 60)
118+
}
117119
return realRes
118120
}

common/src/enums.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export enum ConnectionType {
8282
NODE = "NODE",
8383
JAVA = "JAVA",
8484
GOLANG = "GOLANG",
85-
KUBERNETES = "KUBERNETES"
85+
KUBERNETES = "KUBERNETES",
8686
}
8787

8888
export enum SpecExtension {
@@ -134,7 +134,7 @@ export enum DataType {
134134
BOOLEAN = "boolean",
135135
OBJECT = "object",
136136
ARRAY = "array",
137-
UNKNOWN = "unknown"
137+
UNKNOWN = "unknown",
138138
}
139139

140140
export enum DataSection {
@@ -188,7 +188,8 @@ export enum GCP_SOURCE_TYPE {
188188
export enum API_KEY_TYPE {
189189
GCP = "GCP",
190190
AWS = "AWS",
191-
GENERIC = "GENERIC"
191+
GENERIC = "GENERIC",
192+
ONBOARDING = "ONBOARDING",
192193
}
193194

194195
export enum OperationType {

frontend/src/api/home/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,17 @@ export const getInstanceSettings = async (
3131
return null
3232
}
3333
}
34+
35+
export const getEndpointTracked = async (
36+
headers?: AxiosRequestHeaders,
37+
): Promise<{ exists: boolean }> => {
38+
try {
39+
const resp = await axios.get<{ exists: boolean }>(
40+
`${getAPIURL()}/summary/endpoint-tracked`,
41+
{ headers },
42+
)
43+
return resp.data
44+
} catch (err) {
45+
return { exists: false }
46+
}
47+
}

frontend/src/api/keys/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const getKeys = async (
1717
throw err
1818
}
1919
}
20+
2021
export const deleteKey = async (
2122
key_name: string,
2223
headers?: AxiosRequestHeaders,
@@ -41,15 +42,18 @@ export const deleteKey = async (
4142
throw err
4243
}
4344
}
45+
4446
export const addKey = async (
4547
key_name: string,
48+
keyFor?: string,
4649
headers?: AxiosRequestHeaders,
4750
): Promise<ApiKey & { apiKey: string }> => {
4851
try {
4952
const resp = await axios.post<ApiKey & { apiKey: string }>(
5053
`${getAPIURL()}/keys/create`,
5154
{
5255
name: key_name,
56+
keyFor,
5357
},
5458
{ headers },
5559
)
@@ -66,3 +70,12 @@ export const addKey = async (
6670
throw err
6771
}
6872
}
73+
74+
export const getOnboardingKeys = async (
75+
headers?: AxiosRequestHeaders,
76+
): Promise<ApiKey[]> => {
77+
const resp = await axios.get<ApiKey[]>(`${getAPIURL()}/keys/onboarding`, {
78+
headers,
79+
})
80+
return resp.data
81+
}

0 commit comments

Comments
 (0)