Skip to content

Instantly share code, notes, and snippets.

@jiggneshhgohel
Last active June 4, 2018 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jiggneshhgohel/52bcd562e937ec4dad7b to your computer and use it in GitHub Desktop.
Save jiggneshhgohel/52bcd562e937ec4dad7b to your computer and use it in GitHub Desktop.
Devise Security Extension Schema Migrations

Assuming you already have a Devise model named User and you want to add following Devise Security Extension to it

  • Password Expirable
  • Password Archivable
  • Session Limitable

then your User model should look like following:

User model

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable,
         :password_expirable, :password_archivable, :session_limitable # Devise Security Extensions

   ...
   ...
   ...
end

Let's assume that you have USERS table in your DB schema (there are instructions at the bottom, if it's not the case). As it is known that the migrations are to be created manually and there are no helpers available to create the schema run the following commands to generate the Devise Security Extension modules migrations:

Generate migration for Password Expirable module:

web_app$ rails g migration AddDeviseSecurityExtensionPasswordExpirableColumnsToUsers
      invoke  active_record
      create    db/migrate/20150213121619_add_devise_security_extension_password_expirable_columns_to_users.rb

Open db/migrate/20150213121619_add_devise_security_extension_password_expirable_columns_to_users.rb and add following code to it:

class AddDeviseSecurityExtensionPasswordExpirableColumnsToUsers < ActiveRecord::Migration
  def change
    add_column :users, :password_changed_at, :datetime
    add_index :users, :password_changed_at
  end
end

Now generate migration for Session Limitable module:

web_app$ rails g migration AddDeviseSecurityExtensionSessionLimitableColumnsToUsers
      invoke  active_record
      create    db/migrate/20150213121824_add_devise_security_extension_session_limitable_columns_to_users.rb

Open db/migrate/20150213121824_add_devise_security_extension_session_limitable_columns_to_users.rb add following code to it:

class AddDeviseSecurityExtensionSessionLimitableColumnsToUsers < ActiveRecord::Migration
  def change
    add_column :users, :unique_session_id, :string, limit: 20
  end
end

Now generate migration for Password Archivable module:

web_app$ rails g migration CreateDeviseSecurityExtensionPasswordArchivableOldPasswordsTable
      invoke  active_record
      create    db/migrate/20150213121755_create_devise_security_extension_password_archivable_old_passwords_table.rb

Open db/migrate/20150213121755_create_devise_security_extension_password_archivable_old_passwords_table.rb and add following code to it:

class CreateDeviseSecurityExtensionPasswordArchivableOldPasswordsTable < ActiveRecord::Migration
  def change
    create_table :old_passwords do |t|
      t.string :encrypted_password, :null => false
      t.string :password_salt
      t.string :password_archivable_type, :null => false
      t.integer :password_archivable_id, :null => false
      t.datetime :created_at
    end

    add_index :old_passwords, [:password_archivable_type, :password_archivable_id], :name => :index_password_archivable
  end
end

Note

If you are yet to generate Devise model migration file, then first do it using the generator provided by Devise. The generated migration file say /db/migrate/20150115114522_devise_create_users.rb then should have following code:

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end

To add Password Expirable and Session Limitable Devise Security Extensions columns to your USERS table you have two options:

  1. Directly edit the migration file /db/migrate/20150115114522_devise_create_users.rb and append the extensions columns to it.After appending, the schema file should look like
class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at

      # Security Extension => Password expirable
      t.datetime :password_changed_at
      
      # Security Extension => Session limitable
      t.string :unique_session_id, :limit => 20

      t.timestamps
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
    add_index :users, :password_changed_at
  end
end
  1. Or, generate additional individual migrations as shown at the top.

In any case you will need to create a new migration for Password archivable module because it requires creating a new table.

Then as usual run rake db:migrate command to apply the migrations created above:

$ rake db:migrate
== 20150213121619 AddDeviseSecurityExtensionPasswordExpirableColumnsToUsers: migrating 
-- add_column(:users, :password_changed_at, :datetime)
   -> 0.8296s
-- add_index(:users, :password_changed_at)
   -> 0.3028s
== 20150213121619 AddDeviseSecurityExtensionPasswordExpirableColumnsToUsers: migrated (1.1326s) 

== 20150213121755 CreateDeviseSecurityExtensionPasswordArchivableOldPasswordsTable: migrating 
-- create_table(:old_passwords)
   -> 0.3546s
-- add_index(:old_passwords, [:password_archivable_type, :password_archivable_id], {:name=>:index_password_archivable})
   -> 0.3123s
== 20150213121755 CreateDeviseSecurityExtensionPasswordArchivableOldPasswordsTable: migrated (0.6672s) 

== 20150213121824 AddDeviseSecurityExtensionSessionLimitableColumnsToUsers: migrating 
-- add_column(:users, :unique_session_id, :string, {:limit=>20})
   -> 0.8799s
== 20150213121824 AddDeviseSecurityExtensionSessionLimitableColumnsToUsers: migrated (0.8801s) 

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment