01.13.14 - 05.11.14
User model created with correct attributes and datatypes | 10 pts |
All validations create and functioning correctly | 20 pts |
Generate_codes! method created and working | 10 pts |
password and password= methods created and working | 20 pts |
authenticate methods functioning | 20 pts |
Convenience methods added to model and functioning | 20 pts |
Total | 100 pts |
For this lab you will be creating a model representing a user of a website.
For this user we will assume that there will be a registration page that the user completes to register as a user for the site. After the user completes the registration, an email will be sent to that users email address to confirm that they are who they say they are and a code will be included in a link in that email which when clicked will take the user back to the website and store the fact that they have confirmed their selves. The user will also have the option of canceling their registration with the site which will be done with a cancelation code.
Once the users account has been verified they will be able to login to our site using a username and password that was created when they registered. One of the tricky things about passwords on a website is that we want to be very careful that the password does not become know to anyone but the owner of that account. To accomplish this the password itself will not be stored in our database, instead an encrypted form of the password will be stored instead. The way this will be done is that we will turn their original password into a salted hash. A hash in this context is a way of encoding some information in such a way that every time that information is encoded it will produce the same hash value but the hash value itself cannot be "reversed" and turned back into the original password. The salt is just some other piece of information that is appended to the password so help secure it further. The general process then would be that a user registers, supplying a password which is turned into a salted hash. Then, the next time the user logs into the website we take the password that they enter, turn it into a salted hash using the same process we did during registration and then check to see if the supplied salted hashed password matches the salted hashed password we have stored for that user.
The website user will need to have the following attributes:
field name | data type |
---|---|
username | string |
first_name | string |
last_name | string |
string | |
verified | boolean |
password_hash | string |
password_salt | string |
confirmation_code | string |
cancelation_code | string |
Create the model for User using the generator script.
After the User model has been created and the database migrated, add validations to the model so that the username, firstname, and lastname cannot be blank. Validate that the username is unique. For the email address write or find a regular expression that matches email addresses well and confirm that the submitted address matches the pattern.
Next, add the following require to the top of your User model.
require 'digest/sha2'
With that require in place you will now need to add some custom methods to the User model.
Create an instance method called generatecodes! which will need to, when called, create values for the confirmationcode and cancelation_code fields. This method will be called on a new user that is being registered which is why it will work best as an instance method.
Use the following to generate the confirmation_code:
Digest::SHA256.hexdigest( self.email + self.id.to_s )
And the following to generate the cancelation_code
Digest::SHA256.hexdigest( self.username + self.email + self.id.to_s )
These two codes will be used to confirm that the user really is who they say they are when they are confirming or canceling their account.
For the users password your model will need to accept a field called password so your will need to a add an instance method called password= to your model because there is no actual password field as a part of the user model. This method will need be passed a password and should then generate a random number to be stored in the password_salt attribute using the following code:
[Array.new( 6 ){rand( 256 ).chr}.join].pack( "m" ).chomp
And will then create a value for the password_hash attribute using this code:
Digest::SHA256.hexdigest( password + self.password_salt )
You should also add a password accessor method to your model which returns an empty string for the password if it hasn't been set or 9 x's (xxxxxxxxx) if the password has been set.
Next, your model will need to be able to authenticate a user (confirm that the username and password submitted match what is stored). To do this add a class method to your model called authenticate which will be passed a username and a password. This method should then find the user to be authenticated, and using the same code segments given above for creating the passwordhash should confirm that the passed password for the user when hashed matches the passwordhash value for that user. If the information matches then the user is authenticated and the user object should be returned. If the username or password are found to be incorrect an error should be raised.
This authenticate method would be called whenever a user logs in to the site. Since at that point in the process we don't know which user we are dealing with it would be very inconvenient to try to call this method in a specific user object which is why it should be created as a class method.
Finally, some convenience methods to deal with verifying and canceling users. Add a verify! instance method that will be passed a confirmationcode. This method would be called when the user clicks on the verification link in the registration email that they are sent. We will assume that this link also contains a user id so that this method can be created as an instance method allowing it to be called on a user object. If the code passed matches the confirmationcode for the user who the method was called for, mark that user as verified by setting their verified attribute to true and returning true. If the codes do not match return false.
Create a class method called cancel! for the User model. This method will be passed a user object and a cancelationcode. If the passed code matches the cancelationcode for the user then delete the user and return true, otherwise return false.
Create a class method for the User model called verified which will return an array of all of the verified users which could be used, for example, if we wanted to send out an email to all users but only those who have verified their accounts.
If you would like I've included some files you can use for testing your solution. In the attached zip file there is a file called users.yml that will need to be placed in your test/fixtures directory and a file called user_test.rb that will need to be placed in your test/models directory. Once both of those files are in place you can run the tests from the command line by just typing:
rake