Protect sendEmail endpoint and thread user context through logic function executor (#20369)
- Thread userId and userWorkspaceId through LogicFunctionExecutorService.execute() so application access tokens carry user context when available. This allows logic functions triggered by authenticated HTTP routes to call sendEmail with proper user identity, making the existing verifyOwnership() check work naturally. - Gate the sendEmail resolver with SettingsPermissionGuard(PermissionFlagType.SEND_EMAIL_TOOL) instead of NoPermissionGuard, ensuring only callers with the SEND_EMAIL_TOOL permission can send emails.
This commit is contained in:
+12
@@ -84,10 +84,14 @@ export class LogicFunctionExecutorService {
|
||||
logicFunctionId,
|
||||
workspaceId,
|
||||
payload,
|
||||
userId,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
logicFunctionId: string;
|
||||
workspaceId: string;
|
||||
payload: object;
|
||||
userId?: string;
|
||||
userWorkspaceId?: string;
|
||||
}): Promise<LogicFunctionExecuteResult> {
|
||||
await this.throttleExecution(workspaceId);
|
||||
|
||||
@@ -101,6 +105,8 @@ export class LogicFunctionExecutorService {
|
||||
workspaceId,
|
||||
flatApplication,
|
||||
flatApplicationVariables,
|
||||
userId,
|
||||
userWorkspaceId,
|
||||
});
|
||||
|
||||
const driver = this.logicFunctionDriverFactory.getCurrentDriver();
|
||||
@@ -224,15 +230,21 @@ export class LogicFunctionExecutorService {
|
||||
workspaceId,
|
||||
flatApplication,
|
||||
flatApplicationVariables,
|
||||
userId,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
flatApplication: FlatApplication;
|
||||
flatApplicationVariables: FlatApplicationVariable[];
|
||||
userId?: string;
|
||||
userWorkspaceId?: string;
|
||||
}) {
|
||||
const applicationAccessToken =
|
||||
await this.applicationTokenService.generateApplicationAccessToken({
|
||||
workspaceId,
|
||||
applicationId: flatApplication.id,
|
||||
userId,
|
||||
userWorkspaceId,
|
||||
});
|
||||
|
||||
const baseUrl = cleanServerUrl(this.twentyConfigService.get('SERVER_URL'));
|
||||
|
||||
+4
@@ -9,6 +9,8 @@ export type LogicFunctionTriggerJobData = {
|
||||
logicFunctionId: string;
|
||||
workspaceId: string;
|
||||
payload?: object;
|
||||
userId?: string;
|
||||
userWorkspaceId?: string;
|
||||
};
|
||||
|
||||
@Processor({
|
||||
@@ -29,6 +31,8 @@ export class LogicFunctionTriggerJob {
|
||||
logicFunctionId: logicFunctionPayload.logicFunctionId,
|
||||
workspaceId: logicFunctionPayload.workspaceId,
|
||||
payload: logicFunctionPayload.payload ?? {},
|
||||
userId: logicFunctionPayload.userId,
|
||||
userWorkspaceId: logicFunctionPayload.userWorkspaceId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
+4
@@ -167,6 +167,7 @@ export class RouteTriggerService {
|
||||
const httpRouteSettings = logicFunction.httpRouteTriggerSettings;
|
||||
|
||||
let userWorkspaceId: string | null = null;
|
||||
let userId: string | null = null;
|
||||
|
||||
if (httpRouteSettings?.isAuthRequired) {
|
||||
const authContext = await this.validateWorkspaceFromRequest({
|
||||
@@ -175,6 +176,7 @@ export class RouteTriggerService {
|
||||
});
|
||||
|
||||
userWorkspaceId = authContext.userWorkspaceId ?? null;
|
||||
userId = authContext.user?.id ?? null;
|
||||
}
|
||||
|
||||
const event = buildLogicFunctionEvent({
|
||||
@@ -191,6 +193,8 @@ export class RouteTriggerService {
|
||||
logicFunctionId: logicFunction.id,
|
||||
workspaceId: logicFunction.workspaceId,
|
||||
payload: event,
|
||||
...(userId ? { userId } : {}),
|
||||
...(userWorkspaceId ? { userWorkspaceId } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof RouteTriggerException) {
|
||||
|
||||
Reference in New Issue
Block a user