CVE-2023-51386: Improper Privilege Management (CWE-269)
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 read data from the events table by sending request payloads to the events API, collecting information on planned events, timeframes, budgets and owner email addresses. This data access may allow users to get insights into upcoming events and join events which they have not been invited to. This issue has been patched in version 1.10.0.
CWE description: The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.
CVSS v3 base score: 3.3
Published: 2023-12-22T22: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) : ""