Commit be11ff52

mo khan <mo@mokhan.ca>
2014-08-14 03:47:17
generate session key and add ability to revoke a session.
1 parent ac1caed
app/controllers/application_controller.rb
@@ -6,8 +6,8 @@ class ApplicationController < ActionController::Base
   before_filter :configure_permitted_parameters, if: :devise_controller?
   helper_method :current_user, :user_signed_in?
 
-  def user_session(session_id = cookies.signed[:cookie_monster])
-    UserSession.find_by(id: session_id)
+  def user_session(session_key = cookies.signed[:cookie_monster])
+    UserSession.authenticate(session_key)
   end
 
   def current_user
app/controllers/sessions_controller.rb
@@ -5,7 +5,8 @@ class SessionsController < ApplicationController
 
   def create
     if @session = UserSession.login(session_params[:username], session_params[:password])
-      cookies.signed[:cookie_monster] = @session.id
+      @session.access(request)
+      cookies.signed[:cookie_monster] = @session.key
       redirect_to my_dashboard_path
     else
       flash[:error] = "invalid credentials"
app/models/user_session.rb
@@ -1,7 +1,31 @@
 class UserSession < ActiveRecord::Base
   belongs_to :user
+  before_validation :set_unique_key
+  scope :active, -> { where("accessed_at >= ?", 2.weeks.ago).where(revoked_at: nil) }
+
+  def revoke!
+    self.revoked_at = Time.now
+    save!
+  end
+
+  def access(request)
+    self.accessed_at = Time.now
+    self.ip          = request.ip
+    self.user_agent  = request.user_agent
+    save
+  end
+
+  private
+
+  def set_unique_key
+    self.key = SecureRandom.urlsafe_base64(32)
+  end
 
   class << self
+    def authenticate(key)
+      self.active.find_by(key: key)
+    end
+
     def login(username, password)
       user = User.find_by(email: username)
       return false if user.nil?
app/views/sessions/new.html.erb
@@ -25,7 +25,7 @@
         </div>
       </div>
       <div class="span6">
-        <%= form_for(@session, html: { class: "well form-inline"}) do |f| %>
+        <%= form_for(@session, url: sessions_path(@session), html: { class: "well form-inline"}) do |f| %>
           <legend>Got an account? Login!</legend>
           <%= email_field_tag 'session[username]', '', :placeholder => 'Email', :class=> "input-medium" %>
           <%= password_field_tag 'session[password]', '', :placeholder => 'Password', :class=> "input-medium" %>
db/migrate/20140814031349_add_accessed_at_and_revoked_at_to_user_sessions.rb
@@ -0,0 +1,11 @@
+class AddAccessedAtAndRevokedAtToUserSessions < ActiveRecord::Migration
+  def change
+    change_table :user_sessions do |t|
+      t.column :key, :string
+      t.column :ip, :string
+      t.column :user_agent, :string
+      t.column :accessed_at, :datetime
+      t.column :revoked_at, :datetime, null: true
+    end
+  end
+end
db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20140814030250) do
+ActiveRecord::Schema.define(version: 20140814031349) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -162,6 +162,11 @@ ActiveRecord::Schema.define(version: 20140814030250) do
     t.integer  "user_id"
     t.datetime "created_at"
     t.datetime "updated_at"
+    t.string   "key"
+    t.string   "ip"
+    t.string   "user_agent"
+    t.datetime "accessed_at"
+    t.datetime "revoked_at"
   end
 
   create_table "users", force: true do |t|
spec/controllers/sessions_controller_spec.rb
@@ -22,7 +22,7 @@ describe SessionsController do
 
       it "returns a valid session" do
         expect(cookies.signed[:cookie_monster]).to_not be_nil
-        expect(cookies.signed[:cookie_monster]).to eql(user_session.id)
+        expect(cookies.signed[:cookie_monster]).to eql(user_session.key)
       end
 
       it "redirects to the dashboard" do