Undefined method authenticated? form main::Object - ruby

I'm learning Sinatra now and I'm building a tiny blog. I've got a little problem with an authenticated? method. I've set up authentication, and now I want the rest of my app to be loaded only when the user is authenticated. So, in my app.rb I have the following:
helpers do
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def authenticated?
redirect '/login' unless current_user
end
end
if authenticated?
# get '/' { ... }
# get '/new' { ... }
# post '/new' { ... }
# the rest of an app (creating, updating and deleting posts go here)
end
When I run the app, it returns me the following error
undefined method authenticated? form main::Object
What have I done wrong?

According to docs you'd use your authenticated? method as follows:
get '/' do
if authenticated?
# something
else
# something else
end
end

Related

How to fix the problem, When I try authentication on local system its working perfectly, but when uploaded to heroku it comes back with error 500?

I am new to rails and react, this might be a simple one but i cant seem to figure it out.
I am trying to implement a simple jwt authentication using ruby on rails with react as client. I followed the steps that was suggested in :
https://www.pluralsight.com/guides/token-based-authentication-with-ruby-on-rails-5-api
It works as expected on my local system but when i uploaded my app on to heroku it always comes back with error : 500. All the other 'Post' and 'Get' requests work normally. Its only when i try to authenticate and get the auth_token back it runs into 500 error.
this is the request format
post: localhost:3001/api/authenticate
and body:
{
"email": "evin#xyz.com",
"password": "evin"
}
I verified that this data is available on heroku by using get which works perfectly.
I have been working on resolving this for over 2 days now. There is very little information available online on this authentication. There was plenty of recommendations on using auth0. But i could not find much help with this form of authentication.
This is what i have
#Path: /app/controllers/application_controller.rb
class ApplicationController < ActionController::API
before_action :authenticate_request
attr_reader :current_user
private
def authenticate_request
#current_user = AuthorizeApiRequest.call(request.headers).result
render json: { error: 'Not Authorized' }, status: 401 unless #current_user
end
end
#Path: app/controllers/api/authentication_controller.rb
class Api::AuthenticationController < ApplicationController
skip_before_action :authenticate_request
def authenticate
command = AuthenticateUser.call(params[:email], params[:password])
if command.success?
render json: { auth_token: command.result }
else
render json: { error: command.errors }, status: :unauthorized
end
end
end
#Path: /app/commands/authenticate_user.rb
class AuthenticateUser
prepend SimpleCommand
def initialize(email, password)
#email = email
#password = password
end
def call
JsonWebToken.encode(user_id: user.id) if user
end
private
attr_accessor :email, :password
def user
user = User.find_by_email(email)
return user if user && user.authenticate(password)
errors.add :user_authentication, 'invalid credentials'
nil
end
end
#Path: /app/commands/authorize_api_request.rb
class AuthorizeApiRequest
prepend SimpleCommand
def initialize(headers = {})
#headers = headers
end
def call
user
end
private
attr_reader :headers
def user
#user ||= User.find(decoded_auth_token[:user_id]) if decoded_auth_token
#user || errors.add(:token, 'Invalid token') && nil
end
def decoded_auth_token
#decoded_auth_token ||= JsonWebToken.decode(http_auth_header)
end
def http_auth_header
if headers['Authorization'].present?
return headers['Authorization'].split(' ').last
else
errors.add(:token, 'Missing token')
end
nil
end
end
#Path: /lib/json_web_token.rb
class JsonWebToken
class << self
def encode(payload, exp = 24.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode(payload, Rails.application.secrets.secret_key_base)
end
def decode(token)
body = JWT.decode(token, Rails.application.secrets.secret_key_base)[0]
HashWithIndifferentAccess.new body
rescue
nil
end
end
end
#path: /config/application.rb
require_relative 'boot'
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
# require "sprockets/railtie"
require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Deveycon
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
#Autoload lib for encrypt and decrypt
config.autoload_paths << Rails.root.join('lib')
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true
end
end
I had similar issues, the API works perfectly on localhost after uploading to Heroku, I still got unauthorized on secure pages even with the token on the headers.
I added
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
to config/secrets.yml
Please check the more details log of your heroku application by using Heroku CLI.
heroku logs -t
If the problem with AuthenticateUser::JsonWebToken use auto loaded in your
config/application.rb
class Application < Rails::Application
#.....
config.autoload_paths << Rails.root.join('lib')
#.....
end
I hope that helpful to resolve your issue.
In #lib/JsonWebToken:
Just increase the exp time of token and replace .secrets.secret_key_base with
.credentials.read
class JsonWebToken
class << self
def encode(payload, exp = 1200.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode(payload, Rails.application.credentials.read)
end
def decode(token)
body = JWT.decode(token, Rails.application.credentials.read)[0]
HashWithIndifferentAccess.new body
rescue
nil
end
end
end

Facebook Graph API for websites using Ruby Koala gem in Sinatra

I want to implement Facebook login for web apps. All I need is the basic public information of a user for the account creation steps.
This is what I have done:
Created a basic Facebook app with nearly no custom permissions.
Used the APP_ID and APP_SECRET in Koala to get access_token.
Everything worked perfectly, I am able to login/logout.
Just that the only information I am able to get back when I do: graph.get_object('me') is the logged in user's name and an id (It doesn't look like the default Facebook id).
Surprised whether something changed in the new API, I tested the gem in the console using the access_token from graph explorer (where all permissions are enabled by default). And I get all data using the same method call.
When I review what all the app gets while signing up; I see that the user's basic information, profile pic and other public data will be accessible to the app.
Any idea why this is so? It seems I am missing something obvious. The code is available in Github. But this is pretty much everything to it:
require 'bundler'
Bundler.require :default
Dotenv.load '.env'
require_relative './app/constants.rb'
module Banana
class App < Sinatra::Base
use Rack::Session::Cookie, secret: COOKIE_SECRET
set :public_folder, File.dirname(__FILE__) + '/bower_components'
get '/' do
if logged_in?
haml :welcome_in, layout: :layout
else
haml :log_in, layout: :layout
end
end
get '/log_out' do
session['oauth'] = nil
session['access_token'] = nil
redirect '/'
end
get '/log_in' do
session['oauth'] = Koala::Facebook::OAuth.new(APP_ID, APP_SECRET, "#{request.base_url}/call_back")
redirect session['oauth'].url_for_oauth_code()
end
get '/call_back' do
begin
session['access_token'] = session['oauth'].get_access_token(params[:code])
rescue
redirect '/?error=user_denied'
end
redirect '/'
end
get '/test' do
if logged_in?
p graph.get_object("rakeshbs")
"e"
else
redirect '/'
end
end
def logged_in?
!session['access_token'].nil?
end
def toggle_access
logged_in? ? '/log_out' : '/log_in'
end
def graph
#graph ||= Koala::Facebook::API.new(session['access_token'])
end
def errored?
!params["error"].nil?
end
def user
p graph.get_connections(:me, :photos) # This is just nil
#user ||= OpenStruct.new(
name: graph.get_object("me")["name"], # All I get here is just a hash with the name and an id!
photo: 'http://semantic-ui.com/images/avatar/small/elliot.jpg'
)
end
end
end
You should add fields parameter.
Something like this:
graph.get_object('me', { fields: 'id,first_name,last_name,gender,birthday,photos,email' })

testing padrino post methods are stopped by csrf

I have a padrino controller with a single post method and a single get method. I can use rack-test to test the get method but not the post method. When I am testing the request returns 403. I think this is because of padrino's built in csrf protection because when I comment out the line with set :protect_from_csrf, true I can test the post route. Obviously I don't want to comment out this line as csrf is useful. How can I get temporary access to test these routes for the purpose of testing?
Controller
SailPowerCourses::Admin.controllers :owners do
get :index do
puts 'hello'
end
post :index do
puts params
end
end
Test
class OwnersControllerTest < MiniTest::Test
def setup
app SailPowerCourses::Admin
end
def test_creates_an_owner
email = 'test#example.com'
assert_empty Owner
post '/owners', owner: {email: email}
puts last_response.status
refute_empty Owner
end
def test_other
email = 'test#example.com'
get '/owners', owner: {email: email}
end
end
```
When setting up an app in minitest you can use a block to access and change settings. such as csrf protection. I found the best solution to be the following. in test_config.rb I set up a version of the app with csrf protection off.
class OwnersControllerTest < MiniTest::Test
def setup
app SailPowerCourses::Admin do
set :protect_from_csrf, false
end
end
def test_creates_an_owner
email = 'test#example.com'
assert_empty Owner
post '/owners', owner: {email: email}
puts last_response.status
refute_empty Owner
end
def test_other
email = 'test#example.com'
get '/owners', owner: {email: email}
end
end

How do I do a really simple Sinatra LDAP authentication?

I looked at the Sinatra docs and they only seem to reference HTTP authentication. I'm looking for a really simple way to control access to routes based on a user being authorised/authenticated via an LDAP server.
I've already built a class that does the LDAP bit and returns an LDAP object if the user has successfully authenticated and nil if they haven't:
>>DirectoryUser.authenticate('user', 'password')
#<DirectoryUser:0x007ffb589a2328>
I can use this to determine if they've successfully authenticated or not.
As a next step I want to splice this into a simple Sinatra app that provides a form to collect the LDAP user and password:
require 'directoryUser'
require 'sinatra'
enable :sessions
get '/form' do
username = params[:username]
password = params[:password]
haml :form
end
Then I want to only allow routes if the 'DirectoryUser' object exists:
get '/protected' do # Only if DirectoryUser object exists
"This route is protected"
end
get '/unprotected' do
"This route is unprotected"
end
I've spent hours trying to find an answer to this but so far and can't seem to find anything that works for me.
I'd probably go with something like this:
require 'directoryUser'
require 'sinatra'
enable :sessions
helpers do
def authorize!
redirect(to('/login')) unless session[:user_id]
end
end
get '/login' do
haml :login # with the login form
end
post '/login' do
user = DirectoryUser.authenticate(params[:username], params[:password])
if user
session[:user_id] = user.id
# Or: session[:logged_in] = true, depending on your needs.
redirect to('/protected')
else
redirect to('/login')
end
end
get '/protected' do
authorize!
'This route is protected'
end
get '/unprotected' do
'This route is unprotected'
end

inherited_resources - best practices for missing parent model

Maybe you have seen/read the Railscast/Asciicast about subdomains in Rails 3. I'd like you to ask about best practices on how to implement an application behavior when the parent (in this article: "blog") is not found. Let me explain.
blog1.example.com/articles # it's normal situation
example.com/articles # abnormal situation.
In the second example no blog to find, but articles's routes are still available. I know, I can use something like this ...
def rescue_action(exception)
case exception
when ActiveRecord::RecordNotFound
return redirect_to blogs_path, :status => :moved_permanently
end
super
end
... but is it the "Rails way"? Any idea/comment on this?
What I did in this case, was to restrict the routing based on subdomain or no subdomain. In that case, you can easily have routes that only work on subdomains, resulting in a routing error (404) if someone tries to access that same route without a subdomain.
So for example:
routes.rb
Backend::Application.routes.draw do
constraints AppDomainRoutes.new do
# signup paths
get "/signup" => "accounts#new", as: "signup"
post "/signup" => "accounts#create", as: "signup"
# root
root to: "accounts#new"
end
constraints AccountDomainRoutes.new do
# password reset paths
get "/reset_password/:password_reset_token" => "reset_passwords#edit", as: "reset_user_password"
put "/reset_password/:password_reset_token" => "reset_passwords#update", as: "reset_user_password"
# websites
resources :websites
# root
root to: "websites#new"
end
# request password reset paths
get "/reset_password" => "reset_passwords#new", as: "reset_password_request"
post "/reset_password" => "reset_passwords#create", as: "reset_password_request"
# login paths
get "/login" => "sessions#new", as: "login"
post "/login" => "sessions#create", as: "login"
# logout paths
get "/logout" => "sessions#destroy", as: "logout"
delete "/logout" => "sessions#destroy", as: "logout"
end
And then in lib/routes:
app_domain_routes.rb
class AppDomainRoutes
def matches?(request)
request.subdomain.blank? || request.subdomain == "www"
end
end
account_domain_routes.rb
class AccountDomainRoutes
def matches?(request)
request.subdomain.present? && request.subdomain != "www"
end
end
Now, /signup is only accessible from the main application domain www.mydomain.com or mydomain.com and /websites/new is only accessible from *.mydomain.com. But /login is still accessible in both situations, for convenience sake.
Obviously this doesn't solve the issue of visiting invalid.mydomain.com when invalid in fact is not an account in the database.
For this you go back to the application_controller.rb and handle redirection there, like this:
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :redirect_unknown_account
private
# returns current subdomain (account.subdomain) or nil
def account_subdomain
#account_subdomain ||= request.subdomain if request.subdomain.present? && request.subdomain != "www"
end
def current_account
#current_account ||= Account.find_by_username(account_subdomain) if account_subdomain
end
def redirect_unknown_account
if account_subdomain && ! current_account
redirect_to signup_url(host: app_domain), alert: "This account does not exist."
end
end
def account_domain
#account_domain ||= "#{current_account.username}.#{app_domain}" if current_account
end
def app_domain
#app_domain ||= "mydomain.com"
end
end

Resources