CVE-2023-50928 (published 2023-12-22T21:15Z)

"Sandbox Accounts for Events" provides multiple, temporary AWS accounts to a number of authenticated users simultaneously via a browser-based GUI. Authenticated users could potentially claim and access empty AWS accounts by sending request payloads to the account API containing non-existent event ids and self-defined budget & duration. This issue only affects cleaned AWS accounts, it is not possible to access AWS accounts in use or existing data/infrastructure. This issue has been patched in version 1.1.0.

CVSS v3 base score: 9.0, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-284
cwe_name: Improper Access Control
description: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
extended_description: 
            Access control involves the use of several protection mechanisms such as:
            
               Authentication (proving the identity of an actor)
               Authorization (ensuring that a given actor can access a resource), and
               Accountability (tracking of activities that were performed)
            
            When any mechanism is not applied or otherwise fails, attackers can compromise the security of the product by gaining privileges, reading sensitive information, executing commands, evading detection, etc.
            There are two distinct behaviors that can introduce access control weaknesses:
               
                  Specification: incorrect privileges, permissions, ownership, etc. are explicitly specified for either the user or the resource (for example, setting a password file to be world-writable, or giving administrator capabilities to a guest user). This action could be performed by the program or the administrator.
                  Enforcement: the mechanism contains errors that prevent it from properly enforcing the specified access control requirements (e.g., allowing the user to specify their own privileges, or allowing a syntactically-incorrect ACL to produce insecure settings). This problem occurs within the program itself, in that it does not actually enforce the intended security policy that the administrator specifies.
               
         
		 

Fix information:
repo_url: https://github.com/awslabs/sandbox-accounts-for-events
commit_details: repo_url: https://github.com/awslabs/sandbox-accounts-for-events
author: Rainer Moeller
author_date: 2023-11-04 22:01:52+01:00
msg: Reduced end user API permissions
repository: repo_url: https://github.com/awslabs/sandbox-accounts-for-events
repo_name: awslabs/sandbox-accounts-for-events
description: "Sandbox Accounts for Events" allows to provide multiple, temporary AWS accounts to a number of authenticated users simultaneously via a browser-based GUI.
repo_language: JavaScript
owner: awslabs
file_changes: filename: .eslintignore
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+amplify-codegen-temp/models/models
\ No newline at end of file

programming_language: Batchfile
filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Markdown
filename: Makefile
change_type: ModificationType.MODIFY
diff: @@ -42,9 +42,9 @@ build:
 	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)

programming_language: Shell
filename: amplify.yml
change_type: ModificationType.MODIFY
diff: @@ -19,6 +19,11 @@ backend:
         - 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

programming_language: Shell
filename: admin-queries-cloudformation-template.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
   "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"

programming_language: Swift
filename: cli-inputs.json
change_type: ModificationType.ADD
diff: @@ -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

programming_language: JavaScript
filename: schema.graphql
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,4 @@
 type Event @model @auth(rules: [
-  { allow: private, operations: [read] },
   { allow: groups, groups: ["admin","operator"] }
 ]) {
   id: ID!

programming_language: Scala
filename: safeafe5208f-cloudformation-template.yml
change_type: ModificationType.MODIFY
diff: @@ -216,32 +216,31 @@ Resources:
     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:
@@ -385,4 +384,4 @@ Outputs:
         - 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":{}}

programming_language: CoffeeScript
filename: template.json
change_type: ModificationType.MODIFY
diff: @@ -257,15 +257,16 @@
               "\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: {",
@@ -286,7 +287,7 @@
                 "               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'});",
@@ -298,7 +299,7 @@
           }
         },
         "Handler": "index.handler",
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Timeout": 300,
         "Role": {
           "Fn::GetAtt": [
@@ -365,5 +366,5 @@
       }
     }
   },
-  "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

programming_language: JavaScript
filename: backend-config.json
change_type: ModificationType.MODIFY
diff: @@ -1,109 +1,71 @@
 {
-  "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",
@@ -112,15 +74,159 @@
             "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"
         }
       ]
     }

programming_language: JavaScript
filename: cfnAdminUser-cloudformation-template.json
change_type: ModificationType.MODIFY
diff: @@ -107,5 +107,5 @@
       }
     }
   },
-  "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

programming_language: PowerShell
filename: AdminQueries4757b5b2-cloudformation-template.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
   "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"
@@ -70,7 +70,7 @@
             "Arn"
           ]
         },
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Timeout": 25,
         "Code": {
           "S3Bucket": {
@@ -225,6 +225,14 @@
       "Value": {
         "Ref": "LambdaExecutionRole"
       }
+    },
+    "LambdaExecutionRoleArn": {
+      "Value": {
+        "Fn::GetAtt": [
+          "LambdaExecutionRole",
+          "Arn"
+        ]
+      }
     }
   }
 }
\ No newline at end of file

programming_language: Swift
filename: safeAdminApi-cloudformation-template.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
   "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",
@@ -90,7 +90,7 @@
             "Arn"
           ]
         },
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Layers": [],
         "Timeout": 25
       }
@@ -246,6 +246,14 @@
       "Value": {
         "Ref": "LambdaExecutionRole"
       }
+    },
+    "LambdaExecutionRoleArn": {
+      "Value": {
+        "Fn::GetAtt": [
+          "LambdaExecutionRole",
+          "Arn"
+        ]
+      }
     }
   }
 }
\ No newline at end of file

programming_language: Swift
filename: index.js
change_type: ModificationType.MODIFY
diff: @@ -1,11 +1,19 @@
 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({
@@ -84,25 +92,22 @@ const registerAccount = ({ id, roleName }) => {
 
     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(
@@ -113,9 +118,8 @@ const registerAccount = ({ id, roleName }) => {
                                 ".",
                             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 + ".");
@@ -161,11 +165,8 @@ const updateAccount = ({ id, accountStatus, adminRoleArn }) => {
         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
@@ -176,8 +177,7 @@ const updateAccount = ({ id, accountStatus, adminRoleArn }) => {
                 ":r": adminRoleArn
             },
             ReturnValues: "UPDATED_NEW"
-        })
-        .promise()
+        }))
         .then((response) =>
             respondWithSuccess("DynamoDB table record for account " + id + " successfully updated.", response)
         )
@@ -197,7 +197,10 @@ const removeAccount = ({ id }) => {
         .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)

programming_language: JavaScript
filename: custom-policies.json
change_type: ModificationType.MODIFY
diff: @@ -6,5 +6,22 @@
         "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

programming_language: CoffeeScript
filename: safeLoginApi-cloudformation-template.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
   "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",
@@ -84,7 +84,7 @@
             "Arn"
           ]
         },
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Layers": [],
         "Timeout": 25
       }
@@ -222,6 +222,14 @@
       "Value": {
         "Ref": "LambdaExecutionRole"
       }
+    },
+    "LambdaExecutionRoleArn": {
+      "Value": {
+        "Fn::GetAtt": [
+          "LambdaExecutionRole",
+          "Arn"
+        ]
+      }
     }
   }
 }
\ No newline at end of file

programming_language: Swift
filename: index.js
change_type: ModificationType.MODIFY
diff: @@ -1,8 +1,20 @@
+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);
@@ -62,6 +74,28 @@ const invokeApi = (path, method, body = null) => {
     });
 };
 
+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) => {
@@ -84,88 +118,101 @@ const getLeaseLoginUrl = ({ id }, user) => {
         );
 };
 
-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 }) => {
@@ -183,7 +230,10 @@ const getAwsLoginUrlForLease = ({ id }) => {
         .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)
@@ -197,11 +247,23 @@ exports.handler = async ({ arguments: args }) => {
             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:

programming_language: TypeScript
filename: package-lock.json
change_type: ModificationType.MODIFY
diff: @@ -8,6 +8,11 @@
       "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=="
     }
   }
 }

programming_language: TypeScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -5,6 +5,7 @@
   "main": "index.js",
   "license": "Apache-2.0",
   "dependencies": {
-    "aws4": "^1.11.0"
+    "aws4": "^1.11.0",
+    "moment": "^2.29.4"
   }
 }

programming_language: JavaScript
filename: safeOperatorApi-cloudformation-template.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
   "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",
@@ -90,7 +90,7 @@
             "Arn"
           ]
         },
-        "Runtime": "nodejs14.x",
+        "Runtime": "nodejs18.x",
         "Layers": [],
         "Timeout": 25
       }
@@ -238,6 +238,14 @@
       "Value": {
         "Ref": "LambdaExecutionRole"
       }
+    },
+    "LambdaExecutionRoleArn": {
+      "Value": {
+        "Fn::GetAtt": [
+          "LambdaExecutionRole",
+          "Arn"
+        ]
+      }
     }
   }
 }
\ No newline at end of file

programming_language: Swift
filename: index.js
change_type: ModificationType.MODIFY
diff: @@ -1,11 +1,16 @@
 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({
@@ -164,12 +169,11 @@ const updateLease = ({
             "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,
@@ -185,8 +189,7 @@ const updateLease = ({
                 ":n": budgetNotificationEmails
             },
             ReturnValues: "UPDATED_NEW"
-        })
-        .promise()
+        }))
         .then((response) =>
             respondWithSuccess("DynamoDB table record for lease for " + user + " successfully updated.", response)
         )
@@ -219,18 +222,14 @@ const deleteLease = ({ accountId, principalId, user }) => {
     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));
 };
@@ -278,7 +277,10 @@ 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.");

programming_language: JavaScript
filename: amplify-dependent-resources-ref.d.ts
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: TypeScript
filename: cli.json
change_type: ModificationType.MODIFY
diff: @@ -4,11 +4,14 @@
       "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

programming_language: JavaScript
filename: App.js
change_type: ModificationType.MODIFY
diff: @@ -39,7 +39,7 @@ const AuthContainer = ({ children }) => {
     },[config])
 
     useEffect(() => {
-        if (authStatus === "authenticated") {
+        if (authStatus === "authenticated" && user) {
             dispatch(setCurrentUser(user));
             dispatch(fetchConfig());
         }

programming_language: TypeScript
filename: mutations.js
change_type: ModificationType.MODIFY
diff: @@ -18,6 +18,7 @@ export const createEvent = /* GraphQL */ `
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
@@ -38,6 +39,7 @@ export const updateEvent = /* GraphQL */ `
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
@@ -58,6 +60,7 @@ export const deleteEvent = /* GraphQL */ `
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
@@ -71,6 +74,7 @@ export const createConfig = /* GraphQL */ `
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
@@ -84,6 +88,7 @@ export const updateConfig = /* GraphQL */ `
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
@@ -97,6 +102,7 @@ export const deleteConfig = /* GraphQL */ `
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;

programming_language: Swift
filename: queries.js
change_type: ModificationType.MODIFY
diff: @@ -2,17 +2,17 @@
 // 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)
   }
 `;
@@ -30,6 +30,7 @@ export const getEvent = /* GraphQL */ `
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
@@ -52,8 +53,10 @@ export const listEvents = /* GraphQL */ `
         eventStatus
         createdAt
         updatedAt
+        __typename
       }
       nextToken
+      __typename
     }
   }
 `;
@@ -64,6 +67,7 @@ export const getConfig = /* GraphQL */ `
       config
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
@@ -79,8 +83,10 @@ export const listConfigs = /* GraphQL */ `
         config
         createdAt
         updatedAt
+        __typename
       }
       nextToken
+      __typename
     }
   }
 `;

programming_language: TypeScript
filename: schema.json
change_type: ModificationType.MODIFY
diff: @@ -1905,72 +1905,727 @@
         "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
       }, {
@@ -1994,7 +2649,7 @@
         "possibleTypes" : null
       }, {
         "kind" : "INPUT_OBJECT",
-        "name" : "ModelFloatInput",
+        "name" : "ModelSubscriptionFloatInput",
         "description" : null,
         "fields" : null,
         "inputFields" : [ {
@@ -2065,21 +2720,29 @@
           },
           "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
         } ],
@@ -2097,7 +2760,7 @@
         "possibleTypes" : null
       }, {
         "kind" : "INPUT_OBJECT",
-        "name" : "ModelBooleanInput",
+        "name" : "ModelFloatInput",
         "description" : null,
         "fields" : null,
         "inputFields" : [ {
@@ -2105,7 +2768,7 @@
           "description" : null,
           "type" : {
             "kind" : "SCALAR",
-            "name" : "Boolean",
+            "name" : "Float",
             "ofType" : null
           },
           "defaultValue" : null
@@ -2114,10 +2777,59 @@
           "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,
@@ -2923,27 +3635,6 @@
         "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.",
@@ -2953,29 +3644,33 @@
         "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,
@@ -2990,6 +3685,14 @@
         "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",
@@ -3012,20 +3715,12 @@
         "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,
@@ -3041,21 +3736,38 @@
         "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
       } ]
     }
   }

programming_language: Ruby
filename: subscriptions.js
change_type: ModificationType.MODIFY
diff: @@ -2,8 +2,8 @@
 // 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
@@ -15,12 +15,13 @@ export const onCreateEvent = /* GraphQL */ `
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const onUpdateEvent = /* GraphQL */ `
-  subscription OnUpdateEvent {
-    onUpdateEvent {
+  subscription OnUpdateEvent($filter: ModelSubscriptionEventFilterInput) {
+    onUpdateEvent(filter: $filter) {
       id
       eventName
       eventOn
@@ -32,12 +33,13 @@ export const onUpdateEvent = /* GraphQL */ `
       eventStatus
       createdAt
       updatedAt
+      __typename
     }
   }
 `;
 export const onDeleteEvent = /* GraphQL */ `
-  subscription OnDeleteEvent {
-    onDeleteEvent {
+  subscription OnDeleteEvent($filter: ModelSubscriptionEventFilterInput) {
+    onDeleteEvent(filter: $filter) {
       id
       eventName
       eventOn
@@ -49,36 +51,40 @@ export const onDeleteEvent = /* GraphQL */ `
       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
     }
   }
 `;

programming_language: TypeScript
filename: DetailEvent.js
change_type: ModificationType.MODIFY
diff: @@ -200,7 +200,7 @@ const DetailPanel = () => {
                         </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>
@@ -242,7 +242,7 @@ const DetailPanel = () => {
                         </Box>
                         <Box>
                             <Box variant="h5">Current overall spend</Box>
-                            {Item.eventSpend + " " + Config.BUDGET_CURRENCY}
+                            {Item.eventSpend + " USD"}
                         </Box>
                     </SpaceBetween>
                 )}

programming_language: TypeScript
filename: OverviewEvents.js
change_type: ModificationType.MODIFY
diff: @@ -236,7 +236,7 @@ const ItemTable = () => {
                         id: "eventSpend",
                         header: "Current spend",
                         sortingField: "eventSpend",
-                        cell: (item) => item.eventSpend + " " + Config.BUDGET_CURRENCY
+                        cell: (item) => item.eventSpend + " USD"
                     }
                 ]}
                 items={items}
@@ -433,11 +433,11 @@ const DetailSplitPanel = () => {
                 </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>

programming_language: TypeScript
filename: OverviewUsage.js
change_type: ModificationType.MODIFY
diff: @@ -136,7 +136,7 @@ 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}

programming_language: TypeScript
filename: Statistics.js
change_type: ModificationType.MODIFY
diff: @@ -186,7 +186,7 @@ const Statistics = () => {
                                 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={[

programming_language: JavaScript
filename: EventLogin.js
change_type: ModificationType.MODIFY
diff: @@ -3,10 +3,8 @@ 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();
@@ -16,12 +14,11 @@ const EventLogin = () => {
     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);
@@ -44,7 +41,7 @@ const EventLogin = () => {
         if (!validateInputs(value)) {
             return;
         }
-        dispatch(fetchEndUserEvent(value));
+        dispatch(getEndUserEvent(value));
     };
 
     const updateFormValue = (update) => {
@@ -53,14 +50,14 @@ const EventLogin = () => {
     };
 
     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",
@@ -82,10 +79,10 @@ const EventLogin = () => {
                     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]);
 
@@ -96,7 +93,7 @@ const EventLogin = () => {
     return (
         <Container>
             <SpaceBetween size="xl">
-                {!Event.item.id ? (
+                {!AwsLogin.event.id ? (
                     <SpaceBetween size="m">
                         <FormField
                             description="Ask your event support staff for details"
@@ -105,14 +102,14 @@ const EventLogin = () => {
                         >
                             <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">
@@ -131,26 +128,7 @@ const EventLogin = () => {
                             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

programming_language: TypeScript
filename: CreateEventModal.js
change_type: ModificationType.MODIFY
diff: @@ -208,7 +208,7 @@ const CreateEventModal = () => {
                         />
                     </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 + ")"
                         }

programming_language: TypeScript
filename: CreateLeaseModal.js
change_type: ModificationType.MODIFY
diff: @@ -79,10 +79,10 @@ const CreateLeaseModal = ({ isAdminView }) => {
         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;
@@ -150,7 +150,7 @@ const CreateLeaseModal = ({ isAdminView }) => {
                         />
                     </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}
                     >

programming_language: TypeScript
filename: EditEventModal.js
change_type: ModificationType.MODIFY
diff: @@ -223,7 +223,7 @@ const EditEventModal = () => {
                             />
                         </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 + ")"
                             }

programming_language: TypeScript
filename: EditLeaseModal.js
change_type: ModificationType.MODIFY
diff: @@ -151,7 +151,7 @@ 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}
                         >

programming_language: TypeScript
filename: aws_login.js
change_type: ModificationType.MODIFY
diff: @@ -3,7 +3,9 @@ import * as queries from "../../graphql/queries";
 
 const initialState = {
     status: "hidden",
-    url: ""
+    event_status: "idle",
+    url: "",
+    event: {}
 };
 
 const aws_login = (state = initialState, action) => {
@@ -21,6 +23,25 @@ const aws_login = (state = initialState, action) => {
             };
         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;
@@ -50,4 +71,27 @@ export const fetchAwsLoginUrl = (params) => async (dispatch) => {
     }
 };
 
+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;

programming_language: TypeScript
filename: config.js
change_type: ModificationType.MODIFY
diff: @@ -7,14 +7,11 @@ let initialState = {
     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,

programming_language: JavaScript
filename: current_user.js
change_type: ModificationType.MODIFY
diff: @@ -17,6 +17,8 @@ const current_user = (state = initialState, action) => {
 };
 
 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;

programming_language: TypeScript
filename: events.js
change_type: ModificationType.MODIFY
diff: @@ -45,6 +45,7 @@ const events = (state = initialState, action) => {
     };
 
     switch (action.type) {
+        case "event/loading":
         case "events/loading":
             return {
                 ...state,
@@ -56,11 +57,6 @@ const events = (state = initialState, action) => {
                 status: "idle",
                 items: action.events.map((item) => convertItem(item, action.leases))
             };
-        case "event/loading":
-            return {
-                ...state,
-                status: "loading"
-            };
         case "event/loaded":
             return {
                 ...state,

programming_language: TypeScript
filename: leases.js
change_type: ModificationType.MODIFY
diff: @@ -13,7 +13,7 @@ const leases = (state = initialState, action) => {
         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 {
@@ -288,7 +288,7 @@ export const createLease =
                         principalId,
                         budgetNotificationEmails,
                         user,
-                        budgetCurrency: config.BUDGET_CURRENCY
+                        budgetCurrency: "USD"
                     })
                 })
             ).then((response) => {

programming_language: TypeScript
filename: usage.js
change_type: ModificationType.MODIFY
diff: @@ -11,7 +11,7 @@ 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) {

programming_language: TypeScript