Can can with devise, admin and user - ruby-on-rails

I use cancan and devise, I can update delete and show but I can't create profile.
why I can't create new profile ("ActiveModel::ForbiddenAttributesError")
class Ability
include CanCan::Ability
def initialize(user)
if user.is_a?(Admin)
can :manage, :all
elsif user.is_a?(User)
can :read, Profile do |profile|
profile.try(:user) == user
end
can :update, Profile do |profile|
profile.try(:user) == user
end
can :destroy, Profile do |profile|
profile.try(:user) == user
end
can :create, Profile do |profile|
profile.try(:user) == user
else
can :read, :all
end
end
end

class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
load_and_authorize_resource
# GET /profiles
# GET /profiles.json
def index
user = User.find(params[:user_id])
#profiles = user.profiles
respond_to do |format|
format.html
format.xml {render :xml => #profiles}
end
end
# GET /profiles/1
# GET /profiles/1.json
def show
user = User.find(params[:user_id])
#profiles = user.profiles.find(params[:id])
respond_to do |format|
format.html
format.xml {render :xml => #profile}
end
end
# GET /profiles/new
def new
user = User.find(params[:user_id])
#profile = user.profiles.build
respond_to do |format|
format.html
format.xml {render :xml => #profile}
end
end
# GET /profiles/1/edit
def edit
user = User.find(params[:user_id])
#profiles = user.profiles.find(params[:id])
end
# POST /profiles
# POST /profiles.json
def create
user = User.find(params[:user_id])
#profile = user.profiles.create(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to user_profiles_url, notice: 'Profile was successfully created.' }
format.json { render action: 'show', status: :created, location: #profile }
else
format.html { render action: 'new' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
user = User.find(params[:user_id])
#profiles = user.profiles.find(params[:id])
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to user_profile_url, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
user = User.find(params[:user_id])
#profiles = user.profiles.find(params[:id])
#profile.destroy
respond_to do |format|
format.html { redirect_to job_hunters_path }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params.require(:profile).permit(:full_name, :phone_number, :email, :position, :years_of_experiance, :cover_letter, :resume, :reference)
end
end

If you already have load_and_authorize_resource in your controller code, you need to take another step in sanitizing your inputs via a create_params method call in your controller.
Here's a link to a useful resource
Firstly: your CanCan user ability can be re-written as follows:
can :create, :read, :update, :destroy, Profile, user_id: user.id
Secondly: your admin abilities should be written after your normal user abilities, thus overriding them more successfully:
def initialize(user)
# I prefer to alias CRUD actions to keep my ability files more succint
alias_action :create, :read, :update, :destroy, to: :crud
cannot :manage, :all #Failsafe
can :crud, Profile, user_id: user.id
... #additional abilities for user
if user.admin?
can :manage, :all #Override previous failsafe
Lastly: if your Profile class belongs_to a User, you ought to rewrite it as such. Thus, your profile_params would contain a user_id field.
If you were to follow that (correct) paradigm, your ProfilesController's #create action would look something like:
class ProfilesController < ApplicationController
load_and_authorize_resource
def create
#profile = Profile.new(profile_params)
if #profile.save
...
else
...
end
end
private
def profile_params
params.require(:profile).permit(:user_id, ...)
end
end

Related

link_to executes child and parent delete action

So in my project Project is parent and Medium is Child.
I want to delete one of the childs asynchronously (ajax / js.erb).
When clicking on this link (turbo_method is supposed to imply the asynchronous part of the request but I'm not sure about how it works.) something goes wrong.
<%= link_to "Delete Medium", project_medium_path(medium.project, medium), data: { turbo_method: :delete, turbo_confirm: 'are you sure ?' } %>
Here are my logs.
My problem is that after deleting my media entity, it also deletes my project entity. The fact that deleting my project entity implies a ForeignKeyViolation error is normal, I haven't setup my model correctly yet. Either way, it shouldn't delete my project entity.
Here are my controllers, models and my views disposition :
Projects controller
class ProjectsController < ApplicationController
before_action :set_project, only: %i[ show edit update destroy ]
# GET /projects
def index
#projects = policy_scope(Project).order(date: :desc)
authorize Project
end
# GET /projects/1
def show
#medium = Medium.new(project: #project)
end
# GET /projects/new
def new
#project = Project.new
# Set active user as project owner
#project.user = current_user
authorize #project
end
# GET /projects/1/edit
def edit
end
# POST /projects
def create
#project = Project.new(project_params)
# Set active user as project owner
#project.user = current_user
authorize #project
respond_to do |format|
if #project.save
format.html { redirect_to project_url(#project), notice: "Project was successfully created." }
else
format.html { render :new, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /projects/1
def update
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to project_url(#project), notice: "Project was successfully updated." }
else
format.html { render :edit, status: :unprocessable_entity }
end
end
end
# DELETE /projects/1
def destroy
#project.destroy
respond_to do |format|
format.html { redirect_to projects_url, notice: "Project was successfully destroyed." }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:id])
authorize #project
end
# Only allow a list of trusted parameters through.
def project_params
params.require(:project).permit(:title, :slug, :subject, :category, :description, :location, :date, :user_id)
end
end
Media controller
class MediaController < ApplicationController
before_action :set_medium, only: %i[ edit update destroy ]
before_action :set_project, only: %i[ create edit update ]
# GET /media
# def index
# end
# GET /media/1
#def show
#end
# GET /media/new
def new
#medium = Medium.new
authorize #medium
end
# GET /media/1/edit
def edit
end
# POST /media
def create
#medium = #project.media.create(medium_params)
authorize #medium
respond_to do |format|
if #medium.save
format.html { redirect_to project_url(#medium.project), notice: "Medium was successfully created." }
format.js
else
format.html { render :new, status: :unprocessable_entity }
format.js
end
end
end
# PATCH/PUT /media/1
def update
respond_to do |format|
if #medium.update(medium_params)
format.html { redirect_to project_url(#medium.project), notice: "Medium was successfully updated." }
else
format.html { render :edit, status: :unprocessable_entity }
end
end
end
# DELETE /media/1
def destroy
#project = #medium.project
#medium.destroy
respond_to do |format|
format.html { redirect_to #project, notice: "Medium was successfully destroyed." }
format.js
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:project_id])
authorize #project
end
# Use callbacks to share common setup or constraints between actions.
def set_medium
#medium = Medium.find(params[:id])
authorize #medium
end
# Only allow a list of trusted parameters through.
def medium_params
params.require(:medium).permit(:title, :description, :author, :location, :date, :priority_index, :project_id, :visual)
end
end
Project model
Medium model
Thanks for the help !
I found a solution through this post.
Using status: 303 inside Media#Destroy for the redirect_to method.

Unpermitted parameter: user_id error in rails

am using devise for authentication in a rails app everything is working fine, but i have a post model and after signing up when i try to create a new post it shows up an error:
1 error prohibited this post from being saved:
User must exist
I looked into the terminal and got this:
Unpermitted parameter: user_id
(0.0ms) begin transaction
(0.0ms) rollback transaction
i tried adding a user controller
users_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!
before_action :set_user
before_action :check_ownership, only: [:edit, :update]
respond_to :html, :js
# GET /users
# GET /users.json
def index
#users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
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
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :email)
end
end
my posts_controler.rb
class PostsController < ApplicationController
before_action :authenticate_user!, :except => [:show, :index]
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
def new
#post = Post.new
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:tittle, :body)
end
end
My user model
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts
end
My post model
class Post < ApplicationRecord
belongs_to :user
end
i have also added user_id to user through this migration:
class AddUseridToPost < ActiveRecord::Migration[5.0]
def change
add_column :posts, :user_id, :integer
end
end
as the error says, permit the user_id as follows
def post_params
params.require(:post).permit(:tittle, :body, :user_id)
end
Or, better approach is you generate a new post for the logged_in user as in post create:
def create
#post = current_user.posts.new(post_params)
# ...other logic
end
This way, you don't need to allow user_id also, it will ensure that the params are not tampered with..
If you add column in your model, you should add it to your permitted params. So add :user_id in your permitted params in posts_controller:
def post_params
params.require(:post).permit(:tittle, :body, :user_id)
end

Ruby on Rails API how to soft delete an user

I am able to soft delete an user from the main application.
Here are the details.
The user URL is:
http://0.0.0.0:3000/users/4c7fa12c-3d58-480b-a823-6c67d6e7f0fe.json
Which is presenting a JSON result like this:
{"id":"4c7fa12c-3d58-480b-a823-6c67d6e7f0fe","name":"John","status":"Active","created_at":"2015-11-10T18:31:27.000+00:00","updated_at":"2015-11-10T18:38:20.000+00:00"}
I can do a soft delete by using an inactivation this way:
http://0.0.0.0:3000/users/4c7fa12c-3d58-480b-a823-6c67d6e7f0fe/inactivate
Here is my inactivate method in the users controller:
def inactivate
#user.update status: 'Inactive'
redirect_to users_url
end
The result is shown when reload the user URL:
{"id":"4c7fa12c-3d58-480b-a823-6c67d6e7f0fe","name":"John","status":"Inactive","created_at":"2015-11-10T18:31:27.000+00:00","updated_at":"2015-11-10T19:29:58.000+00:00"}
Now my user is "Inactive".
Here is my routes file:
Rails.application.routes.draw do
resources :wit_dims
resources :wokas
resources :posts
resources :languages
resources :users
root :to => "home#index"
resources :users do
get 'inactivate', on: :member
end
match 'inactivate', to: 'users#inactivate', via: 'delete'
#api
namespace :api do
namespace :v1 do
resources :users, only: [:index, :create, :show, :update, :destroy]
resources :posts, only: [:index, :create, :show, :update, :destroy]
end
end
end
How I can change this routes file and the API users controller to make the same king of inactivation through the API? I want more than a trivial solution using a PUT to change status of the user from Active to Inactive.
Here is my main application users controller:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy, :inactivate]
# GET /users
# GET /users.json
def index
#users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully inactivated.' }
format.json { head :no_content }
end
end
def inactivate
#user.update status: 'Inactive'
redirect_to users_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params[:user]
end
end
Here is my API users controller:
class Api::V1::UsersController < Api::V1::BaseController
before_action :set_user, only: [:show, :update, :destroy]
def show
render(json: Api::V1::UserSerializer.new(#user).to_json)
end
def update
if #user.update_attributes(user_params)
render(
json: Api::V1::UserSerializer.new(#user).to_json,
status: 200,
location: api_v1_user_path(#user.id)
)
else
return api_error(status: 422, errors: #tbm.errors)
end
end
def index
users = User.all
render(
json: ActiveModel::ArraySerializer.new(
users,
each_serializer: Api::V1::UserSerializer,
root: 'users'
)
)
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :status).delete_if{ |k,v| v.nil? }
end
end
Basically I would like to be able to call an inactivate method through the API which is going to change the status from Active to Inactive, instead of physically deleting / destroying the user.
I used PUT and executed an updated on the status attribute to mark the user as Inactive.

Rails 4 testing controller failure

Good afternoon,
I've done a scaffold of a class, here the example: User - At the moment I was executing the test I saw this bug:
Minitest::Assertion: "User.count" didn't change by 1.
Expected: 3
Actual: 2
test/controllers/users_controller_test.rb:20:in `block in <class:UsersControllerTest>'
the refering code of the bug is this one:
test "should create user" do
assert_difference('User.count') do
post :create, user: { name: 'test', password: 'secret', password_confirmation: 'secret' }
end
assert_redirected_to user_path(assigns(:user))
end
So the code above was created by the scaffold, I just change the name reference.
UserController:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy, :reset_password]
# GET /users
# GET /users.json
def index
#users = super
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: get_action_message }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
#user.update(user_params)
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: get_action_message }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: get_action_message }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :password, :password_confirmation)
end
def search_params
super - ['password_digest']
end
def show_attributes
#show_attributes = super - ['password_digest']
end
end
UserModel
class User < ActiveRecord::Base
nilify_blanks
validates :name, presence: true, uniqueness: true;
has_secure_password
end
For what I was able to understand, the "create" it's not being executed. I've put the breakpoint on the Controller create but its not stoping there..
What am I doing wrong, besides my poor english skills?
Tks for the helping!!
Thks guys,
I found this bug.
I had forgotten to put in the user Session

undefined method `firstname' for nil:NilClass

Hi there I am getting a NoMethodError in Users#show error in my Rails 3.2 app using devise.
Error on line 2 of my show below:
<strong>Firstname:</strong>
<%= #user.firstname %>
</p>
<p>
<strong>dateofbirth:</strong>
(If I remove the first name box it does the error for dateofbirth, etc.)
I'm using devise for my users, but generated a user controller in which to place my views.
I'm not sure what the problem is because I ran my migration and firstname exists in the schema
Here's the user controller:
class UsersController < ApplicationController
# before_action :set_user, only: [:show, :edit, :update, :destroy]
def find
#user = User.new
end
def show
#user = User.find(params[:id])
end
# GET /users
# GET /users.json
def index
#users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
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
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:username, :firstname, :dateofbirth, :lastname, :gender, :location, :email, :password)
end
end
Your controller has two show methods defined. Try removing the second one. I believe the empty show method is overwriting your filled-in show method. In your view, #user doesn't exist so it evaluates to nil. Nil has neither a firstname method, nor dateofbirth method. Hence the NoMethodError.

Resources