Skip to content

Lambdas

simo-tuni edited this page Dec 12, 2022 · 18 revisions

These are the Lambda functions that functions as the projects backend:
Python libraries that are used in the project

import psycopg2 # psycopg2 is used for communicating with RDS Postgresql database
import jwt # JWT tokens are used for tracking login status in frontend
import uuid # uuid is used for variety of purposes to give unique identifiers
import bcrypt # bcrypt is used for password encryption
import yagmail # yagmail is used to send emails for account verification purposes
import boto3 # boto3 is used to communicate with the S3 Bucket
  1. signUp:
import json
import psycopg2
import jwt
import uuid
import bcrypt
import yagmail
import datetime
import os

def lambda_handler(event, context):
    #Check the event for username, email, and password. If they are not found in the event, or if they are an empty string, raise error
    if not("username" in event) or not("password" in event) or not("email" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    
    if len(event['email'])<=0 or len(event['username'])<=0 or len(event['password'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    
    response = {
        "statusCode": 401,
        "message": "Catastrophic error"
    }
    try:
        #Connect to the Postgresql database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Check that username is not taken by another user that is pending email verification
        postgreSQL_select_Query = "SELECT * FROM pending_users WHERE username=%s"
        cursor.execute(postgreSQL_select_Query, (event['username'],))
        user_records = cursor.fetchall()
        #If username is taken by another user check the pending users creation date, if 10 minutes has passed, delete pending user
        #If 10 minutes has not passed since user creation date, raise error
        if user_records:
            if datetime.datetime.now(datetime.timezone.utc) - user_records[0][4] > datetime.timedelta(minutes=10):
                delete_query = "DELETE FROM pending_users WHERE id = %s"
                cursor.execute(delete_query, (str(user_records[0][0]),))
                conn.commit()
            else:
                response = {
                    "statusCode": 401,
                    "message": "Account with this username is already pending, check your email"
                }
                raise Exception(json.dumps(response))
        #Check that email is not taken by another user that is pending email verification
        postgreSQL_select_Query = "SELECT * FROM pending_users WHERE email=%s"
        cursor.execute(postgreSQL_select_Query, (event['email'],))
        user_records = cursor.fetchall()
        #If email is taken by another user check the pending users creation date, if 10 minutes has passed, delete pending user
        #If 10 minutes has not passed since user creation date, raise error
        if user_records:
            if datetime.datetime.now(datetime.timezone.utc) - user_records[0][4] > datetime.timedelta(minutes=10):
                delete_query = "DELETE FROM pending_users WHERE id = %s"
                cursor.execute(delete_query, (str(user_records[0][0]),))
                conn.commit()
            else:
                response = {
                    "statusCode": 401,
                    "message": "Account with this email is already pending, check your email"
                }
                raise Exception(json.dumps(response))
        #Check that username is not taken by another user, if it is taken raise error
        postgreSQL_select_Query = "SELECT * FROM users WHERE username=%s"
        cursor.execute(postgreSQL_select_Query, (event['username'],))
        user_records = cursor.fetchall()
        if user_records:
            response = {
                "statusCode": 401,
                "message": "Username taken"
            }
            raise Exception(json.dumps(response))
        #Check that email is not taken by another user, if it is taken raise error
        postgreSQL_select_Query = "SELECT * FROM users WHERE email=%s"
        cursor.execute(postgreSQL_select_Query, (event['email'],))
        user_records = cursor.fetchall()
        if user_records:
            response = {
                "statusCode": 401,
                "message": "Email taken"
            }
            raise Exception(json.dumps(response))
        # create uuid for user, check if it already exists, if it does make a new one until it doesn't exist (redundant, since chance of collision is minimal)
        id = str(uuid.uuid4())
        postgreSQL_select_Query = "SELECT * FROM pending_users WHERE id=%s"
        cursor.execute(postgreSQL_select_Query, (id,))
        user_records = cursor.fetchall()
        while user_records:
            id = str(uuid.uuid4())
            postgreSQL_select_Query = "SELECT * FROM pending_users WHERE id=%s"
            cursor.execute(postgreSQL_select_Query, (id,))
            user_records = cursor.fetchall()
        #Salt and hash password
        pw = event['password'].encode()
        salt = bcrypt.gensalt()
        hashed = bcrypt.hashpw(pw, salt)
        #Create hash used in email verification
        verification_hash = uuid.uuid4().hex
    except:
        raise Exception(json.dumps(response))
    try:
        #Insert user into database as a user that needs email verification
        insert_query = "INSERT INTO pending_users (id, username, email, password_hash, created_at, verification) VALUES (%s,%s,%s,%s,NOW(),%s)"
        insert = (id,event['username'],event['email'], hashed.decode(), verification_hash)
        cursor.execute(insert_query, insert)
        conn.commit()
    except:
        response = {
            "statusCode": 401,
            "message": "Catastrophic error while creating user"
        }
        raise Exception(json.dumps(response))
    try:
        #Send email with verification link
        url = "https://main.dsbrm4nuc1jgz.amplifyapp.com/user/{}/verify-1/{}".format(id, verification_hash)
        contents = ["Please verify your email to access the service via the following link: <a href={}>Here</a>".format(url)]
        yagmail.SMTP(os.environ['sender_email'],os.environ['sender_email_key']).send(event['email'], 'Signup email from Portfolio Pro', contents)
    except:
        response = {
            "statusCode": 401,
            "message": "Catastrophic error while sending email"
        }
        raise Exception(json.dumps(response))
    
    return {
        "message":"Successfully created user and sent email"
    }
  1. logIn:
import json
import psycopg2
import jwt
import bcrypt
import os

def lambda_handler(event, context):
    #Check for username and password in event, if they arent in event or they are an empty string raise error
    if not("username" in event) or not("password" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['username'])<=0 or len(event['password'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    response = {
        "statusCode": 401,
        "message": "Catastrophic error"
    }
    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Check if the user has verified their account by email, if they haven't, prompt them to check their email for the verification link by raising an error
        verification_query = "SELECT * FROM pending_users WHERE username =%s"
        cursor.execute(verification_query, (event['username'],))
        unverified_user = cursor.fetchall()
        if unverified_user:
            response = {
                "statusCode": 401,
                "message": "User verification is in progress, please check your email for the link."
            }
            raise Exception(json.dumps(response))
        #Select user from users table by username
        query = "SELECT * FROM users WHERE username=%s"
        cursor.execute(query, (event['username'],))
        user = cursor.fetchall()
        #If user does not exist raise error
        if(len(user) == 0):
            response = {
                "statusCode": 401,
                "message": "Could not identify user, credetials might be wrong"
            }
            raise Exception(json.dumps(response))
        #Compare password hash to the password in event, if they do not match raise error
        if not bcrypt.checkpw(event['password'].encode(), user[0][3].encode()):
            response = {
                "statusCode": 401,
                "message": "Could not identify user, credetials might be wrong"
            }
            raise Exception(json.dumps(response))
        #Create a JWT token that is used to check if user is logged in in frontend
        token = jwt.encode({"userId": user[0][0], "email": user[0][2]}, os.environ['JWT_KEY'], algorithm='HS256')
    except:
        raise Exception(json.dumps(response))
    return {
        'token': token,
        'userId': user[0][0],
        "public": user[0][5],
        'userUrl': user[0][6]
    }
  1. loadPreviewComponents:
import json
import psycopg2
import os

def lambda_handler(event, context):
    #Check for url and sectionId in event, if they arent in event or they are an empty string raise error
    if not("url" in event) or not("sectionId" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['url'])<=0 or len(event['sectionId'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    response = {
            "statusCode": 401,
            "message": "Catastrophic error"
        }
    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Select user by users url
        postgreSQL_select_Query = "SELECT * FROM users WHERE user_url=%s"
        cursor.execute(postgreSQL_select_Query, (event['url'],))
        user_records = cursor.fetchall()
        #If url is not found in database raise error
        if(len(user_records) == 0):
            response = {
                "statusCode": 401,
                "message": "Url not found"
            }
            raise Exception(json.dumps(response))
        #If user has not set url public, raise error
        if not user_records[0][5]:
            response = {
                "statusCode": 405,
                "message": "No permission to access resource"
            }
            raise Exception(json.dumps(response))
        #Select all components in the section that user has created
        postgreSQL_select_Query = "SELECT * FROM components WHERE sectionid=%s"
        cursor.execute(postgreSQL_select_Query, (event['sectionId'],))
        components = cursor.fetchall()
        #Select section by id to grab layout and background
        section_query = "SELECT * FROM sections WHERE id=%s"
        cursor.execute(section_query, (event['sectionId'],))
        section = cursor.fetchall()
        #Create a JSON array of components
        resp = []
        for component in components:
            print(component)
            item = {
                "type": component[1],
                "value": component[2],
                "slotid":component[3],
                "posid":component[3]
            }
            resp.append(item)
        return {
            "layout": section[0][3],
            "background": section[0][4],
            "components": resp
        }
    except Exception as e:
        raise Exception(json.dumps(response))

    response = {
        "statusCode": 401,
        "message": "Catastrophic error, lambda function did not properly return"
    }
    raise Exception(json.dumps(response))
  1. loadPreviewSections:
import json
import psycopg2
import os

def lambda_handler(event, context):
    #Check event for url, if not found or it is an empty string raise error
    if not("url" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['url'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    
    response = {
        "statusCode": 401,
        "message": "Catastrophic error"
    }
    
    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Select user by the users url
        postgreSQL_select_Query = "SELECT * FROM users WHERE user_url=%s"
        cursor.execute(postgreSQL_select_Query, (event['url'],))
        user_records = cursor.fetchall()
        #If url is not found in database raise error
        if(len(user_records) == 0):
            response = {
                "statusCode": 401,
                "message": "Url not found"
            }
            raise Exception(json.dumps(response))
        #If the user has not set the url to be public, raise error
        if not user_records[0][5]:
            response = {
                "statusCode": 405,
                "message": "No permission to access resource"
            }
            raise Exception(json.dumps(response))
        #Select all of the users sections by user id
        postgreSQL_select_Query = "SELECT * FROM sections WHERE userid=%s"
        cursor.execute(postgreSQL_select_Query, (user_records[0][0],))
        sections = cursor.fetchall()
        #Return JSON array of sections to frontend
        resp = []
        for section in sections:
            item = {
                "id": section[0],
                "name": section[2],
                "position":section[5]
            }
            resp.append(item)
        return resp
    except:
        raise Exception(json.dumps(response))
    
    response = {
        "statusCode": 401,
        "message": "Catastrophic error, lambda function did not properly return"
    }
    raise Exception(json.dumps(response))
  1. verifyPendingUser:
import json
import psycopg2
import os

def lambda_handler(event, context):
    #Check event for the verification hash and userId
    if not("verification" in event) or not("id" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['verification'])<=0 or len(event['id'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Select the pending user that matches both the userId and verification hash
        postgreSQL_select_Query = "SELECT * FROM pending_users WHERE id = %s AND verification=%s"
        cursor.execute(postgreSQL_select_Query, (event['id'],event['verification']))
        user_records = cursor.fetchall()
        #If user is not found raise error
        if len(user_records)>0:
            user_records = user_records[0]
        else:
            response = {
                "statusCode": 401,
                "message": "No user found"
            }
            raise Exception(json.dumps(response))
        #If the user exists in pending_users table, insert the user into the users table and delete the user from pending_users table
        if user_records:
            insert_query = """ INSERT INTO users (id, username, email, password_hash, created_at, is_public, user_url) VALUES (%s,%s,%s,%s,NOW(),%s,%s)"""
            insert = (user_records[0],user_records[1],user_records[2],user_records[3], False, "")
            cursor.execute(insert_query, insert)
            conn.commit()
            delete_query = "DELETE FROM pending_users WHERE id = %s"
            cursor.execute(delete_query, (str(user_records[0]),))
            conn.commit()
    except:
        response = {
            "statusCode": 401,
            "message": "User not found in pending_users"
        }
        raise Exception(json.dumps(response))    
    return {
        "message":"Successfully verified user"
    }
  1. savePortfolio:
import json
import psycopg2
import os

def lambda_handler(event, context):
    #Check event for parameters, and their length, if they are not found raise error
    if not("background" in event) or not("id" in event) or not("layout" in event) or not("name" in event) or not("position" in event) or not("userId" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['background'])<=0 or len(event['id'])<=0 or len(event['layout'])<=0 or len(event['name'])<=0 or len(event['userId'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    try:
        #Connect to database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #check if user has section with given id
        check_if_section_exists_for_user = "SELECT * FROM sections WHERE userid=%s"
        cursor.execute(check_if_section_exists_for_user, (event['userId'],))
        user_sections = cursor.fetchall()
        #if user has section by given id, delete it. this also deletes components linked to the section by cascading.
        if user_sections:
            delete_query = "DELETE FROM sections WHERE id = %s"
            cursor.execute(delete_query, (event['id'],))
            conn.commit()
    except:
        response = {
            "statusCode": 401,
            "message": "Catastrophic error"
        }
        raise Exception(json.dumps(response))
    
    #if user does not have section with given id create one
    try:
        insert_query = """INSERT INTO sections (id, userid, name, layout, background, position) 
                          VALUES (%s,%s,%s,%s,%s,%s) 
                          ON CONFLICT(id) DO
                          UPDATE SET name =%s, layout=%s,background=%s
                       """
        insert = (event['id'],event['userId'],event['name'],event['layout'],event['background'],event['position'],event['name'],event['layout'],event['background'],)
        cursor.execute(insert_query, insert)
        conn.commit()
    except Exception as e:
        print(e)
        response = {
            "statusCode": 401,
            "message": "Catastrophic error while creating sections"
        }
        raise Exception(json.dumps(response))
    try:
        #insert components to the components table, linked to sections table by sectionid
        for item in event['data']:
            insert_query = "INSERT INTO components (sectionid, type, value, slotid, posid) VALUES (%s,%s,%s,%s,%s)"
            insert = (event['id'],item['type'],item['value'],item['slotid'],item['posid'],)
            cursor.execute(insert_query, insert)
            conn.commit()
    except Exception as e:
        response = {
            "statusCode": 401,
            "message": "Catastrophic error while creating components"
        }
        raise Exception(json.dumps(response))
    return {
        "message":"Successfully saved user sections and components"
    }
  1. loadSections:
import json
import psycopg2
import os

def lambda_handler(event, context):
    
    #Check event for parameters, and their length, if they are not found raise error
    if not("userId" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['userId'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    
    try:
        #Connect to database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Select all user sections by user id
        postgreSQL_select_Query = "SELECT * FROM sections WHERE userid=%s"
        cursor.execute(postgreSQL_select_Query, (event['userId'],))
        sections = cursor.fetchall()
        #Make a JSON array
        resp = []
        for section in sections:
            item = {
                "id": section[0],
                "name": section[2],
                "position":section[5]
            }
            resp.append(item)
        return resp
    except:
        response = {
                "statusCode": 401,
                "message": "Catastrophic error when selecting user sections"
            }
        raise Exception(json.dumps(response))
    
    return {
        "statusCode": 401,
        "message": "Catastrophic error, lambda function did not properly return"
    }
  1. loadComponents:
import json
import psycopg2
import os

def lambda_handler(event, context):
    #Check event for parameters, and their length, if they are not found raise error
    if not("sectionId" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    
    if len(event['sectionId'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))

    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Select all components in section by sectionId
        postgreSQL_select_Query = "SELECT * FROM components WHERE sectionid=%s"
        cursor.execute(postgreSQL_select_Query, (event['sectionId'],))
        components = cursor.fetchall()
        #Select section by sectionId to grab layout and background
        section_query = "SELECT * FROM sections WHERE id=%s"
        cursor.execute(section_query, (event['sectionId'],))
        section = cursor.fetchall()
        #Make a JSON array for components
        resp = []
        for component in components:
            print(component)
            item = {
                "type": component[1],
                "value": component[2],
                "slotid":component[3],
                "posid":component[3]
            }
            resp.append(item)
        return {
            "layout": section[0][3],
            "background": section[0][4],
            "components": resp
        }
    except:
        response = {
                "statusCode": 401,
                "message": "Catastrophic error when selecting user sections"
            }
        raise Exception(json.dumps(response))
    
    return {
        "statusCode": 401,
        "message": "Catastrophic error, lambda function did not properly return"
    }
  1. deleteSection:
import json
import psycopg2
import os

def lambda_handler(event, context):
    #Check event for parameters, and their length, if they are not found raise error
    if not("userId" in event) or not("sectionId" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['userId'])<=0 or len(event['sectionId'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    #Default response
    response = {
        "statusCode": 401,
        "message": "Catastrophic error"
    }
    
    try:
        #Connect to database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #check if user has section with given id
        check_if_section_exists_for_user = "SELECT * FROM sections WHERE userid=%s"
        cursor.execute(check_if_section_exists_for_user, (event['userId'],))
        user_sections = cursor.fetchall()
        #if user has section by given id, delete it. this also deletes components linked to the section by cascading.
        if user_sections:
            try:
                delete_query = "DELETE FROM sections WHERE id = %s"
                cursor.execute(delete_query, (event['sectionId'],))
                conn.commit()
                return {
                    "message": "Successfully deleted section"
                }
            except:
                response = {
                    "statusCode": 401,
                    "message": "Catastrophic error while deleting section"
                }
    except:
        raise Exception(json.dumps(response))
    
    return {
        "statusCode": 401,
        "message": "No sections found with given parameters"
    }
  1. deleteAccount:
import json
import psycopg2
import os
import bcrypt

def lambda_handler(event, context):
    #Check event for parameters, and their length, if they are not found raise error
    if not("userId" in event) or not("password" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    
    if len(event['userId'])<=0 or len(event['password'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    

    
    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Select user by id, if no user is found, raise error
        query = "SELECT * FROM users WHERE id=%s"
        cursor.execute(query, (event['userId'],))
        user = cursor.fetchall()
        if(len(user) == 0):
            response = {
                "statusCode": 401,
                "message": "Could not identify user, credetials might be wrong"
            }
            raise Exception(json.dumps(response))
        #Check if password and hash match, if they dont, raise error
        if not bcrypt.checkpw(event['password'].encode(), user[0][3].encode()):
            response = {
                "statusCode": 401,
                "message": "Could not identify user, credetials might be wrong"
                
            }
            raise Exception(json.dumps(response))
        #If they match, delete user account
        else:
            delete_query = "DELETE FROM users WHERE id = %s"
            cursor.execute(delete_query, (str(event['userId']),))
            conn.commit()
            return {
                "message":"Account successfully deleted"
            }
    except:
        response = {
            "statusCode": 401,
            "message": "Failed to delete account"
        }
        raise Exception(json.dumps(response))
    response = {
            "statusCode": 401,
            "message": "Lambda function did not properly return"
        }
    raise Exception(json.dumps(response))
  1. getSignedUrl:
import json
import boto3
import os

def lambda_handler(event, context):
    
    #Check event for parameters, and their length, if they are not found raise error
    if not("fileName" in event) or not ("fileType" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['fileName'])<=0 or len(event['fileType'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    #Connect to S3 and generate a link that frontend can upload files to
    try:
        s3 = boto3.client('s3',
             aws_access_key_id=os.environ['S3_AWS_ACCESS_KEY_ID'], 
             aws_secret_access_key=os.environ['S3_AWS_SECRET_ACCESS_KEY'], 
             region_name=os.environ['S3_REGION']
             )
        url = s3.generate_presigned_url(
            ClientMethod='put_object',
            Params={
                'Bucket': 'portfoliopro',
                'Key': event['fileName'],
                'ContentType': event['fileType']
            },
            ExpiresIn=36000
        )
    except:
        response = {
            "statusCode": 401,
            "message": "Catastrophic error while accessing S3 bucket"
        }
        raise Exception(json.dumps(response))
    return {
        "url": url
    }
  1. updatePreviewStatus:
import json
import psycopg2
import os

def lambda_handler(event, context):
    print(event)
    #Check event for parameters, and their length, if they are not found raise error
    if not("userId" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['userId'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    
    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #If 'is_public' is found in event, update users public status
        if ("is_public" in event):
            print("is_public is " + str(event['is_public']))
            insert_query = "UPDATE users SET is_public=%s WHERE id=%s"
            cursor.execute(insert_query, (event['is_public'], event['userId']),)
            conn.commit()
        #If 'url' is found in event, update users preview url
        if ("url" in event):
            insert_query = "UPDATE users SET user_url=%s WHERE id=%s"
            cursor.execute(insert_query, (event['url'], event['userId']),)
            conn.commit()
    except Exception as e:
        print(e)
        response = {
            "statusCode": 401,
            "message": "Catastrophic error"
        }
        raise Exception(json.dumps(response))
    return {
        "message":"Preview status update successful"
    }
  1. verifyEmail:
import json
import psycopg2
import os

def lambda_handler(event, context):
    
    #Check event for parameters, and their length, if they are not found raise error
    if not("userId" in event) or not("updateHash" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['userId'])<=0 or len(event['updateHash'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    #Default response
    response = {
        "statusCode": 401,
        "message": "Catastrophic error while trying to update email"
    }
    
    try:
        #Connect to database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Select user by id and email update hash
        postgreSQL_select_Query = "SELECT * FROM users WHERE id = %s AND update_hash=%s"
        cursor.execute(postgreSQL_select_Query, (event['userId'],event['updateHash']))
        user_records = cursor.fetchall()
        #If user not found, raise error
        if not user_records:
            raise Exception(json.dumps(response))
        #Update user's email and set pending email and pending email verificarion hash to null
        update_query = "UPDATE users SET email=%s,update_hash=%s,pending_email=%s WHERE id=%s"
        cursor.execute(update_query, (user_records[0][8], None, None, event['userId']),)
        conn.commit()
    except Exception as e:
        raise Exception(json.dumps(response))
    
    return {
        "message":"Email has been successfully updated"
    }
  1. updatePassword:
import json
import psycopg2
import bcrypt
import os

def lambda_handler(event, context):
    
    #Check event for parameters, and their length, if they are not found raise error
    if not("userId" in event) or not("oldpassword" in event) or not("newpassword" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['userId'])<=0 or len(event['oldpassword'])<=0 or len(event['newpassword'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    #Default response
    response = {
        "statusCode": 401,
        "message": "Catastrophic error while updating password"
    }
    
    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Select user by id
        query = "SELECT * FROM users WHERE id=%s"
        cursor.execute(query, (event['userId'],))
        user = cursor.fetchall()
        #If user was not found raise error
        if not user:
            response = {
                "statusCode": 401,
                "message": "Catastrophic error, could not find user"
            }
            raise Exception(json.dumps(response))
        #Check if oldpassword and password hash match
        #If they match, salt and hash newpassword and store it in the database
        if bcrypt.checkpw(event['oldpassword'].encode(), user[0][3].encode()):
            pw = event['newpassword'].encode()
            salt = bcrypt.gensalt()
            hashed = bcrypt.hashpw(pw, salt)
            
            cursor = conn.cursor()
            insert_query = "UPDATE users SET password_hash=%s WHERE id=%s"
            cursor.execute(insert_query, (hashed.decode(), event['userId']),)
            conn.commit()
            return {
                "message":"Password updated"
            }
        response = {
                "statusCode": 401,
                "message": "User credentials do not match"
            }
        raise Exception(json.dumps(response))
    except:
        raise Exception(json.dumps(response))
    response = {
                "statusCode": 401,
                "message": "Lambda function did not properly return"
            }
    raise Exception(json.dumps(response))
  1. updateEmail:
import json
import psycopg2
import yagmail
import uuid
import os

def lambda_handler(event, context):
    
    #Check event for parameters, and their length, if they are not found raise error
    if not("userId" in event) or not("newEmail" in event):
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    if len(event['userId'])<=0 or len(event['newEmail'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
    

    #Default response
    response = {
        "statusCode": 401,
        "message": "Catastrophic error while trying to update email"
    }
    
    try:
        #Connect to the database
        conn = psycopg2.connect(
            host=os.environ['host'],
            database=os.environ['database'],
            user=os.environ['user'],
            password=os.environ['password']
        )
        cursor = conn.cursor()
        #Check if the new email is taken by someone already
        postgreSQL_select_Query = "SELECT * FROM users WHERE email=%s"
        cursor.execute(postgreSQL_select_Query, (event['newEmail'],))
        user_records = cursor.fetchall()
        if user_records:
            response = {
                "statusCode": 401,
                "message": "Account with this email already exists"
            }
            raise Exception(json.dumps(response))
        #Check if the new email is taken by a pending user already
        postgreSQL_select_Query = "SELECT * FROM pending_users WHERE email=%s"
        cursor.execute(postgreSQL_select_Query, (event['newEmail'],))
        user_records = cursor.fetchall()
        if user_records:
            response = {
                "statusCode": 401,
                "message": "Account with this email already exists"
            }
            raise Exception(json.dumps(response))
        #Create a hash that is used for verification
        verification_hash = uuid.uuid4().hex
        #Update users pending email and update hash with new values
        update_query = "UPDATE users SET update_hash=%s, pending_email=%s WHERE id=%s"
        cursor.execute(update_query, (verification_hash, event['newEmail'], event['userId']),)
        conn.commit()
        #Create a url for the user for email verification
        url = "https://main.dsbrm4nuc1jgz.amplifyapp.com/user/{}/verify-2/{}".format(event['userId'], verification_hash)
        contents = ["Please verify your email to update it: <a href={}>Here</a>".format(url)]
        #Send email
        yagmail.SMTP(os.environ['sender_email'],os.environ['sender_email_key']).send(event['newEmail'], 'Email update verification', contents)
        
    except:
        raise Exception(json.dumps(response))
    return {
        "message":"Check your email to verify updated email"
    }
  1. authorizer:
import json
import jwt
import os

def lambda_handler(event, context):
    
    #Check event for token and token length
    if not("authorizationToken" in event):
        response = {
            "statusCode": 401,
            "body": "Missing arguments"
            
        }
        raise Exception(json.dumps(response))
    if len(event['authorizationToken'])<=0:
        response = {
            "statusCode": 401,
            "message": "Missing arguments"
        }
        raise Exception(json.dumps(response))
        
    try:
        #Split authorizationToken to token and userId 
        token, userId = event['authorizationToken'].split(',')
        #Check that userId encoded in token is same as the userId in authorizationToken. If they match allow access to resource.
        decodedToken = jwt.decode(token, os.environ['JWT_KEY'], algorithms=["HS256"])
        if userId == decodedToken['userId']:
            auth='Allow'
        else:
            auth = 'Deny'
        #authResponse has to be in this format for AWS
        authResponse = { 
            "principalId": "abc123", 
            "policyDocument": { 
                "Version": "2012-10-17", 
                "Statement": [{
                    "Action": "execute-api:Invoke", 
                    "Resource": [
                        "arn:aws:execute-api:eu-north-1:372576978893:x4hw8n8xca/*/*/user/*"
                        ], 
                        "Effect": auth}
                    ]
            }
        }
    except:
        response = {
            "statusCode": 500,
            "body": "Something went wrong"
        }
        raise Exception(json.dumps(response))
    return authResponse
Clone this wiki locally