CVE-2023-50928: Improper Access Control (CWE-284)
value: "Sandbox Accounts for Events" provides multiple, temporary AWS accounts to a number of authenticated users simultaneously via a browser-based GUI. Authenticated users could potentially claim and access empty AWS accounts by sending request payloads to the account API containing non-existent event ids and self-defined budget & duration. This issue only affects cleaned AWS accounts, it is not possible to access AWS accounts in use or existing data/infrastructure. This issue has been patched in version 1.1.0.
CWE description: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
CVSS v3 base score: 9
Published: 2023-12-22T21:15Z
Repository: https://github.com/awslabs/sandbox-accounts-for-events
Language: JavaScript
Files changed: .eslintignore, CHANGELOG.md, Makefile, amplify.yml, amplify/backend/api/AdminQueries/admin-queries-cloudformation-template.json, amplify/backend/api/safegraphql/cli-inputs.json, amplify/backend/api/safegraphql/schema.graphql, amplify/backend/auth/safeafe5208f/safeafe5208f-cloudformation-template.yml, amplify/backend/auth/userPoolGroups/template.json, amplify/backend/backend-config.json, amplify/backend/custom/cfnAdminUser/cfnAdminUser-cloudformation-template.json, amplify/backend/function/AdminQueries4757b5b2/AdminQueries4757b5b2-cloudformation-template.json, amplify/backend/function/safeAdminApi/safeAdminApi-cloudformation-template.json, amplify/backend/function/safeAdminApi/src/index.js, amplify/backend/function/safeLoginApi/custom-policies.json, amplify/backend/function/safeLoginApi/safeLoginApi-cloudformation-template.json, amplify/backend/function/safeLoginApi/src/index.js, amplify/backend/function/safeLoginApi/src/package-lock.json, amplify/backend/function/safeLoginApi/src/package.json, amplify/backend/function/safeOperatorApi/safeOperatorApi-cloudformation-template.json, amplify/backend/function/safeOperatorApi/src/index.js, amplify/backend/types/amplify-dependent-resources-ref.d.ts, amplify/cli.json, src/App.js, src/graphql/mutations.js, src/graphql/queries.js, src/graphql/schema.json, src/graphql/subscriptions.js, src/pages/DetailEvent.js, src/pages/OverviewEvents.js, src/pages/OverviewUsage.js, src/pages/Statistics.js, src/pages/components/EventLogin.js, src/pages/modals/CreateEventModal.js, src/pages/modals/CreateLeaseModal.js, src/pages/modals/EditEventModal.js, src/pages/modals/EditLeaseModal.js, src/redux/actions/aws_login.js, src/redux/actions/config.js, src/redux/actions/current_user.js, src/redux/actions/events.js, src/redux/actions/leases.js, src/redux/actions/usage.js
Commit date: 2023-11-04 22:01:52 +0100
Commit message: Reduced end user API permissions

Diff: diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..ab2aba6
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+amplify-codegen-temp/models/models
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae647d6..f9cf659 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,15 +1,28 @@
 # Changelog
 All notable changes to this project will be documented in this file.
 
+
+## [1.1.0] - 2023-11-04
+### Added
+- no additions.
+
+### Changed
+- Updated Lambda functions to Node18 and aws-sdk v3
+- Security upgrade: Blocked ListEvents API call for end users to remove potential data leak
+- Security upgrade: Blocked API calls requesting an AWS account for a non-existent event id
+- Security upgrade: Reduced number of possible parameters for end user API calls to necessary minimum, retrieve event management metadata from backend and auth token only.
+
+### Removed
+- nothing removed.
+
+
 ## [1.0.0] - 2022-06-10
 ### Added
 - Initial release.
 
 ### Changed
-- no changed.
+- no changes.
 
 ### Removed
 - nothing removed.
 
-
-[1.0.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.3.0...v1.0.0
diff --git a/Makefile b/Makefile
index fd4c12e..da3edb1 100644
--- a/Makefile
+++ b/Makefile
@@ -40,13 +40,13 @@ build:
 # create "build-cfn" folder if it doesn't exist and empty it
 	mkdir -p build-cfn
 	rm -f build-cfn/*
 
 # create build artifacts (= zip files) for CloudFormation deployment
-	git archive --format zip --output build-cfn/sandbox-accounts-for-events.zip $(branch)
-	cd install/cfn-lambda/dceHandleTerraFormDeployment && zip -r ../../../build-cfn/sandbox-accounts-for-events-lambda-terraform.zip . && cd -
-	cd install/cfn-lambda/dceHandleAmplifyDeployment && zip -r ../../../build-cfn/sandbox-accounts-for-events-lambda-amplify.zip . && cd -
+	zip -FSr build-cfn/sandbox-accounts-for-events.zip . -x amplify/#current-cloud-backend/\* build/\* build-cfn/\* \*dist/\* \*.DS_Store\* \*.vscode/\* \*.git/\* src/aws-exports.json\* \*amplify-meta.json\* amplify/team-provider-info.json\* \*awscloudformation\* \*node_modules\* amplify/.config/local-\*
+	cd install/cfn-lambda/dceHandleTerraFormDeployment && zip -FSr ../../../build-cfn/sandbox-accounts-for-events-lambda-terraform.zip . && cd -
+	cd install/cfn-lambda/dceHandleAmplifyDeployment && zip -FSr ../../../build-cfn/sandbox-accounts-for-events-lambda-amplify.zip . && cd -
 
 # upload build artifacts and CloudFormation template to specified S3 bucket
 	aws s3 sync build-cfn s3://$(bucket) $(profileString)
 	aws s3 cp install/sandbox-accounts-for-events-install.yaml s3://$(bucket)/sandbox-accounts-for-events-install.yaml $(profileString)
 
diff --git a/amplify.yml b/amplify.yml
index 58667ee..9e6cc2d 100644
--- a/amplify.yml
+++ b/amplify.yml
@@ -17,10 +17,15 @@ backend:
         - export DCE_API_GW=$(cat terraform.output | jq -r .api_url.value)
         - export DCE_LEASES_TABLE=$(cat terraform.output | jq -r .leases_table_name.value)
         - export DCE_ACCOUNTS_TABLE=$(cat terraform.output | jq -r .accounts_table_name.value)
 
         # update Amplify parameter files to connect Amplify frontend with backend
+        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeLoginApi_dceApiGw\",\"Value\":\"${DCE_API_GW}\",\"Overwrite\":true}"
+        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeOperatorApi_dceApiGw\",\"Value\":\"${DCE_API_GW}\",\"Overwrite\":true}"
+        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeAdminApi_dceApiGw\",\"Value\":\"${DCE_API_GW}\",\"Overwrite\":true}"
+        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeOperatorApi_dceLeasesTable\",\"Value\":\"${DCE_LEASES_TABLE}\",\"Overwrite\":true}"
+        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeAdminApi_dceAccountsTable\",\"Value\":\"${DCE_ACCOUNTS_TABLE}\",\"Overwrite\":true}"
         - cat amplify/backend/function/safeAdminApi/parameters.json | jq '.dceApiGw = $var' --arg var $DCE_API_GW | sponge amplify/backend/function/safeAdminApi/parameters.json
         - cat amplify/backend/function/safeAdminApi/parameters.json | jq '.dceAccountsTable = $var' --arg var $DCE_ACCOUNTS_TABLE | sponge amplify/backend/function/safeAdminApi/parameters.json
         - cat amplify/backend/function/safeOperatorApi/parameters.json | jq '.dceApiGw = $var' --arg var $DCE_API_GW | sponge amplify/backend/function/safeOperatorApi/parameters.json
         - cat amplify/backend/function/safeOperatorApi/parameters.json | jq '.dceLeasesTable = $var' --arg var $DCE_LEASES_TABLE | sponge amplify/backend/function/safeOperatorApi/parameters.json
         - cat amplify/backend/function/safeLoginApi/parameters.json | jq '.dceApiGw = $var' --arg var $DCE_API_GW | sponge amplify/backend/function/safeLoginApi/parameters.json
diff --git a/amplify/backend/api/AdminQueries/admin-queries-cloudformation-template.json b/amplify/backend/api/AdminQueries/admin-queries-cloudformation-template.json
index 7f16a15..dce1c78 100644
--- a/amplify/backend/api/AdminQueries/admin-queries-cloudformation-template.json
+++ b/amplify/backend/api/AdminQueries/admin-queries-cloudformation-template.json
@@ -1,8 +1,8 @@
 {
   "AWSTemplateFormatVersion": "2010-09-09",
-  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"api-API Gateway\",\"metadata\":{}}",
+  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"api-API Gateway\",\"metadata\":{}}",
   "Parameters": {
     "authRoleName": {
       "Type": "String"
     },
     "unauthRoleName": {
diff --git a/amplify/backend/api/safegraphql/cli-inputs.json b/amplify/backend/api/safegraphql/cli-inputs.json
new file mode 100644
index 0000000..ae06918
--- /dev/null
+++ b/amplify/backend/api/safegraphql/cli-inputs.json
@@ -0,0 +1,12 @@
+{
+  "version": 1,
+  "serviceConfiguration": {
+    "serviceName": "AppSync",
+    "defaultAuthType": {
+      "mode": "AMAZON_COGNITO_USER_POOLS",
+      "cognitoUserPoolId": "authsafeafe5208f"
+    },
+    "conflictResolution": {},
+    "apiName": "safegraphql"
+  }
+}
\ No newline at end of file
diff --git a/amplify/backend/api/safegraphql/schema.graphql b/amplify/backend/api/safegraphql/schema.graphql
index 376b88b..02cf142 100644
--- a/amplify/backend/api/safegraphql/schema.graphql
+++ b/amplify/backend/api/safegraphql/schema.graphql
@@ -1,7 +1,6 @@
 type Event @model @auth(rules: [
-  { allow: private, operations: [read] },
   { allow: groups, groups: ["admin","operator"] }
 ]) {
   id: ID!
   eventName: String
   eventOn: Int
diff --git a/amplify/backend/auth/safeafe5208f/safeafe5208f-cloudformation-template.yml b/amplify/backend/auth/safeafe5208f/safeafe5208f-cloudformation-template.yml
index 2f0216b..da125d4 100644
--- a/amplify/backend/auth/safeafe5208f/safeafe5208f-cloudformation-template.yml
+++ b/amplify/backend/auth/safeafe5208f/safeafe5208f-cloudformation-template.yml
@@ -214,36 +214,35 @@ Resources:
     DependsOn: UserPoolClient
   UserPoolClientLambda:
     Type: AWS::Lambda::Function
     Properties:
       Code:
-        ZipFile:
-          Fn::Join:
-            - ''
-            - - const response = require('cfn-response');
-              - const aws = require('aws-sdk');
-              - const identity = new aws.CognitoIdentityServiceProvider();
-              - exports.handler = (event, context, callback) => {
-              - ' if (event.RequestType == ''Delete'') { '
-              - '   response.send(event, context, response.SUCCESS, {})'
-              - ' }'
-              - ' if (event.RequestType == ''Update'' || event.RequestType == ''Create'') {'
-              - '   const params = {'
-              - '     ClientId: event.ResourceProperties.clientId,'
-              - '     UserPoolId: event.ResourceProperties.userpoolId'
-              - '   };'
-              - '   identity.describeUserPoolClient(params).promise()'
-              - '     .then((res) => {'
-              - '       response.send(event, context, response.SUCCESS, {''appSecret'': res.UserPoolClient.ClientSecret});'
-              - '     })'
-              - '     .catch((err) => {'
-              - '       response.send(event, context, response.FAILED, {err});'
-              - '     });'
-              - ' }'
-              - '};'
+        ZipFile: | 
+          const response = require('cfn-response');
+          const { CognitoIdentityProviderClient, DescribeUserPoolClientCommand } = require("@aws-sdk/client-cognito-identity-provider");
+          const identity = new CognitoIdentityProviderClient();
+          exports.handler = (event, context, callback) => {
+            console.log("Lambda invoked with the following parameters: ", event, context);
+            if (event.RequestType == "Delete") { 
+              response.send(event, context, response.SUCCESS, {})
+            }
+            if (event.RequestType == "Update" || event.RequestType == "Create") {
+              const params = {
+                ClientId: event.ResourceProperties.clientId,
+                UserPoolId: event.ResourceProperties.userpoolId
+              };
+              identity.send(new DescribeUserPoolClientCommand(params))
+                .then((res) => {
+                  response.send(event, context, response.SUCCESS, {"appSecret": res.UserPoolClient.ClientSecret});
+                })
+                .catch((err) => {
+                  response.send(event, context, response.FAILED, {err});
+              });
+            }
+          };
       Handler: index.handler
-      Runtime: nodejs14.x
+      Runtime: nodejs18.x
       Timeout: 300
       Role:
         Fn::GetAtt:
           - UserPoolClientRole
           - Arn
@@ -383,6 +382,6 @@ Outputs:
       Fn::GetAtt:
         - UserPoolClientInputs
         - appSecret
     Condition: ShouldOutputAppClientSecrets
 Description: >-
-  {"createdOn":"Linux","createdBy":"Amplify","createdWith":"7.6.15","stackType":"auth-Cognito","metadata":{}}
+  {"createdOn":"Mac","createdBy":"Amplify","createdWith":"12.7.1","stackType":"auth-Cognito","metadata":{}}
diff --git a/amplify/backend/auth/userPoolGroups/template.json b/amplify/backend/auth/userPoolGroups/template.json
index 9e57e7a..645823e 100644
--- a/amplify/backend/auth/userPoolGroups/template.json
+++ b/amplify/backend/auth/userPoolGroups/template.json
@@ -255,19 +255,20 @@
           "ZipFile": {
             "Fn::Join": [
               "\n",
               [
                 "const response = require('cfn-response');",
-                "const AWS = require('aws-sdk');",
+                "const { CognitoIdentityClient, SetIdentityPoolRolesCommand } = require('@aws-sdk/client-cognito-identity');",
                 "exports.handler = (event, context) => {",
+                "console.log('Lambda invoked with the following parameters: ', event, context);",
                 "if (event.RequestType == 'Delete') {",
                 "    response.send(event, context, response.SUCCESS, {message: 'Request type delete'})",
                 "};",
                 "if (event.RequestType == 'Create' || event.RequestType == 'Update') {",
                 "    let { identityPoolId, appClientID, appClientIDWeb, userPoolId, region }  = event.ResourceProperties;",
                 "    try {",
-                "       const cognitoidentity = new AWS.CognitoIdentity();",
+                "       const cognitoidentity = new CognitoIdentityClient();",
                 "       let params = {",
                 "           IdentityPoolId: identityPoolId,",
                 "           Roles: {",
                 "               'authenticated': event.ResourceProperties.AuthRoleArn,",
                 "               'unauthenticated': event.ResourceProperties.UnauthRoleArn,",
@@ -284,11 +285,11 @@
                 "           params.RoleMappings[`cognito-idp.${region}.amazonaws.com/${userPoolId}:${appClientID}`] = {",
                 "               Type: 'Token',",
                 "               AmbiguousRoleResolution: 'AuthenticatedRole',",
                 "           }",
                 "       }",
-                "    cognitoidentity.setIdentityPoolRoles(params).promise();",
+                "    cognitoidentity.send(new SetIdentityPoolRolesCommand(params));",
                 "    response.send(event, context, response.SUCCESS, {message: 'Successfully updated identity pool.'})",
                 "    } catch(err) {",
                 "        response.send(event, context, response.FAILED, {message: 'Error updating identity pool'});",
                 "    }",
                 "   };",
@@ -296,11 +297,11 @@
               ]
             ]
           }
         },
         "Handler": "index.handler",
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Timeout": 300,
         "Role": {
           "Fn::GetAtt": [
             "LambdaExecutionRole",
             "Arn"
@@ -363,7 +364,7 @@
           "Arn"
         ]
       }
     }
   },
-  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"auth-Cognito-UserPool-Groups\",\"metadata\":{}}"
+  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"auth-Cognito-UserPool-Groups\",\"metadata\":{}}"
 }
\ No newline at end of file
diff --git a/amplify/backend/backend-config.json b/amplify/backend/backend-config.json
index cc74f80..6c8cc7c 100644
--- a/amplify/backend/backend-config.json
+++ b/amplify/backend/backend-config.json
@@ -1,128 +1,234 @@
 {
-  "auth": {
-    "userPoolGroups": {
-      "service": "Cognito-UserPool-Groups",
-      "providerPlugin": "awscloudformation",
-      "dependsOn": [
-        {
-          "category": "auth",
-          "resourceName": "safeafe5208f",
-          "attributes": [
-            "UserPoolId",
-            "AppClientIDWeb",
-            "AppClientID",
-            "IdentityPoolId"
-          ]
-        }
-      ]
-    },
-    "safeafe5208f": {
-      "service": "Cognito",
-      "providerPlugin": "awscloudformation",
-      "dependsOn": [],
-      "customAuth": false
-    }
-  },
   "api": {
     "AdminQueries": {
-      "service": "API Gateway",
-      "providerPlugin": "awscloudformation",
       "authorizationType": "AMAZON_COGNITO_USER_POOLS",
       "dependsOn": [
         {
-          "category": "auth",
-          "resourceName": "safeafe5208f",
           "attributes": [
             "UserPoolId"
-          ]
+          ],
+          "category": "auth",
+          "resourceName": "safeafe5208f"
         },
         {
-          "category": "function",
-          "resourceName": "AdminQueries4757b5b2",
           "attributes": [
             "Arn",
             "Name"
-          ]
+          ],
+          "category": "function",
+          "resourceName": "AdminQueries4757b5b2"
         }
-      ]
+      ],
+      "providerPlugin": "awscloudformation",
+      "service": "API Gateway"
     },
     "safegraphql": {
-      "service": "AppSync",
-      "providerPlugin": "awscloudformation",
       "output": {
         "authConfig": {
+          "additionalAuthenticationProviders": [],
           "defaultAuthentication": {
             "authenticationType": "AMAZON_COGNITO_USER_POOLS",
             "userPoolConfig": {
               "userPoolId": "authsafeafe5208f"
             }
-          },
-          "additionalAuthenticationProviders": []
+          }
         }
-      }
+      },
+      "providerPlugin": "awscloudformation",
+      "service": "AppSync"
     }
   },
-  "function": {
-    "AdminQueries4757b5b2": {
-      "service": "Lambda",
+  "auth": {
+    "safeafe5208f": {
+      "customAuth": false,
+      "dependsOn": [],
       "providerPlugin": "awscloudformation",
-      "build": true,
+      "service": "Cognito"
+    },
+    "userPoolGroups": {
       "dependsOn": [
         {
-          "category": "auth",
-          "resourceName": "safeafe5208f",
           "attributes": [
-            "UserPoolId"
-          ]
+            "UserPoolId",
+            "AppClientIDWeb",
+            "AppClientID",
+            "IdentityPoolId"
+          ],
+          "category": "auth",
+          "resourceName": "safeafe5208f"
         }
-      ]
-    },
-    "safeOperatorApi": {
-      "build": true,
+      ],
       "providerPlugin": "awscloudformation",
-      "service": "Lambda",
-      "dependsOn": []
-    },
-    "safeLoginApi": {
-      "build": true,
-      "providerPlugin": "awscloudformation",
-      "service": "Lambda",
-      "dependsOn": []
-    },
-    "safeAdminApi": {
-      "build": true,
-      "providerPlugin": "awscloudformation",
-      "service": "Lambda",
-      "dependsOn": []
+      "service": "Cognito-UserPool-Groups"
     }
   },
   "custom": {
     "cfnAdminUser": {
-      "service": "customCloudformation",
-      "providerPlugin": "awscloudformation",
       "dependsOn": [
         {
-          "category": "auth",
-          "resourceName": "safeafe5208f",
           "attributes": [
             "IdentityPoolId",
             "IdentityPoolName",
             "UserPoolId",
             "UserPoolArn",
             "UserPoolName",
             "AppClientIDWeb",
             "AppClientID"
-          ]
+          ],
+          "category": "auth",
+          "resourceName": "safeafe5208f"
         },
         {
-          "category": "auth",
-          "resourceName": "userPoolGroups",
           "attributes": [
             "adminGroupRole",
             "operatorGroupRole"
-          ]
+          ],
+          "category": "auth",
+          "resourceName": "userPoolGroups"
+        }
+      ],
+      "providerPlugin": "awscloudformation",
+      "service": "customCloudformation"
+    }
+  },
+  "function": {
+    "AdminQueries4757b5b2": {
+      "build": true,
+      "dependsOn": [
+        {
+          "attributes": [
+            "UserPoolId"
+          ],
+          "category": "auth",
+          "resourceName": "safeafe5208f"
+        }
+      ],
+      "providerPlugin": "awscloudformation",
+      "service": "Lambda"
+    },
+    "safeAdminApi": {
+      "build": true,
+      "dependsOn": [],
+      "providerPlugin": "awscloudformation",
+      "service": "Lambda"
+    },
+    "safeLoginApi": {
+      "build": true,
+      "dependsOn": [],
+      "providerPlugin": "awscloudformation",
+      "service": "Lambda"
+    },
+    "safeOperatorApi": {
+      "build": true,
+      "dependsOn": [],
+      "providerPlugin": "awscloudformation",
+      "service": "Lambda"
+    }
+  },
+  "parameters": {
+    "AMPLIFY_function_AdminQueries4757b5b2_deploymentBucketName": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "AdminQueries4757b5b2"
+        }
+      ]
+    },
+    "AMPLIFY_function_AdminQueries4757b5b2_s3Key": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "AdminQueries4757b5b2"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeAdminApi_dceAccountsTable": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeAdminApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeAdminApi_dceApiGw": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeAdminApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeAdminApi_deploymentBucketName": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeAdminApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeAdminApi_s3Key": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeAdminApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeLoginApi_dceApiGw": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeLoginApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeLoginApi_deploymentBucketName": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeLoginApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeLoginApi_s3Key": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeLoginApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeOperatorApi_dceApiGw": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeOperatorApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeOperatorApi_dceLeasesTable": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeOperatorApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeOperatorApi_deploymentBucketName": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeOperatorApi"
+        }
+      ]
+    },
+    "AMPLIFY_function_safeOperatorApi_s3Key": {
+      "usedBy": [
+        {
+          "category": "function",
+          "resourceName": "safeOperatorApi"
         }
       ]
     }
   }
 }
\ No newline at end of file
diff --git a/amplify/backend/custom/cfnAdminUser/cfnAdminUser-cloudformation-template.json b/amplify/backend/custom/cfnAdminUser/cfnAdminUser-cloudformation-template.json
index 47e773e..8212bd2 100644
--- a/amplify/backend/custom/cfnAdminUser/cfnAdminUser-cloudformation-template.json
+++ b/amplify/backend/custom/cfnAdminUser/cfnAdminUser-cloudformation-template.json
@@ -105,7 +105,7 @@
       "Value": {
         "Ref": "adminUserEmailAddress"
       }
     }
   },
-  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"custom-customCloudformation\",\"metadata\":{}}"
+  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"custom-customCloudformation\",\"metadata\":{}}"
 }
\ No newline at end of file
diff --git a/amplify/backend/function/AdminQueries4757b5b2/AdminQueries4757b5b2-cloudformation-template.json b/amplify/backend/function/AdminQueries4757b5b2/AdminQueries4757b5b2-cloudformation-template.json
index 958d9af..299e889 100644
--- a/amplify/backend/function/AdminQueries4757b5b2/AdminQueries4757b5b2-cloudformation-template.json
+++ b/amplify/backend/function/AdminQueries4757b5b2/AdminQueries4757b5b2-cloudformation-template.json
@@ -1,8 +1,8 @@
 {
   "AWSTemplateFormatVersion": "2010-09-09",
-  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
+  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
   "Parameters": {
     "env": {
       "Type": "String"
     },
     "authsafeafe5208fUserPoolId": {
@@ -68,11 +68,11 @@
           "Fn::GetAtt": [
             "LambdaExecutionRole",
             "Arn"
           ]
         },
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Timeout": 25,
         "Code": {
           "S3Bucket": {
             "Ref": "deploymentBucketName"
           },
@@ -223,8 +223,16 @@
     },
     "LambdaExecutionRole": {
       "Value": {
         "Ref": "LambdaExecutionRole"
       }
+    },
+    "LambdaExecutionRoleArn": {
+      "Value": {
+        "Fn::GetAtt": [
+          "LambdaExecutionRole",
+          "Arn"
+        ]
+      }
     }
   }
 }
\ No newline at end of file
diff --git a/amplify/backend/function/safeAdminApi/safeAdminApi-cloudformation-template.json b/amplify/backend/function/safeAdminApi/safeAdminApi-cloudformation-template.json
index 64c8163..028536c 100644
--- a/amplify/backend/function/safeAdminApi/safeAdminApi-cloudformation-template.json
+++ b/amplify/backend/function/safeAdminApi/safeAdminApi-cloudformation-template.json
@@ -1,8 +1,8 @@
 {
   "AWSTemplateFormatVersion": "2010-09-09",
-  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
+  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
   "Parameters": {
     "CloudWatchRule": {
       "Type": "String",
       "Default": "NONE",
       "Description": " Schedule Expression"
@@ -88,11 +88,11 @@
           "Fn::GetAtt": [
             "LambdaExecutionRole",
             "Arn"
           ]
         },
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Layers": [],
         "Timeout": 25
       }
     },
     "LambdaExecutionRole": {
@@ -244,8 +244,16 @@
     },
     "LambdaExecutionRole": {
       "Value": {
         "Ref": "LambdaExecutionRole"
       }
+    },
+    "LambdaExecutionRoleArn": {
+      "Value": {
+        "Fn::GetAtt": [
+          "LambdaExecutionRole",
+          "Arn"
+        ]
+      }
     }
   }
 }
\ No newline at end of file
diff --git a/amplify/backend/function/safeAdminApi/src/index.js b/amplify/backend/function/safeAdminApi/src/index.js
index 78066bd..8306a98 100644
--- a/amplify/backend/function/safeAdminApi/src/index.js
+++ b/amplify/backend/function/safeAdminApi/src/index.js
@@ -1,13 +1,21 @@
 const https = require("https");
 const aws4 = require("aws4");
-const AWS = require("aws-sdk");
+const { IAMClient, ListAttachedRolePoliciesCommand, ListAccountAliasesCommand } = require('@aws-sdk/client-iam');
+const { STSClient, AssumeRoleCommand } = require('@aws-sdk/client-sts');
+const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
+const { DynamoDBDocumentClient, UpdateCommand } = require('@aws-sdk/lib-dynamodb');
+
 const apiUrl = new URL(process.env.DCE_API_GW);
 const region = process.env.REGION;
 const accountsTable = process.env.DCE_ACCOUNTS_TABLE;
 const serviceName = "execute-api";
 
+const stsClient = new STSClient({ region });
+const ddbClient = new DynamoDBClient({ region });
+const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);
+
 const respondWithError = (message = "Internal error.", errorObject = {}) => {
     console.error(message, errorObject);
     return JSON.stringify({
         status: "error",
         message,
@@ -82,42 +90,38 @@ const registerAccount = ({ id, roleName }) => {
         return respondWithError("Internal error while trying to register account.", "Parameter 'roleName' missing.");
     if (!id) return respondWithError("Internal error while trying to register account.", "Parameter 'id' missing.");
 
     const roleArn = "arn:aws:iam::" + id + ":role/" + roleName;
 
-    const sts = new AWS.STS({ region: region });
-    return sts
-        .assumeRole({
+    return stsClient
+        .send(new AssumeRoleCommand({
             RoleArn: roleArn,
             RoleSessionName: "testAdminLogin",
             DurationSeconds: 900
-        })
-        .promise()
+        }))
         .then((credentials) => {
-            const iam = new AWS.IAM({
+            const iamClient = new IAMClient({
                 credentials: {
                     accessKeyId: credentials.Credentials.AccessKeyId,
                     secretAccessKey: credentials.Credentials.SecretAccessKey,
                     sessionToken: credentials.Credentials.SessionToken
                 }
             });
-            return iam
-                .listAttachedRolePolicies({ RoleName: roleName })
-                .promise()
+            return iamClient
+                .send(new ListAttachedRolePoliciesCommand({ RoleName: roleName }))
                 .then((policies) => {
                     if (!policies.AttachedPolicies.find((item) => item.PolicyName === "AdministratorAccess"))
                         return respondWithError(
                             "No 'AdministratorAccess' policy attached to role " +
                                 roleName +
                                 " in account " +
                                 id +
                                 ".",
                             error
                         );
-                    return iam
-                        .listAccountAliases({})
-                        .promise()
+                    return iamClient
+                        .send(new ListAccountAliasesCommand({}))
                         .then((aliases) => {
                             if (aliases.AccountAliases.length === 0)
                                 return respondWithError("No account alias found for account " + id + ".");
                             return invokeApi("accounts", "POST", {
                                 adminRoleArn: roleArn,
@@ -159,27 +163,23 @@ const updateAccount = ({ id, accountStatus, adminRoleArn }) => {
     if (!id) return respondWithError("Internal error while trying to update account.", "Parameter 'id' missing.");
     if (!accountStatus)
         return respondWithError("Internal error while trying to update account.", "Parameter 'accountStatus' missing.");
     if (!adminRoleArn)
         return respondWithError("Internal error while trying to update account.", "Parameter 'adminRoleArn' missing.");
-    let ddb = new AWS.DynamoDB.DocumentClient({
-        region: region
-    });
-    return ddb
-        .update({
+    return ddbDocClient
+        .send(new UpdateCommand({
             TableName: accountsTable,
             Key: {
                 Id: id
             },
             UpdateExpression: "set AccountStatus = :s, AdminRoleArn=:r",
             ExpressionAttributeValues: {
                 ":s": accountStatus,
                 ":r": adminRoleArn
             },
             ReturnValues: "UPDATED_NEW"
-        })
-        .promise()
+        }))
         .then((response) =>
             respondWithSuccess("DynamoDB table record for account " + id + " successfully updated.", response)
         )
         .catch((error) =>
             respondWithError("Error trying to update DynamoDB table record for account " + id + ".", error)
@@ -195,11 +195,14 @@ const removeAccount = ({ id }) => {
             return respondWithSuccess("Account " + id + " successfully removed.", response);
         })
         .catch((error) => respondWithError("Failed to remove account " + id + ".", error));
 };
 
-exports.handler = async ({ arguments: args }, context) => {
+exports.handler = async (event, context) => {
+    console.log("Lambda invoked with the following parameters: ", event, context)
+    let args = event.arguments
+
     if (!args)
         return respondWithError("Internal error while trying to execute account task.", "Event arguments missing.");
     if (!args.action)
         return respondWithError("Internal error while trying to execute account task.", "Parameter 'action' missing.");
 
diff --git a/amplify/backend/function/safeLoginApi/custom-policies.json b/amplify/backend/function/safeLoginApi/custom-policies.json
index 8f46bd7..1d55edb 100644
--- a/amplify/backend/function/safeLoginApi/custom-policies.json
+++ b/amplify/backend/function/safeLoginApi/custom-policies.json
@@ -4,7 +4,24 @@
     "Action": [
         "execute-api:Invoke",
         "execute-api:ManageConnections"
     ],
     "Resource": ["arn:aws:execute-api:*:*:*"]
+  },
+  {
+    "Effect": "Allow",
+    "Action": [
+        "dynamodb:UpdateItem",
+        "dynamodb:GetItem",
+        "dynamodb:ListTables"
+    ],
+    "Resource": ["arn:aws:dynamodb:*:*:table/*"]
+  },
+  {
+    "Effect": "Allow",
+    "Action": [
+        "cognito-idp:ListUserPools",
+        "cognito-idp:AdminGetUser"
+    ],
+    "Resource": ["*"]
   }
 ]
\ No newline at end of file
diff --git a/amplify/backend/function/safeLoginApi/safeLoginApi-cloudformation-template.json b/amplify/backend/function/safeLoginApi/safeLoginApi-cloudformation-template.json
index 2e783c5..00af464 100644
--- a/amplify/backend/function/safeLoginApi/safeLoginApi-cloudformation-template.json
+++ b/amplify/backend/function/safeLoginApi/safeLoginApi-cloudformation-template.json
@@ -1,8 +1,8 @@
 {
   "AWSTemplateFormatVersion": "2010-09-09",
-  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
+  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
   "Parameters": {
     "CloudWatchRule": {
       "Type": "String",
       "Default": "NONE",
       "Description": " Schedule Expression"
@@ -82,11 +82,11 @@
           "Fn::GetAtt": [
             "LambdaExecutionRole",
             "Arn"
           ]
         },
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Layers": [],
         "Timeout": 25
       }
     },
     "LambdaExecutionRole": {
@@ -220,8 +220,16 @@
     },
     "LambdaExecutionRole": {
       "Value": {
         "Ref": "LambdaExecutionRole"
       }
+    },
+    "LambdaExecutionRoleArn": {
+      "Value": {
+        "Fn::GetAtt": [
+          "LambdaExecutionRole",
+          "Arn"
+        ]
+      }
     }
   }
 }
\ No newline at end of file
diff --git a/amplify/backend/function/safeLoginApi/src/index.js b/amplify/backend/function/safeLoginApi/src/index.js
index 021f1ae..f339597 100644
--- a/amplify/backend/function/safeLoginApi/src/index.js
+++ b/amplify/backend/function/safeLoginApi/src/index.js
@@ -1,10 +1,22 @@
+const moment = require("moment");
 const https = require("https");
 const aws4 = require("aws4");
 const apiUrl = new URL(process.env.DCE_API_GW);
 const region = process.env.REGION;
 const serviceName = "execute-api";
+const { DynamoDBClient, ListTablesCommand } = require('@aws-sdk/client-dynamodb');
+const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');
+const { CognitoIdentityProviderClient, ListUserPoolsCommand, AdminGetUserCommand } = require('@aws-sdk/client-cognito-identity-provider');
+
+
+const ddbClient = new DynamoDBClient({ region });
+const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);
+let eventsTable
+
+const cognitoClient = new CognitoIdentityProviderClient({ region })
+let userPoolId
 
 const respondWithError = (message = "Internal error.", errorObject = {}) => {
     console.error(message, errorObject);
     return JSON.stringify({
         status: "error",
@@ -60,10 +72,32 @@ const invokeApi = (path, method, body = null) => {
         httpRequest.write(bodyString);
         httpRequest.end();
     });
 };
 
+const getEndUserEvent = ({ id }) => {
+    if (!eventsTable)
+        return respondWithError("Failed to get data for event ID " + id + ".")
+
+    return ddbDocClient.send(new GetCommand({ TableName: eventsTable, Key: { id } }))
+    .then((response) => {
+        if (response.Item) {
+            return respondWithSuccess("Successfully got DynamoDB event item",{
+                id: response.Item.id,
+                eventStatus: response.Item.eventStatus,
+                eventName: response.Item.eventName
+            })
+        } else { 
+            return respondWithError('Could not find matching event for event ID "' + id + '". Please double check if your event ID is correct or ask your event operator for help.')
+        }
+    })
+    .catch((error) =>
+        respondWithError("Failed to get data for event ID " + id + ".", error)
+    );
+};
+
+
 const getLeaseLoginUrl = ({ id }, user) => {
     return invokeApi("leases/" + id + "/auth", "POST", "")
         .then((response) => {
             if (response.consoleUrl) return response.consoleUrl.replace("\\u0026", "?");
             else throw response;
@@ -82,92 +116,105 @@ const getLeaseLoginUrl = ({ id }, user) => {
         .catch((error) =>
             respondWithError("Failed to provide login URL for lease " + id + " for user " + user + ".", error)
         );
 };
 
-const getAwsLoginUrlForEvent = ({
-    eventId,
-    budgetAmount,
-    budgetCurrency,
-    expiresOn,
-    principalId,
-    user,
-    maxAccounts
-}) => {
-    if (!principalId)
-        return respondWithError("Internal error while trying to log in.", "Parameter 'principalId' missing.");
-    if (!budgetAmount)
-        return respondWithError("Internal error while trying to log in.", "Parameter 'budgetAmount' missing.");
-    if (!budgetCurrency)
-        return respondWithError("Internal error while trying to log in.", "Parameter 'budgetCurrency' missing.");
-    if (!expiresOn) return respondWithError("Internal error while trying to log in.", "Parameter 'expiresOn' missing.");
+const getAwsLoginUrlForEvent = async ({eventId}, event) => {
+    
     if (!eventId) return respondWithError("Internal error while trying to log in.", "Parameter 'eventId' missing.");
-    if (!user) return respondWithError("Internal error while trying to log in.", "Parameter 'user' missing.");
-    if (!maxAccounts)
-        return respondWithError("Internal error while trying to log in.", "Parameter 'maxAccounts' missing.");
-
-    return invokeApi("leases", "GET")
-        .then((leases) => {
-            if (leases.message) return respondWithError("Failed to list leases.", leases.message);
-
-            let foundLease = leases.find((item) => item.principalId === principalId);
-            if (foundLease) {
-                switch (foundLease.leaseStatus) {
-                    case "Active":
-                        return getLeaseLoginUrl(foundLease, user);
-                    case "Expired":
-                        return respondWithError(
-                            "Your AWS account has expired its lifetime and has been terminated now."
-                        );
-                    case "OverBudget":
+
+    if (!userPoolId)
+        return respondWithError("Failed to get user authentication pool, cannot claim an AWS account.")
+
+    if (!eventsTable)
+        return respondWithError("Failed to get data for event ID " + eventId + ".")
+
+    return cognitoClient.send(new AdminGetUserCommand({
+            UserPoolId: userPoolId,
+            Username: event.identity.username
+        }))
+        .then(cognitoResponse => {
+            const user = cognitoResponse.UserAttributes.find(attr => attr.Name == "email").Value
+            let principalId = eventId + "__" + user.replace(/[^a-zA-Z0-9]/g, "+")
+            return ddbDocClient.send(new GetCommand({ TableName: eventsTable, Key: { id: eventId } }))
+                .then(response => {
+                    if (response.Item) {
+                        return invokeApi("leases", "GET")
+                            .then((leases) => {
+                                if (leases.message) return respondWithError("Failed to list leases.", leases.message);
+                    
+                                let foundLease = leases.find((item) => item.principalId === principalId);
+                                if (foundLease) {
+                                    switch (foundLease.leaseStatus) {
+                                        case "Active":
+                                            return getLeaseLoginUrl(foundLease, user);
+                                        case "Expired":
+                                            return respondWithError(
+                                                "Your AWS account has expired its lifetime and has been terminated now."
+                                            );
+                                        case "OverBudget":
+                                            return respondWithError(
+                                                "Your AWS account has reached its maximum budget and has been terminated now."
+                                            );
+                                        case "Inactive":
+                                            return respondWithError("Your AWS account has been manually terminated.");
+                                        default:
+                                            return respondWithError("Your account is not available for logging in.");
+                                    }
+                                } else {
+                                    if (leases.filter((lease) => lease.principalId.startsWith(eventId)).length >= response.Item.maxAccounts)
+                                        return respondWithError("No more free AWS accounts available for this event.");
+                                    return invokeApi("leases", "POST", {
+                                        budgetAmount: response.Item.budgetAmount,
+                                        expiresOn: moment
+                                            .unix(response.Item.eventOn)
+                                            .add(response.Item.eventDays, "days")
+                                            .add(response.Item.eventHours, "hours")
+                                            .unix(),
+                                        principalId,
+                                        budgetNotificationEmails: [user],
+                                        budgetCurrency: "USD"
+                                    })
+                                        .then((newLease) => {
+                                            if (newLease.error) {
+                                                switch (newLease.error.code) {
+                                                    case "ServerError":
+                                                        if (newLease.error.message === "No Available accounts at this moment")
+                                                            return respondWithError("No more free AWS accounts available.", newLease.error);
+                                                        else
+                                                            return respondWithError(
+                                                                "Failed to create lease for " + user + ".",
+                                                                newLease.error
+                                                            );
+                                                    case "AlreadyExistsError":
+                                                        return respondWithError(
+                                                            "Found already existing lease for " + user + ".",
+                                                            newLease.error
+                                                        );
+                                                    default:
+                                                        return respondWithError(
+                                                            "Failed to create lease for " + user + ".",
+                                                            newLease.error
+                                                        );
+                                                }
+                                            }
+                                            return getLeaseLoginUrl(newLease, user);
+                                        })
+                                        .catch((error) => respondWithError("Failed to create lease for " + user + ".", error));
+                                }
+                            })
+                            .catch((error) => respondWithError("Failed to list leases.", error));
+                
+                    } else {
                         return respondWithError(
-                            "Your AWS account has reached its maximum budget and has been terminated now."
+                            "Your EventId " + eventId + " is invalid. Please contact your event operator for help."
                         );
-                    case "Inactive":
-                        return respondWithError("Your AWS account has been manually terminated.");
-                    default:
-                        return respondWithError("Your account is not available for logging in.");
-                }
-            } else {
-                if (leases.filter((lease) => lease.principalId.startsWith(eventId)).length >= maxAccounts)
-                    return respondWithError("No more free AWS accounts available for this event.");
-                return invokeApi("leases", "POST", {
-                    budgetAmount,
-                    expiresOn,
-                    principalId,
-                    budgetNotificationEmails: [user],
-                    budgetCurrency
+                    }
                 })
-                    .then((newLease) => {
-                        if (newLease.error) {
-                            switch (newLease.error.code) {
-                                case "ServerError":
-                                    if (newLease.error.message === "No Available accounts at this moment")
-                                        return respondWithError("No more free AWS accounts available.", newLease.error);
-                                    else
-                                        return respondWithError(
-                                            "Failed to create lease for " + user + ".",
-                                            newLease.error
-                                        );
-                                case "AlreadyExistsError":
-                                    return respondWithError(
-                                        "Found already existing lease for " + user + ".",
-                                        newLease.error
-                                    );
-                                default:
-                                    return respondWithError(
-                                        "Failed to create lease for " + user + ".",
-                                        newLease.error
-                                    );
-                            }
-                        }
-                        return getLeaseLoginUrl(newLease, user);
-                    })
-                    .catch((error) => respondWithError("Failed to create lease for " + user + ".", error));
-            }
+                .catch((error) => respondWithError("Failed to get data for event ID " + eventId + ".", error));
         })
-        .catch((error) => respondWithError("Failed to list leases.", error));
+        .catch(error => respondWithError("Failed to get user authentication data, cannot claim an AWS account.", error))
 };
 
 const getAwsLoginUrlForLease = ({ id }) => {
     if (!id) return respondWithError("Internal error while trying to log in.", "Parameter 'id' missing.");
     return invokeApi("leases/" + id + "/auth", "POST", "")
@@ -181,11 +228,14 @@ const getAwsLoginUrlForLease = ({ id }) => {
             return respondWithSuccess("Successfully created login URL for lease " + id + ".", url);
         })
         .catch((error) => respondWithError("Failed to provide login URL for lease " + id + ".", error));
 };
 
-exports.handler = async ({ arguments: args }) => {
+exports.handler = async (event, context) => {
+    console.log("Lambda invoked with the following parameters: ", event, context)
+    let args = event.arguments
+
     if (!args)
         return respondWithError("Internal error while trying to execute lease task.", "Event arguments missing.");
     if (!args.action)
         return respondWithError("Internal error while trying to execute lease task.", "Parameter 'action' missing.");
 
@@ -195,15 +245,27 @@ exports.handler = async ({ arguments: args }) => {
             params = JSON.parse(args.paramJson);
         } catch (e) {
             return respondWithError("'paramJson' contains malformed JSON string.");
         }
     }
+    
+    if (!eventsTable) {
+        const tables = await ddbClient.send(new ListTablesCommand())
+        eventsTable = tables.TableNames.find(name => name.startsWith("Event-"))
+    }
+
+    if (!userPoolId) {
+        const userPools = await cognitoClient.send(new ListUserPoolsCommand({ MaxResults: 10 }))
+        userPoolId = userPools.UserPools.find(userPool => userPool.Name.startsWith("safeafe5208f")).Id
+    }
 
     try {
         switch (args.action) {
+            case "getEndUserEvent":
+                return getEndUserEvent(params);
             case "getAwsLoginUrlForEvent":
-                return getAwsLoginUrlForEvent(params);
+                return getAwsLoginUrlForEvent(params, event);
             case "getAwsLoginUrlForLease":
                 return getAwsLoginUrlForLease(params);
             default:
                 throw new Error("unknown API action '" + args.action + "'");
         }
diff --git a/amplify/backend/function/safeLoginApi/src/package-lock.json b/amplify/backend/function/safeLoginApi/src/package-lock.json
index 376d355..7abedd2 100644
--- a/amplify/backend/function/safeLoginApi/src/package-lock.json
+++ b/amplify/backend/function/safeLoginApi/src/package-lock.json
@@ -6,8 +6,13 @@
   "dependencies": {
     "aws4": {
       "version": "1.11.0",
       "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
       "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
+    },
+    "moment": {
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
     }
   }
 }
diff --git a/amplify/backend/function/safeLoginApi/src/package.json b/amplify/backend/function/safeLoginApi/src/package.json
index b549133..a788e40 100644
--- a/amplify/backend/function/safeLoginApi/src/package.json
+++ b/amplify/backend/function/safeLoginApi/src/package.json
@@ -3,8 +3,9 @@
   "version": "2.0.0",
   "description": "Lambda function generated by Amplify",
   "main": "index.js",
   "license": "Apache-2.0",
   "dependencies": {
-    "aws4": "^1.11.0"
+    "aws4": "^1.11.0",
+    "moment": "^2.29.4"
   }
 }
diff --git a/amplify/backend/function/safeOperatorApi/safeOperatorApi-cloudformation-template.json b/amplify/backend/function/safeOperatorApi/safeOperatorApi-cloudformation-template.json
index a8bd652..e821463 100644
--- a/amplify/backend/function/safeOperatorApi/safeOperatorApi-cloudformation-template.json
+++ b/amplify/backend/function/safeOperatorApi/safeOperatorApi-cloudformation-template.json
@@ -1,8 +1,8 @@
 {
   "AWSTemplateFormatVersion": "2010-09-09",
-  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
+  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
   "Parameters": {
     "CloudWatchRule": {
       "Type": "String",
       "Default": "NONE",
       "Description": " Schedule Expression"
@@ -88,11 +88,11 @@
           "Fn::GetAtt": [
             "LambdaExecutionRole",
             "Arn"
           ]
         },
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Layers": [],
         "Timeout": 25
       }
     },
     "LambdaExecutionRole": {
@@ -236,8 +236,16 @@
     },
     "LambdaExecutionRole": {
       "Value": {
         "Ref": "LambdaExecutionRole"
       }
+    },
+    "LambdaExecutionRoleArn": {
+      "Value": {
+        "Fn::GetAtt": [
+          "LambdaExecutionRole",
+          "Arn"
+        ]
+      }
     }
   }
 }
\ No newline at end of file
diff --git a/amplify/backend/function/safeOperatorApi/src/index.js b/amplify/backend/function/safeOperatorApi/src/index.js
index 90b96ee..85fde16 100644
--- a/amplify/backend/function/safeOperatorApi/src/index.js
+++ b/amplify/backend/function/safeOperatorApi/src/index.js
@@ -1,13 +1,18 @@
 const https = require("https");
 const aws4 = require("aws4");
-const AWS = require("aws-sdk");
+const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
+const { DynamoDBDocumentClient, UpdateCommand, DeleteCommand } = require('@aws-sdk/lib-dynamodb');
+
 const apiUrl = new URL(process.env.DCE_API_GW);
 const region = process.env.REGION;
 const leasesTable = process.env.DCE_LEASES_TABLE;
 const serviceName = "execute-api";
 
+const ddbClient = new DynamoDBClient({ region });
+const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);
+
 const respondWithError = (message = "Internal error.", errorObject = {}) => {
     console.error(message, errorObject);
     return JSON.stringify({
         status: "error",
         message,
@@ -162,16 +167,15 @@ const updateLease = ({
     if (!budgetNotificationEmails)
         return respondWithError(
             "Internal error while trying to update lease.",
             "Parameter 'budgetNotificationEmails' missing."
         );
-    if (!user) return respondWithError("Internal error while trying to update lease.", "Parameter 'user' missing.");
-    const ddb = new AWS.DynamoDB.DocumentClient({
-        region: region
-    });
-    return ddb
-        .update({
+    if (!user) 
+        return respondWithError("Internal error while trying to update lease.", "Parameter 'user' missing.");
+
+    return ddbDocClient
+        .send(new UpdateCommand({
             TableName: leasesTable,
             Key: {
                 AccountId: accountId,
                 PrincipalId: principalId
             },
@@ -183,12 +187,11 @@ const updateLease = ({
                 ":b": budgetAmount,
                 ":e": expiresOn,
                 ":n": budgetNotificationEmails
             },
             ReturnValues: "UPDATED_NEW"
-        })
-        .promise()
+        }))
         .then((response) =>
             respondWithSuccess("DynamoDB table record for lease for " + user + " successfully updated.", response)
         )
         .catch((error) =>
             respondWithError("Error trying to update DynamoDB table record for lease for " + user + ".", error)
@@ -217,22 +220,18 @@ const deleteLease = ({ accountId, principalId, user }) => {
     if (!principalId)
         return respondWithError("Internal error while trying to delete lease.", "Parameter 'principalId' missing.");
     if (!accountId)
         return respondWithError("Internal error while trying to delete lease.", "Parameter 'accountId' missing.");
     if (!user) return respondWithError("Internal error while trying to delete lease.", "Parameter 'user' missing.");
-    const ddb = new AWS.DynamoDB.DocumentClient({
-        region: region
-    });
-    return ddb
-        .delete({
+    return ddbDocClient
+        .send(new DeleteCommand({
             TableName: leasesTable,
             Key: {
                 AccountId: accountId,
                 PrincipalId: principalId
             }
-        })
-        .promise()
+        }))
         .then((response) => respondWithSuccess("Lease for " + user + " successfully deleted.", response))
         .catch((error) => respondWithError("Error trying to delete lease for " + user + ".", error));
 };
 
 const getStatistics = () => {
@@ -276,11 +275,14 @@ const listUsage = () => {
             });
         })
         .catch((error) => respondWithError("Error trying to retrieve usage data.", error));
 };
 
-exports.handler = async ({ arguments: args }) => {
+exports.handler = async (event, context) => {
+    console.log("Lambda invoked with the following parameters: ", event, context)
+    let args = event.arguments
+
     if (!args) return respondWithError("Internal error while trying to execute task.", "Event arguments missing.");
     if (!args.action)
         return respondWithError("Internal error while trying to execute task.", "Parameter 'action' missing.");
 
     let params;
diff --git a/amplify/backend/types/amplify-dependent-resources-ref.d.ts b/amplify/backend/types/amplify-dependent-resources-ref.d.ts
index def8d99..126f025 100644
--- a/amplify/backend/types/amplify-dependent-resources-ref.d.ts
+++ b/amplify/backend/types/amplify-dependent-resources-ref.d.ts
@@ -1,59 +1,63 @@
 export type AmplifyDependentResourcesAttributes = {
-    "auth": {
-        "userPoolGroups": {
-            "adminGroupRole": "string",
-            "operatorGroupRole": "string"
-        },
-        "safeafe5208f": {
-            "IdentityPoolId": "string",
-            "IdentityPoolName": "string",
-            "UserPoolId": "string",
-            "UserPoolArn": "string",
-            "UserPoolName": "string",
-            "AppClientIDWeb": "string",
-            "AppClientID": "string"
-        }
+  "api": {
+    "AdminQueries": {
+      "ApiId": "string",
+      "ApiName": "string",
+      "RootUrl": "string"
     },
-    "api": {
-        "AdminQueries": {
-            "RootUrl": "string",
-            "ApiName": "string",
-            "ApiId": "string"
-        },
-        "safegraphql": {
-            "GraphQLAPIIdOutput": "string",
-            "GraphQLAPIEndpointOutput": "string"
-        }
+    "safegraphql": {
+      "GraphQLAPIEndpointOutput": "string",
+      "GraphQLAPIIdOutput": "string"
+    }
+  },
+  "auth": {
+    "safeafe5208f": {
+      "AppClientID": "string",
+      "AppClientIDWeb": "string",
+      "IdentityPoolId": "string",
+      "IdentityPoolName": "string",
+      "UserPoolArn": "string",
+      "UserPoolId": "string",
+      "UserPoolName": "string"
+    },
+    "userPoolGroups": {
+      "adminGroupRole": "string",
+      "operatorGroupRole": "string"
+    }
+  },
+  "custom": {
+    "cfnAdminUser": {
+      "AdminUserEmailAddress": "string"
+    }
+  },
+  "function": {
+    "AdminQueries4757b5b2": {
+      "Arn": "string",
+      "LambdaExecutionRole": "string",
+      "LambdaExecutionRoleArn": "string",
+      "Name": "string",
+      "Region": "string"
+    },
+    "safeAdminApi": {
+      "Arn": "string",
+      "LambdaExecutionRole": "string",
+      "LambdaExecutionRoleArn": "string",
+      "Name": "string",
+      "Region": "string"
     },
-    "function": {
-        "AdminQueries4757b5b2": {
-            "Name": "string",
-            "Arn": "string",
-            "Region": "string",
-            "LambdaExecutionRole": "string"
-        },
-        "safeOperatorApi": {
-            "Name": "string",
-            "Arn": "string",
-            "Region": "string",
-            "LambdaExecutionRole": "string"
-        },
-        "safeLoginApi": {
-            "Name": "string",
-            "Arn": "string",
-            "Region": "string",
-            "LambdaExecutionRole": "string"
-        },
-        "safeAdminApi": {
-            "Name": "string",
-            "Arn": "string",
-            "Region": "string",
-            "LambdaExecutionRole": "string"
-        }
+    "safeLoginApi": {
+      "Arn": "string",
+      "LambdaExecutionRole": "string",
+      "LambdaExecutionRoleArn": "string",
+      "Name": "string",
+      "Region": "string"
     },
-    "custom": {
-        "cfnAdminUser": {
-            "AdminUserEmailAddress": "string"
-        }
+    "safeOperatorApi": {
+      "Arn": "string",
+      "LambdaExecutionRole": "string",
+      "LambdaExecutionRoleArn": "string",
+      "Name": "string",
+      "Region": "string"
     }
+  }
 }
\ No newline at end of file
diff --git a/amplify/cli.json b/amplify/cli.json
index 48b86c1..852794e 100644
--- a/amplify/cli.json
+++ b/amplify/cli.json
@@ -2,15 +2,18 @@
   "features": {
     "graphqltransformer": {
       "addmissingownerfields": true,
       "improvepluralization": true,
       "validatetypenamereservedwords": true,
-      "useexperimentalpipelinedtransformer": false,
+      "useexperimentalpipelinedtransformer": true,
       "enableiterativegsiupdates": true,
       "secondarykeyasgsi": true,
       "skipoverridemutationinputtypes": true,
-      "showfieldauthnotification": false
+      "showfieldauthnotification": false,
+      "transformerversion": 2,
+      "suppressschemamigrationprompt": true,
+      "securityEnhancementNotification": false
     },
     "frontend-ios": {
       "enablexcodeintegration": true
     },
     "auth": {
diff --git a/src/App.js b/src/App.js
index 8de1244..721fb42 100644
--- a/src/App.js
+++ b/src/App.js
@@ -37,11 +37,11 @@ const AuthContainer = ({ children }) => {
         applyMode(Mode[config.DISPLAY_THEME])
         applyDensity(Density[config.DISPLAY_TEXT_MODE])
     },[config])
 
     useEffect(() => {
-        if (authStatus === "authenticated") {
+        if (authStatus === "authenticated" && user) {
             dispatch(setCurrentUser(user));
             dispatch(fetchConfig());
         }
     }, [dispatch, user, authStatus]);
 
diff --git a/src/graphql/mutations.js b/src/graphql/mutations.js
index 759594b..9670279 100644
--- a/src/graphql/mutations.js
+++ b/src/graphql/mutations.js
@@ -16,10 +16,11 @@ export const createEvent = /* GraphQL */ `
       maxAccounts
       eventBudget
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const updateEvent = /* GraphQL */ `
   mutation UpdateEvent(
@@ -36,10 +37,11 @@ export const updateEvent = /* GraphQL */ `
       maxAccounts
       eventBudget
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const deleteEvent = /* GraphQL */ `
   mutation DeleteEvent(
@@ -56,10 +58,11 @@ export const deleteEvent = /* GraphQL */ `
       maxAccounts
       eventBudget
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const createConfig = /* GraphQL */ `
   mutation CreateConfig(
@@ -69,10 +72,11 @@ export const createConfig = /* GraphQL */ `
     createConfig(input: $input, condition: $condition) {
       id
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const updateConfig = /* GraphQL */ `
   mutation UpdateConfig(
@@ -82,10 +86,11 @@ export const updateConfig = /* GraphQL */ `
     updateConfig(input: $input, condition: $condition) {
       id
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const deleteConfig = /* GraphQL */ `
   mutation DeleteConfig(
@@ -95,8 +100,9 @@ export const deleteConfig = /* GraphQL */ `
     deleteConfig(input: $input, condition: $condition) {
       id
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
diff --git a/src/graphql/queries.js b/src/graphql/queries.js
index 14b9238..aa27eba 100644
--- a/src/graphql/queries.js
+++ b/src/graphql/queries.js
@@ -1,20 +1,20 @@
 /* eslint-disable */
 // this is an auto generated file. This will be overwritten
 
 export const safeLoginApi = /* GraphQL */ `
-  query safeLoginApi($action: String, $paramJson: String) {
+  query SafeLoginApi($action: String, $paramJson: String) {
     safeLoginApi(action: $action, paramJson: $paramJson)
   }
 `;
 export const safeAdminApi = /* GraphQL */ `
-  query safeAdminApi($action: String, $paramJson: String) {
+  query SafeAdminApi($action: String, $paramJson: String) {
     safeAdminApi(action: $action, paramJson: $paramJson)
   }
 `;
 export const safeOperatorApi = /* GraphQL */ `
-  query safeOperatorApi($action: String, $paramJson: String) {
+  query SafeOperatorApi($action: String, $paramJson: String) {
     safeOperatorApi(action: $action, paramJson: $paramJson)
   }
 `;
 export const getEvent = /* GraphQL */ `
   query GetEvent($id: ID!) {
@@ -28,10 +28,11 @@ export const getEvent = /* GraphQL */ `
       maxAccounts
       eventBudget
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const listEvents = /* GraphQL */ `
   query ListEvents(
@@ -50,22 +51,25 @@ export const listEvents = /* GraphQL */ `
         maxAccounts
         eventBudget
         eventStatus
         createdAt
         updatedAt
+        __typename
       }
       nextToken
+      __typename
     }
   }
 `;
 export const getConfig = /* GraphQL */ `
   query GetConfig($id: ID!) {
     getConfig(id: $id) {
       id
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const listConfigs = /* GraphQL */ `
   query ListConfigs(
@@ -77,10 +81,12 @@ export const listConfigs = /* GraphQL */ `
       items {
         id
         config
         createdAt
         updatedAt
+        __typename
       }
       nextToken
+      __typename
     }
   }
 `;
diff --git a/src/graphql/schema.json b/src/graphql/schema.json
index 8a66fa5..a418d8f 100644
--- a/src/graphql/schema.json
+++ b/src/graphql/schema.json
@@ -1903,76 +1903,731 @@
         "name" : "Subscription",
         "description" : null,
         "fields" : [ {
           "name" : "onCreateEvent",
           "description" : null,
-          "args" : [ ],
+          "args" : [ {
+            "name" : "filter",
+            "description" : null,
+            "type" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionEventFilterInput",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          } ],
+          "type" : {
+            "kind" : "OBJECT",
+            "name" : "Event",
+            "ofType" : null
+          },
+          "isDeprecated" : false,
+          "deprecationReason" : null
+        }, {
+          "name" : "onUpdateEvent",
+          "description" : null,
+          "args" : [ {
+            "name" : "filter",
+            "description" : null,
+            "type" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionEventFilterInput",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          } ],
+          "type" : {
+            "kind" : "OBJECT",
+            "name" : "Event",
+            "ofType" : null
+          },
+          "isDeprecated" : false,
+          "deprecationReason" : null
+        }, {
+          "name" : "onDeleteEvent",
+          "description" : null,
+          "args" : [ {
+            "name" : "filter",
+            "description" : null,
+            "type" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionEventFilterInput",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          } ],
+          "type" : {
+            "kind" : "OBJECT",
+            "name" : "Event",
+            "ofType" : null
+          },
+          "isDeprecated" : false,
+          "deprecationReason" : null
+        }, {
+          "name" : "onCreateConfig",
+          "description" : null,
+          "args" : [ {
+            "name" : "filter",
+            "description" : null,
+            "type" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionConfigFilterInput",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          } ],
+          "type" : {
+            "kind" : "OBJECT",
+            "name" : "Config",
+            "ofType" : null
+          },
+          "isDeprecated" : false,
+          "deprecationReason" : null
+        }, {
+          "name" : "onUpdateConfig",
+          "description" : null,
+          "args" : [ {
+            "name" : "filter",
+            "description" : null,
+            "type" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionConfigFilterInput",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          } ],
+          "type" : {
+            "kind" : "OBJECT",
+            "name" : "Config",
+            "ofType" : null
+          },
+          "isDeprecated" : false,
+          "deprecationReason" : null
+        }, {
+          "name" : "onDeleteConfig",
+          "description" : null,
+          "args" : [ {
+            "name" : "filter",
+            "description" : null,
+            "type" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionConfigFilterInput",
+              "ofType" : null
+            },
+            "defaultValue" : null
+          } ],
+          "type" : {
+            "kind" : "OBJECT",
+            "name" : "Config",
+            "ofType" : null
+          },
+          "isDeprecated" : false,
+          "deprecationReason" : null
+        } ],
+        "inputFields" : null,
+        "interfaces" : [ ],
+        "enumValues" : null,
+        "possibleTypes" : null
+      }, {
+        "kind" : "INPUT_OBJECT",
+        "name" : "ModelSubscriptionEventFilterInput",
+        "description" : null,
+        "fields" : null,
+        "inputFields" : [ {
+          "name" : "id",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionIDInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eventName",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionStringInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eventOn",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionIntInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eventDays",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionIntInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eventHours",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionIntInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eventOwner",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionStringInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "maxAccounts",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionIntInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eventBudget",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionIntInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eventStatus",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionStringInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "and",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionEventFilterInput",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "or",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionEventFilterInput",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        } ],
+        "interfaces" : null,
+        "enumValues" : null,
+        "possibleTypes" : null
+      }, {
+        "kind" : "INPUT_OBJECT",
+        "name" : "ModelSubscriptionIDInput",
+        "description" : null,
+        "fields" : null,
+        "inputFields" : [ {
+          "name" : "ne",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eq",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "le",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "lt",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "ge",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "gt",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "contains",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "notContains",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "between",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "ID",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "beginsWith",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "ID",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "in",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "ID",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "notIn",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "ID",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        } ],
+        "interfaces" : null,
+        "enumValues" : null,
+        "possibleTypes" : null
+      }, {
+        "kind" : "INPUT_OBJECT",
+        "name" : "ModelSubscriptionStringInput",
+        "description" : null,
+        "fields" : null,
+        "inputFields" : [ {
+          "name" : "ne",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eq",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "le",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "lt",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "ge",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "gt",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "contains",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "notContains",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "between",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "String",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "beginsWith",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "in",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "String",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "notIn",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "String",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        } ],
+        "interfaces" : null,
+        "enumValues" : null,
+        "possibleTypes" : null
+      }, {
+        "kind" : "INPUT_OBJECT",
+        "name" : "ModelSubscriptionIntInput",
+        "description" : null,
+        "fields" : null,
+        "inputFields" : [ {
+          "name" : "ne",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Int",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "eq",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Int",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "le",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Int",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "lt",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Int",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "ge",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Int",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "gt",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Int",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "between",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "Int",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "in",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "Int",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "notIn",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "Int",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        } ],
+        "interfaces" : null,
+        "enumValues" : null,
+        "possibleTypes" : null
+      }, {
+        "kind" : "INPUT_OBJECT",
+        "name" : "ModelSubscriptionConfigFilterInput",
+        "description" : null,
+        "fields" : null,
+        "inputFields" : [ {
+          "name" : "id",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionIDInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "config",
+          "description" : null,
+          "type" : {
+            "kind" : "INPUT_OBJECT",
+            "name" : "ModelSubscriptionStringInput",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "and",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionConfigFilterInput",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "or",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "INPUT_OBJECT",
+              "name" : "ModelSubscriptionConfigFilterInput",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
+        } ],
+        "interfaces" : null,
+        "enumValues" : null,
+        "possibleTypes" : null
+      }, {
+        "kind" : "INPUT_OBJECT",
+        "name" : "ModelSubscriptionBooleanInput",
+        "description" : null,
+        "fields" : null,
+        "inputFields" : [ {
+          "name" : "ne",
+          "description" : null,
           "type" : {
-            "kind" : "OBJECT",
-            "name" : "Event",
+            "kind" : "SCALAR",
+            "name" : "Boolean",
             "ofType" : null
           },
-          "isDeprecated" : false,
-          "deprecationReason" : null
+          "defaultValue" : null
         }, {
-          "name" : "onUpdateEvent",
+          "name" : "eq",
           "description" : null,
-          "args" : [ ],
           "type" : {
-            "kind" : "OBJECT",
-            "name" : "Event",
+            "kind" : "SCALAR",
+            "name" : "Boolean",
             "ofType" : null
           },
-          "isDeprecated" : false,
-          "deprecationReason" : null
-        }, {
-          "name" : "onDeleteEvent",
+          "defaultValue" : null
+        } ],
+        "interfaces" : null,
+        "enumValues" : null,
+        "possibleTypes" : null
+      }, {
+        "kind" : "INPUT_OBJECT",
+        "name" : "ModelBooleanInput",
+        "description" : null,
+        "fields" : null,
+        "inputFields" : [ {
+          "name" : "ne",
           "description" : null,
-          "args" : [ ],
           "type" : {
-            "kind" : "OBJECT",
-            "name" : "Event",
+            "kind" : "SCALAR",
+            "name" : "Boolean",
             "ofType" : null
           },
-          "isDeprecated" : false,
-          "deprecationReason" : null
+          "defaultValue" : null
         }, {
-          "name" : "onCreateConfig",
+          "name" : "eq",
           "description" : null,
-          "args" : [ ],
           "type" : {
-            "kind" : "OBJECT",
-            "name" : "Config",
+            "kind" : "SCALAR",
+            "name" : "Boolean",
             "ofType" : null
           },
-          "isDeprecated" : false,
-          "deprecationReason" : null
+          "defaultValue" : null
         }, {
-          "name" : "onUpdateConfig",
+          "name" : "attributeExists",
           "description" : null,
-          "args" : [ ],
           "type" : {
-            "kind" : "OBJECT",
-            "name" : "Config",
+            "kind" : "SCALAR",
+            "name" : "Boolean",
             "ofType" : null
           },
-          "isDeprecated" : false,
-          "deprecationReason" : null
+          "defaultValue" : null
         }, {
-          "name" : "onDeleteConfig",
+          "name" : "attributeType",
           "description" : null,
-          "args" : [ ],
           "type" : {
-            "kind" : "OBJECT",
-            "name" : "Config",
+            "kind" : "ENUM",
+            "name" : "ModelAttributeTypes",
             "ofType" : null
           },
-          "isDeprecated" : false,
-          "deprecationReason" : null
+          "defaultValue" : null
         } ],
-        "inputFields" : null,
-        "interfaces" : [ ],
+        "interfaces" : null,
         "enumValues" : null,
         "possibleTypes" : null
       }, {
         "kind" : "ENUM",
         "name" : "ModelSortDirection",
@@ -1992,11 +2647,11 @@
           "deprecationReason" : null
         } ],
         "possibleTypes" : null
       }, {
         "kind" : "INPUT_OBJECT",
-        "name" : "ModelFloatInput",
+        "name" : "ModelSubscriptionFloatInput",
         "description" : null,
         "fields" : null,
         "inputFields" : [ {
           "name" : "ne",
           "description" : null,
@@ -2063,25 +2718,33 @@
               "ofType" : null
             }
           },
           "defaultValue" : null
         }, {
-          "name" : "attributeExists",
+          "name" : "in",
           "description" : null,
           "type" : {
-            "kind" : "SCALAR",
-            "name" : "Boolean",
-            "ofType" : null
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "Float",
+              "ofType" : null
+            }
           },
           "defaultValue" : null
         }, {
-          "name" : "attributeType",
+          "name" : "notIn",
           "description" : null,
           "type" : {
-            "kind" : "ENUM",
-            "name" : "ModelAttributeTypes",
-            "ofType" : null
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "Float",
+              "ofType" : null
+            }
           },
           "defaultValue" : null
         } ],
         "interfaces" : null,
         "enumValues" : null,
@@ -2095,31 +2758,80 @@
         "interfaces" : null,
         "enumValues" : null,
         "possibleTypes" : null
       }, {
         "kind" : "INPUT_OBJECT",
-        "name" : "ModelBooleanInput",
+        "name" : "ModelFloatInput",
         "description" : null,
         "fields" : null,
         "inputFields" : [ {
           "name" : "ne",
           "description" : null,
           "type" : {
             "kind" : "SCALAR",
-            "name" : "Boolean",
+            "name" : "Float",
             "ofType" : null
           },
           "defaultValue" : null
         }, {
           "name" : "eq",
           "description" : null,
           "type" : {
             "kind" : "SCALAR",
-            "name" : "Boolean",
+            "name" : "Float",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "le",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Float",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "lt",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Float",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "ge",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Float",
+            "ofType" : null
+          },
+          "defaultValue" : null
+        }, {
+          "name" : "gt",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "Float",
             "ofType" : null
           },
           "defaultValue" : null
+        }, {
+          "name" : "between",
+          "description" : null,
+          "type" : {
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "Float",
+              "ofType" : null
+            }
+          },
+          "defaultValue" : null
         }, {
           "name" : "attributeExists",
           "description" : null,
           "type" : {
             "kind" : "SCALAR",
@@ -2921,63 +3633,46 @@
         "locations" : [ "FIELD" ],
         "args" : [ ],
         "onOperation" : false,
         "onFragment" : false,
         "onField" : true
-      }, {
-        "name" : "aws_subscribe",
-        "description" : "Tells the service which mutation triggers this subscription.",
-        "locations" : [ "FIELD_DEFINITION" ],
-        "args" : [ {
-          "name" : "mutations",
-          "description" : "List of mutations which will trigger this subscription when they are called.",
-          "type" : {
-            "kind" : "LIST",
-            "name" : null,
-            "ofType" : {
-              "kind" : "SCALAR",
-              "name" : "String",
-              "ofType" : null
-            }
-          },
-          "defaultValue" : null
-        } ],
-        "onOperation" : false,
-        "onFragment" : false,
-        "onField" : false
       }, {
         "name" : "aws_oidc",
         "description" : "Tells the service this field/object has access authorized by an OIDC token.",
         "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
         "args" : [ ],
         "onOperation" : false,
         "onFragment" : false,
         "onField" : false
       }, {
-        "name" : "deprecated",
-        "description" : null,
-        "locations" : [ "FIELD_DEFINITION", "ENUM_VALUE" ],
+        "name" : "aws_cognito_user_pools",
+        "description" : "Tells the service this field/object has access authorized by a Cognito User Pools token.",
+        "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
         "args" : [ {
-          "name" : "reason",
-          "description" : null,
+          "name" : "cognito_groups",
+          "description" : "List of cognito user pool groups which have access on this field",
           "type" : {
-            "kind" : "SCALAR",
-            "name" : "String",
-            "ofType" : null
+            "kind" : "LIST",
+            "name" : null,
+            "ofType" : {
+              "kind" : "SCALAR",
+              "name" : "String",
+              "ofType" : null
+            }
           },
-          "defaultValue" : "\"No longer supported\""
+          "defaultValue" : null
         } ],
         "onOperation" : false,
         "onFragment" : false,
         "onField" : false
       }, {
-        "name" : "aws_cognito_user_pools",
-        "description" : "Tells the service this field/object has access authorized by a Cognito User Pools token.",
-        "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
+        "name" : "aws_publish",
+        "description" : "Tells the service which subscriptions will be published to when this mutation is called. This directive is deprecated use @aws_susbscribe directive instead.",
+        "locations" : [ "FIELD_DEFINITION" ],
         "args" : [ {
-          "name" : "cognito_groups",
-          "description" : "List of cognito user pool groups which have access on this field",
+          "name" : "subscriptions",
+          "description" : "List of subscriptions which will be published to when this mutation is called.",
           "type" : {
             "kind" : "LIST",
             "name" : null,
             "ofType" : {
               "kind" : "SCALAR",
@@ -2988,10 +3683,18 @@
           "defaultValue" : null
         } ],
         "onOperation" : false,
         "onFragment" : false,
         "onField" : false
+      }, {
+        "name" : "aws_api_key",
+        "description" : "Tells the service this field/object has access authorized by an API key.",
+        "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
+        "args" : [ ],
+        "onOperation" : false,
+        "onFragment" : false,
+        "onField" : false
       }, {
         "name" : "aws_auth",
         "description" : "Directs the schema to enforce authorization on a field",
         "locations" : [ "FIELD_DEFINITION" ],
         "args" : [ {
@@ -3010,24 +3713,16 @@
         } ],
         "onOperation" : false,
         "onFragment" : false,
         "onField" : false
       }, {
-        "name" : "aws_lambda",
-        "description" : "Tells the service this field/object has access authorized by a Lambda Authorizer.",
-        "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
-        "args" : [ ],
-        "onOperation" : false,
-        "onFragment" : false,
-        "onField" : false
-      }, {
-        "name" : "aws_publish",
-        "description" : "Tells the service which subscriptions will be published to when this mutation is called. This directive is deprecated use @aws_susbscribe directive instead.",
+        "name" : "aws_subscribe",
+        "description" : "Tells the service which mutation triggers this subscription.",
         "locations" : [ "FIELD_DEFINITION" ],
         "args" : [ {
-          "name" : "subscriptions",
-          "description" : "List of subscriptions which will be published to when this mutation is called.",
+          "name" : "mutations",
+          "description" : "List of mutations which will trigger this subscription when they are called.",
           "type" : {
             "kind" : "LIST",
             "name" : null,
             "ofType" : {
               "kind" : "SCALAR",
@@ -3039,24 +3734,41 @@
         } ],
         "onOperation" : false,
         "onFragment" : false,
         "onField" : false
       }, {
-        "name" : "aws_iam",
-        "description" : "Tells the service this field/object has access authorized by sigv4 signing.",
+        "name" : "aws_lambda",
+        "description" : "Tells the service this field/object has access authorized by a Lambda Authorizer.",
         "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
         "args" : [ ],
         "onOperation" : false,
         "onFragment" : false,
         "onField" : false
       }, {
-        "name" : "aws_api_key",
-        "description" : "Tells the service this field/object has access authorized by an API key.",
+        "name" : "aws_iam",
+        "description" : "Tells the service this field/object has access authorized by sigv4 signing.",
         "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
         "args" : [ ],
         "onOperation" : false,
         "onFragment" : false,
         "onField" : false
+      }, {
+        "name" : "deprecated",
+        "description" : null,
+        "locations" : [ "FIELD_DEFINITION", "ENUM_VALUE" ],
+        "args" : [ {
+          "name" : "reason",
+          "description" : null,
+          "type" : {
+            "kind" : "SCALAR",
+            "name" : "String",
+            "ofType" : null
+          },
+          "defaultValue" : "\"No longer supported\""
+        } ],
+        "onOperation" : false,
+        "onFragment" : false,
+        "onField" : false
       } ]
     }
   }
 }
\ No newline at end of file
diff --git a/src/graphql/subscriptions.js b/src/graphql/subscriptions.js
index 01215bb..6c9957e 100644
--- a/src/graphql/subscriptions.js
+++ b/src/graphql/subscriptions.js
@@ -1,11 +1,11 @@
 /* eslint-disable */
 // this is an auto generated file. This will be overwritten
 
 export const onCreateEvent = /* GraphQL */ `
-  subscription OnCreateEvent {
-    onCreateEvent {
+  subscription OnCreateEvent($filter: ModelSubscriptionEventFilterInput) {
+    onCreateEvent(filter: $filter) {
       id
       eventName
       eventOn
       eventDays
       eventHours
@@ -13,16 +13,17 @@ export const onCreateEvent = /* GraphQL */ `
       maxAccounts
       eventBudget
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const onUpdateEvent = /* GraphQL */ `
-  subscription OnUpdateEvent {
-    onUpdateEvent {
+  subscription OnUpdateEvent($filter: ModelSubscriptionEventFilterInput) {
+    onUpdateEvent(filter: $filter) {
       id
       eventName
       eventOn
       eventDays
       eventHours
@@ -30,16 +31,17 @@ export const onUpdateEvent = /* GraphQL */ `
       maxAccounts
       eventBudget
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const onDeleteEvent = /* GraphQL */ `
-  subscription OnDeleteEvent {
-    onDeleteEvent {
+  subscription OnDeleteEvent($filter: ModelSubscriptionEventFilterInput) {
+    onDeleteEvent(filter: $filter) {
       id
       eventName
       eventOn
       eventDays
       eventHours
@@ -47,38 +49,42 @@ export const onDeleteEvent = /* GraphQL */ `
       maxAccounts
       eventBudget
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const onCreateConfig = /* GraphQL */ `
-  subscription OnCreateConfig {
-    onCreateConfig {
+  subscription OnCreateConfig($filter: ModelSubscriptionConfigFilterInput) {
+    onCreateConfig(filter: $filter) {
       id
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const onUpdateConfig = /* GraphQL */ `
-  subscription OnUpdateConfig {
-    onUpdateConfig {
+  subscription OnUpdateConfig($filter: ModelSubscriptionConfigFilterInput) {
+    onUpdateConfig(filter: $filter) {
       id
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const onDeleteConfig = /* GraphQL */ `
-  subscription OnDeleteConfig {
-    onDeleteConfig {
+  subscription OnDeleteConfig($filter: ModelSubscriptionConfigFilterInput) {
+    onDeleteConfig(filter: $filter) {
       id
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
diff --git a/src/pages/DetailEvent.js b/src/pages/DetailEvent.js
index a84ddd4..28a0b7a 100644
--- a/src/pages/DetailEvent.js
+++ b/src/pages/DetailEvent.js
@@ -198,11 +198,11 @@ const DetailPanel = () => {
                                 <Link href={"mailto:" + Item.eventOwner}>{Item.eventOwner}</Link>
                             </CopyClipboardIconButton>
                         </Box>
                         <Box>
                             <Box variant="h5">Maximum budget per AWS account</Box>
-                            {Item.eventBudget + " " + Config.BUDGET_CURRENCY}
+                            {Item.eventBudget + " USD"}
                         </Box>
                         <Box>
                             <Box variant="h5">Created on</Box>
                             {Item.createdDate}
                         </Box>
@@ -240,11 +240,11 @@ const DetailPanel = () => {
                                 </Box>
                             </Grid>
                         </Box>
                         <Box>
                             <Box variant="h5">Current overall spend</Box>
-                            {Item.eventSpend + " " + Config.BUDGET_CURRENCY}
+                            {Item.eventSpend + " USD"}
                         </Box>
                     </SpaceBetween>
                 )}
             </Container>
         </SpaceBetween>
diff --git a/src/pages/OverviewEvents.js b/src/pages/OverviewEvents.js
index f4a8709..be619d4 100644
--- a/src/pages/OverviewEvents.js
+++ b/src/pages/OverviewEvents.js
@@ -234,11 +234,11 @@ const ItemTable = () => {
                     },
                     {
                         id: "eventSpend",
                         header: "Current spend",
                         sortingField: "eventSpend",
-                        cell: (item) => item.eventSpend + " " + Config.BUDGET_CURRENCY
+                        cell: (item) => item.eventSpend + " USD"
                     }
                 ]}
                 items={items}
                 selectionType="multi"
                 header={
@@ -431,15 +431,15 @@ const DetailSplitPanel = () => {
                     <Box variant="h5">Free AWS accounts</Box>
                     {item.freeAccounts}
                 </Box>
                 <Box>
                     <Box variant="h5">Maximum budget per AWS account</Box>
-                    {item.eventBudget + " " + Config.BUDGET_CURRENCY}
+                    {item.eventBudget + " USD"}
                 </Box>
                 <Box>
                     <Box variant="h5">Current overall spend</Box>
-                    {item.eventSpend + " " + Config.BUDGET_CURRENCY}
+                    {item.eventSpend + " USD"}
                 </Box>
                 <Box>
                     <Box variant="h5">Created on</Box>
                     {item.createdDate}
                 </Box>
diff --git a/src/pages/OverviewUsage.js b/src/pages/OverviewUsage.js
index bf675fd..49a1ff0 100644
--- a/src/pages/OverviewUsage.js
+++ b/src/pages/OverviewUsage.js
@@ -134,11 +134,11 @@ const OverviewUsage = () => {
                 },
                 {
                     id: "costAmount",
                     header: "AWS service spend",
                     sortingField: "costAmount",
-                    cell: (item) => item.costAmount.toFixed(2) + " " + Config.BUDGET_CURRENCY
+                    cell: (item) => item.costAmount.toFixed(2) + " USD"
                 }
             ]}
             items={items}
             header={
                 <Header
diff --git a/src/pages/Statistics.js b/src/pages/Statistics.js
index 83adaed..44a7482 100644
--- a/src/pages/Statistics.js
+++ b/src/pages/Statistics.js
@@ -184,11 +184,11 @@ const Statistics = () => {
                                 xScaleType="categorical"
                                 stackedBars
                                 empty={<NoData />}
                             />
                         </Container>
-                        <Container header={<Header>AWS service spend in {Config.BUDGET_CURRENCY}</Header>}>
+                        <Container header={<Header>AWS service spend in USD</Header>}>
                             <AreaChart
                                 hideFilter
                                 series={[
                                     {
                                         title: "AWS service spend",
diff --git a/src/pages/components/EventLogin.js b/src/pages/components/EventLogin.js
index 53bf2ed..70a5c08 100644
--- a/src/pages/components/EventLogin.js
+++ b/src/pages/components/EventLogin.js
@@ -1,29 +1,26 @@
 import { Alert, Box, Button, Container, FormField, Header, Input, SpaceBetween } from "@cloudscape-design/components";
 import { useEffect, useState } from "react";
 import { useSelector, useDispatch } from "react-redux";
 import { useParams } from "react-router";
 import AwsLoginModal from "../modals/AwsLoginModal";
-import moment from "moment";
 import { isEmpty, regExpAll } from "./utils.js";
-import { fetchAwsLoginUrl } from "../../redux/actions/aws_login";
-import { fetchEndUserEvent } from "../../redux/actions/events";
+import { fetchAwsLoginUrl, getEndUserEvent } from "../../redux/actions/aws_login";
 
 const EventLogin = () => {
     const { urlParamEventId } = useParams();
     const [value, setValue] = useState("");
     const [inputError, setInputError] = useState({});
     const [valueChangedOnce, setValueChangedOnce] = useState(false);
     const NotificationItem = useSelector((state) => state.notification);
     const User = useSelector((state) => state.current_user);
     const Config = useSelector((state) => state.config);
-    const Event = useSelector((state) => state.events);
     const AwsLogin = useSelector((state) => state.aws_login);
     const dispatch = useDispatch();
 
     const clearEvent = () => {
-        dispatch({ type: "event/dismiss" });
+        dispatch({ type: "aws_login/event_dismiss" });
         dispatch({ type: "notification/dismiss" });
         setInputError({});
         setValueChangedOnce(true);
     };
 
@@ -42,27 +39,27 @@ const EventLogin = () => {
     const submit = () => {
         dispatch({ type: "notification/dismiss" });
         if (!validateInputs(value)) {
             return;
         }
-        dispatch(fetchEndUserEvent(value));
+        dispatch(getEndUserEvent(value));
     };
 
     const updateFormValue = (update) => {
         validateInputs(update, true);
         setValue(update);
     };
 
     useEffect(() => {
-        if (!valueChangedOnce && isEmpty(Event.item) && urlParamEventId && User.isLoggedIn) {
-            dispatch(fetchEndUserEvent(urlParamEventId));
+        if (!valueChangedOnce && isEmpty(AwsLogin.event) && urlParamEventId && User.isLoggedIn) {
+            dispatch(getEndUserEvent(urlParamEventId));
         }
-    }, [Event.item, urlParamEventId, dispatch, valueChangedOnce, User]);
+    }, [AwsLogin.event, urlParamEventId, dispatch, valueChangedOnce, User]);
 
     useEffect(() => {
-        if (!Event.item.eventStatus) return;
-        switch (Event.item.eventStatus) {
+        if (!AwsLogin.event.eventStatus) return;
+        switch (AwsLogin.event.eventStatus) {
             case "Waiting":
                 dispatch({
                     type: "notification/waiting",
                     message:
                         "This event hasn't been started yet. Please wait until your event operator allows you to log into your AWS account and then refresh this browser page."
@@ -80,41 +77,41 @@ const EventLogin = () => {
                 dispatch({
                     type: "notification/error",
                     message: "Internal event status error. Please ask your event support for help."
                 });
         }
-    }, [Event.item, dispatch]);
+    }, [AwsLogin.event, dispatch]);
 
     useEffect(() => {
-        dispatch({ type: "event/dismiss" });
+        dispatch({ type: "aws_login/event_dismiss" });
         dispatch({ type: "notification/dismiss" });
     }, [dispatch]);
 
     /////////
     // MAIN
     /////////
 
     return (
         <Container>
             <SpaceBetween size="xl">
-                {!Event.item.id ? (
+                {!AwsLogin.event.id ? (
                     <SpaceBetween size="m">
                         <FormField
                             description="Ask your event support staff for details"
                             errorText={inputError.EVENT_ID}
                             label="Enter the event id for today's event:"
                         >
                             <Input value={value} onChange={({ detail }) => updateFormValue(detail.value)} />
                         </FormField>
-                        <Button variant="primary" onClick={submit} loading={Event.status === "loading"}>
+                        <Button variant="primary" onClick={submit} loading={AwsLogin.event_status === "loading"}>
                             Log in to event
                         </Button>
                     </SpaceBetween>
                 ) : (
                     <>
                         <Header variant="h1" actions={<Button onClick={clearEvent}>Change event</Button>}>
-                            Welcome to "{Event.item.eventName}"
+                            Welcome to "{AwsLogin.event.eventName}"
                         </Header>
                         {User.isOperator || User.isAdmin ? (
                             <Alert type="warning" header="IMPORTANT for operators & admins">
                                 Be careful: Clicking on the button below below will consume an AWS account from the
                                 account pool list.
@@ -129,30 +126,11 @@ const EventLogin = () => {
                         </Box>
                         <Button
                             icon="external"
                             variant="primary"
                             loading={AwsLogin.status === "loading"}
-                            onClick={() =>
-                                dispatch(
-                                    fetchAwsLoginUrl({
-                                        principalId:
-                                            Event.item.id +
-                                            Config.EVENT_PRINCIPAL_SEPARATOR +
-                                            User.email.replace(/[^a-zA-Z0-9]/g, Config.EVENT_EMAIL_SUBST),
-                                        budgetAmount: Event.item.eventBudget,
-                                        budgetCurrency: Config.BUDGET_CURRENCY,
-                                        user: User.email,
-                                        expiresOn: moment
-                                            .unix(Event.item.eventOn)
-                                            .add(Event.item.eventDays, "days")
-                                            .add(Event.item.eventHours, "hours")
-                                            .unix(),
-                                        maxAccounts: Event.item.maxAccounts,
-                                        eventId: Event.item.id
-                                    })
-                                )
-                            }
+                            onClick={() => dispatch(fetchAwsLoginUrl({ eventId: AwsLogin.event.id })) }
                             disabled={NotificationItem.visible}
                         >
                             Open AWS Console
                         </Button>
                         <AwsLoginModal />
diff --git a/src/pages/modals/CreateEventModal.js b/src/pages/modals/CreateEventModal.js
index f6767f1..fe20f81 100644
--- a/src/pages/modals/CreateEventModal.js
+++ b/src/pages/modals/CreateEventModal.js
@@ -206,11 +206,11 @@ const CreateEventModal = () => {
                             onChange={({ detail }) => updateFormValue({ maxAccounts: detail.value })}
                             value={value.maxAccounts}
                         />
                     </FormField>
                     <FormField
-                        label={"Budget in " + Config.BUDGET_CURRENCY}
+                        label={"Budget in USD"}
                         description={
                             "Enter budget cap for each AWS account (maximum " + Config.ACCOUNT_MAX_BUDGET + ")"
                         }
                         errorText={inputError.BUDGET}
                     >
diff --git a/src/pages/modals/CreateLeaseModal.js b/src/pages/modals/CreateLeaseModal.js
index 4fb21c1..67d18f5 100644
--- a/src/pages/modals/CreateLeaseModal.js
+++ b/src/pages/modals/CreateLeaseModal.js
@@ -77,14 +77,14 @@ const CreateLeaseModal = ({ isAdminView }) => {
 
     const updateFormValue = (update) => {
         setValue((prev) => {
             let newValue = { ...prev, ...update };
             validateInputs(newValue, true);
-            let user = newValue.user.replace(/[^a-zA-Z0-9]/g, Config.EVENT_EMAIL_SUBST);
+            let user = newValue.user.replace(/[^a-zA-Z0-9]/g, "+");
 
             if (newValue.eventId !== "")
-                newValue.principalId = newValue.eventId + Config.EVENT_PRINCIPAL_SEPARATOR + user;
+                newValue.principalId = newValue.eventId + "__" + user;
             else newValue.principalId = user;
 
             return newValue;
         });
     };
@@ -148,11 +148,11 @@ const CreateLeaseModal = ({ isAdminView }) => {
                             onChange={({ detail }) => updateFormValue({ user: detail.value })}
                             value={value.user}
                         />
                     </FormField>
                     <FormField
-                        label={"Budget in " + Config.BUDGET_CURRENCY}
+                        label={"Budget in USD"}
                         description={"Enter budget cap for this lease (maximum " + Config.ACCOUNT_MAX_BUDGET + ")"}
                         errorText={inputError.BUDGET}
                     >
                         <Input
                             onChange={({ detail }) => updateFormValue({ budgetAmount: detail.value })}
diff --git a/src/pages/modals/EditEventModal.js b/src/pages/modals/EditEventModal.js
index 4669bb7..1a42346 100644
--- a/src/pages/modals/EditEventModal.js
+++ b/src/pages/modals/EditEventModal.js
@@ -221,11 +221,11 @@ const EditEventModal = () => {
                                 onChange={({ detail }) => updateFormValue({ maxAccounts: detail.value })}
                                 value={value.maxAccounts}
                             />
                         </FormField>
                         <FormField
-                            label={"Budget in " + Config.BUDGET_CURRENCY}
+                            label={"Budget in USD"}
                             description={
                                 "Enter budget cap for each AWS account (maximum " + Config.ACCOUNT_MAX_BUDGET + ")"
                             }
                             errorText={inputError.BUDGET}
                         >
diff --git a/src/pages/modals/EditLeaseModal.js b/src/pages/modals/EditLeaseModal.js
index 94486a2..c6f7209 100644
--- a/src/pages/modals/EditLeaseModal.js
+++ b/src/pages/modals/EditLeaseModal.js
@@ -149,11 +149,11 @@ const EditLeaseModal = ({ isAdminView }) => {
                                     ]}
                                 />
                             </FormField>
                         ) : null}
                         <FormField
-                            label={"Budget in " + Config.BUDGET_CURRENCY}
+                            label={"Budget in USD"}
                             description={"Enter budget cap for this lease (maximum " + Config.ACCOUNT_MAX_BUDGET + ")"}
                             errorText={inputError.BUDGET}
                         >
                             <Input
                                 onChange={({ detail }) => updateFormValue({ budgetAmount: detail.value })}
diff --git a/src/redux/actions/aws_login.js b/src/redux/actions/aws_login.js
index e7fa331..d94e8cd 100644
--- a/src/redux/actions/aws_login.js
+++ b/src/redux/actions/aws_login.js
@@ -1,11 +1,13 @@
 import { API, graphqlOperation } from "@aws-amplify/api";
 import * as queries from "../../graphql/queries";
 
 const initialState = {
     status: "hidden",
-    url: ""
+    event_status: "idle",
+    url: "",
+    event: {}
 };
 
 const aws_login = (state = initialState, action) => {
     switch (action.type) {
         case "aws_login/loading":
@@ -19,10 +21,29 @@ const aws_login = (state = initialState, action) => {
                 status: "visible",
                 url: action.payload.url
             };
         case "aws_login/loadError":
         case "aws_login/dismiss":
+            return {
+                ...state,
+                url: "",
+                status: "hidden"
+            };
+                
+        case "aws_login/event_loading":
+            return {
+                ...state,
+                event_status: "loading"
+            };
+        case "aws_login/event_loaded":
+            return {
+                ...state,
+                event_status: "idle",
+                event: action.payload
+            };
+        case "aws_login/event_loadError":
+        case "aws_login/event_dismiss":
             return initialState;
         default:
             return state;
     }
 };
@@ -48,6 +69,29 @@ export const fetchAwsLoginUrl = (params) => async (dispatch) => {
         dispatch({ type: "aws_login/loadError" });
         dispatch({ type: "notification/error", message: error.message });
     }
 };
 
+export const getEndUserEvent = (id) => async (dispatch) => {
+    dispatch({ type: "aws_login/event_loading" });
+    try {
+        const response = await API.graphql(
+            graphqlOperation(queries.safeLoginApi, {
+                action: "getEndUserEvent",
+                paramJson: JSON.stringify({id})
+            })
+        );
+        const payload = JSON.parse(response.data.safeLoginApi);
+        if (!payload || payload.status === "error") {
+            throw payload;
+        }
+        dispatch({ type: "aws_login/event_loaded", payload: payload.body });
+        dispatch({ type: "notification/dismiss" });
+    } catch (error) {
+        console.error(error);
+        dispatch({ type: "aws_login/event_loadError" });
+        dispatch({ type: "notification/error", message: error.message });
+    }
+};
+
+
 export default aws_login;
diff --git a/src/redux/actions/config.js b/src/redux/actions/config.js
index 2cf8861..12b0e8e 100644
--- a/src/redux/actions/config.js
+++ b/src/redux/actions/config.js
@@ -5,18 +5,15 @@ import * as mutations from "../../graphql/mutations";
 let initialState = {
     id: "default",
     EVENT_MAX_DAYS: 90,
     EVENT_MAX_ACCOUNTS: 50,
     ACCOUNT_MAX_BUDGET: 1000,
-    BUDGET_CURRENCY: "USD",
     EVENT_DEFAULT_LENGTH_DAYS: 0,
     EVENT_DEFAULT_LENGTH_HOURS: 8,
     EVENT_DEFAULT_ACCOUNT_BUDGET: 10,
     EVENT_DEFAULT_ACCOUNTS: 5,
     EVENT_ID_LENGTH: 10,
-    EVENT_PRINCIPAL_SEPARATOR: "__",
-    EVENT_EMAIL_SUBST: "+",
     EMAIL_REGEX: "\\S+@\\S+\\.\\S+",
     LEASE_ID_REGEX: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
     ITEM_PAGE_SIZE: 20,
     SUBITEM_PAGE_SIZE: 10,
     ADMIN_GROUP: "admin",
diff --git a/src/redux/actions/current_user.js b/src/redux/actions/current_user.js
index 2c920e0..ab4c946 100644
--- a/src/redux/actions/current_user.js
+++ b/src/redux/actions/current_user.js
@@ -15,10 +15,12 @@ const current_user = (state = initialState, action) => {
             return state;
     }
 };
 
 export const setCurrentUser = (data) => (dispatch, getState) => {
+    if (!data.attributes)
+        return
     const config = getState().config;
     let useremail = data.attributes.email;
     let tokenPayload = data.signInUserSession.accessToken.payload;
     let username = useremail.substring(0, useremail.indexOf("@"));
     username = username[0].toUpperCase() + username.slice(1);
diff --git a/src/redux/actions/events.js b/src/redux/actions/events.js
index c65b737..415e55b 100644
--- a/src/redux/actions/events.js
+++ b/src/redux/actions/events.js
@@ -43,10 +43,11 @@ const events = (state = initialState, action) => {
             eventSpend: eventSpend.toFixed(2)
         };
     };
 
     switch (action.type) {
+        case "event/loading":
         case "events/loading":
             return {
                 ...state,
                 status: "loading"
             };
@@ -54,15 +55,10 @@ const events = (state = initialState, action) => {
             return {
                 ...state,
                 status: "idle",
                 items: action.events.map((item) => convertItem(item, action.leases))
             };
-        case "event/loading":
-            return {
-                ...state,
-                status: "loading"
-            };
         case "event/loaded":
             return {
                 ...state,
                 item: convertItem(action.event, action.leases),
                 status: "idle"
diff --git a/src/redux/actions/leases.js b/src/redux/actions/leases.js
index 443f645..9b329f0 100644
--- a/src/redux/actions/leases.js
+++ b/src/redux/actions/leases.js
@@ -11,11 +11,11 @@ const initialState = {
 const leases = (state = initialState, action) => {
     const convertItem = (item) => {
         let spendAmount = item.spendAmount ?? 0;
         if (typeof spendAmount !== "string") spendAmount = spendAmount.toFixed(2);
         let principalIdParts = item.principalId.match(
-            new RegExp("^(" + action.config.EVENT_ID_REGEX + ")" + action.config.EVENT_PRINCIPAL_SEPARATOR + "\\S{5,}$")
+            new RegExp("^(" + action.config.EVENT_ID_REGEX + ")__\\S{5,}$")
         );
 
         return {
             ...item,
             eventId: principalIdParts ? principalIdParts[1] : "",
@@ -286,11 +286,11 @@ export const createLease =
                         budgetAmount: parseInt(budgetAmount),
                         expiresOn,
                         principalId,
                         budgetNotificationEmails,
                         user,
-                        budgetCurrency: config.BUDGET_CURRENCY
+                        budgetCurrency: "USD"
                     })
                 })
             ).then((response) => {
                 const payload = JSON.parse(response.data.safeOperatorApi);
                 if (!payload || payload.status === "error") {
diff --git a/src/redux/actions/usage.js b/src/redux/actions/usage.js
index 1d940e6..ff8bb3a 100644
--- a/src/redux/actions/usage.js
+++ b/src/redux/actions/usage.js
@@ -9,11 +9,11 @@ const initialState = {
 
 const usage = (state = initialState, action) => {
     const convertItem = (item) => ({
         ...item,
         startDate: moment.unix(item.startDate).format(action.config.FORMAT_DATE),
-        eventId: item.principalId.includes(action.config.EVENT_PRINCIPAL_SEPARATOR) ? item.principalId.substring(0, action.config.EVENT_ID_LENGTH) : ""
+        eventId: item.principalId.includes("__") ? item.principalId.substring(0, action.config.EVENT_ID_LENGTH) : ""
     });
 
     switch (action.type) {
         case "usage/loading":
             return {

Vulnerable code: - no changed.

[1.0.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.3.0...v1.0.0
	git archive --format zip --output build-cfn/sandbox-accounts-for-events.zip $(branch)
	cd install/cfn-lambda/dceHandleTerraFormDeployment && zip -r ../../../build-cfn/sandbox-accounts-for-events-lambda-terraform.zip . && cd -
	cd install/cfn-lambda/dceHandleAmplifyDeployment && zip -r ../../../build-cfn/sandbox-accounts-for-events-lambda-amplify.zip . && cd -
  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"api-API Gateway\",\"metadata\":{}}",
  { allow: private, operations: [read] },
        ZipFile:
          Fn::Join:
            - ''
            - - const response = require('cfn-response');
              - const aws = require('aws-sdk');
              - const identity = new aws.CognitoIdentityServiceProvider();
              - exports.handler = (event, context, callback) => {
              - ' if (event.RequestType == ''Delete'') { '
              - '   response.send(event, context, response.SUCCESS, {})'
              - ' }'
              - ' if (event.RequestType == ''Update'' || event.RequestType == ''Create'') {'
              - '   const params = {'
              - '     ClientId: event.ResourceProperties.clientId,'
              - '     UserPoolId: event.ResourceProperties.userpoolId'
              - '   };'
              - '   identity.describeUserPoolClient(params).promise()'
              - '     .then((res) => {'
              - '       response.send(event, context, response.SUCCESS, {''appSecret'': res.UserPoolClient.ClientSecret});'
              - '     })'
              - '     .catch((err) => {'
              - '       response.send(event, context, response.FAILED, {err});'
              - '     });'
              - ' }'
              - '};'
      Runtime: nodejs14.x
  {"createdOn":"Linux","createdBy":"Amplify","createdWith":"7.6.15","stackType":"auth-Cognito","metadata":{}}
                "const AWS = require('aws-sdk');",
                "       const cognitoidentity = new AWS.CognitoIdentity();",
                "    cognitoidentity.setIdentityPoolRoles(params).promise();",
        "Runtime": "nodejs14.x",
  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"auth-Cognito-UserPool-Groups\",\"metadata\":{}}"
  "auth": {
    "userPoolGroups": {
      "service": "Cognito-UserPool-Groups",
      "providerPlugin": "awscloudformation",
      "dependsOn": [
        {
          "category": "auth",
          "resourceName": "safeafe5208f",
          "attributes": [
            "UserPoolId",
            "AppClientIDWeb",
            "AppClientID",
            "IdentityPoolId"
          ]
        }
      ]
    },
    "safeafe5208f": {
      "service": "Cognito",
      "providerPlugin": "awscloudformation",
      "dependsOn": [],
      "customAuth": false
    }
  },
      "service": "API Gateway",
      "providerPlugin": "awscloudformation",
          "category": "auth",
          "resourceName": "safeafe5208f",
          ]
          "category": "function",
          "resourceName": "AdminQueries4757b5b2",
          ]
      ]
      "service": "AppSync",
      "providerPlugin": "awscloudformation",
          },
          "additionalAuthenticationProviders": []
      }
  "function": {
    "AdminQueries4757b5b2": {
      "service": "Lambda",
      "build": true,
          "category": "auth",
          "resourceName": "safeafe5208f",
            "UserPoolId"
          ]
      ]
    },
    "safeOperatorApi": {
      "build": true,
      "service": "Lambda",
      "dependsOn": []
    },
    "safeLoginApi": {
      "build": true,
      "providerPlugin": "awscloudformation",
      "service": "Lambda",
      "dependsOn": []
    },
    "safeAdminApi": {
      "build": true,
      "providerPlugin": "awscloudformation",
      "service": "Lambda",
      "dependsOn": []
      "service": "customCloudformation",
      "providerPlugin": "awscloudformation",
          "category": "auth",
          "resourceName": "safeafe5208f",
          ]
          "category": "auth",
          "resourceName": "userPoolGroups",
          ]
  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"custom-customCloudformation\",\"metadata\":{}}"
  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
        "Runtime": "nodejs14.x",
  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
        "Runtime": "nodejs14.x",
const AWS = require("aws-sdk");
    const sts = new AWS.STS({ region: region });
    return sts
        .assumeRole({
        })
        .promise()
            const iam = new AWS.IAM({
            return iam
                .listAttachedRolePolicies({ RoleName: roleName })
                .promise()
                    return iam
                        .listAccountAliases({})
                        .promise()
    let ddb = new AWS.DynamoDB.DocumentClient({
        region: region
    });
    return ddb
        .update({
        })
        .promise()
exports.handler = async ({ arguments: args }, context) => {
  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
        "Runtime": "nodejs14.x",
const getAwsLoginUrlForEvent = ({
    eventId,
    budgetAmount,
    budgetCurrency,
    expiresOn,
    principalId,
    user,
    maxAccounts
}) => {
    if (!principalId)
        return respondWithError("Internal error while trying to log in.", "Parameter 'principalId' missing.");
    if (!budgetAmount)
        return respondWithError("Internal error while trying to log in.", "Parameter 'budgetAmount' missing.");
    if (!budgetCurrency)
        return respondWithError("Internal error while trying to log in.", "Parameter 'budgetCurrency' missing.");
    if (!expiresOn) return respondWithError("Internal error while trying to log in.", "Parameter 'expiresOn' missing.");
    if (!user) return respondWithError("Internal error while trying to log in.", "Parameter 'user' missing.");
    if (!maxAccounts)
        return respondWithError("Internal error while trying to log in.", "Parameter 'maxAccounts' missing.");

    return invokeApi("leases", "GET")
        .then((leases) => {
            if (leases.message) return respondWithError("Failed to list leases.", leases.message);

            let foundLease = leases.find((item) => item.principalId === principalId);
            if (foundLease) {
                switch (foundLease.leaseStatus) {
                    case "Active":
                        return getLeaseLoginUrl(foundLease, user);
                    case "Expired":
                        return respondWithError(
                            "Your AWS account has expired its lifetime and has been terminated now."
                        );
                    case "OverBudget":
                            "Your AWS account has reached its maximum budget and has been terminated now."
                    case "Inactive":
                        return respondWithError("Your AWS account has been manually terminated.");
                    default:
                        return respondWithError("Your account is not available for logging in.");
                }
            } else {
                if (leases.filter((lease) => lease.principalId.startsWith(eventId)).length >= maxAccounts)
                    return respondWithError("No more free AWS accounts available for this event.");
                return invokeApi("leases", "POST", {
                    budgetAmount,
                    expiresOn,
                    principalId,
                    budgetNotificationEmails: [user],
                    budgetCurrency
                    .then((newLease) => {
                        if (newLease.error) {
                            switch (newLease.error.code) {
                                case "ServerError":
                                    if (newLease.error.message === "No Available accounts at this moment")
                                        return respondWithError("No more free AWS accounts available.", newLease.error);
                                    else
                                        return respondWithError(
                                            "Failed to create lease for " + user + ".",
                                            newLease.error
                                        );
                                case "AlreadyExistsError":
                                    return respondWithError(
                                        "Found already existing lease for " + user + ".",
                                        newLease.error
                                    );
                                default:
                                    return respondWithError(
                                        "Failed to create lease for " + user + ".",
                                        newLease.error
                                    );
                            }
                        }
                        return getLeaseLoginUrl(newLease, user);
                    })
                    .catch((error) => respondWithError("Failed to create lease for " + user + ".", error));
            }
        .catch((error) => respondWithError("Failed to list leases.", error));
exports.handler = async ({ arguments: args }) => {
                return getAwsLoginUrlForEvent(params);
    "aws4": "^1.11.0"
  "Description": "{\"createdOn\":\"Linux\",\"createdBy\":\"Amplify\",\"createdWith\":\"7.6.15\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
        "Runtime": "nodejs14.x",
const AWS = require("aws-sdk");
    if (!user) return respondWithError("Internal error while trying to update lease.", "Parameter 'user' missing.");
    const ddb = new AWS.DynamoDB.DocumentClient({
        region: region
    });
    return ddb
        .update({
        })
        .promise()
    const ddb = new AWS.DynamoDB.DocumentClient({
        region: region
    });
    return ddb
        .delete({
        })
        .promise()
exports.handler = async ({ arguments: args }) => {
    "auth": {
        "userPoolGroups": {
            "adminGroupRole": "string",
            "operatorGroupRole": "string"
        },
        "safeafe5208f": {
            "IdentityPoolId": "string",
            "IdentityPoolName": "string",
            "UserPoolId": "string",
            "UserPoolArn": "string",
            "UserPoolName": "string",
            "AppClientIDWeb": "string",
            "AppClientID": "string"
        }
    "api": {
        "AdminQueries": {
            "RootUrl": "string",
            "ApiName": "string",
            "ApiId": "string"
        },
        "safegraphql": {
            "GraphQLAPIIdOutput": "string",
            "GraphQLAPIEndpointOutput": "string"
        }
    "function": {
        "AdminQueries4757b5b2": {
            "Name": "string",
            "Arn": "string",
            "Region": "string",
            "LambdaExecutionRole": "string"
        },
        "safeOperatorApi": {
            "Name": "string",
            "Arn": "string",
            "Region": "string",
            "LambdaExecutionRole": "string"
        },
        "safeLoginApi": {
            "Name": "string",
            "Arn": "string",
            "Region": "string",
            "LambdaExecutionRole": "string"
        },
        "safeAdminApi": {
            "Name": "string",
            "Arn": "string",
            "Region": "string",
            "LambdaExecutionRole": "string"
        }
    "custom": {
        "cfnAdminUser": {
            "AdminUserEmailAddress": "string"
        }
      "useexperimentalpipelinedtransformer": false,
      "showfieldauthnotification": false
        if (authStatus === "authenticated") {
  query safeLoginApi($action: String, $paramJson: String) {
  query safeAdminApi($action: String, $paramJson: String) {
  query safeOperatorApi($action: String, $paramJson: String) {
          "args" : [ ],
            "kind" : "OBJECT",
            "name" : "Event",
          "isDeprecated" : false,
          "deprecationReason" : null
          "name" : "onUpdateEvent",
          "args" : [ ],
            "kind" : "OBJECT",
            "name" : "Event",
          "isDeprecated" : false,
          "deprecationReason" : null
        }, {
          "name" : "onDeleteEvent",
          "args" : [ ],
            "kind" : "OBJECT",
            "name" : "Event",
          "isDeprecated" : false,
          "deprecationReason" : null
          "name" : "onCreateConfig",
          "args" : [ ],
            "kind" : "OBJECT",
            "name" : "Config",
          "isDeprecated" : false,
          "deprecationReason" : null
          "name" : "onUpdateConfig",
          "args" : [ ],
            "kind" : "OBJECT",
            "name" : "Config",
          "isDeprecated" : false,
          "deprecationReason" : null
          "name" : "onDeleteConfig",
          "args" : [ ],
            "kind" : "OBJECT",
            "name" : "Config",
          "isDeprecated" : false,
          "deprecationReason" : null
        "inputFields" : null,
        "interfaces" : [ ],
        "name" : "ModelFloatInput",
          "name" : "attributeExists",
            "kind" : "SCALAR",
            "name" : "Boolean",
            "ofType" : null
          "name" : "attributeType",
            "kind" : "ENUM",
            "name" : "ModelAttributeTypes",
            "ofType" : null
        "name" : "ModelBooleanInput",
            "name" : "Boolean",
            "name" : "Boolean",
      }, {
        "name" : "aws_subscribe",
        "description" : "Tells the service which mutation triggers this subscription.",
        "locations" : [ "FIELD_DEFINITION" ],
        "args" : [ {
          "name" : "mutations",
          "description" : "List of mutations which will trigger this subscription when they are called.",
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "String",
              "ofType" : null
            }
          },
          "defaultValue" : null
        } ],
        "onOperation" : false,
        "onFragment" : false,
        "onField" : false
        "name" : "deprecated",
        "description" : null,
        "locations" : [ "FIELD_DEFINITION", "ENUM_VALUE" ],
          "name" : "reason",
          "description" : null,
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          "defaultValue" : "\"No longer supported\""
        "name" : "aws_cognito_user_pools",
        "description" : "Tells the service this field/object has access authorized by a Cognito User Pools token.",
        "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
          "name" : "cognito_groups",
          "description" : "List of cognito user pool groups which have access on this field",
        "name" : "aws_lambda",
        "description" : "Tells the service this field/object has access authorized by a Lambda Authorizer.",
        "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
        "args" : [ ],
        "onOperation" : false,
        "onFragment" : false,
        "onField" : false
      }, {
        "name" : "aws_publish",
        "description" : "Tells the service which subscriptions will be published to when this mutation is called. This directive is deprecated use @aws_susbscribe directive instead.",
          "name" : "subscriptions",
          "description" : "List of subscriptions which will be published to when this mutation is called.",
        "name" : "aws_iam",
        "description" : "Tells the service this field/object has access authorized by sigv4 signing.",
        "name" : "aws_api_key",
        "description" : "Tells the service this field/object has access authorized by an API key.",
  subscription OnCreateEvent {
    onCreateEvent {
  subscription OnUpdateEvent {
    onUpdateEvent {
  subscription OnDeleteEvent {
    onDeleteEvent {
  subscription OnCreateConfig {
    onCreateConfig {
  subscription OnUpdateConfig {
    onUpdateConfig {
  subscription OnDeleteConfig {
    onDeleteConfig {
                            {Item.eventBudget + " " + Config.BUDGET_CURRENCY}
                            {Item.eventSpend + " " + Config.BUDGET_CURRENCY}
                        cell: (item) => item.eventSpend + " " + Config.BUDGET_CURRENCY
                    {item.eventBudget + " " + Config.BUDGET_CURRENCY}
                    {item.eventSpend + " " + Config.BUDGET_CURRENCY}
                    cell: (item) => item.costAmount.toFixed(2) + " " + Config.BUDGET_CURRENCY
                        <Container header={<Header>AWS service spend in {Config.BUDGET_CURRENCY}</Header>}>
import moment from "moment";
import { fetchAwsLoginUrl } from "../../redux/actions/aws_login";
import { fetchEndUserEvent } from "../../redux/actions/events";
    const Event = useSelector((state) => state.events);
        dispatch({ type: "event/dismiss" });
        dispatch(fetchEndUserEvent(value));
        if (!valueChangedOnce && isEmpty(Event.item) && urlParamEventId && User.isLoggedIn) {
            dispatch(fetchEndUserEvent(urlParamEventId));
    }, [Event.item, urlParamEventId, dispatch, valueChangedOnce, User]);
        if (!Event.item.eventStatus) return;
        switch (Event.item.eventStatus) {
    }, [Event.item, dispatch]);
        dispatch({ type: "event/dismiss" });
                {!Event.item.id ? (
                        <Button variant="primary" onClick={submit} loading={Event.status === "loading"}>
                            Welcome to "{Event.item.eventName}"
                            onClick={() =>
                                dispatch(
                                    fetchAwsLoginUrl({
                                        principalId:
                                            Event.item.id +
                                            Config.EVENT_PRINCIPAL_SEPARATOR +
                                            User.email.replace(/[^a-zA-Z0-9]/g, Config.EVENT_EMAIL_SUBST),
                                        budgetAmount: Event.item.eventBudget,
                                        budgetCurrency: Config.BUDGET_CURRENCY,
                                        user: User.email,
                                        expiresOn: moment
                                            .unix(Event.item.eventOn)
                                            .add(Event.item.eventDays, "days")
                                            .add(Event.item.eventHours, "hours")
                                            .unix(),
                                        maxAccounts: Event.item.maxAccounts,
                                        eventId: Event.item.id
                                    })
                                )
                            }
                        label={"Budget in " + Config.BUDGET_CURRENCY}
            let user = newValue.user.replace(/[^a-zA-Z0-9]/g, Config.EVENT_EMAIL_SUBST);
                newValue.principalId = newValue.eventId + Config.EVENT_PRINCIPAL_SEPARATOR + user;
                        label={"Budget in " + Config.BUDGET_CURRENCY}
                            label={"Budget in " + Config.BUDGET_CURRENCY}
                            label={"Budget in " + Config.BUDGET_CURRENCY}
    url: ""
    BUDGET_CURRENCY: "USD",
    EVENT_PRINCIPAL_SEPARATOR: "__",
    EVENT_EMAIL_SUBST: "+",
        case "event/loading":
            return {
                ...state,
                status: "loading"
            };
            new RegExp("^(" + action.config.EVENT_ID_REGEX + ")" + action.config.EVENT_PRINCIPAL_SEPARATOR + "\\S{5,}$")
                        budgetCurrency: config.BUDGET_CURRENCY
        eventId: item.principalId.includes(action.config.EVENT_PRINCIPAL_SEPARATOR) ? item.principalId.substring(0, action.config.EVENT_ID_LENGTH) : ""
Fixed code: amplify-codegen-temp/models/models

## [1.1.0] - 2023-11-04
### Added
- no additions.

### Changed
- Updated Lambda functions to Node18 and aws-sdk v3
- Security upgrade: Blocked ListEvents API call for end users to remove potential data leak
- Security upgrade: Blocked API calls requesting an AWS account for a non-existent event id
- Security upgrade: Reduced number of possible parameters for end user API calls to necessary minimum, retrieve event management metadata from backend and auth token only.

### Removed
- nothing removed.


- no changes.
	zip -FSr build-cfn/sandbox-accounts-for-events.zip . -x amplify/#current-cloud-backend/\* build/\* build-cfn/\* \*dist/\* \*.DS_Store\* \*.vscode/\* \*.git/\* src/aws-exports.json\* \*amplify-meta.json\* amplify/team-provider-info.json\* \*awscloudformation\* \*node_modules\* amplify/.config/local-\*
	cd install/cfn-lambda/dceHandleTerraFormDeployment && zip -FSr ../../../build-cfn/sandbox-accounts-for-events-lambda-terraform.zip . && cd -
	cd install/cfn-lambda/dceHandleAmplifyDeployment && zip -FSr ../../../build-cfn/sandbox-accounts-for-events-lambda-amplify.zip . && cd -
        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeLoginApi_dceApiGw\",\"Value\":\"${DCE_API_GW}\",\"Overwrite\":true}"
        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeOperatorApi_dceApiGw\",\"Value\":\"${DCE_API_GW}\",\"Overwrite\":true}"
        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeAdminApi_dceApiGw\",\"Value\":\"${DCE_API_GW}\",\"Overwrite\":true}"
        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeOperatorApi_dceLeasesTable\",\"Value\":\"${DCE_LEASES_TABLE}\",\"Overwrite\":true}"
        - aws ssm put-parameter --cli-input-json "{\"Type\":\"String\",\"Name\":\"/amplify/${AWS_APP_ID}/main/AMPLIFY_function_safeAdminApi_dceAccountsTable\",\"Value\":\"${DCE_ACCOUNTS_TABLE}\",\"Overwrite\":true}"
  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"api-API Gateway\",\"metadata\":{}}",
{
  "version": 1,
  "serviceConfiguration": {
    "serviceName": "AppSync",
    "defaultAuthType": {
      "mode": "AMAZON_COGNITO_USER_POOLS",
      "cognitoUserPoolId": "authsafeafe5208f"
    },
    "conflictResolution": {},
    "apiName": "safegraphql"
  }
}
        ZipFile: | 
          const response = require('cfn-response');
          const { CognitoIdentityProviderClient, DescribeUserPoolClientCommand } = require("@aws-sdk/client-cognito-identity-provider");
          const identity = new CognitoIdentityProviderClient();
          exports.handler = (event, context, callback) => {
            console.log("Lambda invoked with the following parameters: ", event, context);
            if (event.RequestType == "Delete") { 
              response.send(event, context, response.SUCCESS, {})
            }
            if (event.RequestType == "Update" || event.RequestType == "Create") {
              const params = {
                ClientId: event.ResourceProperties.clientId,
                UserPoolId: event.ResourceProperties.userpoolId
              };
              identity.send(new DescribeUserPoolClientCommand(params))
                .then((res) => {
                  response.send(event, context, response.SUCCESS, {"appSecret": res.UserPoolClient.ClientSecret});
                })
                .catch((err) => {
                  response.send(event, context, response.FAILED, {err});
              });
            }
          };
      Runtime: nodejs18.x
  {"createdOn":"Mac","createdBy":"Amplify","createdWith":"12.7.1","stackType":"auth-Cognito","metadata":{}}
                "const { CognitoIdentityClient, SetIdentityPoolRolesCommand } = require('@aws-sdk/client-cognito-identity');",
                "console.log('Lambda invoked with the following parameters: ', event, context);",
                "       const cognitoidentity = new CognitoIdentityClient();",
                "    cognitoidentity.send(new SetIdentityPoolRolesCommand(params));",
        "Runtime": "nodejs18.x",
  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"auth-Cognito-UserPool-Groups\",\"metadata\":{}}"
          ],
          "category": "auth",
          "resourceName": "safeafe5208f"
          ],
          "category": "function",
          "resourceName": "AdminQueries4757b5b2"
      ],
      "providerPlugin": "awscloudformation",
      "service": "API Gateway"
          "additionalAuthenticationProviders": [],
          }
      },
      "providerPlugin": "awscloudformation",
      "service": "AppSync"
  "auth": {
    "safeafe5208f": {
      "customAuth": false,
      "dependsOn": [],
      "service": "Cognito"
    },
    "userPoolGroups": {
            "UserPoolId",
            "AppClientIDWeb",
            "AppClientID",
            "IdentityPoolId"
          ],
          "category": "auth",
          "resourceName": "safeafe5208f"
      ],
      "service": "Cognito-UserPool-Groups"
          ],
          "category": "auth",
          "resourceName": "safeafe5208f"
          ],
          "category": "auth",
          "resourceName": "userPoolGroups"
        }
      ],
      "providerPlugin": "awscloudformation",
      "service": "customCloudformation"
    }
  },
  "function": {
    "AdminQueries4757b5b2": {
      "build": true,
      "dependsOn": [
        {
          "attributes": [
            "UserPoolId"
          ],
          "category": "auth",
          "resourceName": "safeafe5208f"
        }
      ],
      "providerPlugin": "awscloudformation",
      "service": "Lambda"
    },
    "safeAdminApi": {
      "build": true,
      "dependsOn": [],
      "providerPlugin": "awscloudformation",
      "service": "Lambda"
    },
    "safeLoginApi": {
      "build": true,
      "dependsOn": [],
      "providerPlugin": "awscloudformation",
      "service": "Lambda"
    },
    "safeOperatorApi": {
      "build": true,
      "dependsOn": [],
      "providerPlugin": "awscloudformation",
      "service": "Lambda"
    }
  },
  "parameters": {
    "AMPLIFY_function_AdminQueries4757b5b2_deploymentBucketName": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "AdminQueries4757b5b2"
        }
      ]
    },
    "AMPLIFY_function_AdminQueries4757b5b2_s3Key": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "AdminQueries4757b5b2"
        }
      ]
    },
    "AMPLIFY_function_safeAdminApi_dceAccountsTable": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeAdminApi"
        }
      ]
    },
    "AMPLIFY_function_safeAdminApi_dceApiGw": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeAdminApi"
        }
      ]
    },
    "AMPLIFY_function_safeAdminApi_deploymentBucketName": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeAdminApi"
        }
      ]
    },
    "AMPLIFY_function_safeAdminApi_s3Key": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeAdminApi"
        }
      ]
    },
    "AMPLIFY_function_safeLoginApi_dceApiGw": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeLoginApi"
        }
      ]
    },
    "AMPLIFY_function_safeLoginApi_deploymentBucketName": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeLoginApi"
        }
      ]
    },
    "AMPLIFY_function_safeLoginApi_s3Key": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeLoginApi"
        }
      ]
    },
    "AMPLIFY_function_safeOperatorApi_dceApiGw": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeOperatorApi"
        }
      ]
    },
    "AMPLIFY_function_safeOperatorApi_dceLeasesTable": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeOperatorApi"
        }
      ]
    },
    "AMPLIFY_function_safeOperatorApi_deploymentBucketName": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeOperatorApi"
        }
      ]
    },
    "AMPLIFY_function_safeOperatorApi_s3Key": {
      "usedBy": [
        {
          "category": "function",
          "resourceName": "safeOperatorApi"
  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"custom-customCloudformation\",\"metadata\":{}}"
  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
        "Runtime": "nodejs18.x",
    },
    "LambdaExecutionRoleArn": {
      "Value": {
        "Fn::GetAtt": [
          "LambdaExecutionRole",
          "Arn"
        ]
      }
  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
        "Runtime": "nodejs18.x",
    },
    "LambdaExecutionRoleArn": {
      "Value": {
        "Fn::GetAtt": [
          "LambdaExecutionRole",
          "Arn"
        ]
      }
const { IAMClient, ListAttachedRolePoliciesCommand, ListAccountAliasesCommand } = require('@aws-sdk/client-iam');
const { STSClient, AssumeRoleCommand } = require('@aws-sdk/client-sts');
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, UpdateCommand } = require('@aws-sdk/lib-dynamodb');

const stsClient = new STSClient({ region });
const ddbClient = new DynamoDBClient({ region });
const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);

    return stsClient
        .send(new AssumeRoleCommand({
        }))
            const iamClient = new IAMClient({
            return iamClient
                .send(new ListAttachedRolePoliciesCommand({ RoleName: roleName }))
                    return iamClient
                        .send(new ListAccountAliasesCommand({}))
    return ddbDocClient
        .send(new UpdateCommand({
        }))
exports.handler = async (event, context) => {
    console.log("Lambda invoked with the following parameters: ", event, context)
    let args = event.arguments

  },
  {
    "Effect": "Allow",
    "Action": [
        "dynamodb:UpdateItem",
        "dynamodb:GetItem",
        "dynamodb:ListTables"
    ],
    "Resource": ["arn:aws:dynamodb:*:*:table/*"]
  },
  {
    "Effect": "Allow",
    "Action": [
        "cognito-idp:ListUserPools",
        "cognito-idp:AdminGetUser"
    ],
    "Resource": ["*"]
  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
        "Runtime": "nodejs18.x",
    },
    "LambdaExecutionRoleArn": {
      "Value": {
        "Fn::GetAtt": [
          "LambdaExecutionRole",
          "Arn"
        ]
      }
const moment = require("moment");
const { DynamoDBClient, ListTablesCommand } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');
const { CognitoIdentityProviderClient, ListUserPoolsCommand, AdminGetUserCommand } = require('@aws-sdk/client-cognito-identity-provider');


const ddbClient = new DynamoDBClient({ region });
const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);
let eventsTable

const cognitoClient = new CognitoIdentityProviderClient({ region })
let userPoolId
const getEndUserEvent = ({ id }) => {
    if (!eventsTable)
        return respondWithError("Failed to get data for event ID " + id + ".")

    return ddbDocClient.send(new GetCommand({ TableName: eventsTable, Key: { id } }))
    .then((response) => {
        if (response.Item) {
            return respondWithSuccess("Successfully got DynamoDB event item",{
                id: response.Item.id,
                eventStatus: response.Item.eventStatus,
                eventName: response.Item.eventName
            })
        } else { 
            return respondWithError('Could not find matching event for event ID "' + id + '". Please double check if your event ID is correct or ask your event operator for help.')
        }
    })
    .catch((error) =>
        respondWithError("Failed to get data for event ID " + id + ".", error)
    );
};


const getAwsLoginUrlForEvent = async ({eventId}, event) => {
    

    if (!userPoolId)
        return respondWithError("Failed to get user authentication pool, cannot claim an AWS account.")

    if (!eventsTable)
        return respondWithError("Failed to get data for event ID " + eventId + ".")

    return cognitoClient.send(new AdminGetUserCommand({
            UserPoolId: userPoolId,
            Username: event.identity.username
        }))
        .then(cognitoResponse => {
            const user = cognitoResponse.UserAttributes.find(attr => attr.Name == "email").Value
            let principalId = eventId + "__" + user.replace(/[^a-zA-Z0-9]/g, "+")
            return ddbDocClient.send(new GetCommand({ TableName: eventsTable, Key: { id: eventId } }))
                .then(response => {
                    if (response.Item) {
                        return invokeApi("leases", "GET")
                            .then((leases) => {
                                if (leases.message) return respondWithError("Failed to list leases.", leases.message);
                    
                                let foundLease = leases.find((item) => item.principalId === principalId);
                                if (foundLease) {
                                    switch (foundLease.leaseStatus) {
                                        case "Active":
                                            return getLeaseLoginUrl(foundLease, user);
                                        case "Expired":
                                            return respondWithError(
                                                "Your AWS account has expired its lifetime and has been terminated now."
                                            );
                                        case "OverBudget":
                                            return respondWithError(
                                                "Your AWS account has reached its maximum budget and has been terminated now."
                                            );
                                        case "Inactive":
                                            return respondWithError("Your AWS account has been manually terminated.");
                                        default:
                                            return respondWithError("Your account is not available for logging in.");
                                    }
                                } else {
                                    if (leases.filter((lease) => lease.principalId.startsWith(eventId)).length >= response.Item.maxAccounts)
                                        return respondWithError("No more free AWS accounts available for this event.");
                                    return invokeApi("leases", "POST", {
                                        budgetAmount: response.Item.budgetAmount,
                                        expiresOn: moment
                                            .unix(response.Item.eventOn)
                                            .add(response.Item.eventDays, "days")
                                            .add(response.Item.eventHours, "hours")
                                            .unix(),
                                        principalId,
                                        budgetNotificationEmails: [user],
                                        budgetCurrency: "USD"
                                    })
                                        .then((newLease) => {
                                            if (newLease.error) {
                                                switch (newLease.error.code) {
                                                    case "ServerError":
                                                        if (newLease.error.message === "No Available accounts at this moment")
                                                            return respondWithError("No more free AWS accounts available.", newLease.error);
                                                        else
                                                            return respondWithError(
                                                                "Failed to create lease for " + user + ".",
                                                                newLease.error
                                                            );
                                                    case "AlreadyExistsError":
                                                        return respondWithError(
                                                            "Found already existing lease for " + user + ".",
                                                            newLease.error
                                                        );
                                                    default:
                                                        return respondWithError(
                                                            "Failed to create lease for " + user + ".",
                                                            newLease.error
                                                        );
                                                }
                                            }
                                            return getLeaseLoginUrl(newLease, user);
                                        })
                                        .catch((error) => respondWithError("Failed to create lease for " + user + ".", error));
                                }
                            })
                            .catch((error) => respondWithError("Failed to list leases.", error));
                
                    } else {
                            "Your EventId " + eventId + " is invalid. Please contact your event operator for help."
                    }
                .catch((error) => respondWithError("Failed to get data for event ID " + eventId + ".", error));
        .catch(error => respondWithError("Failed to get user authentication data, cannot claim an AWS account.", error))
exports.handler = async (event, context) => {
    console.log("Lambda invoked with the following parameters: ", event, context)
    let args = event.arguments

    
    if (!eventsTable) {
        const tables = await ddbClient.send(new ListTablesCommand())
        eventsTable = tables.TableNames.find(name => name.startsWith("Event-"))
    }

    if (!userPoolId) {
        const userPools = await cognitoClient.send(new ListUserPoolsCommand({ MaxResults: 10 }))
        userPoolId = userPools.UserPools.find(userPool => userPool.Name.startsWith("safeafe5208f")).Id
    }
            case "getEndUserEvent":
                return getEndUserEvent(params);
                return getAwsLoginUrlForEvent(params, event);
    },
    "moment": {
      "version": "2.29.4",
      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
    "aws4": "^1.11.0",
    "moment": "^2.29.4"
  "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.7.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
        "Runtime": "nodejs18.x",
    },
    "LambdaExecutionRoleArn": {
      "Value": {
        "Fn::GetAtt": [
          "LambdaExecutionRole",
          "Arn"
        ]
      }
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, UpdateCommand, DeleteCommand } = require('@aws-sdk/lib-dynamodb');

const ddbClient = new DynamoDBClient({ region });
const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);

    if (!user) 
        return respondWithError("Internal error while trying to update lease.", "Parameter 'user' missing.");

    return ddbDocClient
        .send(new UpdateCommand({
        }))
    return ddbDocClient
        .send(new DeleteCommand({
        }))
exports.handler = async (event, context) => {
    console.log("Lambda invoked with the following parameters: ", event, context)
    let args = event.arguments

  "api": {
    "AdminQueries": {
      "ApiId": "string",
      "ApiName": "string",
      "RootUrl": "string"
    "safegraphql": {
      "GraphQLAPIEndpointOutput": "string",
      "GraphQLAPIIdOutput": "string"
    }
  },
  "auth": {
    "safeafe5208f": {
      "AppClientID": "string",
      "AppClientIDWeb": "string",
      "IdentityPoolId": "string",
      "IdentityPoolName": "string",
      "UserPoolArn": "string",
      "UserPoolId": "string",
      "UserPoolName": "string"
    },
    "userPoolGroups": {
      "adminGroupRole": "string",
      "operatorGroupRole": "string"
    }
  },
  "custom": {
    "cfnAdminUser": {
      "AdminUserEmailAddress": "string"
    }
  },
  "function": {
    "AdminQueries4757b5b2": {
      "Arn": "string",
      "LambdaExecutionRole": "string",
      "LambdaExecutionRoleArn": "string",
      "Name": "string",
      "Region": "string"
    },
    "safeAdminApi": {
      "Arn": "string",
      "LambdaExecutionRole": "string",
      "LambdaExecutionRoleArn": "string",
      "Name": "string",
      "Region": "string"
    "safeLoginApi": {
      "Arn": "string",
      "LambdaExecutionRole": "string",
      "LambdaExecutionRoleArn": "string",
      "Name": "string",
      "Region": "string"
    "safeOperatorApi": {
      "Arn": "string",
      "LambdaExecutionRole": "string",
      "LambdaExecutionRoleArn": "string",
      "Name": "string",
      "Region": "string"
  }
      "useexperimentalpipelinedtransformer": true,
      "showfieldauthnotification": false,
      "transformerversion": 2,
      "suppressschemamigrationprompt": true,
      "securityEnhancementNotification": false
        if (authStatus === "authenticated" && user) {
      __typename
      __typename
      __typename
      __typename
      __typename
      __typename
  query SafeLoginApi($action: String, $paramJson: String) {
  query SafeAdminApi($action: String, $paramJson: String) {
  query SafeOperatorApi($action: String, $paramJson: String) {
      __typename
        __typename
      __typename
      __typename
        __typename
      __typename
          "args" : [ {
            "name" : "filter",
            "description" : null,
            "type" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionEventFilterInput",
              "ofType" : null
            },
            "defaultValue" : null
          } ],
          "type" : {
            "kind" : "OBJECT",
            "name" : "Event",
            "ofType" : null
          },
          "isDeprecated" : false,
          "deprecationReason" : null
        }, {
          "name" : "onUpdateEvent",
          "description" : null,
          "args" : [ {
            "name" : "filter",
            "description" : null,
            "type" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionEventFilterInput",
              "ofType" : null
            },
            "defaultValue" : null
          } ],
          "type" : {
            "kind" : "OBJECT",
            "name" : "Event",
            "ofType" : null
          },
          "isDeprecated" : false,
          "deprecationReason" : null
        }, {
          "name" : "onDeleteEvent",
          "description" : null,
          "args" : [ {
            "name" : "filter",
            "description" : null,
            "type" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionEventFilterInput",
              "ofType" : null
            },
            "defaultValue" : null
          } ],
          "type" : {
            "kind" : "OBJECT",
            "name" : "Event",
            "ofType" : null
          },
          "isDeprecated" : false,
          "deprecationReason" : null
        }, {
          "name" : "onCreateConfig",
          "description" : null,
          "args" : [ {
            "name" : "filter",
            "description" : null,
            "type" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionConfigFilterInput",
              "ofType" : null
            },
            "defaultValue" : null
          } ],
          "type" : {
            "kind" : "OBJECT",
            "name" : "Config",
            "ofType" : null
          },
          "isDeprecated" : false,
          "deprecationReason" : null
        }, {
          "name" : "onUpdateConfig",
          "description" : null,
          "args" : [ {
            "name" : "filter",
            "description" : null,
            "type" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionConfigFilterInput",
              "ofType" : null
            },
            "defaultValue" : null
          } ],
          "type" : {
            "kind" : "OBJECT",
            "name" : "Config",
            "ofType" : null
          },
          "isDeprecated" : false,
          "deprecationReason" : null
        }, {
          "name" : "onDeleteConfig",
          "description" : null,
          "args" : [ {
            "name" : "filter",
            "description" : null,
            "type" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionConfigFilterInput",
              "ofType" : null
            },
            "defaultValue" : null
          } ],
          "type" : {
            "kind" : "OBJECT",
            "name" : "Config",
            "ofType" : null
          },
          "isDeprecated" : false,
          "deprecationReason" : null
        } ],
        "inputFields" : null,
        "interfaces" : [ ],
        "enumValues" : null,
        "possibleTypes" : null
      }, {
        "kind" : "INPUT_OBJECT",
        "name" : "ModelSubscriptionEventFilterInput",
        "description" : null,
        "fields" : null,
        "inputFields" : [ {
          "name" : "id",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionIDInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eventName",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionStringInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eventOn",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionIntInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eventDays",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionIntInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eventHours",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionIntInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eventOwner",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionStringInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "maxAccounts",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionIntInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eventBudget",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionIntInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eventStatus",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionStringInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "and",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionEventFilterInput",
              "ofType" : null
            }
          },
          "defaultValue" : null
        }, {
          "name" : "or",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionEventFilterInput",
              "ofType" : null
            }
          },
          "defaultValue" : null
        } ],
        "interfaces" : null,
        "enumValues" : null,
        "possibleTypes" : null
      }, {
        "kind" : "INPUT_OBJECT",
        "name" : "ModelSubscriptionIDInput",
        "description" : null,
        "fields" : null,
        "inputFields" : [ {
          "name" : "ne",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eq",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "le",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "lt",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "ge",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "gt",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "contains",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "notContains",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "between",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "ID",
              "ofType" : null
            }
          },
          "defaultValue" : null
        }, {
          "name" : "beginsWith",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "ID",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "in",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "ID",
              "ofType" : null
            }
          },
          "defaultValue" : null
        }, {
          "name" : "notIn",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "ID",
              "ofType" : null
            }
          },
          "defaultValue" : null
        } ],
        "interfaces" : null,
        "enumValues" : null,
        "possibleTypes" : null
      }, {
        "kind" : "INPUT_OBJECT",
        "name" : "ModelSubscriptionStringInput",
        "description" : null,
        "fields" : null,
        "inputFields" : [ {
          "name" : "ne",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eq",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "le",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "lt",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "ge",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "gt",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "contains",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "notContains",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "between",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "String",
              "ofType" : null
            }
          },
          "defaultValue" : null
        }, {
          "name" : "beginsWith",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "in",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "String",
              "ofType" : null
            }
          },
          "defaultValue" : null
        }, {
          "name" : "notIn",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "String",
              "ofType" : null
            }
          },
          "defaultValue" : null
        } ],
        "interfaces" : null,
        "enumValues" : null,
        "possibleTypes" : null
      }, {
        "kind" : "INPUT_OBJECT",
        "name" : "ModelSubscriptionIntInput",
        "description" : null,
        "fields" : null,
        "inputFields" : [ {
          "name" : "ne",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Int",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "eq",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Int",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "le",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Int",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "lt",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Int",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "ge",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Int",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "gt",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Int",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "between",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "Int",
              "ofType" : null
            }
          },
          "defaultValue" : null
        }, {
          "name" : "in",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "Int",
              "ofType" : null
            }
          },
          "defaultValue" : null
        }, {
          "name" : "notIn",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "Int",
              "ofType" : null
            }
          },
          "defaultValue" : null
        } ],
        "interfaces" : null,
        "enumValues" : null,
        "possibleTypes" : null
      }, {
        "kind" : "INPUT_OBJECT",
        "name" : "ModelSubscriptionConfigFilterInput",
        "description" : null,
        "fields" : null,
        "inputFields" : [ {
          "name" : "id",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionIDInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "config",
          "description" : null,
          "type" : {
            "kind" : "INPUT_OBJECT",
            "name" : "ModelSubscriptionStringInput",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "and",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionConfigFilterInput",
              "ofType" : null
            }
          },
          "defaultValue" : null
        }, {
          "name" : "or",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "INPUT_OBJECT",
              "name" : "ModelSubscriptionConfigFilterInput",
              "ofType" : null
            }
          },
          "defaultValue" : null
        } ],
        "interfaces" : null,
        "enumValues" : null,
        "possibleTypes" : null
      }, {
        "kind" : "INPUT_OBJECT",
        "name" : "ModelSubscriptionBooleanInput",
        "description" : null,
        "fields" : null,
        "inputFields" : [ {
          "name" : "ne",
          "description" : null,
            "kind" : "SCALAR",
            "name" : "Boolean",
          "defaultValue" : null
          "name" : "eq",
            "kind" : "SCALAR",
            "name" : "Boolean",
          "defaultValue" : null
        } ],
        "interfaces" : null,
        "enumValues" : null,
        "possibleTypes" : null
      }, {
        "kind" : "INPUT_OBJECT",
        "name" : "ModelBooleanInput",
        "description" : null,
        "fields" : null,
        "inputFields" : [ {
          "name" : "ne",
            "kind" : "SCALAR",
            "name" : "Boolean",
          "defaultValue" : null
          "name" : "eq",
            "kind" : "SCALAR",
            "name" : "Boolean",
          "defaultValue" : null
          "name" : "attributeExists",
            "kind" : "SCALAR",
            "name" : "Boolean",
          "defaultValue" : null
          "name" : "attributeType",
            "kind" : "ENUM",
            "name" : "ModelAttributeTypes",
          "defaultValue" : null
        "interfaces" : null,
        "name" : "ModelSubscriptionFloatInput",
          "name" : "in",
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "Float",
              "ofType" : null
            }
          "name" : "notIn",
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "Float",
              "ofType" : null
            }
        "name" : "ModelFloatInput",
            "name" : "Float",
            "name" : "Float",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "le",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Float",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "lt",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Float",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "ge",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Float",
            "ofType" : null
          },
          "defaultValue" : null
        }, {
          "name" : "gt",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "Float",
        }, {
          "name" : "between",
          "description" : null,
          "type" : {
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "Float",
              "ofType" : null
            }
          },
          "defaultValue" : null
        "name" : "aws_cognito_user_pools",
        "description" : "Tells the service this field/object has access authorized by a Cognito User Pools token.",
        "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
          "name" : "cognito_groups",
          "description" : "List of cognito user pool groups which have access on this field",
            "kind" : "LIST",
            "name" : null,
            "ofType" : {
              "kind" : "SCALAR",
              "name" : "String",
              "ofType" : null
            }
          "defaultValue" : null
        "name" : "aws_publish",
        "description" : "Tells the service which subscriptions will be published to when this mutation is called. This directive is deprecated use @aws_susbscribe directive instead.",
        "locations" : [ "FIELD_DEFINITION" ],
          "name" : "subscriptions",
          "description" : "List of subscriptions which will be published to when this mutation is called.",
      }, {
        "name" : "aws_api_key",
        "description" : "Tells the service this field/object has access authorized by an API key.",
        "locations" : [ "OBJECT", "FIELD_DEFINITION" ],
        "args" : [ ],
        "onOperation" : false,
        "onFragment" : false,
        "onField" : false
        "name" : "aws_subscribe",
        "description" : "Tells the service which mutation triggers this subscription.",
          "name" : "mutations",
          "description" : "List of mutations which will trigger this subscription when they are called.",
        "name" : "aws_lambda",
        "description" : "Tells the service this field/object has access authorized by a Lambda Authorizer.",
        "name" : "aws_iam",
        "description" : "Tells the service this field/object has access authorized by sigv4 signing.",
      }, {
        "name" : "deprecated",
        "description" : null,
        "locations" : [ "FIELD_DEFINITION", "ENUM_VALUE" ],
        "args" : [ {
          "name" : "reason",
          "description" : null,
          "type" : {
            "kind" : "SCALAR",
            "name" : "String",
            "ofType" : null
          },
          "defaultValue" : "\"No longer supported\""
        } ],
        "onOperation" : false,
        "onFragment" : false,
        "onField" : false
  subscription OnCreateEvent($filter: ModelSubscriptionEventFilterInput) {
    onCreateEvent(filter: $filter) {
      __typename
  subscription OnUpdateEvent($filter: ModelSubscriptionEventFilterInput) {
    onUpdateEvent(filter: $filter) {
      __typename
  subscription OnDeleteEvent($filter: ModelSubscriptionEventFilterInput) {
    onDeleteEvent(filter: $filter) {
      __typename
  subscription OnCreateConfig($filter: ModelSubscriptionConfigFilterInput) {
    onCreateConfig(filter: $filter) {
      __typename
  subscription OnUpdateConfig($filter: ModelSubscriptionConfigFilterInput) {
    onUpdateConfig(filter: $filter) {
      __typename
  subscription OnDeleteConfig($filter: ModelSubscriptionConfigFilterInput) {
    onDeleteConfig(filter: $filter) {
      __typename
                            {Item.eventBudget + " USD"}
                            {Item.eventSpend + " USD"}
                        cell: (item) => item.eventSpend + " USD"
                    {item.eventBudget + " USD"}
                    {item.eventSpend + " USD"}
                    cell: (item) => item.costAmount.toFixed(2) + " USD"
                        <Container header={<Header>AWS service spend in USD</Header>}>
import { fetchAwsLoginUrl, getEndUserEvent } from "../../redux/actions/aws_login";
        dispatch({ type: "aws_login/event_dismiss" });
        dispatch(getEndUserEvent(value));
        if (!valueChangedOnce && isEmpty(AwsLogin.event) && urlParamEventId && User.isLoggedIn) {
            dispatch(getEndUserEvent(urlParamEventId));
    }, [AwsLogin.event, urlParamEventId, dispatch, valueChangedOnce, User]);
        if (!AwsLogin.event.eventStatus) return;
        switch (AwsLogin.event.eventStatus) {
    }, [AwsLogin.event, dispatch]);
        dispatch({ type: "aws_login/event_dismiss" });
                {!AwsLogin.event.id ? (
                        <Button variant="primary" onClick={submit} loading={AwsLogin.event_status === "loading"}>
                            Welcome to "{AwsLogin.event.eventName}"
                            onClick={() => dispatch(fetchAwsLoginUrl({ eventId: AwsLogin.event.id })) }
                        label={"Budget in USD"}
            let user = newValue.user.replace(/[^a-zA-Z0-9]/g, "+");
                newValue.principalId = newValue.eventId + "__" + user;
                        label={"Budget in USD"}
                            label={"Budget in USD"}
                            label={"Budget in USD"}
    event_status: "idle",
    url: "",
    event: {}
            return {
                ...state,
                url: "",
                status: "hidden"
            };
                
        case "aws_login/event_loading":
            return {
                ...state,
                event_status: "loading"
            };
        case "aws_login/event_loaded":
            return {
                ...state,
                event_status: "idle",
                event: action.payload
            };
        case "aws_login/event_loadError":
        case "aws_login/event_dismiss":
export const getEndUserEvent = (id) => async (dispatch) => {
    dispatch({ type: "aws_login/event_loading" });
    try {
        const response = await API.graphql(
            graphqlOperation(queries.safeLoginApi, {
                action: "getEndUserEvent",
                paramJson: JSON.stringify({id})
            })
        );
        const payload = JSON.parse(response.data.safeLoginApi);
        if (!payload || payload.status === "error") {
            throw payload;
        }
        dispatch({ type: "aws_login/event_loaded", payload: payload.body });
        dispatch({ type: "notification/dismiss" });
    } catch (error) {
        console.error(error);
        dispatch({ type: "aws_login/event_loadError" });
        dispatch({ type: "notification/error", message: error.message });
    }
};


    if (!data.attributes)
        return
        case "event/loading":
            new RegExp("^(" + action.config.EVENT_ID_REGEX + ")__\\S{5,}$")
                        budgetCurrency: "USD"
        eventId: item.principalId.includes("__") ? item.principalId.substring(0, action.config.EVENT_ID_LENGTH) : ""