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: "*"