Devise restrict sign up to Admin - ruby-on-rails

I am working on a Rails App that uses Devise as the authentication module, however I want to customize it so that CanCan will only permit Administrators to create a new user. I am having a hard time understanding how to customize the controller for Devise so that this can be done. Any help would be appreciated.

You don't need to customize anything :D
Remove :registerable from your Devise model.
Create your Users CRUD* (just scaffold users)
Use CanCan for user permission on your Users
Controller.
*Check Devise's wiki on how to create a Users CRUD, there is a routing trick you need to do

You can create a "User" controller that will manage users and then simply set permissions for it. So in your new User controller you can have something like:
class UserController < ApplicationController
load_and_authorize_resource
def index
end
def new
end
def show
end
def create
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
def edit
end
def update
params[:user].delete(:password) if params[:user][:password].blank?
params[:user].delete(:password_confirmation) if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
if #user.update_attributes(params[:user])
flash[:notice] = "Successfully updated User."
redirect_to user_index_path
else
render :action => 'edit'
end
end
def destroy
if #user.destroy
flash[:notice] = "Successfully deleted User."
redirect_to user_index_path
end
end
end
Assuming you have administrators set to:
can :manage, :all
You should be good to go.
In your routes file you'll need to set up your routes:
resources :user, :controller => "user"
Hope this helps!

Related

Ruby on Rails - Unable to create a new profile for each user

I'm building an events app with users who will each have a personal profile. I've set up a few users for the site but when I try and create and/or edit a profile for each user it refers me back to a flash message "That profile doesn't belong to you!" which is in reference to my first user profile which was set up and works fine.
I'm using Devise gem for initial set up but have built out from their with my own user controller. Here's the code from that controller -
class UsersController < ApplicationController
before_action :authenticate_user!
before_action :set_user
before_action :owned_profile, only: [:edit, :update]
def new
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
end
def edit
#user = current_user #User.find_by(params[:id])
end
def update
#user = User.find_by(params[:id])
if #user.update(user_params)
redirect_to user_path, notice: "Profile successfully updated!"
else
render 'edit'
end
end
private
def user_params
params.require(:user).
permit(:name, :username, :biography, :email, :url)
end
def owned_profile
unless current_user == #user
flash[:alert] = "That profile doesn't belong to you!"
redirect_to root_path
end
end
def set_user
#user = User.find_by(params[:id])
end
end
Any assistance would be appreciated.
I would create an admin. An easy way to do this is to add a column to your users table called admin and make it a boolean. Migrate the db.
Then check to whether a user is an admin before running the owned_profile method. In that method, change: unless current_user == #user to
unless current_user == #user || current_user.admin
Then set yourself as an admin in the console, save and then freely add profiles without that callback running.
If the issue is that Users are not able to edit their own profile, then I believe it is caused by the use of find_by within set_user:
#user = User.find_by(params[:id])
Should be:
#user = User.find(params[:id])
If you truly wanted to use find_by you could do:
#user = User.find_by_id(params[:id])
Or
#user = User.find_by(id: params[:id])
Find_by used as the 2 examples above will not throw an error if a User is not found, while find will.
Sidenote: You can remove the #user assignment within the show action.
You can do it by this way.
When user signing up, automatically creates profile. Good point of this ID of user and profile tables will be the same.
rails g model profile first_name last_name email
rails g migration add_user_id_to_profiles user_id:integer
Profile.rb
belongs_to :user
User.rb
has_one :profile, dependent: :destroy
before_create :set_profile
def set_profile
build_profile(id: self.id, user_id: self.id, email: self.email)
end
GoodLuck.

Rails Devise two edit routes one with password and another without password

I want to allow Users to have two edit pages, one which is the default Devise page that requires a password and the other edit page which allows them to edit some parts of their profile without a password.
I have seen their guide pages like this which allows them to edit their profile without providing a password. However I want to have both options available to the user instead of just one.
How would I go about doing this?
My Attempt
I have tried to create my own update route in the Users controller which will solve the problem but this creates a problem when a User resets their password as it gets routed to the User#update which will cause an error as there is no User available during the password reset in Devise.
class UsersController < ApplicationController
before_action :authenticate_user!, :only => [:crop] #:edit , :update
def show
#user = User.find(params[:id])
authorize #user
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update(user_params)
flash[:success] = "You have successfully updated your profile!"
redirect_to user_path(#user)
else
render :edit
end
end
def crop
#user = User.find(params[:id])
authorize #user
end
def index
end
private
def user_params
params.require(:user).permit(:poster_image_crop_x, :poster_image_crop_y, :poster_image_crop_w, :poster_image_crop_h)
end
end
Routes
Rails.application.routes.draw do
resources :users,only: [:show] do
member do
get :crop
end
end
devise_for :users, :controllers => { :omniauth_callbacks => "callbacks",:registrations => :registrations,:passwords => "passwords" }
as :user do
get "/login" => "devise/sessions#new"
get "/register" => "devise/registrations#new"
get "/edit" => "devise/registrations#edit"
delete "/logout" => "devise/sessions#destroy"
end
The code by Devise is suggesting to create your own controller. They probably always require password to be passed if it comes from an action in the UsersController. So you should create a seperate controller, let's call it ProfilesController, this controller is like your normal controller although it does not update a Profile model, but the User model directly... nothing special actually, just check authorization and let the User update any field you'd like directly on the User mode, do not forget to authorize the fields you'd wish to let the user update:
class ProfilesController < ApplicationController
def index
end
....... more code
def update
#user = User.find(params[:id])
authorize #user
if #user.update(user_params)
flash[:success] = "You have successfully updated your profile!"
redirect_to user_path(#user)
else
render :edit
end
end
....... more code
private
def user_params
params.require(:user).permit(:poster_image_crop_x, :poster_image_crop_y, :poster_image_crop_w, :poster_image_crop_h)
end
end
And add resources :profiles to your routes file.

Rails Actions for Form Posting

I am using Rails 4 and moved from CakePHP.
I have a User Model and to create a new record it uses two Actions - New and Create.
Now when i want to over ride the default for my app. i would like the users to go to Signup action to create a new user. Now when i have a Server side validation and it fails i am posting the form to lets say "create" action the user is shown in the url
'app.com/user/create' instead of 'app.com/user/signup'
Is there any way to keep the user in the same action instead of have multiple action just to display form and save the form?
# GET /users/new
def new
#user = User.new
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to #user, notice: 'User was successfully created.'
else
render :new
end
end
You should simply add a redirect in your create action when the user creation fails.
redirect_to :back, #user
I would not recommend using :back all the time but this is going to be helpful for now as by understanding the scenario you have mentioned.
By default, action new just initialize model with-or-without params. Action create save model to database. app.com/user/create is not RESTful and "Rails Way".
users_path #=> app.com/users
new_user_path #=> app.com/users/new
user_path(:id) #=> app.com/user/:id
edit_user_path(:id) #=> app.com/user/:id/edit
# and so on
In controllers you can define redirections for every action. For example:
def create
if #user.save
redirect_to user_path(#user)
else
redirect_to :back # return to previous page
end
end
More information about routing here: http://guides.rubyonrails.org/routing.html
I would stick with rails conventions but you should be able to do this if you really wanted
Routes.rb
get 'signup', to: 'users#signup'
post 'signup', to: 'users#signup'
Controller
class UsersController < ApplicationController
def signup
if request.get?
#user = User.new
elsif request.post?
#user = User.new(user_params)
if #user.save
redirect_to root_url, notice: 'Signed In'
else
#should just render signup as it's signup action
end
end
end
end

RoR : Are devise controllers generated automatically or should we write on our own?

I am new to ruby on rails. I am trying to use devise gem for authentication. i am going through the tutorials in github. I have created devise views using rails generate devise:views. But i dont find any controllers. Do i need to create on my own or is there any command to generate controllers for it?
Plz help
Devise already creates the required controllers for you behind the scenes. Few of these controllers are: RegistrationController, SessionController.
To customize or override any controller, say RegistrationController; you can do following (snippet from my one application):
class RegistrationsController < Devise::RegistrationsController
before_filter :admin_user, :only => [:destroy]
def new
super
end
def create
if simple_captcha_valid? #verifying user registration by captcha
super
else
build_resource
clean_up_passwords(resource)
flash.now[:alert] = "There was an error with the captcha code below. Please re-enter the code."
render :new
end
end
def update
# required for settings form to submit when password is left blank
if params[:user][:password].blank?
params[:user].delete("password")
params[:user].delete("password_confirmation")
end
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to rooth_path
end
end
For more you can follow: https://github.com/plataformatec/devise#configuring-controllers

How to permit creator to destroy his own record with Devise on Rails3

When the user is logged in, only the user who create the record can destroy his own record.
What should I add to the code below??
def destroy
#topic = Topic.find(params[:id])
#topic.destroy
flash[:notice] = "topic deleted!"
end
What you are looking for is not really devise but a authorization solution like CanCan.
Devise can only authenticate users and verify that they are logged in and active. What you need is a way to determine if the user has the right to delete this topic or not.
You can of course roll your own like this:
def destroy
#topic = Topic.find(params[:id])
if #topic.user_id == current_user.id
#topic.destroy
flash[:notice] = "topic deleted!"
else
flash[:error] = "not allowed"
end
end
(The code assumes you have a belongs_to :creator, :class_name => :user association set up in your Topic.. But you get the idea).
But using something like CanCan will make your life a whole lot easier and would reduce the code to something like this:
def destroy
#topic = Topic.find(params[:id])
authorize! :destroy, #topic
#topic.destroy
flash[:notice] = "topic deleted!"
end
With your ability file (See defining abilities) set up like this:
can :manage, Topic, :owner_id => user.id

Resources