The following pattern is encountered:
params[:vehicle][:user_id]
params[:location][:user_id]
params[...etc...][:user_id]
what syntax allows the creation of a method that inputs the current class as the symbol within the param ? for example
class VehiclesController
def edit
v = Vehicle.where('user_id = ? AND user_id = ?', params[:vehicle][:user_id], current_user.id).first
end
class LocationsController
def edit
l = Location.where('user_id = ? AND user_id = ?', params[:location][:user_id], current_user.id).first
end
You can add a method to ApplicationController:
class ApplicationController < ActionController::Base
private
def form_params
params[controller_name.singularize]
end
end
and use it in other controllers:
class VehiclesController < ApplicationController
def create
form_params # => #<ActionController::Parameters {"user_id"=>"1"} permitted: false>
end
end
For permitted params, could be this:
class ApplicationController < ActionController::Base
private
def form_params
params.require(controller_name.singularize).permit(permitted_params)
end
def permitted_params
[] # nothing is permitted by default
end
end
class VehiclesController < ApplicationController
def create
form_params # => #<ActionController::Parameters {"user_id"=>"1"} permitted: true>
end
private
# Override `permitted_params`
def permitted_params
# FIXME: Seems `user_id` is not needed when you have `current_user.id`.
# Besides, it is bad to expose current `user_id` in the form,
# because I can start messing with it and start submitting
# different ids in your forms; like a payment form, make someone
# else pay for my charges.
[:user_id]
end
end
Related
I'm working on a rails app where I wrote a personalized route called "all_designs"; with the corresponding method on the controller and the view, before I add pundit to my project it was working fine.
Now I'm having this error:
Pundit::AuthorizationNotPerformedError in DesignsController#all_designs
I understand that I'm missing a policy for this action, but the way I'm trying is not working.
How can I add a policy for this method?
Controller:
class DesignsController < ApplicationController
before_action :set_design, only: [:show,:edit,:update,:destroy]
def index
#designs = policy_scope(Design.where(user: current_user, status: 'activo'))
#user = current_user
end
def all_designs
#designs = Design.where(user: current_user)
#user = current_user
end
...
end
Policy:
class DesignPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def create?
true
end
def show?
true
end
def destroy?
user == record.user
end
def update?
# If the user is the owner of the design
user == record.user
end
def all_designs?
true
end
end
I would consider separate controller and policy for this as what you're doing is really just a nested route (designs belonging to a singleton resource).
scope 'user', module: :users do
resources :designs, only: :index
end
module Users
class DesignsPolicy
class Scope < Scope
def resolve
#user.designs # make sure user has a `has_many :designs` assocation.
end
end
end
def index?
true
end
end
# Represents designs belonging to the current user
module Users
class DesignsController < ApplicationController
# GET /user/designs
def index
#designs = policy_scope
end
end
end
This lets you separate the logic of displaying the the current users designs from /designs which would display everything in a clean way.
Every method on the controller which needs to be authorized, needs to contains an explicit declaration like this:
def all_designs
#designs = Design.where(user: current_user)
#user = current_user
authorize #designs
end
The reason it wasn't working was: I missed the authorize line
Doing API for my first Rails project.
I have base class ApiController for all the APIs:
module Api
class ApiController < ::ApplicationController
respond_to :json
skip_before_action :verify_authenticity_token
def index
#collection = resource_class.all
render json: #collection.as_json(as_json_collection)
end
private
def resource_class
raise NotImplementedError
end
def as_json_collection
{}
end
end
end
And I have child class UsersController:
module Api
class UsersController < ApiController
private
def resource_class
User
end
def resource_params
params.require(:user).permit(:name, :email)
end
end
end
My routes:
namespace :api do
resources :users
end
Then I'm going to my_app/api/users I have error:
The action 'index' could not be found for Api::UsersController
But then I changing UsersController writing it's own index class, everything works fine and I'm having all my Users in JSON format.
I've alrady tried to comment all private marks in both classes, but that doesn't help.
I don't want to write an API for every entity in my project and I'd like to avoid this problem in future.
I got it to work with this:
module Api
class ApiController < ::ApplicationController
def index
respond_to do |format|
format.json { render json: '"abc"' }
end
end
end
end
module Api
class UsersController < ApiController
end
end
The URL was http://localhost:3000/api/users.json
So for you I suggest:
module Api
class ApiController < ::ApplicationController
def index
respond_to do |format|
format.json do
#collection = resource_class.all
render json: #collection.as_json(as_json_collection)
end
end
end
end
end
module Api
class UsersController < ApiController
def resource_class
User
end
def resource_params
params.require(:user).permit(:name, :email)
end
end
end
Its supposed to be like this:
class Api::ApiController < ApplicationController
and do not forget to remove extra end, end of the file!
#sample
- api(folder)
-- api_controller.rb (Api::ApiController < ApplicationController)
-- users_controller.rb (Api::UsersController < Api::ApiController)
application_controller.rb
You need to read this my friend:
rails routes
When you do this:
namespace :api do
resources :users
end
rails creates CRUD routes automatically which means that my_app/api/users will translate to: .../users#index.
Do this to see your routes created by rails:
rails routes and for specific word (e.g. user): rails routes | grep user
Seeing is believing ;)
I'm creating a controller in Rails, and I'm looking for ways to have different strong parameters for different controller methods
In update and new actions, I would want to require post
params.require(:post).permit(:body, :is_public, :title, :id)
But in post/index, i don't need to require these parameters.
How do you make different requirements strong parameters for different controller methods?
Your "strong parameters methods" are just Ruby methods. You can have however many you want.
class PostsController < ApplicationController
def create
#post = Post.new(create_params)
end
def update
#post = Post.find(params[:id])
if #post.update(update_params)
# ...
else
# ...
end
end
private
def base_params
params.require(:post)
end
# Don't take IDs from the user for assignment!
def update_params
base_params.permit(:body, :title)
end
def create_params
base_params.permit(:body, :title, :foo, :bar)
end
end
You can also name them whatever you want. Calling it [resource_name]_params is just a scaffolding convention.
Just do something like
class FooController < ApplicationController
def create
#post = Post.new(create_params)
if #post.save
blah
else
blah
end
end
def index
... something else
end
private
def create_params
params.require(:post).permit(:body, :is_public, :title, :id)
end
end
The policy_scope works perfectly finding the correct policy named Admin::RemittancePolicy but authorize method not.
module Admin
class RemittancesController < AdminController # :nodoc:
...
def index
#remittances = policy_scope(Remittance).all
render json: #remittances
end
def show
authorize #remittance
render json: #remittance
end
...
end
end
Take a look at output error:
"#<Pundit::NotDefinedError: unable to find scope `RemittancePolicy::Scope` for `Remittance(...)`>"
Perhaps a error with pundit, I really not know how fix it. Thanks.
More information below:
# policies/admin/admin_policy.rb
module Admin
class AdminPolicy < ApplicationPolicy # :nodoc:
def initialize(user, record)
#user = user
#record = record.is_a?(Array) ? record.last : record
end
def scope
Pundit.policy_scope! user, record.class
end
class Scope # :nodoc:
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope.is_a?(Array) ? scope.last : scope
end
def resolve
scope
end
end
end
end
# controllers/admin/admin_controller.rb
module Admin
class AdminController < ActionController::API # :nodoc:
include Knock::Authenticable
include Pundit
before_action :authenticate_user
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index
# def policy_scope!(user, scope)
# model = scope.is_a?(Array) ? scope.last : scope
# PolicyFinder.new(scope).scope!.new(user, model).resolve
# end
def policy_scope(scope)
super [:admin, scope]
end
def authorize(record, query = nil)
super [:admin, record], query
end
end
end
Your stacktrace says the error comes from
app/policies/admin/admin_policy.rb:9:in 'scope'
That's this:
def scope
Pundit.policy_scope! user, record.class
end
record.class evaluates to Remittance, so if I understand what you're trying to do, you need to change scope to
def scope
Pundit.policy_scope! user, [:admin, record.class]
end
I have a helper, controller and template like:
Helper:
# app/helpers/application_helper.rb
module ApplicationHelper
def current_user
#current_user ||= User.find_by(access_token: access_token)
end
private
def access_token
pattern = /^Bearer /
header = request.headers["Authorization"]
header.gsub(pattern, "") if header && header.match(pattern)
end
end
Controller:
# app/controllers/api/v1/companies_controller.rb
class Api::V1::CompaniesController < Api::V1::BaseController
before_action :set_company, only: [:show]
def show
render #company
end
private
def set_company
#company ||= Company.find(params[:id])
end
end
# app/controllers/api/v1/base_controller.rb
class Api::V1::BaseController < ApplicationController
respond_to :json
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include ApplicationHelper
protect_from_forgery with: :null_session
end
RABL-Rails template:
object :#company
attributes :id, :name, :description, :website
# --- How can I call a helper method here?
# if (#company.owner?(current_user) or current_user.kind_of?(Admin))
# attributes :contact
# end
attributes :created_at, :updated_at
When I call a helper method from RABL template, it will raise an error:
undefined local variable or method `current_user' for #<RablRails::Compiler:0x00000002494c68>
How can I call a helper method from RABL template?
Note: I used gem rabl-rails '~> 0.4.1'.
It seems your calling it the right way but the real problem is that your controller doesn't have inheritance from ApplicationController (unless there is more in Api::V1::BaseController that we can't see). So this means that your probably not getting ApplicationHelper included.
I would suggest you just add it to your controller
class Api::V1::CompaniesController < Api::V1::BaseController
include ApplicationHelper
...
end