mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
misc: add total api requests metric
This commit is contained in:
12
backend/src/@types/fastify.d.ts
vendored
12
backend/src/@types/fastify.d.ts
vendored
@@ -136,8 +136,20 @@ declare module "@fastify/request-context" {
|
||||
interface RequestContextData {
|
||||
reqId: string;
|
||||
orgId?: string;
|
||||
orgName?: string;
|
||||
userAuthInfo?: {
|
||||
userId: string;
|
||||
email: string;
|
||||
};
|
||||
projectDetails?: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
identityAuthInfo?: {
|
||||
identityId: string;
|
||||
identityName: string;
|
||||
authMethod: string;
|
||||
oidc?: {
|
||||
claims: Record<string, string>;
|
||||
};
|
||||
|
||||
@@ -337,6 +337,12 @@ export const permissionServiceFactory = ({
|
||||
throw new NotFoundError({ message: `Project with ${projectId} not found` });
|
||||
}
|
||||
|
||||
requestContext.set("projectDetails", {
|
||||
id: projectDetails.id,
|
||||
name: projectDetails.name,
|
||||
slug: projectDetails.slug
|
||||
});
|
||||
|
||||
if (projectDetails.orgId !== actorOrgId) {
|
||||
throw new ForbiddenRequestError({ name: "You are not logged into this organization" });
|
||||
}
|
||||
|
||||
@@ -1,21 +1,99 @@
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import opentelemetry from "@opentelemetry/api";
|
||||
import fp from "fastify-plugin";
|
||||
|
||||
export const apiMetrics = fp(async (fastify) => {
|
||||
const apiMeter = opentelemetry.metrics.getMeter("API");
|
||||
|
||||
const latencyHistogram = apiMeter.createHistogram("API_latency", {
|
||||
unit: "ms"
|
||||
});
|
||||
|
||||
const infisicalMeter = opentelemetry.metrics.getMeter("Infisical");
|
||||
|
||||
const requestCounter = infisicalMeter.createCounter("infisical.http.server.request.count", {
|
||||
description: "Total number of API requests to Infisical (covers both human users and machine identities)",
|
||||
unit: "{request}"
|
||||
});
|
||||
|
||||
fastify.addHook("onResponse", async (request, reply) => {
|
||||
const { method } = request;
|
||||
const route = request.routerPath;
|
||||
const { statusCode } = reply;
|
||||
|
||||
// Record latency
|
||||
latencyHistogram.record(reply.elapsedTime, {
|
||||
route,
|
||||
method,
|
||||
statusCode
|
||||
});
|
||||
|
||||
// Get context data
|
||||
const orgId = requestContext.get("orgId");
|
||||
const orgName = requestContext.get("orgName");
|
||||
const userAuthInfo = requestContext.get("userAuthInfo");
|
||||
const identityAuthInfo = requestContext.get("identityAuthInfo");
|
||||
const projectDetails = requestContext.get("projectDetails");
|
||||
|
||||
// Build attributes object
|
||||
const attributes: Record<string, string | number> = {
|
||||
"http.request.method": method,
|
||||
"http.route": route,
|
||||
"http.response.status_code": statusCode
|
||||
};
|
||||
|
||||
// Add organization info
|
||||
if (orgId) {
|
||||
attributes["infisical.organization.id"] = orgId;
|
||||
}
|
||||
if (orgName) {
|
||||
attributes["infisical.organization.name"] = orgName;
|
||||
}
|
||||
|
||||
// Add user info (for human users)
|
||||
if (userAuthInfo) {
|
||||
if (userAuthInfo.userId) {
|
||||
attributes["infisical.user.id"] = userAuthInfo.userId;
|
||||
}
|
||||
if (userAuthInfo.email) {
|
||||
attributes["infisical.user.email"] = userAuthInfo.email;
|
||||
}
|
||||
}
|
||||
|
||||
// Add identity info (for machine identities)
|
||||
if (identityAuthInfo) {
|
||||
if (identityAuthInfo.identityId) {
|
||||
attributes["infisical.identity.id"] = identityAuthInfo.identityId;
|
||||
}
|
||||
if (identityAuthInfo.identityName) {
|
||||
attributes["infisical.identity.name"] = identityAuthInfo.identityName;
|
||||
}
|
||||
if (identityAuthInfo.authMethod) {
|
||||
attributes["infisical.auth.method"] = identityAuthInfo.authMethod;
|
||||
}
|
||||
}
|
||||
|
||||
// Add project info
|
||||
if (projectDetails) {
|
||||
if (projectDetails.id) {
|
||||
attributes["infisical.project.id"] = projectDetails.id;
|
||||
}
|
||||
if (projectDetails.name) {
|
||||
attributes["infisical.project.name"] = projectDetails.name;
|
||||
}
|
||||
}
|
||||
|
||||
// Add user agent
|
||||
const userAgent = request.headers["user-agent"];
|
||||
if (userAgent) {
|
||||
attributes["user_agent.original"] = userAgent;
|
||||
}
|
||||
|
||||
// Add client IP address
|
||||
if (request.realIp) {
|
||||
attributes["client.address"] = request.realIp;
|
||||
}
|
||||
|
||||
requestCounter.add(1, attributes);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import { requestContext, RequestContextData } from "@fastify/request-context";
|
||||
import { FastifyRequest } from "fastify";
|
||||
import fp from "fastify-plugin";
|
||||
import type { JwtPayload } from "jsonwebtoken";
|
||||
@@ -159,10 +159,11 @@ export const injectIdentity = fp(
|
||||
|
||||
switch (authMode) {
|
||||
case AuthMode.JWT: {
|
||||
const { user, tokenVersionId, orgId, rootOrgId, parentOrgId } =
|
||||
const { user, tokenVersionId, orgId, orgName, rootOrgId, parentOrgId } =
|
||||
await server.services.authToken.fnValidateJwtIdentity(token, subOrganizationSelector);
|
||||
requestContext.set("orgId", orgId);
|
||||
|
||||
requestContext.set("orgName", orgName);
|
||||
requestContext.set("userAuthInfo", { userId: user.id, email: user.email || "" });
|
||||
req.auth = {
|
||||
authMode: AuthMode.JWT,
|
||||
user,
|
||||
@@ -186,6 +187,7 @@ export const injectIdentity = fp(
|
||||
);
|
||||
const serverCfg = await getServerCfg();
|
||||
requestContext.set("orgId", identity.orgId);
|
||||
requestContext.set("orgName", identity.orgName);
|
||||
req.auth = {
|
||||
authMode: AuthMode.IDENTITY_ACCESS_TOKEN,
|
||||
actor,
|
||||
@@ -198,24 +200,23 @@ export const injectIdentity = fp(
|
||||
isInstanceAdmin: serverCfg?.adminIdentityIds?.includes(identity.identityId),
|
||||
token
|
||||
};
|
||||
const identityAuthInfo: RequestContextData["identityAuthInfo"] = {
|
||||
identityId: identity.identityId,
|
||||
identityName: identity.name,
|
||||
authMethod: identity.authMethod
|
||||
};
|
||||
|
||||
if (token?.identityAuth?.oidc) {
|
||||
requestContext.set("identityAuthInfo", {
|
||||
identityId: identity.identityId,
|
||||
oidc: token?.identityAuth?.oidc
|
||||
});
|
||||
identityAuthInfo.oidc = token?.identityAuth?.oidc;
|
||||
}
|
||||
if (token?.identityAuth?.kubernetes) {
|
||||
requestContext.set("identityAuthInfo", {
|
||||
identityId: identity.identityId,
|
||||
kubernetes: token?.identityAuth?.kubernetes
|
||||
});
|
||||
identityAuthInfo.kubernetes = token?.identityAuth?.kubernetes;
|
||||
}
|
||||
if (token?.identityAuth?.aws) {
|
||||
requestContext.set("identityAuthInfo", {
|
||||
identityId: identity.identityId,
|
||||
aws: token?.identityAuth?.aws
|
||||
});
|
||||
identityAuthInfo.aws = token?.identityAuth?.aws;
|
||||
}
|
||||
|
||||
requestContext.set("identityAuthInfo", identityAuthInfo);
|
||||
break;
|
||||
}
|
||||
case AuthMode.SERVICE_TOKEN: {
|
||||
|
||||
@@ -210,6 +210,7 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL, orgD
|
||||
if (!user || !user.isAccepted) throw new NotFoundError({ message: `User with ID '${session.userId}' not found` });
|
||||
|
||||
let orgId = "";
|
||||
let orgName = "";
|
||||
let rootOrgId = "";
|
||||
let parentOrgId = "";
|
||||
if (token.organizationId) {
|
||||
@@ -235,9 +236,11 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL, orgD
|
||||
throw new ForbiddenRequestError({ message: "User organization membership is inactive" });
|
||||
}
|
||||
orgId = subOrganization.id;
|
||||
orgName = subOrganization.name;
|
||||
rootOrgId = token.organizationId;
|
||||
parentOrgId = subOrganization.parentOrgId as string;
|
||||
} else {
|
||||
const organization = await orgDAL.findOne({ id: token.organizationId });
|
||||
const orgMembership = await membershipUserDAL.findOne({
|
||||
actorUserId: user.id,
|
||||
scopeOrgId: token.organizationId,
|
||||
@@ -253,12 +256,13 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL, orgD
|
||||
}
|
||||
|
||||
orgId = token.organizationId;
|
||||
orgName = organization.name;
|
||||
rootOrgId = token.organizationId;
|
||||
parentOrgId = token.organizationId;
|
||||
}
|
||||
}
|
||||
|
||||
return { user, tokenVersionId: token.tokenVersionId, orgId, rootOrgId, parentOrgId };
|
||||
return { user, tokenVersionId: token.tokenVersionId, orgId, orgName, rootOrgId, parentOrgId };
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -210,6 +210,7 @@ export const identityAccessTokenServiceFactory = ({
|
||||
});
|
||||
}
|
||||
let orgId = "";
|
||||
let orgName = "";
|
||||
let parentOrgId = "";
|
||||
const identityOrgDetails = await orgDAL.findOne({ id: identityAccessToken.identityScopeOrgId });
|
||||
const rootOrgId = identityOrgDetails.rootOrgId || identityOrgDetails.id;
|
||||
@@ -229,8 +230,12 @@ export const identityAccessTokenServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Identity does not belong to any organization" });
|
||||
}
|
||||
orgId = subOrganization.id;
|
||||
orgName = subOrganization.name;
|
||||
|
||||
parentOrgId = subOrganization.parentOrgId as string;
|
||||
} else {
|
||||
const organization = await orgDAL.findOne({ id: rootOrgId });
|
||||
|
||||
const identityOrgMembership = await membershipIdentityDAL.findOne({
|
||||
scope: AccessScope.Organization,
|
||||
actorIdentityId: identityAccessToken.identityId,
|
||||
@@ -242,6 +247,7 @@ export const identityAccessTokenServiceFactory = ({
|
||||
}
|
||||
|
||||
orgId = rootOrgId;
|
||||
orgName = organization.name;
|
||||
parentOrgId = rootOrgId;
|
||||
}
|
||||
|
||||
@@ -253,7 +259,7 @@ export const identityAccessTokenServiceFactory = ({
|
||||
await validateAccessTokenExp({ ...identityAccessToken, accessTokenNumUses });
|
||||
|
||||
await accessTokenQueue.updateIdentityAccessTokenStatus(identityAccessToken.id, Number(accessTokenNumUses) + 1);
|
||||
return { ...identityAccessToken, orgId, rootOrgId, parentOrgId };
|
||||
return { ...identityAccessToken, orgId, rootOrgId, parentOrgId, orgName };
|
||||
};
|
||||
|
||||
return { renewAccessToken, revokeAccessToken, fnValidateIdentityAccessToken };
|
||||
|
||||
Reference in New Issue
Block a user