From 68b8c0daf8ec126b4fb052301fd91de94c662999 Mon Sep 17 00:00:00 2001 From: Yaroslav Pshenichnikov <yaroslav.pshenichnikov@onlyoffice.com> Date: Tue, 19 Dec 2023 08:39:00 +0300 Subject: [PATCH] ADD test lambda function version --- .github/workflows/sam-pipeline.yml | 1 + docspace-reverse-proxy/index.mjs | 217 +++-------------------------- test-config.toml | 35 +++++ test-template.yaml | 155 +++++++++++++++++++++ 4 files changed, 212 insertions(+), 196 deletions(-) create mode 100644 test-config.toml create mode 100644 test-template.yaml diff --git a/.github/workflows/sam-pipeline.yml b/.github/workflows/sam-pipeline.yml index 3628023..bb8e229 100644 --- a/.github/workflows/sam-pipeline.yml +++ b/.github/workflows/sam-pipeline.yml @@ -3,6 +3,7 @@ on: branches: - develop - main + - test jobs: build-deploy: diff --git a/docspace-reverse-proxy/index.mjs b/docspace-reverse-proxy/index.mjs index 52861b8..a5b6ca3 100644 --- a/docspace-reverse-proxy/index.mjs +++ b/docspace-reverse-proxy/index.mjs @@ -1,198 +1,23 @@ 'use strict'; -const cachedItem = {}; -const regionsMap = { - "default": "default_region_elb_placeholder", - "eu-central-1": "eu_central_1_region_elb_placeholder", - "us-east-2": "us_east_2_region_elb_placeholder" -}; - -const ddbRegionsMap = { - - "default": "eu-central-1", - - "us-east-1": "us-east-1", - "us-east-2": "us-east-2", - "us-west-1": "us-east-2", - "us-west-2": "us-east-2", - - "eu-central-1": "eu-central-1", - "eu-west-1": "eu-central-1", - "eu-west-2": "eu-central-1", - "eu-west-3": "eu-central-1", - "eu-north-1": "eu-central-1", - "me-central-1": "eu-central-1", - - "ap-south-1": "eu-central-1", - "ap-northeast-3": "eu-central-1", - "ap-northeast-2": "eu-central-1", - "ap-southeast-1": "eu-central-1", - "ap-southeast-2": "eu-central-1", - "ap-northeast-1": "eu-central-1" - -}; - -const dynamodbTableName = "dynamodb_table_name_placeholder"; - -const execRegionCode = process.env.AWS_REGION; - -var ddbClientRegion = ddbRegionsMap["default"]; - -if (ddbRegionsMap[execRegionCode]) { - ddbClientRegion = ddbRegionsMap[execRegionCode]; - - console.log("change ddbClient region from %s to %s", ddbRegionsMap["default"], ddbClientRegion); -} - -import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb"; - -async function getTenantRegion(ddbRegion, tenantDomain) { - - console.log("getTenantRegion params ddbRegion: %s, tenantDomain: %s", ddbRegion, tenantDomain); - - const ddbClient = new DynamoDBClient({ region: ddbRegion }); - - const getItemParams = { - Key: { - 'tenant_domain': { S: tenantDomain } - }, - ProjectionExpression: "tenant_region", - TableName: dynamodbTableName - }; - - console.log("[DynamoDb] before send command get item %s with tenant domain %s", getItemParams, tenantDomain); - - const response = await ddbClient.send(new GetItemCommand(getItemParams)); - - console.log("[DynamoDb] responce send command get item %s with tenant domain %s", response, tenantDomain); - - if (response && response.Item) { - - const tenantRegion = regionsMap[response.Item["tenant_region"]["S"]]; - - console.log("Added item %s to cache with key %s", tenantRegion, tenantDomain); - - return tenantRegion; - - } else { - - console.log("Error recieve data from DynamoDB with tenant domain %s in region %s", tenantDomain, ddbRegion); - - return null; - } -} - -export const handler = async (event, context, callback) => { - console.log(JSON.stringify(event)); - - // Extract the request from the CloudFront event that is sent to Lambda@Edge - var request = event.Records[0].cf.request; - - const headers = request.headers; - let tenantDomain = headers.host[0].value; - - console.log("current tenant_domain from host header: %s", tenantDomain); - - - - // Redirect from DS to WS if match - const workspaceRegex = /(\/products\/|\/addons\/|.aspx)/i; - - if (workspaceRegex.test(request.uri)) - { - const newurl = `https://${tenantDomain.replace('onlyoffice.io', 'teamlab.info')}${request.uri}?${request.querystring}`; - console.log("redirect to: %s", newurl); - const response = { - status: '302', - statusDescription: 'Found', - headers: { - location: [{ - key: 'Location', - value: newurl, - }], - }, - }; - - return callback(null, response); - - } - - let originDomain; - - if (request.uri == "/apisystem/portal/register" && request.method == "POST") { - console.log("this is body %s", request.body); - - let body = JSON.parse(Buffer.from(request.body.data, 'base64').toString('utf8')); - let regionFromRequest = body["awsRegion"]; - let portalName = body["portalName"]; - - originDomain = regionsMap[regionFromRequest]; - - console.log("Register portal request: Origin Domain is %s, awsRegion is %s", originDomain, regionFromRequest); - - if (regionFromRequest == null) { - console.log("Register portal request: awsRegion param is null. Trying set to default"); - - body["awsRegion"] = "eu-central-1"; - request.body.data = Buffer.from(JSON.stringify(body), 'utf8').toString('base64'); - request.body.action = "replace"; - - console.log(request.body.data); - - } - - if (originDomain == null) { - originDomain = regionsMap["default"]; - } - - console.log("Register portal request: portalName: %s, regionFromRequest: %s, originDomain: %s", portalName, regionFromRequest, originDomain); - - if (originDomain != regionsMap["default"]) { - request.origin.custom["domainName"] = originDomain; - console.log("Register portal request: Change request origin to %s", originDomain); - } - - console.log("request after changed %s", JSON.stringify(request)); - - // Return to CloudFront - return callback(null, request); - } - - - if (cachedItem[tenantDomain] && cachedItem[tenantDomain].expiresOn > Date.now()) { - originDomain = cachedItem[tenantDomain].value; - - console.log("[CACHE] Recieved item %s from cache with key %s", originDomain, tenantDomain); - - } - else { - originDomain = await getTenantRegion(ddbClientRegion, tenantDomain); - - if (originDomain == null) { - - console.log("originDomain is null. Attempt 2. Trying get value from default region %s", ddbRegionsMap["default"]); - - originDomain = await getTenantRegion(ddbRegionsMap["default"], tenantDomain); - - if (originDomain == null) { - console.log("originDomain is null. Using default"); - - originDomain = regionsMap["default"]; - } - } - } - - cachedItem[tenantDomain] = { - expiresOn: Date.now() + 15 * 60 * 1000, // Set expiry time of 15 minutes - value: originDomain - }; - - if (originDomain != regionsMap["default"]) { - request.origin.custom["domainName"] = originDomain; - - console.log("Change request origin to %s", originDomain); - } - - // Return to CloudFront - return callback(null, request); -}; \ No newline at end of file +// Redirect from DS to WS if match +const workspaceRegex = /(\/products\/|\/addons\/|.aspx)/i; + +if (workspaceRegex.test(request.uri)) +{ + const newurl = `https://${tenantDomain.replace('onlyoffice.io', 'teamlab.info')}${request.uri}?${request.querystring}`; + console.log("redirect to: %s", newurl); + const response = { + status: '302', + statusDescription: 'Found', + headers: { + location: [{ + key: 'Location', + value: newurl, + }], + }, + }; + + return callback(null, response); + +} \ No newline at end of file diff --git a/test-config.toml b/test-config.toml new file mode 100644 index 0000000..ededf16 --- /dev/null +++ b/test-config.toml @@ -0,0 +1,35 @@ +# More information about the configuration file can be found here: +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html +version = 0.1 + +[default] +[default.global.parameters] +stack_name = "docspace-reverse-proxy-test" + +[default.build.parameters] +cached = true +parallel = true + +[default.validate.parameters] +lint = true + +[default.deploy.parameters] +capabilities = "CAPABILITY_IAM" +confirm_changeset = false +fail_on_empty_changeset = false +resolve_s3 = true +s3_prefix = "docspace-reverse-proxy-test" +region = "us-east-1" +image_repositories = [] + +[default.package.parameters] +resolve_s3 = true + +[default.sync.parameters] +watch = true + +[default.local_start_api.parameters] +warm_containers = "EAGER" + +[default.local_start_lambda.parameters] +warm_containers = "EAGER" \ No newline at end of file diff --git a/test-template.yaml b/test-template.yaml new file mode 100644 index 0000000..aa28f40 --- /dev/null +++ b/test-template.yaml @@ -0,0 +1,155 @@ +# This AWS SAM template has been generated from your function's +# configuration. If your function has one or more triggers, note +# that the AWS resources associated with these triggers aren't fully +# specified in this template and include placeholder values.Open this template +# in AWS Application Composer or your favorite IDE and modify +# it to specify a serverless application with other AWS resources. +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: An AWS Serverless Specification template describing your function. +Resources: + docspacereverseproxy: + Type: AWS::Serverless::Function + Properties: + CodeUri: docspace-reverse-proxy/ + Description: "" + MemorySize: 256 + Timeout: 3 + Handler: index.handler + Runtime: nodejs18.x + Architectures: + - x86_64 + EventInvokeConfig: + MaximumEventAgeInSeconds: 21600 + MaximumRetryAttempts: 2 + EphemeralStorage: + Size: 512 + RuntimeManagementConfig: + UpdateRuntimeOn: Auto + SnapStart: + ApplyOn: None + PackageType: Zip + Role: !GetAtt DocspaceReverseProxyRole.Arn + + # ==== ROLES ==== # + DocspaceReverseProxyRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: sts:AssumeRole + Principal: + Service: + - "lambda.amazonaws.com" + - "edgelambda.amazonaws.com" + # ==== POLICIES ==== # + PublishLogsPolicy: + Type: AWS::IAM::ManagedPolicy + Properties: + Description: Allows functions to write logs + Roles: + - !Ref DocspaceReverseProxyRole + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: "*" + + DyanmoDBFullAccessPolicy: + Type: AWS::IAM::ManagedPolicy + Properties: + Description: Allows functions to write logs + Roles: + - !Ref DocspaceReverseProxyRole + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - dynamodb:* + - dax:* + - application-autoscaling:DeleteScalingPolicy + - application-autoscaling:DeregisterScalableTarget + - application-autoscaling:DescribeScalableTargets + - application-autoscaling:DescribeScalingActivities + - application-autoscaling:DescribeScalingPolicies + - application-autoscaling:PutScalingPolicy + - application-autoscaling:RegisterScalableTarget + - cloudwatch:DeleteAlarms + - cloudwatch:DescribeAlarmHistory + - cloudwatch:DescribeAlarms + - cloudwatch:DescribeAlarmsForMetric + - cloudwatch:GetMetricStatistics + - cloudwatch:ListMetrics + - cloudwatch:PutMetricAlarm + - cloudwatch:GetMetricData + - datapipeline:ActivatePipeline + - datapipeline:CreatePipeline + - datapipeline:DeletePipeline + - datapipeline:DescribeObjects + - datapipeline:DescribePipelines + - datapipeline:GetPipelineDefinition + - datapipeline:ListPipelines + - datapipeline:PutPipelineDefinition + - datapipeline:QueryObjects + - ec2:DescribeVpcs + - ec2:DescribeSubnets + - ec2:DescribeSecurityGroups + - iam:GetRole + - iam:ListRoles + - kms:DescribeKey + - kms:ListAliases + - sns:CreateTopic + - sns:DeleteTopic + - sns:ListSubscriptions + - sns:ListSubscriptionsByTopic + - sns:ListTopics + - sns:Subscribe + - sns:Unsubscribe + - sns:SetTopicAttributes + - lambda:CreateFunction + - lambda:ListFunctions + - lambda:ListEventSourceMappings + - lambda:CreateEventSourceMapping + - lambda:DeleteEventSourceMapping + - lambda:GetFunctionConfiguration + - lambda:DeleteFunction + - resource-groups:ListGroups + - resource-groups:ListGroupResources + - resource-groups:GetGroup + - resource-groups:GetGroupQuery + - resource-groups:DeleteGroup + - resource-groups:CreateGroup + - tag:GetResources + - kinesis:ListStreams + - kinesis:DescribeStream + - kinesis:DescribeStreamSummary + Resource: "*" + - Effect: Allow + Action: + - iam:PassRole + Condition: + StringLike: + iam:PassedToService: + - application-autoscaling.amazonaws.com + - application-autoscaling.amazonaws.com.cn + - dax.amazonaws.com + Resource: "*" + - Effect: Allow + Action: + - iam:CreateServiceLinkedRole + Condition: + StringEquals: + iam:AWSServiceName: + - replication.dynamodb.amazonaws.com + - dax.amazonaws.com + - dynamodb.application-autoscaling.amazonaws.com + - contributorinsights.dynamodb.amazonaws.com + - kinesisreplication.dynamodb.amazonaws.com + Resource: "*"