Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
pkspro committed Jan 11, 2025
2 parents aa9773c + 258d19c commit f714920
Show file tree
Hide file tree
Showing 25 changed files with 289 additions and 61 deletions.
4 changes: 2 additions & 2 deletions backend/src/ee/services/audit-log/audit-log-dal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ export const auditLogDALFactory = (db: TDbClient) => {

// Filter by date range
if (startDate) {
void sqlQuery.where(`${TableName.AuditLog}.createdAt`, ">=", startDate);
void sqlQuery.whereRaw(`"${TableName.AuditLog}"."createdAt" >= ?::timestamptz`, [startDate]);
}
if (endDate) {
void sqlQuery.where(`${TableName.AuditLog}.createdAt`, "<=", endDate);
void sqlQuery.whereRaw(`"${TableName.AuditLog}"."createdAt" <= ?::timestamptz`, [endDate]);
}

// we timeout long running queries to prevent DB resource issues (2 minutes)
Expand Down
48 changes: 45 additions & 3 deletions backend/src/ee/services/audit-log/audit-log-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type TListProjectAuditLogDTO = {

export type TCreateAuditLogDTO = {
event: Event;
actor: UserActor | IdentityActor | ServiceActor | ScimClientActor | PlatformActor;
actor: UserActor | IdentityActor | ServiceActor | ScimClientActor | PlatformActor | UnknownUserActor;
orgId?: string;
projectId?: string;
} & BaseAuthData;
Expand Down Expand Up @@ -229,7 +229,10 @@ export enum EventType {
GET_APP_CONNECTION = "get-app-connection",
CREATE_APP_CONNECTION = "create-app-connection",
UPDATE_APP_CONNECTION = "update-app-connection",
DELETE_APP_CONNECTION = "delete-app-connection"
DELETE_APP_CONNECTION = "delete-app-connection",
CREATE_SHARED_SECRET = "create-shared-secret",
DELETE_SHARED_SECRET = "delete-shared-secret",
READ_SHARED_SECRET = "read-shared-secret"
}

interface UserActorMetadata {
Expand All @@ -252,6 +255,8 @@ interface ScimClientActorMetadata {}

interface PlatformActorMetadata {}

interface UnknownUserActorMetadata {}

export interface UserActor {
type: ActorType.USER;
metadata: UserActorMetadata;
Expand All @@ -267,6 +272,11 @@ export interface PlatformActor {
metadata: PlatformActorMetadata;
}

export interface UnknownUserActor {
type: ActorType.UNKNOWN_USER;
metadata: UnknownUserActorMetadata;
}

export interface IdentityActor {
type: ActorType.IDENTITY;
metadata: IdentityActorMetadata;
Expand Down Expand Up @@ -1907,6 +1917,35 @@ interface DeleteAppConnectionEvent {
};
}

interface CreateSharedSecretEvent {
type: EventType.CREATE_SHARED_SECRET;
metadata: {
id: string;
accessType: string;
name?: string;
expiresAfterViews?: number;
usingPassword: boolean;
expiresAt: string;
};
}

interface DeleteSharedSecretEvent {
type: EventType.DELETE_SHARED_SECRET;
metadata: {
id: string;
name?: string;
};
}

interface ReadSharedSecretEvent {
type: EventType.READ_SHARED_SECRET;
metadata: {
id: string;
name?: string;
accessType: string;
};
}

export type Event =
| GetSecretsEvent
| GetSecretEvent
Expand Down Expand Up @@ -2083,4 +2122,7 @@ export type Event =
| GetAppConnectionEvent
| CreateAppConnectionEvent
| UpdateAppConnectionEvent
| DeleteAppConnectionEvent;
| DeleteAppConnectionEvent
| CreateSharedSecretEvent
| DeleteSharedSecretEvent
| ReadSharedSecretEvent;
13 changes: 12 additions & 1 deletion backend/src/ee/services/dynamic-secret/providers/sql-database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {

const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>) => {
const ssl = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined;
const isMsSQLClient = providerInputs.client === SqlProviders.MsSQL;

const db = knex({
client: providerInputs.client,
connection: {
Expand All @@ -43,7 +45,16 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {
user: providerInputs.username,
password: providerInputs.password,
ssl,
pool: { min: 0, max: 1 }
pool: { min: 0, max: 1 },
// @ts-expect-error this is because of knexjs type signature issue. This is directly passed to driver
// https://github.com/knex/knex/blob/b6507a7129d2b9fafebf5f831494431e64c6a8a0/lib/dialects/mssql/index.js#L66
// https://github.com/tediousjs/tedious/blob/ebb023ed90969a7ec0e4b036533ad52739d921f7/test/config.ci.ts#L19
options: isMsSQLClient
? {
trustServerCertificate: !providerInputs.ca,
cryptoCredentialsDetails: providerInputs.ca ? { ca: providerInputs.ca } : {}
}
: undefined
},
acquireConnectionTimeout: EXTERNAL_REQUEST_TIMEOUT
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ export const secretRotationQueueFactory = ({
provider.template.client === TDbProviderClients.MsSqlServer
? ({
encrypt: appCfg.ENABLE_MSSQL_SECRET_ROTATION_ENCRYPT,
// when ca is provided use that
trustServerCertificate: !ca,
cryptoCredentialsDetails: ca ? { ca } : {}
} as Record<string, unknown>)
: undefined;
Expand Down
10 changes: 9 additions & 1 deletion backend/src/server/plugins/audit-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,21 @@ export const getUserAgentType = (userAgent: string | undefined) => {
export const injectAuditLogInfo = fp(async (server: FastifyZodProvider) => {
server.decorateRequest("auditLogInfo", null);
server.addHook("onRequest", async (req) => {
if (!req.auth) return;
const userAgent = req.headers["user-agent"] ?? "";
const payload = {
ipAddress: req.realIp,
userAgent,
userAgentType: getUserAgentType(userAgent)
} as typeof req.auditLogInfo;

if (!req.auth) {
payload.actor = {
type: ActorType.UNKNOWN_USER,
metadata: {}
};
req.auditLogInfo = payload;
return;
}
if (req.auth.actor === ActorType.USER) {
payload.actor = {
type: ActorType.USER,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/server/routes/v1/integration-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
body: z.object({
app: z.string().trim().optional().describe(INTEGRATION.UPDATE.app),
appId: z.string().trim().optional().describe(INTEGRATION.UPDATE.appId),
isActive: z.boolean().describe(INTEGRATION.UPDATE.isActive),
isActive: z.boolean().optional().describe(INTEGRATION.UPDATE.isActive),
secretPath: z
.string()
.trim()
Expand Down
45 changes: 45 additions & 0 deletions backend/src/server/routes/v1/secret-sharing-router.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from "zod";

import { SecretSharingSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { SecretSharingAccessType } from "@app/lib/types";
import {
publicEndpointLimit,
Expand Down Expand Up @@ -88,6 +89,21 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
orgId: req.permission?.orgId
});

if (sharedSecret.secret?.orgId) {
await server.services.auditLog.createAuditLog({
orgId: sharedSecret.secret.orgId,
...req.auditLogInfo,
event: {
type: EventType.READ_SHARED_SECRET,
metadata: {
id: req.params.id,
name: sharedSecret.secret.name || undefined,
accessType: sharedSecret.secret.accessType
}
}
});
}

return sharedSecret;
}
});
Expand Down Expand Up @@ -151,6 +167,23 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId,
...req.body
});

await server.services.auditLog.createAuditLog({
orgId: req.permission.orgId,
...req.auditLogInfo,
event: {
type: EventType.CREATE_SHARED_SECRET,
metadata: {
accessType: req.body.accessType,
expiresAt: req.body.expiresAt,
expiresAfterViews: req.body.expiresAfterViews,
name: req.body.name,
id: sharedSecret.id,
usingPassword: !!req.body.password
}
}
});

return { id: sharedSecret.id };
}
});
Expand Down Expand Up @@ -181,6 +214,18 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
sharedSecretId
});

await server.services.auditLog.createAuditLog({
orgId: req.permission.orgId,
...req.auditLogInfo,
event: {
type: EventType.DELETE_SHARED_SECRET,
metadata: {
id: sharedSecretId,
name: deletedSharedSecret.name || undefined
}
}
});

return { ...deletedSharedSecret };
}
});
Expand Down
3 changes: 2 additions & 1 deletion backend/src/services/auth/auth-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export enum ActorType { // would extend to AWS, Azure, ...
SERVICE = "service",
IDENTITY = "identity",
Machine = "machine",
SCIM_CLIENT = "scimClient"
SCIM_CLIENT = "scimClient",
UNKNOWN_USER = "unknownUser"
}

// This will be null unless the token-type is JWT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@ import picomatch from "picomatch";

export const doesFieldValueMatchOidcPolicy = (fieldValue: string, policyValue: string) =>
policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);

export const doesAudValueMatchOidcPolicy = (fieldValue: string | string[], policyValue: string) => {
if (Array.isArray(fieldValue)) {
return fieldValue.some((entry) => entry === policyValue || picomatch.isMatch(entry, policyValue));
}

return policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TOrgBotDALFactory } from "../org/org-bot-dal";
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
import { doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
import { doesAudValueMatchOidcPolicy, doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
import {
TAttachOidcAuthDTO,
TGetOidcAuthDTO,
Expand Down Expand Up @@ -148,7 +148,7 @@ export const identityOidcAuthServiceFactory = ({
if (
!identityOidcAuth.boundAudiences
.split(", ")
.some((policyValue) => doesFieldValueMatchOidcPolicy(tokenData.aud, policyValue))
.some((policyValue) => doesAudValueMatchOidcPolicy(tokenData.aud, policyValue))
) {
throw new UnauthorizedError({
message: "Access denied: OIDC audience not allowed."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1289,7 +1289,10 @@ const syncSecretsAWSSecretManager = async ({

if (metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE) {
for await (const [key, value] of Object.entries(secrets)) {
await processAwsSecret(key, value.value, value.secretMetadata);
await processAwsSecret(key, value.value, value.secretMetadata).catch((error) => {
error.secretKey = key;
throw error;
});
}
} else {
await processAwsSecret(integration.app as string, getSecretKeyValuePair(secrets));
Expand Down
32 changes: 21 additions & 11 deletions backend/src/services/integration-auth/integration-team.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { AxiosResponse } from "axios";

import { request } from "@app/lib/config/request";
import { BadRequestError } from "@app/lib/errors";

Expand All @@ -11,19 +13,27 @@ const getTeamsGitLab = async ({ url, accessToken }: { url: string; accessToken:
const gitLabApiUrl = url ? `${url}/api` : IntegrationUrls.GITLAB_API_URL;

let teams: Team[] = [];
const res = (
await request.get<{ name: string; id: string }[]>(`${gitLabApiUrl}/v4/groups`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
let page: number = 1;
while (page > 0) {
// eslint-disable-next-line no-await-in-loop
const { data, headers }: AxiosResponse<{ name: string; id: string }[]> = await request.get(
`${gitLabApiUrl}/v4/groups?page=${page}`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
}
}
})
).data;
);

teams = res.map((t) => ({
name: t.name,
id: t.id.toString()
}));
page = Number(headers["x-next-page"] ?? "");
teams = teams.concat(
data.map((t) => ({
name: t.name,
id: t.id.toString()
}))
);
}

return teams;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,13 @@ export const secretSharingServiceFactory = ({

const orgName = sharedSecret.orgId ? (await orgDAL.findOrgById(sharedSecret.orgId))?.name : "";

if (accessType === SecretSharingAccessType.Organization && orgId !== sharedSecret.orgId)
if (accessType === SecretSharingAccessType.Organization && orgId === undefined) {
throw new UnauthorizedError();
}

if (accessType === SecretSharingAccessType.Organization && orgId !== sharedSecret.orgId) {
throw new ForbiddenRequestError();
}

// all secrets pass through here, meaning we check if its expired first and then check if it needs verification
// or can be safely sent to the client.
Expand Down
10 changes: 7 additions & 3 deletions backend/src/services/secret/secret-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,8 @@ export const secretQueueFactory = ({
});
}

const { secretKey } = (err as { secretKey: string }) || {};

const message =
// eslint-disable-next-line no-nested-ternary
(err instanceof AxiosError
Expand All @@ -979,6 +981,8 @@ export const secretQueueFactory = ({
: err?.message
: (err as Error)?.message) || "Unknown error occurred.";

const errorLog = `${secretKey ? `[Secret Key: ${secretKey}] ` : ""}${message}`;

await auditLogService.createAuditLog({
projectId,
actor: await $generateActor(actorId, isManual),
Expand All @@ -989,7 +993,7 @@ export const secretQueueFactory = ({
isSynced: false,
lastSyncJobId: job?.id ?? "",
lastUsed: new Date(),
syncMessage: message
syncMessage: errorLog
}
}
});
Expand All @@ -1001,13 +1005,13 @@ export const secretQueueFactory = ({

await integrationDAL.updateById(integration.id, {
lastSyncJobId: job.id,
syncMessage: message,
syncMessage: errorLog,
isSynced: false
});

integrationsFailedToSync.push({
integrationId: integration.id,
syncMessage: message
syncMessage: errorLog
});
}
}
Expand Down
Loading

0 comments on commit f714920

Please sign in to comment.