Skip to content

Commit

Permalink
Fix permissions for serverless functions (#6555)
Browse files Browse the repository at this point in the history
Fixes #6525

(@martmull fyi it was not related to AWS but linked to the fact that we
recently enforced passing a token to access files)
  • Loading branch information
FelixMalfait authored Aug 6, 2024
1 parent 67c4125 commit 4157a67
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ export class FileController {
const workspaceId = (req as any)?.workspaceId;

if (!workspaceId) {
return res.status(401).send({ error: 'Unauthorized' });
return res
.status(401)
.send({ error: 'Unauthorized, missing workspaceId' });
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { join } from 'path';

import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';

import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
import { SOURCE_FILE_NAME } from 'src/engine/integrations/serverless/drivers/constants/source-file-name';
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
import { readFileContent } from 'src/engine/integrations/file-storage/utils/read-file-content';
import { SOURCE_FILE_NAME } from 'src/engine/integrations/serverless/drivers/constants/source-file-name';
import { compileTypescript } from 'src/engine/integrations/serverless/drivers/utils/compile-typescript';
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';

export class BaseServerlessDriver {
getFolderPath(serverlessFunction: ServerlessFunctionEntity) {
return join(
'workspace-' + serverlessFunction.workspaceId,
FileFolder.ServerlessFunction,
serverlessFunction.workspaceId,
serverlessFunction.id,
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';

import { Observable, map } from 'rxjs';

import { FileService } from 'src/engine/core-modules/file/services/file.service';

@Injectable()
export class ServerlessFunctionInterceptor implements NestInterceptor {
constructor(private readonly fileService: FileService) {}

intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(async (data) => {
if (data.edges && Array.isArray(data.edges)) {
return {
...data,
edges: Promise.all(
data.edges.map((item) => ({
...item,
node: this.processItem(item.node),
})),
),
};
} else {
return this.processItem(data);
}
}),
);
}

private async processItem(item: any): Promise<any> {
if (item && item.sourceCodeFullPath) {
const workspaceId = item.workspace?.id || item.workspaceId;

if (!workspaceId) {
return item;
}

const signedPayload = await this.fileService.encodeFileToken({
serverlessFunctionId: item.id,
workspace_id: workspaceId,
});

return {
...item,
sourceCodeFullPath: `${item.sourceCodeFullPath}?token=${signedPayload}`,
};
}

return item;
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { SortDirection } from '@ptc-org/nestjs-query-core';
import {
NestjsQueryGraphQLModule,
PagingStrategies,
} from '@ptc-org/nestjs-query-graphql';
import { SortDirection } from '@ptc-org/nestjs-query-core';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';

import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
import { ServerlessModule } from 'src/engine/integrations/serverless/serverless.module';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { ServerlessFunctionResolver } from 'src/engine/metadata-modules/serverless-function/serverless-function.resolver';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
import { FileModule } from 'src/engine/core-modules/file/file.module';
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard';
import { ServerlessModule } from 'src/engine/integrations/serverless/serverless.module';
import { ServerlessFunctionDTO } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto';
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
import { ServerlessFunctionInterceptor } from 'src/engine/metadata-modules/serverless-function/serverless-function.interceptor';
import { ServerlessFunctionResolver } from 'src/engine/metadata-modules/serverless-function/serverless-function.resolver';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';

@Module({
imports: [
Expand All @@ -27,6 +29,7 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-
'metadata',
),
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
FileModule,
],
services: [ServerlessFunctionService],
resolvers: [
Expand All @@ -42,6 +45,7 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-
update: { disabled: true },
delete: { disabled: true },
guards: [JwtAuthGuard],
interceptors: [ServerlessFunctionInterceptor],
},
],
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UseGuards } from '@nestjs/common';
import { UseGuards, UseInterceptors } from '@nestjs/common';
import { Args, Mutation, Resolver } from '@nestjs/graphql';
import { InjectRepository } from '@nestjs/typeorm';

Expand All @@ -21,6 +21,7 @@ import {
ServerlessFunctionException,
ServerlessFunctionExceptionCode,
} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
import { ServerlessFunctionInterceptor } from 'src/engine/metadata-modules/serverless-function/serverless-function.interceptor';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { serverlessFunctionGraphQLApiExceptionHandler } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils';

Expand Down Expand Up @@ -66,6 +67,7 @@ export class ServerlessFunctionResolver {
}
}

@UseInterceptors(ServerlessFunctionInterceptor)
@Mutation(() => ServerlessFunctionDTO)
async updateOneServerlessFunction(
@Args('input')
Expand All @@ -84,6 +86,7 @@ export class ServerlessFunctionResolver {
}
}

@UseInterceptors(ServerlessFunctionInterceptor)
@Mutation(() => ServerlessFunctionDTO)
async createOneServerlessFunction(
@Args('input')
Expand All @@ -106,6 +109,7 @@ export class ServerlessFunctionResolver {
}
}

@UseInterceptors(ServerlessFunctionInterceptor)
@Mutation(() => ServerlessFunctionDTO)
async createOneServerlessFunctionFromFile(
@Args({ name: 'file', type: () => GraphQLUpload })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ import { InjectRepository } from '@nestjs/typeorm';

import { join } from 'path';

import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { FileUpload } from 'graphql-upload';
import { Repository } from 'typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { v4 } from 'uuid';

import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
import { ServerlessExecuteResult } from 'src/engine/integrations/serverless/drivers/interfaces/serverless-driver.interface';

import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
import { readFileContent } from 'src/engine/integrations/file-storage/utils/read-file-content';
import { SOURCE_FILE_NAME } from 'src/engine/integrations/serverless/drivers/constants/source-file-name';
import { ServerlessService } from 'src/engine/integrations/serverless/serverless.service';
import { CreateServerlessFunctionFromFileInput } from 'src/engine/metadata-modules/serverless-function/dtos/create-serverless-function-from-file.input';
import { UpdateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/update-serverless-function.input';
import {
ServerlessFunctionEntity,
ServerlessFunctionSyncStatus,
Expand All @@ -20,12 +25,7 @@ import {
ServerlessFunctionException,
ServerlessFunctionExceptionCode,
} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
import { readFileContent } from 'src/engine/integrations/file-storage/utils/read-file-content';
import { FileStorageService } from 'src/engine/integrations/file-storage/file-storage.service';
import { SOURCE_FILE_NAME } from 'src/engine/integrations/serverless/drivers/constants/source-file-name';
import { serverlessFunctionCreateHash } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-create-hash.utils';
import { CreateServerlessFunctionFromFileInput } from 'src/engine/metadata-modules/serverless-function/dtos/create-serverless-function-from-file.input';
import { UpdateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/update-serverless-function.input';

@Injectable()
export class ServerlessFunctionService extends TypeOrmQueryService<ServerlessFunctionEntity> {
Expand Down Expand Up @@ -119,8 +119,8 @@ export class ServerlessFunctionService extends TypeOrmQueryService<ServerlessFun

if (codeHasChanged) {
const fileFolder = join(
'workspace-' + workspaceId,
FileFolder.ServerlessFunction,
workspaceId,
existingServerlessFunction.id,
);

Expand Down Expand Up @@ -164,13 +164,18 @@ export class ServerlessFunctionService extends TypeOrmQueryService<ServerlessFun

const serverlessFunctionId = v4();

const fileFolder = join(
const fileFolderWithoutWorkspace = join(
FileFolder.ServerlessFunction,
workspaceId,
serverlessFunctionId,
);

const sourceCodeFullPath = fileFolder + '/' + SOURCE_FILE_NAME;
const fileFolder = join(
'workspace-' + workspaceId,
fileFolderWithoutWorkspace,
);

const sourceCodeFullPath =
fileFolderWithoutWorkspace + '/' + SOURCE_FILE_NAME;

const serverlessFunction = await super.createOne({
...serverlessFunctionInput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,23 @@ yarn command:prod cron:calendar:calendar-event-list-fetch
### Data enrichment and AI

<ArticleTable options={[
['OPENROUTER_API_KEY', '', "The API key for openrouter.ai, an abstraction layer over models from Mistral, OpenAI and more"],
['OPENAI_API_KEY', 'sk-proj-abcdabcd...', "OpenAI API key"],
['LLM_CHAT_MODEL_DRIVER', 'openai', "LLM provider"],
['LLM_TRACING_DRIVER', 'langfuse', "Where to output LangChain logs. 'langfuse' or 'console'."],
['LANGFUSE_SECRET_KEY', 'sk-lf-abcdabcd-abcd...', "Langfuse secret key"],
['LANGFUSE_PUBLIC_KEY', 'pk-lf-abcdabcd-abcd...', "Langfuse public key"],
]}></ArticleTable>

### Serverless functions
This feature is WIP and is not yet useful for most users.
<ArticleTable options={[
['SERVERLESS_TYPE', 'local', "Functions can either be executed through Lambda or directly by the main node process"],
['SERVERLESS_LAMBDA_REGION', 'us-east-1', 'If you use the Lambda driver, region of the Lambda function'],
['SERVERLESS_LAMBDA_ROLE', 'arn:aws:iam::121334:role/lambda-execution-role', "If you use the Lambda drive, name of the IAM role with the right permissions"],
]}></ArticleTable>




### Support Chat

Expand Down

0 comments on commit 4157a67

Please sign in to comment.