I have a Rails 3.2.20 app which uses Devise 2.1.2. Everything works fine, but Id like to add the :lockable feature to my project. After 5 failed attempts lock the account for 2 hours.
I've been reading over the Devise documentation and various StackOverflow questions and answers but I have a different setup than some.
I handle sessions in my own controller and also use the strategy to login via email or username from the Devise WiKi.
Here is my current setup:
routes.rb
devise_for :users, :controllers => { :sessions => "my_sessions" }
my_sesssions_controller.rb
class MySessionsController < Devise::SessionsController
skip_before_filter :check_concurrent_session
def create
super
set_login_token
end
private
def set_login_token
token = Devise.friendly_token
session[:token] = token
current_user.login_token = token
current_user.save(validate: false)
end
end
user.rb
attr_accessor :login
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
config/initializers/devise.rb (Excerpt)
config.authentication_keys = [ :login ]
config.case_insensitive_keys = [ :username ]
config.strip_whitespace_keys = [ :username ]
So my question is, considering I handle sessions in my own controller and am also passing authentication via either email or username, how can I implement :lockable to lock out the account for 2 hours after 5 failed attempts?
I'm assuming there's a migration that I will need to generate and run, as well as setting the locking and unlocking strategy.
I read this post How to make devise lockable with number of failed attempts but I'm a bit unsure of the unlock_keys and how that works. Also in this post it talks about generating an index for the unlock_token, but I don't think I will need that since I'll be using a :time unlock_strategy.
Basically, I have Devise working well and I don't want to muddy the water or introduce any bugs into my project.
If anyone can help guide me through setting this up, I'd appreciate your assistance. Sorry if the question is redundant or not clear.
Thanks in advance for your help.
Related
I'm relatively new to testing and very new to Rails 4 and rSpec. I am trying to test a controller that uses Devise for authentication and I am stuck. All of the examples I can find are for Rails 3.
I'm using Rails 4.0.3, Devise 3.2.3, rSpec 2.14.1 and FactoryGirl 4.4.0.
class LessonPlansController < ApplicationController
before_action :authenticate_user!
# GET /lesson_plans
def index
#lesson_plans = current_user.lesson_plans.to_a
end
.
.
.
private
# Use callbacks to share common setup or constraints between actions.
def set_lesson_plan
#lesson_plan = LessonPlan.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def lesson_plan_params
params[:lesson_plan]
end
def lesson_plan_params
params.require(:lesson_plan).permit(:title, :synopsis)
end
end
Here are my factory definitions: (Maybe I don't need to define user_id in the lesson_plan factory?)
FactoryGirl.define do
factory :user do
sequence( :username ) { |n| "user#{n}" }
sequence( :email ) { |n| "foo#{n}#example.com" }
password 'foobarbaz'
password_confirmation 'foobarbaz'
created_at Time.now
updated_at Time.now
end
end
FactoryGirl.define do
factory :lesson_plan do
user_id 1
title "The French Revolution"
synopsis "Background and events leading up to the French Revolution"
end
end
And the test part is where I get stuck.
describe LessonPlansController do
let(:valid_attributes) { { } }
let(:valid_session) { {} }
# describe "GET index" do
it "assigns all lesson_plans as #lesson_plans" do
user=FactoryGirl.create(:user)
sign_in user
lesson_plan = LessonPlan.create! valid_attributes
get :index, {}, valid_session
assigns(:lesson_plans).should eq([lesson_plan])
end
end
I'm not sure what to put in valid_attributes and valid_session (or if I even need them). The test will get as far as signing in the user, but will fail on creation of the lesson_plan. Admittedly this is the default/generated test for rSpec, but I am not sure how to proceed.
Examples I have seen use a before block to set up the user. I haven't been able to find anything on the Devise wiki page covering how to write basic rSpec tests for a controller that requires the user to be logged in. Any pointers would be greatly appreciated!
"I'm not sure what to put in valid_attributes and valid_session (or if I even need them)."
Well that depends what you're testing for.. Say you're testing validations & want to ensure that a record not be created if x column is set to null... then you could try to specifically create a record with invalid attributes (e.g. column: nil) and expect the result to not return true; maybe you want to ensure that it IS created with valid attributes.
You can btw, use `attributes_for(:factory_name)`` since you're using FactoryGirl. And no you don't necessarily need to specify the user's id in your lesson plan factory; unless you always want it to reference user 1. You can simply reference user with no value. Check out http://everydayrails.com/2012/03/12/testing-series-intro.html and especially parts 3-5 for an introduction to testing with RSPec.. I found this a pretty easy to follow guide when I was getting started.
I have implemented the cancan with active_admin using below link.
https://github.com/gregbell/active_admin/wiki/How-to-work-with-cancan
Just in my case the only change is below written code.
app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= AdminUser.new # guest user (not logged in)
if user.id == 1
can :manage, :all
puts ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> manage all"
else
can :read, :all
puts ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> read all"
end
end
end
For now just put condition on user.id.
so when i run my application i can see my puts are on right login.
Question:
so if i login with user whos user.id != 1 then also i can manage all my modules in active_admin.
cancan ability not working for me.
(In short my can :code isn't working in any condition)
Using rails 3.1.1, cancan 1.6.7, activeadmin 0.4.4, ruby 1.9.3
Followed commands in link correctly, double checked.
Used authorize_resource in AdminUser.
Using socery not devise, does this thing affecting the cancan?
I write the following in code in every model in /admin
then my conditions in ability model start working.
menu :if => proc{ can?(:manage, #ModelName) }
controller.authorize_resource
Before posting this question i just written the above code only in admin users
I have a rails 3.1 app using Devise and CanCan to mange users and roles. I want to make sure that users can update their password, but not their roles. (So ordinary users can't give themselves an admin role, basically). I have overridden the Devise class "RegistrationsController" with some code like this:
def update
# this is my attempt to stop people from updating their roles
# and giving themselves "Admin" priveledges.
params.delete("role_ids")
super
end
I'm hoping this will prevent hackers from updating the "role_ids" field in the user to change their priviledges. (If there is a better way to achieve this, please say!) My problem is I can't seem to write a spec that will test that this code works. My spec looks like this:
require 'spec_helper'
describe RegistrationsController do
before (:each) do
#user = Factory(:user)
sign_in #user
end
it "should update the user attributes but not the roles" do
user_params = {"name" => "new_name", "role_ids" => ["2"],}
put :update, { :id => #user.id, :user => user_params}
#user = User.find(#user.id)
#user.name.should == "new_name"
#user.roles.should be_empty
end
end
The trouble is this test doesn't execute. I get an error message like this:
Failures:
1) RegistrationsController should update the user attributes but not the roles
Failure/Error: put :update, { :id => #user.id, :user => user_params}
AbstractController::ActionNotFound:
Could not find devise mapping for path "/user?id=29&user%5Bname%5D=new_name&user%5Brole_ids%5D%5B%5D=2".
Maybe you forgot to wrap your route inside the scope block? For example:
devise_scope :user do
match "/some/route" => "some_devise_controller"
end
# ./spec/controllers/registrations_controller_spec.rb:13:in `block (2 levels) in <top (required)>'
I don't understand what the error message is asking me to do. My routes seem fine and my application seems to work otherwise. Can anyone help?
Try this in your setup
#request.env["devise.mapping"] = Devise.mappings[:user]
For details see How To: Controllers and Views tests with Rails 3
I'm trying to implement switch_user gem in my existing rails 3.0.9 application.
There are two models on my application, they are
User - for my customer accounts and it has_one Account
AdminUser - This was created by ActiveAdmin
I have already enabled devise authentication for Users and ActiveAdmin also working pretty much well with AdminUser. Now from my Active Admin interface I'd like to select the Accounts and login to those account just like the account owner does. Switch user is working fine but the problem is anyone can simply login to the user accounts now if they know the urls.
http://localhost:3000/switch_user?scope_identifier=user_1
All I need is allow only an AdminUser (i.e if there is an ActiveAdmin session) to access the User's accounts.
This is how my /config/initializers/switch_user.rb looks like
SwitchUser.setup do |config|
config.controller_guard = lambda { |current_user, request| current_admin_user.nil?}
config.redirect_path = lambda { |request, params| "/dashboard" }
end
But I get this error
NameError in SwitchUserController#set_current_user
undefined local variable or method `current_admin_user' for main:Object
Is there anyway I can access the active admin session?
Code for /config/initializers/active_admin.rb
ActiveAdmin.setup do |config|
config.site_title = "MyAppName"
config.authentication_method = :authenticate_admin_user!
config.current_user_method = :current_admin_user
end
btw in my application controller I haven't created any methods for authenticate_admin_user , current_admin_user active admin works fine without them.
You need modify local config/initializers/switch_user.rb:
config.controller_guard = lambda { |current_user, request, original_user, controller|
controller.admin_user_signed_in?
}
Original lambda has 2 arguments.
Just append more (up to 4) and use it.
Don't forget restart rails server :)
OK I think I found a solution to secure the switch_user. All I did is moving the routes inside the admin_users scope
ActiveAdmin.routes(self)
devise_for :admin_users, ActiveAdmin::Devise.config do
match '/admin/switch_user', :controller => 'switch_user', :action => 'set_current_user'
end
I need to do something like this from Rails console to do some testing and experiments:
User.authenticate(username, password)
I'm using Devise, but I have no idea how to do this.
I saw this other answer here
How to sign in a user using Devise from a Rails console?
But I need something cleaner and more direct. If necessary, I just need the algorithm to hash an attempted password with the salt and compare it to the encryped_password.
Is it this?
User.find(1).valid_password?('password123')
General info:
Check out the Devise README found here.
As an example, here is a helper method that you can use to do this test:
class User
def self.authenticate(username, password)
user = User.find_for_authentication(:username => username)
user.valid_password?(password) ? user : nil
end
end
Testing it, I get:
1.9.3p194 :001 > user = User.find(1)
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User id: 1, {...USER DETAILS SHOWN HERE...}
1.9.3p194 :002 > user.valid_password?('is this it?')=> false
Also working: User.find(1).valid_password?('1') in Rails console:
1.9.3p194 :011 > User.find(1).valid_password?('1')
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> false
For reference, here's my Devise setup for the User model:
class User < ActiveRecord::Base`
devise :database_authenticatable, :registerable, :timeoutable, :recoverable, :rememberable, :trackable, :validatable
{...}
end
Like you said in the comments, there is no sessions_controller since Devise. Look into the Devise config file in /config/initializers/devise.rb. Depending on your application configuration, you may also want to look at the token.rb and session_store.rb initializer configs.
EDIT: Can you check your user model to see if devise is properly configured for it? Compare it against my devise line. The :validatable property could be missing.
EDIT 2: Added the helper method to do this test if you want to do it outside of the console. Check up top.
Side-note: The rails_admin gem is pretty useful and may be worth checking out!
Hope that helps.
I think implement can be much simpler without using any kind of gem.
At least we know the email id.
email : 'currentuser#email.com'
Current password: 'rubyuser'
user = User.find_by_email('currentuser#email.com')
user.valid_password?('wrong_password') #returns false
user.valid_password?('rubyuser') #returns true