I have a problem with my devise gem. When I log out with a user, devise deletes my user out of my mysql database. I recognized this error first on yesterday, before it was working. I don't remember what register and login is also still possible.
User Controller
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
def destroy
#user = User.find(params[:id2])
#user.destroy
respond_to do |format|
format.html { redirect_to root_url}
end
end
end
Logout-Link
<%= link_to " Logout", destroy_user_session_path(:id2 => current_user.id), :class => "btn btn-danger icon-share-alt ",:style=>"font-family: FontAwesome;font-weight: bold;color:black", :method => :delete %>
User Model
class User < ActiveRecord::Base
rolify
belongs_to :ressourcen
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :vorname, :nachname, :email, :password, :password_confirmation, :roleid,:id
validates :email, :uniqueness => true
That seems really weird. If devise and routes are setup in a default way, I can't see how this should happen. What does:
rake routes
show you for destroy_user_session_path? If you haven't overridden devise's SessionsController, and haven't changed devise's routes, the only entry for destroy_user_session should look something like this:
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
It definitely shouldn't be going to your UsersController at all. It should be going to devise's SessionsController.
In any case, this logout link works for me:
<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>
destroy_user_session_path shouldn't need the user id. It's only destroying the user in the session, so there is no need for an id.
UPDATE:
When you log out, what page are you returned to? If you haven't defined the SessionsController yourself and haven't overridden after_sign_out_path(), it should try to return to root_path. If you don't have root defined in your routes, it will return to "/". Can you confirm from your log file (e.g. development.log) exactly what happens when you click on the 'logout' link? Which actions are called and when/what SQL queries are called? Just when you click on the link, not when you later browse to look at users. Also, what does the devise_for line in your routes.rb look like? It's worth digging in to exactly what is happening when you only click on the logout link.
As far as I know the only thing inside devise which could delete a user from the database is RegistrationsController.destroy() which would be called if a DELETE request was sent to /users (with no id). It would find the currently-logged-in user from the session (which is why it doesn't need an id) and destroy them. In the standard devise views, it looks like this can only be called from devise/registrations/edit.html.erb and presumably you aren't doing this.
Did you customize/override the devise sessions controller?
If not your logout link should probably look like this:
<%= link_to " Logout", destroy_user_session_path, :class => "btn btn-danger icon-share-alt ",:style=>"font-family: FontAwesome;font-weight: bold;color:black", :method => :delete %>
Devise doesn't know what the :id2 param is.
Try replacing this:
destroy_user_session_path
with this:
destroy_session_path(:user_id => current_user.id)
And than in SessionsController destroy your session. This way you are destroying your user every time someone click on logout link.
Related
I am working on a rails 3 app, and i am trying to link_to a delete path. It seems pretty straightforward, and my routes look like this
namespace :admin do
resources :users, :except => :destroy do
...
delete :delete_contacts, :on => :collection
end
end
and my controller looks like this
class Admin::UsersController < ApplicationController
...
def delete_contacts
user = User.find(params[:user_id])
user.contacts.destroy_all
redirect_to edit_admin_user_path(current_user.id)
flash[:notice] = "Successfully deleted #{user.name} contacts"
end
end
and my current link_to looks like this
<%= link_to delete_contacts_admin_users_path(user_id: #user.id), method: :delete, data: { confirm: 'Are you sure you want to delete this users Contacts?' }, remote: true do %>
<h4 style="color: #BF3430;"><i class="material-icons">delete_forever</i> Delete Contacts</h4>
<% end %>
I have tried the routes both with and without the :on => :collection, and i seem to keep getting the same error:
ERROR
Started DELETE "/admin/users/955/delete_properties/" for 127.0.0.1 at 2018-01-17 17:58:34 -0800
Processing by Admin::UsersController#delete_properties as JS
Parameters: {"id"=>"955"}
But the link that is made, /admin/users/delete_properties/?user_id=955 , seems like just the link needed?
Does anyone see what I'm doing wrong here? this is pretty straightforward, I'm not sure why it's not behaving the way i'm thinking it should. Any help is much appreciated!
Personally, I think I would have done:
namespace :admin do
resources :users, :except => :destroy do
delete :delete_contacts, :on => :member
end
end
Which would give you:
delete_contacts_admin_user DELETE /admin/users/:id/delete_contacts(.:format) admin/users#delete_contacts
Then, you could do:
<%= link_to delete_contacts_admin_user_path(#user) ... %>
My thinking is that you're working with a particular user and so using a member path instead of a collection path, IMO, seems a little more natural.
Of course, you'd have to change your action slightly:
class Admin::UsersController < ApplicationController
...
def delete_contacts
user = User.find(params[:id])
user.contacts.destroy_all
redirect_to edit_admin_user_path(current_user.id)
flash[:notice] = "Successfully deleted #{user.name} contacts"
end
...
end
That edit_admin_user_path may be munged based on this, so you might have to look at the route for that, too (and possibly others, as well).
I have a checkbox (like a terms of use) that I need to be checked every time a user signs in.
I've seen some examples on adding a checkbox on the sign up page, adding a virtual attribute to the User model, etc.
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
%p
= f.label :username, 'Username'
= f.text_field :username
%p
= f.label :password
= f.password_field :password
%p
%span
= check_box_tag :terms_of_use
I have read the
= link_to 'Terms of Use', '#'
%p
= f.submit 'Sign in'
Here's my devise route:
devise_for :users, controllers: { sessions: 'sessions' }
And here's the custom controller:
class SessionsController < Devise::SessionsController
def create
if params[:terms_of_use]
super
else
# Not sure what to put here? Is this even the right track?
# Also, redirect the user back to the sign in page and let
# them know they must agree to the terms of use.
end
end
end
How would I go about requiring the checkbox to be selected every time a user signs in?
This blog post may help: http://hollandaiseparty.com/order-of-abstractcontrollercallbacks/
Adding a prepend_before_action should allow you to check for the terms_of_use and redirect if needed before allowing Devise to take over. Something like:
class SessionsController < Devise::SessionsController
prepend_before_action :check_terms_of_use, only: [:create]
def check_terms_of_use
unless params[:terms_of_use]
# Since it's before the session creation, root_path will take you back to login
redirect_to root_path
end
end
end
I'm trying to make it so only admins can add uses with devise. I've gotten it mostly working however now when I'm logged in as an admin and submit the sign up form it kicks me back with the error: You are already signed in.
I've tried to follow the instructions here: http://wiki.summercode.com/rails_authentication_with_devise_and_cancan but it doesn't seem to mention this situation.
Do I need to do further overriding in the editors_controller to allow this?
Here are my routes ("editors" is the name of my user model):
devise_for :admins, :skip => [:registrations]
as :admin do
get 'admin/editors' => 'editors#index', as: :admin_editors
get 'admin/editors/new' => 'editors#new', as: :new_editor
delete 'admin/editors/:id' => 'editors#destroy', as: :destroy_editor
end
devise_for :editors, :skip => [:registrations], :controllers => { :registrations => "editors" }
and my editors_controller in "app/controllers/"
class EditorsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def dashboard
render "editors/dashboard.html.haml"
end
def index
#editors = Editor.all
respond_to do |format|
format.html
end
end
private
def check_permissions
authorize! :create, resource
end
end
EDIT
I noticed this Processing by Devise::RegistrationsController#create as HTML in the logs when I submit the form. I had suspected that perhaps the skip_before_filter :require_no_authentication wasn't being called, but assumed that because the EditorsController was inheriting from RegistrationController that before filter would work properly. Is that not the case?
You'll want to implement your own create method on EditorsController instead of inheriting that action from Devise::RegistrationsController. As you're seeing, the method in Devise::RegistrationsController will first check to see if you're already logged in and kick you back if you are. If you're not logged in it will create a User account and then log you in as that user.
You're trying to get around that problem with skip_before_filter :require_no_authentication, but it's likely that your form is POSTing to /editors instead of /admin/editors. So, you'll need to add a route that allows you to get to create on the EditorsController :
as :admin do
post 'admin/editors' => 'editors#create'
# your other :admin routes here
end
Then you'd want to implement a scaled down version of create. You probably want something kind of like this :
class EditorsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
if resource.save
redirect_to admin_editors_path
else
clean_up_passwords resource
respond_with resource
end
end
# your other methods here
end
You'll also want to make sure that the admin/editors/new template is pointing the form to the correct route ('admin/editors').
None of the googleable solutions worked when I tried them. This works
What I did was create a new action in the controller and a new route for it, and connect the links on my views that normally connect to create to now call my route and action.
But that wasn't enough. Because Devise is listening and will grab any add you try to do and validate it through it's own code. So instead I just add the new user record with a sql insert.
Add this route
post 'savenew', to: 'users#savenew'
Add this action to the user controller:
def savenew
rawsql = "insert into users (email, created_at,updated_at) values ('#{user_params[:email]}',now(), now())"
sql = rawsql
ActiveRecord::Base.connection.execute(sql)
redirect_to action: 'index''
end
View: new.html.erb
change the form_for so that submit will go to the new route and action, not the default Rails one.
<%= form_for User, :url => {:action => "savenew"} do |f| %>
Using Rails 4.2.6 here (my model is User instead of Editor). The following solution bypasses (I think) any devise actions that may interfere with new User creation by the admin:
Add this action to the Users controller:
def savenew
User.create_new_user(user_params)
redirect_to action: 'index'
end
Add this private method to the Users controller if it does not exist:
private
def user_params
params.require(:user).permit(:email, :password,
:password_confirmation)
end
Add this to config/routes.rb:
match '/savenew', to: 'users#savenew', via: :post
Add this class method to the User model:
def self.create_new_user(params)
#user = User.create!(params)
end
I don't have a separate Admin class in my application. Instead, I defined an admin attribute for Users and check for it with a :validate_admin before_action filter in the UsersController.
I wanted to be able to create a new user from the :index view, so I added a button:
<%= button_to 'New User', '/new_user', class: 'btn btn-primary',
method: :get %>
You might have to tweak the above solution if you have any after_create actions in the User model (e.g. sending a welcome email).
My User.rb:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,:confirmable,:token_authenticatable,
:recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:name]
My routes:
devise_for :users, :controllers => { :sessions => "sessions", :confirmations => "confirmations", :passwords => "passwords", :registrations => "registrations" }
My ConfirmationsController is a standard controller but with different redirect.
I have link on my email like:
/users/confirmation?confirmation_token=167bad44a15e02b0bd570b51e1bf927b88368d8855d92b9833a24017a2bad4be
In database user has
confirmation_token:167bad44a15e02b0bd570b51e1bf927b88368d8855d92b9833a24017a2bad4be
But when i click on that link i only see page with:
Resend confirmation instructions
Confirmation token is invalid
What i dont do - what else i have to set.
CONFIRMATIONCONTROLLER:
def resource_params
params.require(:user).permit(:confirmation_token)
end
private :resource_params
def show
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
if resource.errors.empty?
set_flash_message(:notice, :confirmed) if is_navigational_format?
sign_in(resource_name, resource)
session['new_user'] = true
respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
else
respond_with_navigational(resource.errors, :status => :unprocessable_entity){ render :new }
end
end
protected
# The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(resource_name)
new_registration_path(resource_name)
end
I say "standard controller" because when i remove it and do not use custom controller problem is that same.
Which version of devise are you using? If you're on 3.1.0 or higher, this behavior is expected:
CHANGELOG.md
The tokens that are stored in the database are not supposed to match the tokens that you send in the confirmation e-mails. See devise/lib/devise/models/confirmable.rb, which now contains the following:
def confirm_by_token(confirmation_token)
original_token = confirmation_token
confirmation_token = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
As you can see, the token that you pass in via query string params is consumed by the Devise.token_generator, and the result of that operation is what's compared with the token in the database to discover the user record.
It looks like it's temporarily possible (in 3.1 but not 3.2) to turn this off by setting
config.allow_insecure_token_lookup = true
in your devise initializer. But the default behavior has been changed to make devise more secure. See this blog post for a complete rundown of the security improvements in devise 3.1, including this change.
You can use the solution below (setting config.allow_insecure_token_lookup = true) but, according to the Devise changelog, this will be only available temporarily.
The problem likely arose because you ran the devise generator to dump all of their views into yours back before they made these changes. Then you updated your Devise gem and all the back end stuff changed but your views didn't. Now all your devise-related views and mailers are out of date and won't work with the new token styles.
You can see the new emails at: https://github.com/plataformatec/devise/tree/master/app/views/devise/mailer. The major change is changing this line:
<p><%= link_to 'Confirm my account', confirmation_url(#resource, :confirmation_token => #resource.confirmation_token) %></p>
to
<p><%= link_to 'Confirm my account', confirmation_url(#resource, confirmation_token: #token) %></p>
The main difference is #resource.confirmation_token becomes just #token.
I change #resource.confirmation_token to #token then it works.
I'm working through the Railscast on implementing Devise and OmniAuth (along with the Devise documentation) -- currently, I've got a site going where visitors can sign up using their facebook accounts or by filling out a form.
I'm running into trouble when users that sign up via OmniAuth try to edit their profiles, though. Devise looks for the user's current password when they submit changes to their profiles, but those that logged in with facebook don't know their passwords (they're set automatically in the user model):
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(first_name:auth.extra.raw_info.first_name,
last_name:auth.extra.raw_info.last_name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
When a user edits his information, the app should not require password confirmation if he set up his account through OmniAuth. The tutorial suggests that the handy password_required? method will help me achieve this outcome. Specifically, adding this method to the user model means that it should only return true if the user didn't sign up through OmniAuth (the provider attribute would be nil in that case):
def password_required?
super && provider.blank?
end
Thus, a piece of code like:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<%= render :partial => "essential_user_info_inputs", :locals => { :f => f } %>
<%= render :partial => "inessential_user_info_inputs", :locals => { :f => f } %>
<% if f.object.password_required? %>
<%= render :partial => "password_inputs", :locals => { :f => f } %>
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %>
<% end %>
<%= f.submit "Update" %>
<% end %>
would theoretically only display password inputs when needed. It also suggests that Devise has built in logic saying that OmniAuth users don't need to use passwords to edit their accounts. I have no idea if this is true, but the tutorial kind of makes it look like that. But when an OmniAuth user tries to edit his account, I get "Current password can't be blank." Same thing with non-OmniAuth users (this makes sense, since the password fields don't show up on those users' edit pages either).
Some poking around confirms that the password_required? method is returning false, both when the user signed up through OmniAuth and through the site's regular user signup. Even when I change it to simply run the superclass method, it returns false.
Any ideas of what's going on with the password_required method? I can't find anything about it anywhere, but I feel like that's what's tripping things up right now.
Update:
This is now working, but not using the method outlined in the Railscast, which relies on requires_password? method, a topic that I still know nothing about. Instead, I implemented the solution outlined here, as suggested here. So I am now only requiring passwords to update non-OmniAuth accounts with the code:
class Users::RegistrationsController < Devise::RegistrationsController
def update
#user = User.find(current_user.id)
email_changed = #user.email != params[:user][:email]
is_facebook_account = !#user.provider.blank?
successfully_updated = if !is_facebook_account
#user.update_with_password(params[:user])
else
#user.update_without_password(params[:user])
end
if successfully_updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
end
The easiest way is to overwrite the update_resource method in your RegistrationsController. This is advised by devise in their own implementation of the controller:
# By default we want to require a password checks on update.
# You can overwrite this method in your own RegistrationsController.
def update_resource(resource, params)
resource.update_with_password(params)
end
So the solution is to overwrite this method in your own controller like this:
class Users::RegistrationsController < Devise::RegistrationsController
# Overwrite update_resource to let users to update their user without giving their password
def update_resource(resource, params)
if current_user.provider == "facebook"
params.delete("current_password")
resource.update_without_password(params)
else
resource.update_with_password(params)
end
end
end
I've added an update to the link below that includes my solution to the Devise/ OmniAuth change user profile/password issue and collected some helpful links:
stackoverflow - Allowing users to edit accounts without saving passwords in devise
I saw this used somewhere.
def update
params[:user].delete(:current_password)
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
if current_user.update_without_password(params[:user])
redirect_to somewhere_wicked_path, notice => "You rock"
else
render 'edit', :alert => 'you roll'
end
end
use something like this in your update method in your controller. Pretty sure that method is in Devise too.