Devise doesn't control the exact id to edit a user - ruby-on-rails

I installed devise in a rails app. If a user is log in he could access to every other users edit pages.
For example, i'm user_id 2, i can edit profile of user 1/3/4/5.....when i modify params manually in the route.
Here my App Controller :
class ApplicationController < ActionController::Base
protect_from_forgery
before_action :authenticate_user!
before_action :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
# For additional fields in app/views/devise/registrations/new.html.erb
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :company, :position, :office_phone, :mobile_phone, :address, :description, :radius, :photo_company_logo, :photo_presentation, photos_projet_1: [], photos_projet_2: [], photos_projet_3: [], photos_projet_4: []])
# For additional in app/views/devise/registrations/edit.html.erb
devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name, :company, :position, :office_phone, :mobile_phone, :address, :description, :radius, :photo_company_logo, :photo_presentation, photos_projet_1: [], photos_projet_2: [], photos_projet_3: [], photos_projet_4: []])
end
end
Here my User Controller :
class UsersController < ApplicationController
skip_before_action :authenticate_user!, only: [:index, :show]
before_action :set_user, only: [:show, :edit, :update]
def index
#client = Client.new
#users = User.all
#users = User.where.not(latitude: nil, longitude: nil)
#hash = Gmaps4rails.build_markers(#users) do |user, marker|
marker.lat user.latitude
marker.lng user.longitude
end
end
def show
#client = Client.new
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
#user.save
redirect_to users_path
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
#user.update(user_params)
redirect_to user_path(#user)
end
private
def user_params
params.require(:user).permit(:company, :first_name, :last_name, :position, :mobile_phone, :office_phone, :email, :address, :description, :radius, :nettoyage_toiture, :photo_company_logo, :photo_presentation, photos_projet_1: [], photos_projet_2: [], photos_projet_3: [], photos_projet_4: [])
end
def set_user
#user = User.find(params[:id])
end
end
Here mu User model :
class User < ApplicationRecord
has_attachment :photo_presentation
has_attachment :photo_company_logo
has_many :projects, dependent: :nullify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
#geocoder for google maps
geocoded_by :address
after_validation :geocode, if: :address_changed?
end
Here my routes :
Rails.application.routes.draw do
ActiveAdmin.routes(self)
devise_for :users
root to: 'pages#home'
resources :users do
resources :projects
end
resources :clients, only: [:new, :create, :show]
mount Attachinary::Engine => "/attachinary"
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
Thanks a lot!

Devise is an authentication library. You have authorization problem. So, you should use something for the authorization. I use CanCan. With it, you can define privileges like this:
can :edit, User, id: current_user.id
If you don't want to learn another library, you can always do ghetto-authorization in your controllers.
class UsersController
before_action :can_edit_only_self, only: [:edit, :update, :destroy]
private
def can_edit_only_self
redirect_to root_path unless params[:id] == current_user.id
end
end
* authentication - I know who you are
* authorization - I know what you are allowed to do

The problem is this:
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
#user.update(user_params)
redirect_to user_path(#user)
end
If you don't want to users to be able to edit other users then you don't need these 2 methods. You can also change your route file to:
resources :users, except: [:edit, :update] do
resources :projects
end
Update
If you make the previous edits and type rake routes in the terminal, you should see that Devise provides a controller action for editing users. It should be users/edit without the :id param. In the Devise::RegistrationsController, the edit method should just use the current_user helper method for editing the currently logged in user's account information.
If you do want admin user's to be able to edit other users then you'll want to read about one of the following Gems
Can Can
Pundit
With these gems you can define which user "roles" are allowed to edit other users based on their roles. You can also use these gems to give permission to create, read, update, delete other resources.

Related

Rails 7. Devise error validation: Email can't be blank Password can't be blank

I get an error of validation when try to register a new user.
Email can't be blank, Password can't be blank
I create custom devise controllers. I have the next code in registrations' controller:
class Users::RegistrationsController < Devise::RegistrationsController
include ApplicationHelper
before_action :configure_sign_up_params, only: [:create]
before_action :check_existing_user, only: [:new]
def new
super
end
def create
super
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :email, :password, :password_confirmation])
end
def check_existing_user
#user = User.last
if #user
redirect_to sign_in_path, danger: 'User alredy exists. Please sign in.'
else
render :new
end
end
end
And I left permited parameters for devise in applocation controller:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
end
end
My routes:
devise_for :users, controllers: { registrations: "users/registrations" }
devise_scope :user do
get 'sign_in', to: 'devise/sessions#new'
get 'sign_up', to: 'users/registrations#new'
get 'sign_out', to: 'devise/sessions#destroy'
resources :users, only: [:show]
end
and User model:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
Console log doesn't return some errors. I fill form right. Why do I get it?

Can't assign created_by as current_user. Database is saving as nil. Rails

I created a userstamps method to know which user created and edited a data. I have this table Illustrator that I am trying to assign created_by to the current_user id. But when it saves, created_by is nil and don't show any error. When I put raise right before illustrator.save, the #illustrator.created_by is exactly the current_user.id but when I check the value in db, is nil.
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:custom_authenticatable
belongs_to :account
belongs_to :role
cattr_accessor :current_user
validates :email, presence: true, uniqueness: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password,
length: { minimum: 6 },
if: -> { new_record? || !password.nil? }
enum gen: { masculino: 1, feminino: 2 }
private
#apenas usuário com role de admim pode logar.
def valid_for_custom_authentication?(password)
#self.role.name == 'admin'
self.role_id === 2
end
end
class ApplicationController < ActionController::Base
before_action :authenticate_user!
helper_method :current_user
private
def current_user=(user)
#current_user = user
end
end
class IllustratorsController < ApplicationController
before_action :authenticate_user!
def new
#illustrator = Illustrator.new
end
def create
#illustrator = Illustrator.new(illustrator_params)
#illustrator.created_by = current_user.id
if #illustrator.save
redirect_to illustrator_path(#illustrator), notice: 'O autor foi criado com sucesso.'
else
render :new
end
end
private
def illustrator_params
params.require(:illustrator).permit(:name, :display_name, :email, :phone, :status, :notes, :created_by)
end
end
Rails.application.routes.draw do
get 'payments/index'
devise_for :users
root to: 'pages#home'
resources :books
resources :authors
resources :publishers
resources :illustrators
resources :plans do
resources :accounts, only: %i[new create] do
end
end
resources :payments, only: %i[index]
resources :accounts, only: %i[index show edit update destroy] do
resources :users, only: %i[new create] do
resources :roles
end
end
resources :users, only: %i[edit update index show destroy]
#API routes
namespace :api do
namespace :v1 do
get 'default/:id', to: 'plans#change_plans'
get 'plans', to: 'plans#index'
get 'plans/id', to: 'plans#show'
get 'accounts', to: 'accounts#return'
post 'users', to: 'users#create'
#resources :users
post '/auth/login', to: 'authentication#login'
get '/*a', to: 'application#not_found'
post 'password/forgot', to: 'users#forgot_password'
end
end
end
If you are using devise, you dont need this part:
helper_method :current_user
private
def current_user=(user)
#current_user = user
end
Where did you even find it? Remove it from your codebase!
With devise you don't need any getter or setter methods.
It can possibly be the reason it's not working for you.
If in application_controller you have the before_action :authenticate_user!, current_user method should work right out of the box!
As well you don't need to add the before_action :authenticate_user! again here
class IllustratorsController < ApplicationController
before_action :authenticate_user!
, because you already made it application-wide in application_controller.
Your create method is good. Try removing the abundancies and it should all work.
You have just setter method in your controller where you are setting current_user
def current_user=(user)
#current_user = user
end
You need to add getter method as well like following so that you can use it in your controller
def current_user
#current_user
end
In ruby there is short cut for this attr_accessor :current_user Ref this

Trouble deploying pundit authorization in Rails 4.2.4 with devise

I am a junior developer working on my first web application for a customer, an electronic commerce portal using Rails 4.2.4, Devise and a pins scaffolding.
Devise is working great and anyone can signup and login and CRUD a pin.
Problem: Users cannot be given access to CUD as the pins contain live customer products that are for sale.
I am therfore trying to implement pundit so that users are purely RESTRICTED to read only and CANNOT create, update or destroy pins, Only the business owner can do so in an Admin capacity.
Currently, I am getting the error "Pundit::NotDefinedError in PinsController#new
1.) How can I add myself as the admin(is this through a migration?)
2) How can I get Pundit working.
class PinsController < ApplicationController
before_action :set_pin, only: [:show, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
if params[:search].present? && !params[:search].nil?
#pins = Pin.where("description LIKE ?", "%#{params[:search]}%").paginate(:page => params[:page], :per_page => 15)
else
#pins = Pin.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 15)
end
end
def show
end
def new
#pin = current_user.pins.build
authorize #pins
end
def edit
end
def create
#pin = current_user.pins.build(pin_params)
if #pin.save
redirect_to #pin, notice: 'Pin was successfully created.'
else
render :new
end
end
def update
if #pin.update(pin_params)
redirect_to #pin, notice: 'Pin was successfully updated.'
else
render :edit
end
end
def destroy
#pin.destroy
redirect_to pins_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_pin
#pin = Pin.find_by(id: params[:id])
end
def correct_user
#pin = current_user.pins.find_by(id: params[:id])
redirect_to pins_path, notice: "Not authorized to edit this pin" if #pin.nil?
end
# Never trust parameters from the scary internet, only allow the white list through.
def pin_params
params.require(:pin).permit(:description, :image)
end
end
Blockquote
class ApplicationPolicy
attr_reader :user, :pin
def initialize(user, pin)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
#user = user
#pin = pin
end
def index?
true
end
def show?
scope.where(:id => record.id).exists?
end
def create?
user.admin?
end
def new?
user.admin?
end
def update?
user.admin?
end
def edit?
update?
end
def destroy?
user.admin?
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
scope
end
end
end
Blockquote
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :name
devise_parameter_sanitizer.for(:account_update) << :name
end
private
def user_not_authorized
flash[:warning] = "You are not authorized to perform this action."
redirect_to(request.referrer || root_path)
end
end
Blockquote
class Pin < ActiveRecord::Base
belongs_to :user
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png"]
validates :image, presence: true
validates :description, presence: true
end
Blockquote
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins, dependent: :destroy
validates :name, presence: true
end

Can only create one user profile - Rails 4.2.4

I'm using devise for my user auth and registration. I can register a user no problem. Im also using friendly. My issue is, I can only create one user profile.
The setup...
user.rb:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
validates :name, uniqueness: true, presence: true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile # each user should have just one profile
end
profile.rb:
class Profile < ActiveRecord::Base
belongs_to :user
end
profiles_controller.rb:
class ProfilesController < ApplicationController
before_action :authenticate_user!
before_action :only_current_user
def new
# form where a user can fill out their OWN profile
#user = User.friendly.find( params[:user_id] )
#profile = Profile.new
end
def create
#user = User.friendly.find( params[:user_id] )
#profile = #user.build_profile(profile_params)
if #profile.save # Not saving!!
flash[:success] = 'Profile Created!'
redirect_to user_path( params[:user_id] )
else
render action: :new # keeps rendering!
end
end
private
def profile_params
params.require(:profile).permit(:first_name, :last_name, :avatar, :job_title, :phone_number, :business_name)
end
end
Why is it that only one user can create a profile and not others? Is it has to do with the relations?
We use this setup with some of our apps - User -> Profile.
In short, you should build the profile at User creation. Then you can edit the profile as you need. Your problem of having a Profile.new method is very inefficient...
#app/models/user.rb
class User < ActiveRecord::Base
has_one :profile
before_create :build_profile #-> saves blank associated "Profile" object after user create
end
This will mean that each time a User is created, their corresponding Profile object is also appended to the db.
This will give you the capacity to edit the profile as required:
#config/routes.rb
resources :users, path_names: { edit: "profile", update: "profile" }, only: [:show, :edit, :update]
This will give you the opportunity to use the following:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update]
before_action :authorize, only: [:edit, :update]
def show
#user = User.find params[:id]
end
def edit
#user = current_user
end
def update
#user = current_user
#user.update user_params
end
private
def authorize
id = params[:id]
redirect_to user_show_path(id) if current_user.id != id #-> authorization
end
def user_params
params.require(:user).permit(:x, :y, :z, profile_attributes: [:homepage, :other, :profile, :attributes])
end
end
The view/form would be the following:
#app/views/users/edit.html.erb
<%= form_for #user do |f| %>
<%= f.fields_for :profile do |f| %>
<%= f.text_field :homepage %>
...
<% end %>
<%= f.submit %>
<% end %>
In regards your current setup:
def new
#profile = current_user.profile.new
end
def create
#profile = current_user.profile.new profile_params
if #profile.save
redirect_to user_path(params[:id]), notice: "Profile Created!"
else
render action: :new
end
end
private
def profile_params
params.require(:profile).permit(:x, :y, :z)
end
Not sure why you don't create the profile in the after_create event of the user. As soon the user is created - create an empty (but associated) profile.
class User
has_one :profile, dependent: :destroy
after_create {
build_profile unless profile
profile.save
}
end
class Profile
belongs_to :user, autosave: true
end
so then, in your controller you just need the update method.
def update
if current_user.profile.update_attributes(user_params)
flash_it :success
return redirect_to edit_user_profile_path
else
flash_it :error
render :edit
end
end

Can I edit all users as admin in devise with cancan

I have set up users with devise and each user can select a role. What I am trying to do is allow admins to be able to edit any user on the site if they have role admin. I currently have a UsersController setup like this:
class UsersController < ApplicationController
before_filter :authenticate_user!, only: [:index, :new, :edit, :update, :destroy]
skip_before_filter
def index
#users = User.order('created_at DESC').all
end
def show
#user = User.friendly.find(params[:id])
#users_authors = User.all_authors
end
# get authors index in here
def authors
end
def create
#user = User.create(user_params)
end
def edit
#user = User.find(params[:id])
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
if #user.destroy
redirect_to users_url, notice: "User deleted."
end
end
private
def user_params
params.require(:user).permit(:avatar, :email, :name, :biography, :role_id, :book_id, :username, :password, :password_confirmation)
end
end
This is trying to create a CRUD to edit users which works but I need to be able to populate the forms in the users/edit view wityh the correct selected users details. I my devise controller I have this setup:
class Admin::UsersController < Admin::BaseController
helper_method :sort_column, :sort_direction
before_filter :find_user, :only => [:edit, :update, :show, :destroy]
def index
#q = User.search(params[:q])
#users = find_users
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to admin_users_path, :notice => "Successfully created user."
else
render :new
end
end
def show
end
def edit
#user = User.find(params[:id])
end
def update
if #user.update_attributes(user_params)
redirect_to admin_users_path, :notice => "Successfully updated user."
else
render :edit
end
end
def destroy
#user.destroy
redirect_to admin_users_path, :notice => "User deleted."
end
protected
def find_user
#user = User.find(params[:id])
end
def find_users
search_relation = #q.result
#users = search_relation.order(sort_column + " " + sort_direction).references(:user).page params[:page]
end
def sort_column
User.column_names.include?(params[:sort]) ? params[:sort] : "created_at"
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : "desc"
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:email,:username,:name,:biography,:role_id,:book_id,:role_name,:password,:password_confirmation,:encrypted_password,:reset_password_token,:reset_password_sent_at,:remember_created_at,:sign_in_count,:current_sign_in_at,:last_sign_in_at,:current_sign_in_ip,:last_sign_in_ip)
end
end
For clarity here is the user model:
class User < ActiveRecord::Base
belongs_to :role
has_many :books, dependent: :destroy
has_many :ideas, dependent: :destroy
accepts_nested_attributes_for :books
accepts_nested_attributes_for :ideas
def confirmation_required?
false
end
extend FriendlyId
friendly_id :username, use: [:slugged, :finders]
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_attached_file :avatar, styles: {
large: "600x450#",
medium: "250x250#",
small: "100x100#"
}, :default_url => "/images/:style/filler.png"
#validates_attachment_content_type :avatar, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
validates :avatar, :email, :username, :password, presence: true
def self.all_authors
User.select('users.id, users.username, users.role_id AS USER_ROLE')
.joins(:role).where(users: {role_id: '2'})
end
before_create :set_default_role
private
def set_default_role
self.role ||= Role.find_by_name('Admin')
end
end
In my routes I added a new route for users below the devise users resource as suggested on devise wiki like so:
devise_for :users, :path_prefix => 'my', :path_names => { :sign_up => "register" }
namespace :admin do
resources :users
end
Can anyone help with adding the ability of admins being able to edit all users here, I think its right but I cannot get the correct data into the forms in edit, it uses the current logged users details only.
A first draft for your ability.rb would be:
class Ability
include CanCan::Ability
def initialize(user)
# ...
if user.admin?
can :manage, User
end
# ...
end
end
And then in your user's controller remove the before_filter :find_user, :only => [:edit, :update, :show, :destroy] and related method, and use
load_and_authorize_resource :user
That would load the user from the URL and authorize! it using CanCan. You'll also need to handle the CanCan::AccessDenied exception for non-admin users visiting those pages, but that is another question that you can check in the CanCan docs.
When you visit admin_users_path routes you'll be able to CRUD them if you have the views ready and working.

Resources