feat: changed from token to headers for audit log streams api

This commit is contained in:
Akhil Mohan
2024-05-03 17:43:14 +05:30
parent f369761920
commit 2dcb409d3b
9 changed files with 242 additions and 99 deletions
@@ -8,11 +8,11 @@ export async function up(knex: Knex): Promise<void> {
await knex.schema.createTable(TableName.AuditLogStream, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("url").notNullable();
t.text("encryptedTokenCiphertext");
t.text("encryptedTokenIV");
t.text("encryptedTokenTag");
t.string("encryptedTokenAlgorithm");
t.string("encryptedTokenKeyEncoding");
t.text("encryptedHeadersCiphertext");
t.text("encryptedHeadersIV");
t.text("encryptedHeadersTag");
t.string("encryptedHeadersAlgorithm");
t.string("encryptedHeadersKeyEncoding");
t.uuid("orgId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
+5 -5
View File
@@ -10,11 +10,11 @@ import { TImmutableDBKeys } from "./models";
export const AuditLogStreamsSchema = z.object({
id: z.string().uuid(),
url: z.string(),
encryptedTokenCiphertext: z.string().nullable().optional(),
encryptedTokenIV: z.string().nullable().optional(),
encryptedTokenTag: z.string().nullable().optional(),
encryptedTokenAlgorithm: z.string().nullable().optional(),
encryptedTokenKeyEncoding: z.string().nullable().optional(),
encryptedHeadersCiphertext: z.string().nullable().optional(),
encryptedHeadersIV: z.string().nullable().optional(),
encryptedHeadersTag: z.string().nullable().optional(),
encryptedHeadersAlgorithm: z.string().nullable().optional(),
encryptedHeadersKeyEncoding: z.string().nullable().optional(),
orgId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
@@ -22,7 +22,14 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
],
body: z.object({
url: z.string().min(1).describe(AUDIT_LOG_STREAMS.CREATE.url),
token: z.string().optional().describe(AUDIT_LOG_STREAMS.CREATE.token)
headers: z
.object({
key: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.CREATE.headers.key),
value: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.CREATE.headers.value)
})
.describe(AUDIT_LOG_STREAMS.CREATE.headers.desc)
.array()
.optional()
}),
response: {
200: z.object({
@@ -38,7 +45,7 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
url: req.body.url,
token: req.body.token
headers: req.body.headers
});
return { auditLogStream };
@@ -63,7 +70,14 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
}),
body: z.object({
url: z.string().optional().describe(AUDIT_LOG_STREAMS.UPDATE.url),
token: z.string().optional().describe(AUDIT_LOG_STREAMS.UPDATE.token)
headers: z
.object({
key: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.UPDATE.headers.key),
value: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.UPDATE.headers.value)
})
.describe(AUDIT_LOG_STREAMS.UPDATE.headers.desc)
.array()
.optional()
}),
response: {
200: z.object({
@@ -80,7 +94,7 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
actorAuthMethod: req.permission.authMethod,
id: req.params.id,
url: req.body.url,
token: req.body.token
headers: req.body.headers
});
return { auditLogStream };
@@ -141,7 +155,15 @@ export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) =
}),
response: {
200: z.object({
auditLogStream: SanitizedAuditLogStreamSchema.extend({ token: z.string().optional() })
auditLogStream: SanitizedAuditLogStreamSchema.extend({
headers: z
.object({
key: z.string(),
value: z.string()
})
.array()
.optional()
})
})
}
},
@@ -13,6 +13,7 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-p
import { TPermissionServiceFactory } from "../permission/permission-service";
import { TAuditLogStreamDALFactory } from "./audit-log-stream-dal";
import {
LogStreamHeaders,
TCreateAuditLogStreamDTO,
TDeleteAuditLogStreamDTO,
TGetDetailsAuditLogStreamDTO,
@@ -33,7 +34,14 @@ export const auditLogStreamServiceFactory = ({
permissionService,
licenseService
}: TAuditLogStreamServiceFactoryDep) => {
const create = async ({ url, actor, token, actorId, actorOrgId, actorAuthMethod }: TCreateAuditLogStreamDTO) => {
const create = async ({
url,
actor,
headers = [],
actorId,
actorOrgId,
actorAuthMethod
}: TCreateAuditLogStreamDTO) => {
if (!actorOrgId) throw new BadRequestError({ message: "Missing org id from token" });
const plan = await licenseService.getPlan(actorOrgId);
@@ -62,30 +70,37 @@ export const auditLogStreamServiceFactory = ({
}
// testing connection first
const headers: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
if (token) headers.Authorization = `Bearer ${token}`;
await request.post(
url,
{ ping: "ok" },
{
headers,
// request timeout
timeout: AUDIT_LOG_STREAM_TIMEOUT,
// connection timeout
signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT)
}
);
const encryptedToken = token ? infisicalSymmetricEncypt(token) : undefined;
const streamHeaders: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
if (headers.length)
headers.forEach(({ key, value }) => {
streamHeaders[key] = value;
});
await request
.post(
url,
{ ping: "ok" },
{
headers: streamHeaders,
// request timeout
timeout: AUDIT_LOG_STREAM_TIMEOUT,
// connection timeout
signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT)
}
)
.catch((err) => {
throw new Error(`Failed to connect with the source ${(err as Error)?.message}`);
});
const encryptedHeaders = headers ? infisicalSymmetricEncypt(JSON.stringify(headers)) : undefined;
const logStream = await auditLogStreamDAL.create({
orgId: actorOrgId,
url,
...(encryptedToken
...(encryptedHeaders
? {
encryptedTokenCiphertext: encryptedToken.ciphertext,
encryptedTokenIV: encryptedToken.iv,
encryptedTokenTag: encryptedToken.tag,
encryptedTokenAlgorithm: encryptedToken.algorithm,
encryptedTokenKeyEncoding: encryptedToken.encoding
encryptedHeadersCiphertext: encryptedHeaders.ciphertext,
encryptedHeadersIV: encryptedHeaders.iv,
encryptedHeadersTag: encryptedHeaders.tag,
encryptedHeadersAlgorithm: encryptedHeaders.algorithm,
encryptedHeadersKeyEncoding: encryptedHeaders.encoding
}
: {})
});
@@ -96,7 +111,7 @@ export const auditLogStreamServiceFactory = ({
id,
url,
actor,
token,
headers = [],
actorId,
actorOrgId,
actorAuthMethod
@@ -119,13 +134,17 @@ export const auditLogStreamServiceFactory = ({
if (url) validateLocalIps(url);
// testing connection first
const headers: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
if (token) headers.Authorization = `Bearer ${token}`;
const streamHeaders: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
if (headers.length)
headers.forEach(({ key, value }) => {
streamHeaders[key] = value;
});
await request.post(
url || logStream.url,
{ ping: "ok" },
{
headers,
headers: streamHeaders,
// request timeout
timeout: AUDIT_LOG_STREAM_TIMEOUT,
// connection timeout
@@ -133,16 +152,16 @@ export const auditLogStreamServiceFactory = ({
}
);
const encryptedToken = token ? infisicalSymmetricEncypt(token) : undefined;
const encryptedHeaders = headers ? infisicalSymmetricEncypt(JSON.stringify(headers)) : undefined;
const updatedLogStream = await auditLogStreamDAL.updateById(id, {
url,
...(encryptedToken
...(encryptedHeaders
? {
encryptedTokenCiphertext: encryptedToken.ciphertext,
encryptedTokenIV: encryptedToken.iv,
encryptedTokenTag: encryptedToken.tag,
encryptedTokenAlgorithm: encryptedToken.algorithm,
encryptedTokenKeyEncoding: encryptedToken.encoding
encryptedHeadersCiphertext: encryptedHeaders.ciphertext,
encryptedHeadersIV: encryptedHeaders.iv,
encryptedHeadersTag: encryptedHeaders.tag,
encryptedHeadersAlgorithm: encryptedHeaders.algorithm,
encryptedHeadersKeyEncoding: encryptedHeaders.encoding
}
: {})
});
@@ -171,17 +190,19 @@ export const auditLogStreamServiceFactory = ({
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
const token =
logStream?.encryptedTokenCiphertext && logStream?.encryptedTokenIV && logStream?.encryptedTokenTag
? infisicalSymmetricDecrypt({
tag: logStream.encryptedTokenTag,
iv: logStream.encryptedTokenIV,
ciphertext: logStream.encryptedTokenCiphertext,
keyEncoding: logStream.encryptedTokenKeyEncoding as SecretKeyEncoding
})
const headers =
logStream?.encryptedHeadersCiphertext && logStream?.encryptedHeadersIV && logStream?.encryptedHeadersTag
? (JSON.parse(
infisicalSymmetricDecrypt({
tag: logStream.encryptedHeadersTag,
iv: logStream.encryptedHeadersIV,
ciphertext: logStream.encryptedHeadersCiphertext,
keyEncoding: logStream.encryptedHeadersKeyEncoding as SecretKeyEncoding
})
) as LogStreamHeaders[])
: undefined;
return { ...logStream, token };
return { ...logStream, headers };
};
const list = async ({ actor, actorId, actorOrgId, actorAuthMethod }: TListAuditLogStreamDTO) => {
@@ -1,14 +1,19 @@
import { TOrgPermission } from "@app/lib/types";
export type LogStreamHeaders = {
key: string;
value: string;
};
export type TCreateAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
url: string;
token?: string;
headers?: LogStreamHeaders[];
};
export type TUpdateAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
id: string;
url?: string;
token?: string;
headers?: LogStreamHeaders[];
};
export type TDeleteAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
@@ -8,6 +8,7 @@ import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TAuditLogStreamDALFactory } from "../audit-log-stream/audit-log-stream-dal";
import { LogStreamHeaders } from "../audit-log-stream/audit-log-stream-types";
import { TLicenseServiceFactory } from "../license/license-service";
import { TAuditLogDALFactory } from "./audit-log-dal";
import { TCreateAuditLogDTO } from "./audit-log-types";
@@ -74,20 +75,31 @@ export const auditLogQueueServiceFactory = ({
const logStreams = orgId ? await auditLogStreamDAL.find({ orgId }) : [];
await Promise.allSettled(
logStreams.map(
async ({ url, encryptedTokenTag, encryptedTokenIV, encryptedTokenKeyEncoding, encryptedTokenCiphertext }) => {
const token =
encryptedTokenIV && encryptedTokenCiphertext && encryptedTokenTag
? infisicalSymmetricDecrypt({
keyEncoding: encryptedTokenKeyEncoding as SecretKeyEncoding,
iv: encryptedTokenIV,
tag: encryptedTokenTag,
ciphertext: encryptedTokenCiphertext
})
: undefined;
async ({
url,
encryptedHeadersTag,
encryptedHeadersIV,
encryptedHeadersKeyEncoding,
encryptedHeadersCiphertext
}) => {
const streamHeaders =
encryptedHeadersIV && encryptedHeadersCiphertext && encryptedHeadersTag
? (JSON.parse(
infisicalSymmetricDecrypt({
keyEncoding: encryptedHeadersKeyEncoding as SecretKeyEncoding,
iv: encryptedHeadersIV,
tag: encryptedHeadersTag,
ciphertext: encryptedHeadersCiphertext
})
) as LogStreamHeaders[])
: [];
const headers: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
if (token) headers.Authorization = `Bearer ${token}`;
if (headers.length)
streamHeaders.forEach(({ key, value }) => {
headers[key] = value;
});
return request.post(url, auditLog, {
headers,
+10 -2
View File
@@ -618,12 +618,20 @@ export const INTEGRATION = {
export const AUDIT_LOG_STREAMS = {
CREATE: {
url: "The HTTP URL to push logs to.",
token: "Authentication token for the external provider used for identification."
headers: {
desc: "The HTTP headers attached for the external prrovider requests.",
key: "The HTTP header key name.",
value: "The HTTP header value."
}
},
UPDATE: {
id: "The ID of the audit log stream to update.",
url: "The HTTP URL to push logs to.",
token: "Authentication token for the external provider used for identification."
headers: {
desc: "The HTTP headers attached for the external prrovider requests.",
key: "The HTTP header key name.",
value: "The HTTP header value."
}
},
DELETE: {
id: "The ID of the audit log stream to delete."