Admin user administration with Devise - ruby-on-rails

I am trying out Devise for the first time. One of the things that I wanted to do is provide an interface for Admin users to create, find and edit users. Here's where I may have gone wrong.
I created a PeopleController class which inherits from ApplicationController that lists people and provides methods and views for creating and updating users. Everything works fine with one exception. When the admin user updates their own record, the session is cleared and they have to login again after saving it.
In this application I'm not using the registerable module. Only an admin user can create new users. What is the right way in devise to provide user management tools. Creating my own controller seems to have been the wrong path to take.
Thanks in advance for your help.

Thank you very much for the help. This is essentially exactly what I am doing. I discovered a clue that helped me solve the problem of the user's session being cleared when they edit their own record in this wiki:
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password
This is the line I needed:
sign_in resource_name, resource, :bypass => true
This method is located in Devise::Controllers::Helpers so I did this in my controller.
class PeopleController < ApplicationController
include Devise::Controllers::Helpers
Then in my update method I call it only if the current_user.id equals the id that is being edited:
def update
#person = User.find(params[:id])
if #person.update_attributes(params[:user])
sign_in #person, :bypass => true if current_user.id == #person.id
redirect_to person_path(#person), :notice => "Successfully updated user."
else
render :action => 'edit'
end
end
Now if the current user edits their own record, the session is restored after it is saved.
Thanks again for your responses.

This is how I manage users in one of my apps. I have only one User class generated with
rails g devise User
to which I added a role column with this migration:
class AddRoleToUser < ActiveRecord::Migration
def change
add_column :users, :role, :string, :default => "client"
end
end
and my User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
def admin?
self.role == "admin"
end
end
Then to create new users all you would have to do is provide a custom method in a controller (maybe even subclass Devise::RegistrationsController) like this:
# some_controller.rb
def custom_create_user
if current_user.admin?
User.create(:email => params[:email], password => params[:password])
redirect_to(some_path, :notice => 'sucessfully updated user.')
else
redirect_to(some_other_path, :notice => 'You are not authorized to do this.')
end
end

Related

Skip confirmation mail in devise

I want to skip the sending of confirmation mail from devise in my rails 4 application. I tried so many things, but none of them worked.
In my user.rb
before_create :my_method
def my_method
self.skip_confirmation
end
I tried this also
before_create :my_method
def my_method
!confirmed
end
I tried removing :confirmable from user.rb but none of them seems to work. Thanks in advance :)
Note: If you want to skip only on certain places
If you are calling User.create before skip_confirmation!, you need to call User.new and user.save.
#user = User.new(:first_name => "John", :last_name => "Doe")
#user.skip_confirmation!
#user.confirm!
#user.save
A/C to your way
before_create :my_method
def my_method
self.skip_confirmation!
self.confirm!
end
excluding the term confirmable in devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable should skip in a whole
Try this
#user = User.new(:email => 'email#example.com', :password => 'password')
#user.skip_confirmation_notification!
#user.save
skip_confirmation_notification! generate a confirmation token but does not send confirmation email.
Try adding this to user.rb
protected
def confirmation_required?
false
end
For every User instance:
class User < ApplicationRecord
after_initialize do |user|
user.skip_confirmation_notification!
end
end
For some particular instance:
john = User.new
john.skip_confirmation_notification!
P.S. The answers above are about disabling the confirmation process entirely, whereas the author asked about disabling just sending an email.

Couldn't find User without an ID - Ruby 2.1.4 / Rails 4.1.6

I am facing an error while trying to link the :username in my User table and my Room table. I made a custom auth with devise and added :username.
I would like the username to be the link between the User table from devise and my Room table.
I am trying to build this app to recreate a kind of airbnb but mainly as an exercise as I started programming in ruby few months ago.
I get the error:
ActiveRecord::RecordNotFound in RoomsController#new
Couldn't find User without an ID
line #19 #room.username = User.find(params[:username])
Thank you very much for your help. I am stuck in here for hours now :-(
rooms_controller
def new
#room = Room.new
#room.username = User.find(params[:username]) #error seems to come from here
end
routes.rb
Rails.application.routes.draw do
devise_for :users
get "home/info"
root :to => "home#info"
resources :rooms
resources :users do
resources :rooms
end
room.rb
class Room < ActiveRecord::Base
mount_uploader :photo, PictureUploader
belongs_to :user
validates_presence_of :username, :location, :description, :capacity, :price_day, :photo
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :username
validates_uniqueness_of :username
has_many :rooms
end
It should be something like this
def new
#room = Room.new
#room.username = User.find_by_username(params[:username])
end
If you just use .find() it expects the id of the user. Also see http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders
There is a logic error in that you are saving #room.username to the User Object. You should be setting #room.user = User.find_by(...) OR #room.user_id = User.find_by(...).id
Active record will automagically make a method for you that will be #room.user.username if you want to get the username.
Now, here are the ways to find a user.
#room = Room.new #Then either of the following
#room.user = User.find_by(username: params[:username]) #Returns only one value
#room.user = User.find_by_username(params[:username]) #Returns only one value
#room.user = User.where(username: params[:username]) #Returns all users which meet condition.
As already mentioned in the answers, User.find() takes an ID. One thing to know is that all methods that start with .find for active record return a single record even if many meet the condition.
If you are having any problems still, then show us your Database Schema, and we can help further.
I found a solution. The Room is created with the the right :username and nothing is seen by the user.
In my Rooms controller, I kept
def new
#room = Room.new end
And I added this line in the "def create" part :
def create
#room = Room.new(room_params)
#room.username = current_user.username
Thank you for your help, this help me to understand better the relations in rails.
Have a nice day !

Restrict Login with Google OAuth2.0 and Devise to Specific Whitelist Table using Ruby

So I was trying to use omniauth2 to check if the email had the right #domain.com but I think using a database table will allow more functionality as well as being more secure and such.
My previous question: Restrict Login with Google OAuth2.0 to Specific Whitelisted Domain Name on Ruby
I think I want to use a database table to check the email that google authenticated against a whitelist of emails, is there anyway to do this with devise and omniauth2? That way I can say only certain users are authorized after they get authenticated with Google. I have most info listed on my previous question but if there is some more info I can give let me know.
Thanks.
EDIT: Not sure how much this helps but here is a question similar; however, I am still using google and omniauth Whitelisting with devise
EDIT: I think the above "Whitelisting with devise" is pretty close to the answer, but there are still a few kinks to work out. I'm not sure how to start implementing everything I'm pretty new to ruby in particular.
Here is my route:
devise_for :user, :controllers => { :omniauth_callbacks => "user/omniauth_callbacks" }
And that controller:
class User::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def google_oauth2
#user = User.find_for_google_oauth2(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.google_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
If I understand the Whitelisting with devise correctly I have to create another controller in between and use that to check the email? Any help would be greatly appreciated.
EDIT: Here is my user.rb I think this might hold the answer possibly?:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :omniauthable,
:recoverable, :rememberable, :trackable, :validatable, :omniauth_providers => [:google_oauth2]
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :provider, :uid, :avatar
def self.find_for_google_oauth2(access_token, signed_in_resource=nil)
data = access_token.info
user = User.where(:email => data["email"]).first
unless user
user = User.create(name: data["name"],
email: data["email"],
password: Devise.friendly_token[0,20]
)
end
user
end
end
I'd add a validation to the User Model so, no user would be created if the email that comes from oauth is not form a certain domain:
validates :email,
presence: true,
uniqueness: true,
format: {
message: 'domain must be example.com',
with: /\A[\w+-.]+#example.com\z/i
}

Where is user.admin? defined in rails-devise-pundit starter app?

I used RailsApps rails-composer to create a rails-devise-pundit starter application. I am still a little new to ruby on rails and newer to devise, pundit and rails 4.
I was looking at the code to learn how it works. There are many places in controllers and in policy classes where user.admin? is called. But I can't find the admin? method. I would expect it to be in the User model but it isn't there. Here's the user class:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
end
Used in part of users_controller.rb:
def show
#user = User.find(params[:id])
unless current_user.admin?
unless #user == current_user
redirect_to root_path, :alert => "Access denied."
end
end
end
Does pundit or devise create this method somehow? I've seen it used in the pundit documentation but it just uses it as an example. It doesn't say the method needs to be created or that it handles it. Is it somehow using the role enum which has :admin as a possible value? If anyone can explain how this works, I'd appreciate it.
I'm planning to add in use of the rolify gem soon to handle roles instead of the role enum in the user class. Maybe I'll want to make role names that are completely different for some reason. I want to make sure I understand how to keep everything working. Thanks.
Roles are defined in the app/models/User.rb file (the User model).
class User < ActiveRecord::Base
.
.
.
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
end
The application uses the ActiveRecord enum method to manage roles. ActiveRecord provides convenient methods to query the role attribute:
user.admin! # sets the role to "admin"
user.admin? # => true
user.role # => "admin"
See documentation for ActiveRecord::Enum for details. The ActiveRecord enum method is new in Rails 4.1.
I've updated the README for the rails-devise-pundit application to include this information. It's also covered in my Rails Pundit Tutorial.
It is an attribute of the User model. Same as first_name or last_name, there is a field called admin which is a boolean.

How to associate a Devise User with another existing model?

I am very new to Ruby on Rails and have setup Devise for authentication. I have an existing model that I created prior to adding Devise. That model is called Article. I believe I have done everything I need to do in order to use the association=(associate) method that "assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object’s foreign key to the same value" which is exactly what I need to do.
Here is Devise's User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_one :article
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Here is my Article model:
class Article < ActiveRecord::Base
belongs_to :user
validates :name, presence: true, length: { minimum: 5 }
end
Here is my migration:
class AddUserRefToArticles < ActiveRecord::Migration
def change
add_reference :articles, :user, index: true
end
end
Here is my create method from my articles_controller.rb:
def create
#article.user = current_user
#article = Article.new(post_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
And here is what happens when my controller runs:
NoMethodError in ArticlesController#create
undefined method `user=' for nil:NilClass
The highlighted code is #article.user = current_user. I was at least glad to know that I wrote that line of code similar to the popular answer in the Devise how to associate current user to post? question that I saw on here before posting this one.
I know I'm making a rookie mistake. What is it?
A new User instance needs to be assigned to #article before you can access any of the instance's attributes/associations. Try the following:
#article = Article.new(post_params) # Assign first
#article.user = current_user # Then access attributes/associations
The code posted in the question yields a nil:NilClassexception because the user association is being invoked on #article, which is nil because nothing has yet been assigned to it.

Resources