authenticate_with_http_token method not found - ruby-on-rails

I am making an api with ruby on rails, and I am trying to use token based authentication. Everything works just fine, but Rails is saying that the method authenticate_with_http_token is undefined.
This is the error it is giving:
{"status":500,"error":"Internal Server Error","exception":"#\u003cNoMethodError: undefined method `authenticate_with_http_token' for #\u003cUsersController:0x007fa8ac16dee0\u003e\u003e","traces":{"Application Trace":[{"id":0,"trace":"app/controllers/users_controller.rb:59:in `authenticate_token'"},{"id":1,"trace":"app/controllers/users_controller.rb:55:in `authenticate'"}],"Framework Trace":[{"id":2,"trace":"activesupport (5.0.0.beta3) lib/active_support/callbacks.rb:382:in `block in make_lambda'"}
This is the code for my controller:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
before_action :authenticate, only: [:show, :update, :destroy]
# GET /users
def index
#users = User.all
render json: #users
end
# GET /users/1
def show
render json: #user
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
render json: #user, status: :created, location: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /users/1
def update
if #user.update(user_params)
render json: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
# DELETE /users/1
def destroy
#user.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :school_id, :auth_token, :password_digest)
end
def authenticate
authenticate_token || render_unauthorized
end
def authenticate_token
authenticate_with_http_token { |token, options| User.find_by(auth_token: token) }
end
end

Try to include that in your ApplicationController or UsersController
include ActionController::HttpAuthentication::Token::ControllerMethods

I guess you are trying to use the new Rails 5 API-only?
If so you probably inherited your ApplicationController from
class ApplicationController < ActionController::API
instead of
class ApplicationController < ActionController::Base
Please take note that ActionController::API is a downsized version of ActionController which does not include ALL modules. One of the ones left out is actually ActionController::HttpAuthentication::Token.
Including it in your ApplicationController (or a specialized controller if you just need it in one place) should fix it:
include ActionController::HttpAuthentication::Basic

Related

Devise and Knock gem current_user

I have an application that is using both Devise and Knock. It is using Devise to power the authentication for Active Admin and Knock gem is providing the authentication for my API's
The issue I have is that Knock can't seem to find current_user and I believe this is likely because I am using Devise in the same project.
I have the following setup:
Api Controller
class ApiController < ActionController::API
include Knock::Authenticable
end
User Controller (for API not ActiveAdmin)
module Api
module V1
class UsersController < ApiController
before_action :set_user, only: [:show, :update, :destroy]
# GET /users/1
def show
render json: #user
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
render json: #user.id, status: :created
else
render json: #user.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /users/1
def update
if #user.update(user_params)
render json: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
# DELETE /users/1
def destroy
#user.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
end
end
Auth Controller
module Api
module V1
class AuthController < ApiController
def auth
render json: { status: 200, user: current_user }
end
end
end
end
Current User in this Auth controller returns nothing however in another project I have, without devise, this will correctly return the user.
Is there a way to redefine what current_user is or assign it to something different for the purposes of using Knock?
Try this in your ApplicationController
# JWT: Knock defines it's own current_user method unless one is already
# defined. As controller class is cached between requests, this method
# stays and interferes with a browser-originated requests which rely on
# Devise's implementation of current_user. As we define the method here,
# Knock does not reimplement it anymore but we have to do its thing
# manually.
def current_user
if token
#_current_user ||= begin
Knock::AuthToken.new(token: token).entity_for(User)
rescue
nil
end
else
super
end
end
private
# JWT: No need to try and load session as there is none in an API request
def skip_session
request.session_options[:skip] = true if token
end
# JWT: overriding Knock's method to manually trigger Devise's auth.
# When there is no token we assume the request comes from the browser so
# has a session (potentially with warden key) attached.
def authenticate_entity(entity_name)
if token
super(entity_name)
else
current_user
end
end

How do you use Rails 5.2 wrap_parameters?

I have a Rails 5.2 app built as API only. I am using Postman to test the endpoints. Currently, I am wrapping all of my JSON POSTS with the root element in order to get it to pass the 'strong parameters'. According to the Rails docs, however, with wrap_parameters set to accept JSON, I shouldn't have to be doing this, however, my POSTs fail if I don't. What am I doing wrong?
# users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
.....
# POST /users
def create
#user = User.new(user_params)
if #user.save
render json: #user, status: :created, location: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
.....
private
.....
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:email, :password, :is_admin, :is_agent)
end
end
# config/initializers/wrap_parameters.rb
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json]
end
If I wrap the JSON in a user it works just fine, however, according to the Rails docs, I shouldn't have to do that.
http://api.rubyonrails.org/v5.2.0/classes/ActionController/ParamsWrapper.html
I have tried adding wrap_parameters User and wrap_parameters :user and wrap_parameters format: [:json] directly to the users_controller.rb but that doesn't do anything.
What am I doing wrong?
I figured it out. I had to add wrap_parameters :user, include: %i[email password is_admin is_agent] to the Users controller like so:
# users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
wrap_parameters :user, include: %i[email password is_admin is_agent]
.....
# POST /users
def create
#user = User.new(user_params)
if #user.save
render json: #user, status: :created, location: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
.....
private
.....
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:email, :password, :is_admin, :is_agent)
end
end
I still don't fully understand why, but, it works. The docs imply this should be happening automagically through the wrap parameters module.
This is because of the parameter whitelisting.
def user_params
params.require(:user).permit(:email, :password, :is_admin, :is_agent)
end
Here , user key is required. As per the security point of view, its a good feature in RoR to whitelist the parameters
If you write only params.permit(:email, :password, :is_admin, :is_agent) , then you same request will work but removing the user object is not recommended.

Security issue with controller

I have my Banks Controller
class Api::V1::BanksController < ApplicationController
before_action :authenticate_user!
respond_to :json
# PUT /api/v1/banks/:id.json
def update
#bank = UserBank.find_by!(uuid: params[:id])
if #bank.update_attributes bank_params
render json: #bank
else
render json: #bank.errors, status: :unprocessable_entity
end
end
private
def bank_params
params.require(:bank).permit(:iban, :bic)
end
end
I'm using devise for the authentication. My problem comes from the fact that any users can update another user's bank object just by getting the access-token from the login response.
Is there a clean/secure/automatic way of preventing a user to interact with somebody else's details ?
or should I just make sure that the bank object I'm updating belongs to the logged-in user ?
thanks a lot
Your mixing up authentication and authorization.
Authentication is concerned with the identity of the user.
Authorization is a set of rules for who is allowed to do what in your application.
Devise provides authentication, you can either create your own authorization system or use a library (recommended) such as Pundit or CanCanCan.
A hacky home rolled authorization check would look like:
class AuthorizationError < StandardError; end
class ApplicationController
rescue_from AuthorizationError, with: :deny_access
def deny_access
head :unauthorized and return
end
end
class Api::V1::BanksController < ApplicationController
before_action :authenticate_user!
before_action :set_bank!
before_action :authorize!
respond_to :json
# PUT /api/v1/banks/:id.json
def update
#bank = UserBank.find_by!(uuid: params[:id])
respond_with #bank.update(bank_params)
end
private
def set_bank!
#bank = UserBank.find_by!(uuid: params[:id])
end
def authorize!
# this is the authorization rule.
unless #bank.user == current_user
raise AuthorizationError
end
end
def bank_params
params.require(:bank).permit(:iban, :bic)
end
end

Rails: No Pundit policy found in Rails

I have used the Pundit Gem before, but I've never tried doing what I'm trying to do now, and for some reason Pundit is not happy.
What I'm aiming to do, is to have a modal with the 'create' (Foo) form on my 'index'(Foos) page. Thus I need to instantiate an empty Foo object for the modal form to work.
The issue that I'm experiencing, is that Pundit throws an error when I submit the form remotely. The error is:
Pundit::NotDefinedError - unable to find policy of nil
I have tried to understand why this is happening but I've not been able to solve it yet.
Here is my foos_controller.rb#index:
...
def index
#foo = Foo.new
authorize #foo, :new?
#foos = policy_scope(Foo)
end
...
I then have the following 'before_action' filter that runs for my other actions i.e. 'create'
...
before_action :run_authorisation_check, except: [:index]
def run_authorisation_check
authorize #foo
end
...
The policies that I'm using in foo_policy.rb:
....
def index?
user.has_any_role? :super_admin
end
def create?
user.has_any_role? :super_admin
end
def new?
create?
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
if user.has_any_role? :super_admin
scope.all
end
end
end
....
The error does not present itself until I submit the form. Could anybody familiar with Pundit please help guide me to understand what I'm doing incorrectly?
UPDATE
Full foos_controller.rb
class FoosController < ApplicationController
def index
#foo = Foo.new
authorize #foo, :create?
#foos = policy_scope(Foo)
end
def new
#foo = Foo.new
end
def create
#foo = Foo.new(foo_params)
respond_to do |format|
if #foo.save
flash[:notice] = I18n.t("foo.flash.created")
format.json { render json: #foo, status: :ok }
else
format.json { render json: #foo.errors, status: :unprocessable_entity }
end
end
end
private
before_action :run_authorisation_check, except: [:index]
def foo_params
params.fetch(:foo, {}).permit(:bar)
end
def run_authorisation_check
authorize #foo
end
end
Yeah, you're not setting the value of #foo, that's why you're getting the error unable to find policy of nil.
Most times, you would have something like this in your foos_controller.rb:
before_action :set_foo, only: [:show, :edit, :update, :destroy]
before_action :run_authorisation_check, except: [:index]
...
private
def set_foo
#foo = Foo.find(params[:id])
end
Let me know if that works
I had this issue when working on a Rails 6 API only application with the Pundit gem.
I was running into the error below when I test my Pundit authorization for my controller actions:
Pundit::NotDefinedError - unable to find policy of nil
Here's how I solved:
Say I have a policy called SchoolPolicy:
class SchoolPolicy < ApplicationPolicy
attr_reader :user, :school
def initialize(user, school)
#user = user
#school = school
end
def index?
user.admin?
end
def show?
user.admin?
end
def new
create?
end
def edit
update?
end
def create
user.admin?
end
def update?
user.admin?
end
def destroy?
user.admin?
end
end
Then in my SchoolsController, I will have the following:
class Api::V1::SchoolsController < ApplicationController
before_action :set_school, only: [:show, :update, :destroy]
after_action :verify_authorized, except: :show
# GET /schools
def index
#schools = School.all
authorize #schools
render json: SchoolSerializer.new(#schools).serializable_hash.to_json
end
# GET /schools/1
def show
render json: SchoolSerializer.new(#school).serializable_hash.to_json
end
# POST /schools
def create
#school = School.new(school_params)
authorize #school
if #school.save
render json: SchoolSerializer.new(#school).serializable_hash.to_json, status: :created, location: api_v1_school_url(#school)
else
render json: #school.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /schools/1
def update
authorize #school
if #school.update(school_params)
render json: SchoolSerializer.new(#school).serializable_hash.to_json
else
render json: #school.errors, status: :unprocessable_entity
end
end
# DELETE /schools/1
def destroy
authorize #school
#school.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_school
#school = School.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def school_params
params.require(:school).permit(:name, :alias, :code)
end
end
Note:
I used an after_action callback to call the verify_authorized method to enforce authorization for the controller actions
I did not call the authorize method on the show action because it was skipped for authorization by me out of choice based on my design.
The instance variables called by the authorize method corresponds to the instance variable of the controller actions being called. So for the index action it is #schools and for the create action it is #school and so on.
That's all.
I hope this helps

Multi user in devise + Rails

I want to create multi user application. Admin user can create new users. how can i do this using devise. Because when after login as admin user i want add new user devise show error that "you are already signed in". How i do this using devise.
I was able to create the Admin User and logged in
User-Controller
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
def user_params
params.require(:user).permit(:email, :username, :password, :password_confirmation,:propic)
end
end
Admin Controller
class ClientsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:index, :new, :create]
def new
#client = Client.new
#client.build_owner
render layout: 'sign'
end
def index
#clients = Client.all
render layout: 'welcome'
end
def create
#client = Client.new(client_params)
if #client.valid? then
Apartment::Tenant.create(#client.subdomain)
Apartment::Tenant.switch(#client.subdomain)
#client.save
redirect_to new_user_session_url(subdomain: #client.subdomain)
else
render action: 'new'
end
end
private
def client_params
params.require(:client).permit(:name, :subdomain, owner_attributes: [:email, :username, :password, :password_confirmation,:propic])
end
end
Application Controller
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :load_tenant
before_filter :authenticate_user!
#rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
private
def record_not_found
render 'record_not_found'
end
def load_tenant
Apartment::Tenant.switch(nil)
return unless request.subdomain.present?
client = Client.find_by(subdomain: request.subdomain)
if client then
Apartment::Tenant.switch(request.subdomain)
else
redirect_to root_url(subdomain: false)
end
end
def after_signout_path_for(resource_or_scope)
new_user_session_path
end
end
Anyone? I am super new to Ruby on Rails. All the code is the result of hefty trial and errors.

Resources