User:Mdong3: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(33 intermediate revisions by 2 users not shown)
Line 10: Line 10:
There are several ways to ensure security: Encryption, LDAP, Rails Authentication, Rails Authorization, Rails Captcha, Security Tools and Spam Detection. And devise is for Rails Authentication.
There are several ways to ensure security: Encryption, LDAP, Rails Authentication, Rails Authorization, Rails Captcha, Security Tools and Spam Detection. And devise is for Rails Authentication.


===User Authentication ===
===User Authentication Process ===
how the user authentication process works.
User Authentication is responsible for the following actions: 
Signup: create a new user. This user is going to register with a username, password (which will be encrypted in the database), email, etc.
*Signup: create a new user. This user will have to register with a username, password (which will be encrypted in the database), email, and other relevant details.  
Login: allow a user to sign in with her/his valid username and password. The authentication process happens by matching the username and password in the database, allowing the user access to the protected actions only if the given information matches the recorded values successfully. If not, the user will be redirected to the login page again.
*Login: allow a user to sign in with her/his valid username and password. The authentication process happens by matching the username and password in the database, allowing the user access to the protected actions only if the given information matches the recorded values successfully. If not, the user will be redirected to the login page again.
Access Restriction: create a session to hold the authenticated user ID after login, so navigation through additional protected actions can be done easily by just checking the userID in the current session.
*Access Restriction: create a session to hold the authenticated user ID after login, so navigation through additional protected actions can be done easily by just checking the userID in the current session.
Logout: allow the user to sign out and set the authenticated userID in session file to nil.
*Logout: allow the user to sign out and set the authenticated userID in session file to nil.
<br>


== Getting Started ==
== Getting Started ==
===Devise===
===Devise===
Devise is a flexible authentication solution for Rails helping developers save  time and effort while implementing authentication mechanisms from start. User authentication is a major component of most of the web applications, primarily to determine if the user is in fact, who it is declared to be. Devise is based on Warden<ref>https://github.com/hassox/warden/wiki</ref>. (Customized [https://github.com/rack/rack Rack] middleware that provides authentication for rack applications). Rack can be considered a middleware between web server such as Mongreal, WEBrick and frameworks such as Rails, Sinatra. <br>  
Devise is a flexible authentication solution for Rails helping developers save  time and effort while implementing authentication mechanisms from start. User authentication is a major component of most of the web applications, primarily to determine if the user is in fact, who it is declared to be. Devise is based on Warden<ref>https://github.com/hassox/warden/wiki</ref>. (Customized [https://github.com/rack/rack Rack] middleware that provides authentication for rack applications). Rack can be considered a middleware between web server such as Mongreal, WEBrick and frameworks such as Rails, Sinatra. <br> <br>
To list, Rails can provide the following functionality for a Rails app:
<br>
* Sign up (create account)<br>
* Login<br>
* “Forgot password?” feature<br>
* “Remember me” (stay logged in) feature<br>
* Edit account (edit user profile)<br>
* Validate user details while regestiring<br>
* Confirm user registration
 
Current Version: 3.4.1<br>
Current Version: 3.4.1<br>
First Release: 5 years ago<br>
First Release: 5 years ago<br>
===Setup Devise===
===Setup Devise===
Once you have created a Rails app using the "rails new <app name>" command we first need to add a line to the "Gemfile" using the following command:
Once you have created a Rails app using the "rails new <app name>" command we first need to add a line to the "Gemfile" using the following command:
<pre>echo "gem 'devise'" >> Gemfile</pre> <br>  
<pre>echo "gem 'devise'" >> Gemfile</pre>
And then install the gem using:
And then install the gem using:
<pre>bundle install</pre>
<pre>bundle install</pre>
Line 30: Line 42:
<pre>
<pre>
rails generate devise:install           
rails generate devise:install           
</pre>
</pre>To create config files.<br>
To create config files.<br><br>
 
<br>


<pre>
<pre>
Line 55: Line 68:
<pre>
<pre>
rails generate devise:views users       
rails generate devise:views users       
</pre>To create the directory /app/views/users with all the devise views, such as login form, registration form .
</pre>To create the directory /app/views/users with all the devise views, such as login form, registration form .<br><br>
===Devise Methods===
 
==Devise Methods==
Devise provides classes, models, views, controllers, views, helpers, routes. However, these functionality are exposed in only a small number of helper methods,  not all of these components would be required to know. Some of the important Devise helper methods provided are:
Devise provides classes, models, views, controllers, views, helpers, routes. However, these functionality are exposed in only a small number of helper methods,  not all of these components would be required to know. Some of the important Devise helper methods provided are:
<br>
<br>
====authenticate_user!====
====Method: authenticate_user!====
The <i>authenticate_user!</i> method is a class method that can be called from a controller only. The method determines if the user has access to all or a specific set of controller actions. This method is invoked via a <i>before_filter</i>, for example:<br>
The <i>authenticate_user!</i> method is a class method that can be called from a controller only. The method determines if the user has access to all or a specific set of controller actions. This method is invoked via a <i>before_filter</i>, for example:<br>
<pre>
<pre>
Line 66: Line 80:
end
end
</pre>
</pre>
<br>to make make any exceptions for accessing any actions without authentications we can update the above statement as:
The above statements requires the user to be logged in before they can access the controller actions. To make exceptions for accessing any actions without authentications we can update the above statement as:
<pre>
<pre>
before_action :authenticate_user!, except: [:show]
before_action :authenticate_user!, except: [:show]
</pre>
</pre>
In the above example, user authentication would be required to call any actions in the controller except the <i>show</i> action.  
In the above example, user authentication would be required to call any actions in the controller except the <i>show</i> action.  
So if the <i>root</i> in the <pre>config/routes.rb</pre> field is set to obtain any view belonging to the UsersController in this case, the Sign in page will be loaded before any of the actions can be accessed. <br> Once the user is authenticate, the required action is obtained.
So if the <i>root</i> in the <code>config/routes.rb</code> field is set to obtain any view belonging to the UsersController in this case, the Sign in page will be loaded before any of the actions can be accessed. <br> Once the user is authenticate, the required action is obtained.
<br>


====current_user====
====Method: current_user====
The <i>current_user</i> method is used to return the model class to whom the signed in user belongs. The method returns nil if a user has not yet signed in.  
The <i>current_user</i> method is used to return the model class to whom the signed in user belongs. The method returns nil if a user has not yet signed in.  
The <authenticate_user!> will make sure that the <i>current_user</i> method would never return <i>nil</i>. <br><br>
The <authenticate_user!> will make sure that the <i>current_user</i> method would never return <i>nil</i>. <br>
====user_signed_in?====
 
====Method: user_signed_in?====
Checks if the <i>current_user</i> method returns a non nil value. <br>
Checks if the <i>current_user</i> method returns a non nil value. <br>


====sign_in(@user) and sign_out(@user)====
====Method: sign_in(@user) and sign_out(@user)====
The sign_in(@user) and the sign_out(@user) are useful to login or logout a newly created user.
The sign_in(@user) and the sign_out(@user) are useful to login or logout a newly created user.
<br>
<br>


====user_session====
====Method: user_session====
This method returns metadata regarding the logged in user.
This method returns metadata regarding the logged in user.
<br><br>
==Devise Modules==
===Database Authenticatable===
Ensures that the user has entered the correct password and also to encrypt and stores the password in the database when the user registers for the first time. The authentication can be done both through POST requests or HTTP Basic Authentication.<br>
<b>Example:</b> <br><pre>User.find(1).valid_password?('password123')        # returns true/false </pre>
===Omniauthable===
Adds OmniAuth (https://github.com/intridea/omniauth) support.<br>
===Confirmable===
Sends confirmation emails to Users following successful registration. This is to prevent bot registrations. <br>
<b>Example:</b> <br><pre>User.find(1).confirm!      # returns true unless it's already confirmed
User.find(1).confirmed?    # true/false
User.find(1).send_confirmation_instructions # manually send instructions</pre>
===Recoverable===
Resets the user password and sends reset instructions.<br>
<b>Example:</b> <br><pre># resets the user password and save the record, true if valid passwords are given, otherwise false
User.find(1).reset_password!('password123', 'password123')
# only resets the user password, without saving the record
user = User.find(1)
user.reset_password('password123', 'password123')
# creates a new token and send it with instructions about how to reset the password
User.find(1).send_reset_password_instructions </pre>
===Registerable===
Handles signing up users through a registration process, also allowing them to edit and destroy their account.<br>
Helps user to register themselves and also to make changes to their login credentials including deleting their account.
===Rememberable===
Cookie handling module to manage generating and clearing of tokens for remembering the user.<br>
<b>Example :</b><pre>User.find(1).remember_me!  # regenerating the token
User.find(1).forget_me!    # clearing the token
# generating info to put into cookies
User.serialize_into_cookie(user)
# lookup the user based on the incoming cookie information
User.serialize_from_cookie(cookie_string)</pre>
===Trackable===
Tracks login details for a specific user using log in count, last log in, IP address. These details help Site admin to investigate any unusual activity.<br>
===Timeoutable===
Expires sessions that have not been active in a specified period of time.<br>
===Validatable===
Provides validations of email and password to make sure that the log in details follow a given format. These can be modified to have customized validations.<br>
<b>Example :</b><pre>#email_required? ⇒ Boolean protected
#password_required? ⇒ Boolean protected</pre>
===Lockable===
Locks an account after a specified number of failed sign-in attempts. Can unlock via email or after a specified time period.
<br>
<br>


== Example applications ==
== Example application ==
=== Devise and Rails<ref>https://github.com/RailsApps/rails-devise/</ref> ===
Consider an application that can be accessed by an end user and a site admin. Since the admin would have extra priviledges, partitioned access would be required. This can be implemented by representing both types of users as seperate models and having their respective controller with appropiate actions.
Rails 4.2 starter app with Devise for authentication.
<pre>
====What is implemented====
# All administrator controllers should inherit from this controller
&bull; Home page<br>
class AdminController < ApplicationController
&bull; Navigation bar<br>
  before_action :authenticate_admin!
&bull; Sign up (create account)<br>
end
&bull; Login<br>
&bull; “Forgot password?” feature<br>
&bull; “Remember me” (stay logged in) feature<br>
&bull; Edit account (edit user profile)<br>
&bull; List of users<br>
==== Installing ====
To build the example application, run:
<pre>rails new rails-devise -m https://raw.github.com/RailsApps/rails-composer/master/composer.rb</pre>
This will create a Rails app named <code>rails-devise</code>.<br>
Then, select “Build a RailsApps example application”. After that, select”6) rails-devise”.<br>
As for additional preferences:<br>
&bull; If you plan to deploy to Heroku, select “Unicorn" as your production web server.<br>
&bull; Use “SQLite" for development on Mac or Linux. If you plan to deploy to Heroku, use “PostgreSQL"<br>
&bull; The example application uses the default “ERB” Rails template engine.<br>
&bull; If you are a beginner, for test framework, select “None”.<br>
&bull; if you choose either “Foundation" or “Bootstrap", it will automatically install Devise views with attractive styling.<br>
&bull; “Gmail" is for development if you have one. if your site will be heavily used, then choose “SendGrid" or “Mandrill" for production.<br>
&bull; The example uses "Devise with default modules".<br>


===Devise and Pundit and Rails<ref>https://github.com/RailsApps/rails-devise-pundit</ref>===
# All end-user controllers should inherit from this controller
It extends the rails-devise example application to add authorization with Pundit.
class EndUserController < ApplicationController
====What is implemented====
  before_action :authenticate_user!
It adds authorization with Pundit, showing how to implement user roles, and limit access to pages based on user role.
end
&bull; an admin can see a list of users<br>
</pre>
&bull; an admin can change a user’s role<br>
 
&bull; an ordinary user can’t see a list of users<br>
An alternate implementaton method would be to have just one user(model) but with an <code>admin</code> flag set to true for the admin in the model record.
&bull; an ordinary user can’t change their role<br>
<pre>
&bull; an ordinary user can’t see (or edit) another user’s profile<br>
class ApplicationController < ActionController::Base
&bull; an ordinary user can see (and edit) their own user profile<br>
  before_filer :authenticate_user!
====Installing====
end
To build the example application, run:
 
<pre>rails new rails-devise-pundit -m https://raw.github.com/RailsApps/rails-composer/master/composer.rb</pre>
# All administrative controllers should inherit from this controller
This will create a new Rails app named <code>rails-devise-pundit</code>.<br>
class AdminController < ApplicationController
Then, select “Build a RailsApps example application”. After that, select ”8) rails-devise-pundit”.<br>
  before_filter :ensure_admin!
The following steps are the same as Devise and Rails.
 
  private
 
  def ensure_admin!
    unless current_user.admin?
      sign_out current_user
 
      redirect_to root_path
 
      return false
    end
  end
end
</pre>
With this way specific controllers can have private methods. In this case, the private method checks if the user is an admin or not. Only if the user is an admin, can they access <i>AdminController</i> actions
 
 
==Rails-Devise-Pundit<ref>https://github.com/RailsApps/rails-devise-pundit</ref>==
Pundit extends the Devise funtionailty by providing authorization services in addition to the authentication funtionality provided by Devise. Pundit can be used to implement user roles, and limit access to pages based on user role.  
To exemplify, Pundit can provide following features:<br>
* An admin can see a list of users<br>
* An admin can change a user’s role<br>
* An ordinary user can’t see a list of users<br>
* An ordinary user can’t change their role<br>
* An ordinary user can’t see (or edit) another user’s profile<br>
* An ordinary user can see (and edit) their own user profile<br>
<br>


==other Rails Authentication==
==Other Rails Authentication==
OmniAuth<ref>https://github.com/intridea/omniauth</ref>: A generalized Rack framework for multiple-provider authentication.<br>
OmniAuth<ref>["https://github.com/intridea/omniauth" asd]</ref>: A generalized Rack framework for multiple-provider authentication.<br>
Authlogic<ref>https://github.com/binarylogic/authlogic</ref>: A clean, simple, and unobtrusive ruby authentication solution.<br>
Authlogic<ref>https://github.com/binarylogic/authlogic</ref>: A clean, simple, and unobtrusive ruby authentication solution.<br>
Restful-authentication<ref>https://github.com/technoweenie/restful-authentication</ref>: Generates common user authentication code for Rails/Merb, with a full test/unit and rspec suite and optional Acts as State Machine support built-in.<br>
Restful-authentication<ref>https://github.com/technoweenie/restful-authentication</ref>: Generates common user authentication code for Rails/Merb, with a full test/unit and rspec suite and optional Acts as State Machine support built-in.<br>

Latest revision as of 20:39, 18 February 2015

Devise

Devise <ref>https://github.com/plataformatec/devise</ref> is a Rails gem used for authenticating and managing users.

The topic write up for this page can be found here.

Introduction

Security Background

Web applications are relatively easy to attack, as they are simple to understand and manipulate. The Gartner Group estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack"<ref>http://www.primeon.com/press/article001.php</ref>.Security depends on the people using the framework, and sometimes on the development method. There are several ways to ensure security: Encryption, LDAP, Rails Authentication, Rails Authorization, Rails Captcha, Security Tools and Spam Detection. And devise is for Rails Authentication.

User Authentication Process

User Authentication is responsible for the following actions:

  • Signup: create a new user. This user will have to register with a username, password (which will be encrypted in the database), email, and other relevant details.
  • Login: allow a user to sign in with her/his valid username and password. The authentication process happens by matching the username and password in the database, allowing the user access to the protected actions only if the given information matches the recorded values successfully. If not, the user will be redirected to the login page again.
  • Access Restriction: create a session to hold the authenticated user ID after login, so navigation through additional protected actions can be done easily by just checking the userID in the current session.
  • Logout: allow the user to sign out and set the authenticated userID in session file to nil.


Getting Started

Devise

Devise is a flexible authentication solution for Rails helping developers save time and effort while implementing authentication mechanisms from start. User authentication is a major component of most of the web applications, primarily to determine if the user is in fact, who it is declared to be. Devise is based on Warden<ref>https://github.com/hassox/warden/wiki</ref>. (Customized Rack middleware that provides authentication for rack applications). Rack can be considered a middleware between web server such as Mongreal, WEBrick and frameworks such as Rails, Sinatra.

To list, Rails can provide the following functionality for a Rails app:

  • Sign up (create account)
  • Login
  • “Forgot password?” feature
  • “Remember me” (stay logged in) feature
  • Edit account (edit user profile)
  • Validate user details while regestiring
  • Confirm user registration

Current Version: 3.4.1
First Release: 5 years ago

Setup Devise

Once you have created a Rails app using the "rails new <app name>" command we first need to add a line to the "Gemfile" using the following command:

echo "gem 'devise'" >> Gemfile

And then install the gem using:

bundle install

The Gem will be installed for your rails application. Then run the following commands:

rails generate devise:install          

To create config files.


rails generate devise user              

To create model(User) class and routes and to also associate the 'User' model with 'Devise'.

rake db:migrate                        

To run the migration and create the table with certain fields appropriate for user authentication. The result should be something like:

== 20150217043439 DeviseCreateUsers: migrating ================================
-- create_table(:users)
   -> 0.0178s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0010s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0055s
== 20150217043439 DeviseCreateUsers: migrated (0.0255s) =======================


rails generate devise:views users      

To create the directory /app/views/users with all the devise views, such as login form, registration form .

Devise Methods

Devise provides classes, models, views, controllers, views, helpers, routes. However, these functionality are exposed in only a small number of helper methods, not all of these components would be required to know. Some of the important Devise helper methods provided are:

Method: authenticate_user!

The authenticate_user! method is a class method that can be called from a controller only. The method determines if the user has access to all or a specific set of controller actions. This method is invoked via a before_filter, for example:

class UsersController < ApplicationController
  before_filter :authenticate_user!
end

The above statements requires the user to be logged in before they can access the controller actions. To make exceptions for accessing any actions without authentications we can update the above statement as:

before_action :authenticate_user!, except: [:show]

In the above example, user authentication would be required to call any actions in the controller except the show action. So if the root in the config/routes.rb field is set to obtain any view belonging to the UsersController in this case, the Sign in page will be loaded before any of the actions can be accessed.
Once the user is authenticate, the required action is obtained.

Method: current_user

The current_user method is used to return the model class to whom the signed in user belongs. The method returns nil if a user has not yet signed in. The <authenticate_user!> will make sure that the current_user method would never return nil.

Method: user_signed_in?

Checks if the current_user method returns a non nil value.

Method: sign_in(@user) and sign_out(@user)

The sign_in(@user) and the sign_out(@user) are useful to login or logout a newly created user.

Method: user_session

This method returns metadata regarding the logged in user.

Devise Modules

Database Authenticatable

Ensures that the user has entered the correct password and also to encrypt and stores the password in the database when the user registers for the first time. The authentication can be done both through POST requests or HTTP Basic Authentication.

Example:

User.find(1).valid_password?('password123')         # returns true/false 

Omniauthable

Adds OmniAuth (https://github.com/intridea/omniauth) support.

Confirmable

Sends confirmation emails to Users following successful registration. This is to prevent bot registrations.

Example:

User.find(1).confirm!      # returns true unless it's already confirmed
User.find(1).confirmed?    # true/false
User.find(1).send_confirmation_instructions # manually send instructions

Recoverable

Resets the user password and sends reset instructions.

Example:

# resets the user password and save the record, true if valid passwords are given, otherwise false
User.find(1).reset_password!('password123', 'password123')

# only resets the user password, without saving the record
user = User.find(1)
user.reset_password('password123', 'password123')

# creates a new token and send it with instructions about how to reset the password
User.find(1).send_reset_password_instructions 

Registerable

Handles signing up users through a registration process, also allowing them to edit and destroy their account.
Helps user to register themselves and also to make changes to their login credentials including deleting their account.

Rememberable

Cookie handling module to manage generating and clearing of tokens for remembering the user.

Example :

User.find(1).remember_me!  # regenerating the token
User.find(1).forget_me!    # clearing the token

# generating info to put into cookies
User.serialize_into_cookie(user)

# lookup the user based on the incoming cookie information
User.serialize_from_cookie(cookie_string)

Trackable

Tracks login details for a specific user using log in count, last log in, IP address. These details help Site admin to investigate any unusual activity.

Timeoutable

Expires sessions that have not been active in a specified period of time.

Validatable

Provides validations of email and password to make sure that the log in details follow a given format. These can be modified to have customized validations.

Example :

#email_required? ⇒ Boolean protected
#password_required? ⇒ Boolean protected

Lockable

Locks an account after a specified number of failed sign-in attempts. Can unlock via email or after a specified time period.

Example application

Consider an application that can be accessed by an end user and a site admin. Since the admin would have extra priviledges, partitioned access would be required. This can be implemented by representing both types of users as seperate models and having their respective controller with appropiate actions.

# All administrator controllers should inherit from this controller
class AdminController < ApplicationController
  before_action :authenticate_admin!
end

# All end-user controllers should inherit from this controller
class EndUserController < ApplicationController
  before_action :authenticate_user!
end

An alternate implementaton method would be to have just one user(model) but with an admin flag set to true for the admin in the model record.

class ApplicationController < ActionController::Base
  before_filer :authenticate_user!
end

# All administrative controllers should inherit from this controller
class AdminController < ApplicationController
  before_filter :ensure_admin!

  private

  def ensure_admin!
    unless current_user.admin?
      sign_out current_user

      redirect_to root_path

      return false
    end
  end
end

With this way specific controllers can have private methods. In this case, the private method checks if the user is an admin or not. Only if the user is an admin, can they access AdminController actions


Rails-Devise-Pundit<ref>https://github.com/RailsApps/rails-devise-pundit</ref>

Pundit extends the Devise funtionailty by providing authorization services in addition to the authentication funtionality provided by Devise. Pundit can be used to implement user roles, and limit access to pages based on user role. To exemplify, Pundit can provide following features:

  • An admin can see a list of users
  • An admin can change a user’s role
  • An ordinary user can’t see a list of users
  • An ordinary user can’t change their role
  • An ordinary user can’t see (or edit) another user’s profile
  • An ordinary user can see (and edit) their own user profile


Other Rails Authentication

OmniAuth<ref>["https://github.com/intridea/omniauth" asd]</ref>: A generalized Rack framework for multiple-provider authentication.
Authlogic<ref>https://github.com/binarylogic/authlogic</ref>: A clean, simple, and unobtrusive ruby authentication solution.
Restful-authentication<ref>https://github.com/technoweenie/restful-authentication</ref>: Generates common user authentication code for Rails/Merb, with a full test/unit and rspec suite and optional Acts as State Machine support built-in.

Conclusion

Devise is the most popular Rails Authentication tools. It provides a full gamut of features, and can be configured to meet most requirements. Devise often interacts with Warden which does not provide helper methods, controller classes, views, configuration options and log in failure handling. All of these things are what Devise supplies. So if you need to extend or augment Devise, you may need to implement a customized Strategy class for your own.

References

<references/>