[SOLVED] WEB422 Assignment 6

75.00 $

Category: Tags: ,
Click Category Button to View Your Next Assignment | Homework

You will receive the following solution file(s) instantly after successful payment:

zip file icon WEB422assign6-8h5puc.zip (1311 KB)
Assignment Instructions Updated Recently? Submit Below and we will provide new Solution!
Submit New Instructions
πŸ”’ Securely Powered by:
Secure Checkout
5/5 - (2 votes)

For this assignment, we will continue our development effort from Assignment 5.

Note: If you require a working version of assignment 5 to continue with this assignment, please email your professor.

For this assignment, we will restrict access to our app to only users who have registered.Β  Registered users will also have the benefit of having their favourites and history lists saved, so that they can return to them later and on a different device.Β  To achieve this, we will primarily be working with concepts from Weeks 8 and 9, such as incorporating JWT in a Web API, as well as UI considerations for working with a secured web API in Next.js

Sample Solution:

https://web422–a6–fall–2022.vercel.app

Step 1: Creating a β€œUser” API

To enable our β€œMet Artwork” App to register / authenticate users and persist their β€œfavourites” / β€œhistory” lists, we will need to create our own β€œUser” API and publish it online (Cyclic).Β  However, before we begin writing code we must first create a β€œusers” Database on MongoDB Atlas to persist the data.Β  This can be accomplished by:

Β 

  • Logging into your account on MongoDB Atlas: https://account.mongodb.com/account/login
  • Click on the β€œBrowse Collections” button in the β€œDatabase Deployments” screen (next to the β€œβ€¦β€ button)
  • Once MongoDB Atlas is finished β€œRetrieving list of databases and collections…”, you should see a list of your databases with a β€œ+ Create Database” button.
  • Choose whatever β€œDATABASE NAME” you like, and add β€œusers” as your β€œCOLLECTION NAME”
  • Once this is complete, go back to the previous view (β€œDatabase Deployments”) and click the β€œConnect” button, followed by β€œConnect your application”
  • Copy the β€œconnection string” – it should look something like:

Β 

mongodb+srv://YourMongoDBUser:<password>@clusterInfo.abc123.mongodb.net/?retryWrites=true&w=majo rity

  • Add your Database User password in place of <password> and your β€œDATABASE NAME” (from above) after the text net/ in the above connection string
  • Save your updated β€œconnection string” value (we’ll need it when we create our User API)

Β 

Now that we have a database created on MongoDB Atlas, we can proceed to create our User API using Node / Express.Β  To begin, you can use the following code as a starting point:

Β 

https://pat–crawford–sdds.netlify.app/shared/fall–2022/web422/A6/user–api.zip

Β 

You will notice that the starter code contains everything that we will need to start building our API.Β  The only task left is for us to secure the routes and publish the server online (Cyclic).Β  You will notice however that (like assignment 1) this solution also makes use of β€œ.env”.Β  Once the code is online, you will once again need to ensure that Cyclic is aware of the values for the variables.

Β 

At the moment, .env contains two values: MONGO_URL and JWT_SECRET.Β  MONGO_URL is used by the β€œuserservice” module and JWT_SECRET will be used by your code to sign a JWT payload as well as to validate an incoming JWT.

Β 

Begin by updating this file, such that the MONGO_URL is your updated β€œconnection string” (from above, without quotes) and your JWT_SECRET is a β€œlong, unguessable string” (also without quotes).Β  You may wish to use a Password Generator, ie: https://www.lastpass.com/password–generator to help generate a secret.

Β 

With our environment variables in place, we can now concentrate on securing our routes.Β  This will involve correctly setting up β€œpassportβ€œ to use a β€œJwtStrategyβ€œ (passportJWT.Strategy) and initializing the passport middleware for use in our server.Β  Everything required to accomplish this task is outlined in the JSON Web Tokens (JWT) section of the course notes.Β  The primary differences are:

Β 

  • We will be using the value of β€œsecretOrKey” from env.JWT_SECRET (from our .env file) instead of hardcoding it in our server.js
  • The Strategy will not be making use of β€œfullName” (jwt_payload.fullName) or β€œrole” (jwt_payload.role), since our User data does not contain these properties

Β 

The following is a list of specifications required for our User API once β€œpassport” has been set up (HINT most of the code described below is very similar to the code outlined in JSON Web Tokens (JWT), so make sure you have them close by for reference):

Β 

Β 

POST /api/user/login

Β 

This is the only route that contains logic that needs to be updated, specifically:

Β 

  • If the user is valid (ie, the β€œcheckUser()” promise resolves successfully) use the returned β€œuser” object to generate a β€œpayload” object consisting of two properties: _id and userName that match the value returned in the β€œuser” object. This will be the content of the JWT sent back to the client.

Β 

Sign the payload using β€œjwt” (Hint: Using the β€œjsonwebtokenβ€œ module) with the secret from process.env.JWT_SECRET (from our .env file).

Β 

Once you have your signed token, include it a β€œtoken” property within the JSON β€œmessage” returned to the client.

Β 

Routes Protected Using the passport.authenticate() Middleware

Β 

The final step in securing the API is to make sure that the majority of our routes are protected from unauthorized access.Β  This involves correctly adding the β€œpassport.authenticate()” middleware to the following routes:

Β 

  • GET β€œ/api/user/favourites”
  • PUT β€œ/api/user/favourites/:id”
  • DELETE β€œ/api/user/favourites/:id”
  • GET β€œ/api/user/history”
  • PUT β€œ/api/user/history/:id”
  • DELETE β€œ/api/user/history/:id”

Β 

With these changes in place, your User API should now be complete.Β  The final step is to push it to Cyclic (recall: Getting Started With Cyclic from WEB322 and our Assignment 1 in this course).

Β 

However, there is one small addition that we need to ensure is in place for our User API to work once it’s on Cyclic – Setting up the MONGO_URL and JWT_SECRET Config Variables:

Β 

  • Login to Cyclic to see your dashboard
  • Click on the β€œwrench” (Options and Configs) icon for your newly created application
  • Click on the β€œVariables” tab at the top (next to β€œEnvironments”)
  • Enter your JWT_SECRET (from .env) in the corresponding textbox (without quotes)
  • Similarly, enter MONGO_URL in the corresponding textbox (without quotes) and hit the β€œSave” button

Β 

NOTE: If Cyclic did not automatically detect the β€œJWT_SECRET” and β€œMONGO_URL” environment variables, you will have to add them using β€œCreate New”

This will ensure that when we refer to either MONGO_URL or JWT_SECRET in our code using process.env, we will end up with the correct value.

Β 

This completes the first part of the assignment (setting up your User API).Β  Please record the URI, ie: β€œhttps://some-randomName.cyclic.app/api/user” somewhere handy, as this will be the β€œNEXT_PUBLIC_API_URL” used in our Next.js application.

Β 

Β 

Step 2: Updating our Next.js App (utility / β€œlib” functions)

Β 

Now that we have our User API in place, we can make some key changes in our Next.js App to ensure that only registered / logged in users can view the data, as well as to finally persist their favourites / history lists in our mongoDB β€œusers” collection.

Β 

HINT: Once again, most of the code described below is very similar to the code outlined in the Authentication (Logging In) section of the notes, so make sure you have them close by for reference.

Β 

Β 

Adding .env

Β 

Since we just completed setting up our User API on Cyclic, why don’t we start by adding it to a new .env file as: NEXT_PUBLIC_API_URL, ie:

Β 

NEXT_PUBLIC_API_URL=”https://some-randomName.cyclic.app/api/user”

Β 

Β 

Creating an β€œAuthenticate” library

Β 

We will be requiring users to be authenticated to view / interact with our data, so our next step should be to write the logic to enable this feature in a separate library, ie:Β  β€œmy-app/lib/authenticate.jsβ€œ:

Β 

Once you have created the β€œauthenticate.js” file, you can use Building an β€œAuthentication” Library from the course notes as a starting point:

Β 

  • Include the following functions from the notes (these can remain the same)

Β 

Β 

  • We must also create another function: registerUser(user, password, password2). This function is almost identical to β€œauthenticateUser(user, password), however it has the following key differences:

Β 

  • Makes a β€œpost” request to β€œ/register” instead of β€œ/login”

Β 

  • In addition to providing β€œuserName” and β€œpassword” in the body of the request, it also passes β€œpassword2”

Β 

  • If it was successful (ie: status is 200), we do not invoke the β€œsetToken()” function – we simply return true

Β 

Β 

Creating a β€œUserData” library

Β 

For this application, we will require a second library to work with the new functionality available from our User API, specifically: adding, modifying and deleting favourites and history items.Β  To begin, create the file β€œuserData.js” within the newly create β€œlib” folder, ie: β€œmy-app/lib/userData.jsβ€œ: Once you have created the β€œuserData.js” file, add the following functions:

Β 

NOTE: Each of the following functions must be defined as β€œasynchronous” (ie: β€œasync”) and follows the same logic, ie (pseudocode):

Β 

Β 

Make a GET, PUT or DELETE request using fetch to the appropriate route starting with process.env.NEXT_PUBLIC_API_URL, ie: process.env.NEXT_PUBLIC_API_URL/favourites/someID, etc.

Β 

(For every request, make sure to include an β€œAuthorization” header with a value in the format β€œJWT TOKENβ€œ, where TOKEN is the value obtained from executing the β€œgetToken()” function (defined above) from your β€œAuthenticate” library

Β 

If the operation was successful (ie: status is 200), return the data (ie: the result from calling res.json())

Β 

If the operation was not successful (ie: status was not 200), return an empty array, ie: []

Β 

Β 

Apply the above logic to each of the below functions:

Β 

  • addToFavourites(id) – PUT request to /favourites/id

Β 

  • removeFromFavourites(id) – DELETE request to /favourites/id

Β 

  • getFavourites() – GET request to /favourites

Β 

  • addToHistory(id) – PUT request to /history/id

Β 

  • removeFromHistory(id) – DELETE request to /history/id

Β 

  • getHistory() – GET request to /history

Β 

Step 3: Updating our Next.js App (Login and Register components)

Β 

Before we start working with the history / favourites directly in the database using our new β€œlib” functions, we should add the components / pages to enable the user to register and log into the system.

Β 

Β 

Creating a β€œlogin” Page

Β 

To begin, start by creating a new file: login.js within the pages directory of your app.

Β 

Once this is created, proceed to follow the course notes on: β€œCreating A β€˜Login’ Pageβ€œ (making sure to redirect to β€œ/favourites” instead of β€œ/vehicles” after a successful login).

Β 

After implementing the β€œAlert” to show errors, test the app by running β€œnpm run dev” and navigating manually to the /login route.

Β 

You should see that you are unable to login, as no users are currently in the system.Β  However, you should be able to confirm that the request is being made and that the errors are showing correctly within the β€œAlert” component.

Β 

Before moving on to the β€œRegister” component and re-testing the login functionality, we must write some additional code to ensure that the atoms defined in store.js are correctly updated with the values from the back end once the user logs in.Β  To achieve this, we must:

Β 

  • Reference both the β€œfavouritesAtom” and the β€œsearchHistoryAtom” using the β€œuseAtom” hook (HINT: Be sure to include the corresponding import statements).

Β 

  • Import both the β€œgetFavourites” and β€œgetHistory” functions from our newly created β€œuserData.js” file

Β 

  • Create an β€œasynchronous” (async) function called β€œupdateAtoms” within the β€œLogin” component that updates both the favourites and history with the return values from the β€œgetFavourites” and β€œgetHistory” functions, ie:

Β 

async function updateAtoms(){

setFavouritesList(await getFavourites());Β Β Β Β Β  setSearchHistory(await getHistory());

}

Β 

  • Invoke the β€œupdateAtoms” function once the user has been authenticated, before redirecting to the β€œ/favourites” route, ie:

Β 

await updateAtoms();

Β 

Here, we can pull the correct favourites and history lists from the API for the logged in user, before they begin to navigate the site.

Β 

Β 

Creating a β€œregister” Page

Β 

Next, we will focus on creating the β€œregister” page, so that we may create users in the system and correctly test the new functionality.Β  Begin by creating a new file: register.js within the pages directory of your app.

Β 

Once this is created, you can use the now completed login.js file as a starting point.Β  Proceed to copy the whole file into β€œregister.js” and rename the component from β€œLogin” to β€œRegister”.Β  Next, make the following modifications to the code:

Β 

  • Replace the import for β€œauthenticateUser” with β€œregisterUserβ€œ, ie:

Β 

import { registerUser } from β€œ../lib/authenticate”;

Β 

  • Remove the imports for β€œgetFavourites”, β€œgetHistory”, β€œuseAtom”, β€œfavouritesAtom” and β€œsearchHistoryAtom”

Β 

  • Remove the β€œuseAtom()” function calls from within the β€œRegister” component function

Β 

  • Remove the β€œupdateAtoms()” function and the code that invokes it (ie: await updateAtoms())

Β 

  • Add a β€œpassword2” value to the state (using useState) with a default value of β€œβ€

Β 

  • When the form is submitted, instead of invoking β€œauthenticateUser”, invoke β€œregisterUser” with the β€œpassword2” value from the state, ie:

Β 

await registerUser(user, password, password2);

Β 

  • Instead of redirecting to β€œ/favourites” when the user has logged in, redirect to β€œ/login” once the user has registered

Β 

  • Change the card content to read something related to registering (instead of logging in), ie:

Β 

Register

Register for an account:

Β 

  • Add another <Form.Group> to capture the β€œpassword2” value. Be sure to include an appropriate label, ie: β€œConfirm Password”

Β 

  • Finally, change the button text from β€œLogin” to β€œRegister”

Β 

With all of these changes in place, we should have a functioning β€œRegister” component / page.Β  To test this, ensure that your app is running (npm run dev) and manually navigate to the β€œ/register” route and attempt to register for an account on the system.

Β 

NOTE: Be sure to test all aspects of the functionality, ie: registering for a duplicate user, mismatched passwords, etc.

Β 

Once you have successfully registered for an account, you should be redirected to β€œ/login”.Β  Proceed to log in with your newly created account.Β  You should be redirected to β€œ/favourites” (although there will be no favourites shown) and the JWT should be added to local storage.

Β 

Β 

Step 4: Updating our Next.js App (New β€œFavourites” functionality)

Β 

With our system now able to allow users to log in and store the resulting JWT in local storage, let’s update the favourites functionality to use the new API / functionality:

Β 

Updating β€œArtworkCardDetail”

Β 

The main UI for adding / removing favourites exists primarily within the β€œArtworkCardDetail” component (specifically, the β€œ+ favourite” button).Β  To add the new functionality here, we must make the following changes to the β€œArtworkCardDetail.js” file, containing the β€œArtworkCardDetail” component:

Β 

  • Import both the β€œaddtoFavourites” and β€œremoveFromFavourites” functions from our β€œuserData.js” file

Β 

  • Change the default value for the β€œshowAdded” state value to false

Β 

  • Use the React β€œuseEffect” hook to update showAdded instead, ie:

Β 

useEffect(()=>{

setShowAdded(favouritesList?.includes(objectID)) }, [favouritesList])

Β 

  • Modify the β€œfavouritesClicked” function so that its β€œasynchronous” (async)

Β 

  • Change the code to β€œsetFavouritesList” (ie: updating the atom value) according to the following:

Β 

  • If showAdded is true (ie: it is in the favourites list), set the favourites list by invoking:

setFavouritesList(await removeFromFavourites(objectID value)) (where objectID value, is the value passed by β€œprops” to the component)

Β 

  • If showAdded is false (ie: it is not in the favourites list), set the favourites list by invoking:

setFavouritesList(await addToFavourites(objectID value))

Β 

Updating β€œFavourites”

Β 

Finally, we must make one small update to the β€œFavourites” component (β€œpages/favourites.js”).Β  Since the process of populating the favourites list is not instantaneous (ie: pulling it from the API), we want to make sure that our favourites list doesn’t temporarily show the β€œNothing Here” message.Β  To resolve this, simply add the following line of code below the line to β€œuseAtom()” within the component function (ie: after our hooks):

Β 

if(!favouritesList) return null;

Β 

Additionally, we must ensure that we remove the default value (empty array) for the favouritesAtom within the β€œstore.js” file, ie:

Β 

export const favouritesAtom = atom();

Β 

If you test the functionality now, you should see that you’re able to add favourites as before, after first logging in with your test user (created when testing the register functionality).Β  However, if you inspect the user in the database, you should also see that the item.

Β 

Unfortunately, if you refresh the β€œfavourites” page, you will see that once again your favourites list is empty.Β  We will fix this issue later on in the assignment.

Β 

Β 

Step 5: Updating our Next.js App (New β€œHistory” functionality)

Β 

The next step is to use a similar strategy to update our β€œhistory” list, such that the values are added / removed from the database.

Β 

Updating β€œMainNav”

Β 

Begin by opening the β€œMainNav” Component (components/MainNav.js) and making the following changes:

Β 

  • Import the β€œaddtoHistory” function from our β€œuserData.js” file

Β 

  • Modify the β€œsubmitForm” function so that its β€œasynchronous” (async)

Β 

  • Change the code to β€œsetSearchHistory” (ie: updating the atom value) to the following

Β 

o setSearchHistory(await addToHistory(`title=true&q=${searchField}`))

Β 

(where searchField is the value of the β€œsearch” form field in the navigation bar)

Β 

Updating β€œAdvancedSearch” (search.js)

Β 

Next, we must update the logic in our β€œAdvancedSearch” component (pages/search.js) so that it also makes use of our new logic for persisting the data:

Β 

  • Import the β€œaddtoHistory” function from our β€œuserData.js” file

Β 

  • Modify the β€œsubmitForm” function so that its β€œasynchronous” (async)

Β 

  • Change the code to β€œsetSearchHistory” (ie: updating the atom value) to the following

Β 

o setSearchHistory(await addToHistory(queryString))

Β 

(where queryString is the calculated value generated within the β€œsubmitForm” function)

Β 

Updating β€œHistory”

Β 

Finally, we must make a few small changes to the β€œHistory” component (β€œpages/history.js”). As with our favourites component, the process of populating the history list is not instantaneous (ie: pulling it from the API).Β  Therefore, we want to make sure that our history list doesn’t temporarily show the β€œNothing Here” message.Β  To resolve this, simply add the following line of code below the line to β€œuseRouter()” within the component function (ie: after our hooks):

Β 

if(!favouritesList) return null;

Β 

Also as before, we must ensure that we remove the default value (empty array) for the searchHistoryAtom within the β€œstore.js” file, ie:

Β 

export const searchHistoryAtom = atom();

Β 

However, since it’s also possible to manipulate the history list on this page (ie: removing history items), we must also make the following additional changes to the β€œHistory” component (β€œpages/history”):

Β 

  • Import the β€œremoveHistory” function from our β€œuserData.js” file

Β 

  • Modify the β€œremoveHistoryClicked” function so that its β€œasynchronous” (async)

Β 

  • Change the code to β€œsetSearchHistory” (ie: updating the atom value) to the following

Β 

o setSearchHistory(await removeFromHistory(searchHistory[index]))

Β 

(where searchHistory is the value from your β€œsearchHistoryAtom”)

Β 

If you test the functionality now, you should see that you’re able to add history as before, after first logging in with your test user (created when testing the register functionality).Β  However, if you inspect the user in the database, you should also see that the item.

Β 

Unfortunately, (as with the favourites page) if you refresh the β€œhistory” page, you will see that your history list is empty.Β  Once again, we will fix this issue later on in the assignment.

Β 

Β 

Step 6: Updating our Next.js App (β€œRoute Guard” functionality)

Β 

To ensure that users can only access the search / favourites functionality after they have successfully logged into the system, we must implement a β€œRoute Guard” as discussed in the course notes.Β  Additionally, we will add some logic to populate our β€œatoms” when the route guard is first mounted – this will help us resolve the issue of our β€œfavourites” and β€œhistory” lists disappearing when we refresh the pages.

Β 

Begin by recreating the β€œRoute Guard” example from the notes, including:

Β 

  • Adding the complete β€œRouteGuard.js” file within the β€œcomponents” directory.
  • Updating _app.js to use the new <RouteGuard>…</RouteGuard> Component

Β 

Once this is complete, we must make the following changes to the β€œRouteGuard” component:

Β 

  • Add β€œ/register” to the PUBLIC_PATHS array
  • Reference both the β€œfavouritesAtom” and the β€œsearchHistoryAtom” using the β€œuseAtom” hook (HINT: Be sure to include the corresponding import statements).

Β 

  • Import both the β€œgetFavourites” and β€œgetHistory” functions from our newly created β€œuserData.js” file

Β 

  • Copy the β€œasynchronous” (async) function β€œupdateAtoms()” defined in the β€œLogin” component (above) and paste it within the β€œRouteGuard” component function

Β 

  • Invoke the β€œupdateAtoms()” at the beginning of the β€œuseEffect()” hook function (this will ensure that our atoms are up to date when the user refreshes the page)

Β 

With this step completed, try testing your app again.Β  You should see that the favourites and history lists are saved for the logged in user and they also remain in the UI even after a page is refreshed.Β  Additionally, if you manually remove the token from LocalStorage (β€œApplication Tab” in the Chrome Dev Tools) and try refreshing or accessing a secure page, you should be redirected back to the β€œlogin” page.

Β 

Β 

Step 7: Updating our Next.js App (β€œNavbar” UI)

Β 

As with the example from the notes, we must also update our β€œNavbar” (ie: β€œMainNav” component) to reflect whether or not the current user is logged in and give them the ability to β€œlog out”:

Β 

First, to create the β€œLog out” functionality, define a function (ie: β€œlogout()” within the β€œMainNav” (components/MainNav.js) component function), according the following guidelines:

Β 

  • It must set the β€œexpanded” state value to false (in order to collapse the menu)
  • Invoke the β€œremoveToken()” function from the β€œauthenticate” lib
  • Use the β€œuseRouter()” hook (router.push()) to redirect the user to the β€œ/login” page

Β 

With our β€œlogout()” function in place, we can finally concentrate on updating the Navbar content as well as showing / hiding specific elements within <Navbar.Collapse>…</Navbar.Collapse>.

Β 

Before we begin however, we must ensure that we have access to current value of the token by invoking the β€œreadToken()” function from the β€œauthenticate” lib (see: β€œUpdating the Navigation Componentβ€œ).

Β 

Now that we potentially have the token (stored in a token variable), we can use the value to update the Navbar to show user content / add new items:

Β 

  • If the user is logged in (ie: value of token is truthy)

Β 

  • Show the β€œAdvanced Search” navigation item o Show the β€œSearch” form o Show the β€œUser Name” dropdown
    • Update the text β€œUser Name” to show the userName value from the token
    • Add a new <NavDropdown.Item>…</NavDropdown.Item> item with the text Logout that, when clicked, will invoke the newly created β€œlogout()” function (above)
    • (Optionally) remove the β€œactive” property from the β€œFavourites” and β€œSearch History” items

Β 

  • If the user is not logged in (ie: the value of token is falsy)

Β 

  • Show a new <Nav>…</Nav> element (beneath the <Nav className=”me-auto”>…</Nav> element that contains two links for Register (β€œ/register”) and Login (β€œ/login”)

Β 

NOTE: For each of the links, be sure to include the active property and to set the β€œexpanded” state value to false when clicked (use the β€œ/” and β€œ/search” links as examples)

Β 

Step 8: Publishing our App on Vercel

Β 

If you test the app locally now, you should see that it functions the same as the example code, ie: you can create multiple accounts and for each account, store different favourites / search histories.

Β 

As a final step, we must place this code online.Β  For this purpose, we will use β€œVercel”, as in the course notes.Β  For this final part of the assignment, follow the β€œIntroduction to Vercelβ€œ and record the production URL for your assignment submission.

Β 

NOTE: The instructions assume that you have already pushed your Next.js code to a private GitHub repository.

Β 

Β 

Assignment Submission:

  • Add the following declaration at the top of your index file:

/********************************************************************************* *Β  WEB422 – Assignment 06

  • I declare that this assignment is my own work in accordance with Seneca Academic Policy.Β  No part of this *Β  assignment has been copied manually or electronically from any other source (including web sites) orΒ  *Β  distributed to other students.

*

  • Name: ______________________ Student ID: ______________ Date: ________________

*

  • Vercel App (Deployed) Link: _____________________________________________________

*

********************************************************************************/

Β 

  • Next, Compress (.zip) both your User API and your js App source code folders together (omitting the node_modules folder, as usual) in order to produce a single .zip file for your submission.
  • Submit your compressed file (containing both your User API and the Node.js App) to My.Seneca under Assignments -> Assignment 6

Important Note:

  • NO LATE SUBMISSIONS for assignments. Late assignment submissions will not be accepted and will receive a grade of zero (0).
  • After the end (11:59PM) of the due date, the assignment submission link on My.Seneca will no longer be available.
  • Submitted assignments must run locally, ie: start up errors causing the assignment/app to fail on startup will result in a grade of zero (0) for the assignment.
  • WEB422assign6-8h5puc.zip