How do I do a really simple Sinatra LDAP authentication? - ruby

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

Related

session[:id] variable not being picked up within another route from sinatra

I am following this tutorial here on setting up a user login https://learn.co/lessons/sinatra-user-auth
It says that in order to save a session[:id] in my route I would do something like this:
post '/login' do
#user = User.get_id(params['email'],params['password'])
session[:id] = #user # equals a numeric value of the matching user
redirect '/home'
end
however, when i get to my /home route I am no longer able to access the session[:id] that was set in my /login route, it returns nil
# this doesnt return the session id
# consequently I cant lookup my user records
get '/home' do
#user = User.object(session[:id]) # looks up user object with numeric value
erb :home
end
I have tried enabling sessions in my config controller, that didnt work either
module Controllers
class ApplicationController < Sinatra::Base
# . . . .
configure :production, :development do
enable :sessions, :logging
end
end
end
how do I make my app persist the session[:id] between routes?
Have you tried enable :sessions after you require your sinatra gem in your sinatra app? Not in your controller

Undefined method authenticated? form main::Object

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

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' })

Session data not being saved in Sinatra simple authentication

I created a simple authentication for Sinatra, however the session object seems to be cleaning up ALL custom keys. For example, when user logs in:
session[:user_id] = current_user.id
This is effectively stored in the session object for the current request. When a new request occurs the session[:user_id] is not there anymore. Session is active, cookies are enabled. I tried everything I can't figure out what it wrong (here is the all the relevant code: https://gist.github.com/ksiomelo/7656296).
application:
use Rack::Session::Cookie , :secret => "82e042cd6fde2bf1764f777236db799e"
enable :sessions # for flash messages
helpers:
def require_auth
unless session[:user_id]
flash[:error] = "You need to be logged in."
redirect to("/login")
end
end
def current_user
#current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]
end
signin:
authorized_user = User.authenticate(params[:email],params[:password])
if authorized_user
# update session / redirect
session[:user_id] = authorized_user.mongo_id.to_s
session.options[:expire_after] = 2592000 unless params[:remember].nil? # 30 days
# redirect to the wizard
flash[:info] = "Welcome back #{authorized_user.first_name}"
redirect to("/home")
You should likely try to set a provider for session handling, e. g.:
use Rack::Session::Pool, :expire_after => 2592000
Glad to help.

Showing users unique info when facebook authenticate , with Sinatra/ Ruby

Super beginner here.
Here's what I am trying to do:
Build a basic to do list app, where User X logs in with facebook, adds some items, sees them, logs out. User Y/Z/M/etc, should be able to log in see their OWN list, add their OWN items, etc.
AKA: a standard web app where you log in to your account and see your own info.
What I have so far:
Ability to build a list, log in with Facebook and having it know your name.
However, the list stays the same whether I log in or whether my friend logs in with her account.
What I need to do, and don't know how:
I need each user to be able to create and see their own list, and be able to come back to it and still see it/ add to it, etc.
I don't even know how this is called, would this be a database of users each with their own set of data? Would the lists need to be set up so they could be stored as a chunk of data?
Does it have something to do with this :Sessions in Sinatra using Facebook authentication If so, what?
If anyone could be give me some really really basic directions as to where to go from here, any tutorials or what I should be googling for, that'd be awesome.
Here's my main piece of code (warning: it's really messy) :
require 'sinatra'
require 'data_mapper'
require 'time'
require 'rubygems'
require 'json'
require 'omniauth'
require 'omniauth-facebook'
#TODO require 'omniauth-att'
SCOPE = 'email,read_stream'
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/queue.db")
class SinatraApp < Sinatra::Base
configure do
set :sessions, true
set :inline_templates, true
set :protection, :except => :frame_options
end
class Note
include DataMapper::Resource
property :id, Serial
property :content, Text, :required => true
property :complete, Boolean, :required => true, :default => false
property :created_at, DateTime
property :updated_at, DateTime
end
class User
include DataMapper::Resource
property :id, Serial
property :uid, String
property :name, String
property :created_at, DateTime
end
###### no clue what this does ##############
DataMapper.finalize
DataMapper.auto_upgrade!
enable :session
use OmniAuth::Builder do
provider :facebook, '464630283595639','5e4c7ad43bf111c10287c981d51127a3',:scope => SCOPE, :display => "popup"
#provider :att, 'client_id', 'client_secret', :callback_url => (ENV['BASE_DOMAIN']
end
###### root ##############
get '/' do
if current_user
#notes = Note.all :order => :id.desc
#title = 'Movie Queue'
erb :home
else
' sign in with Facebook'
end
end
###### authentication ##############
["/sign_in/?", "/signup/?"].each do |path|
get path do
redirect '/auth/facebook'
end
end
get '/auth/:name/callback' do
auth = request.env["omniauth.auth"]
user = User.first_or_create({ :uid => auth["uid"]}, {
:uid => auth["uid"],
:name => auth["first_name"],
:created_at => Time.now })
session[:user_id] = user.id
redirect '/'
end
helpers do
def current_user
#current_user ||= User.get(session[:user_id]) if session[:user_id]
end
end
##list making part###
post '/' do
n = Note.new
n.content = params[:content]
n.save
redirect '/'
end
get '/:id/delete' do
n = Note.get params[:id]
if n.destroy
redirect '/', :notice => 'Note deleted successfully.'
else
redirect '/', :error => 'Error deleting note.'
end
end
get '/:id/complete' do
n = Note.get params[:id]
n.complete = n.complete ? 0 : 1 # flip it
n.save
redirect '/'
end
########## logout and error handlers #############
get '/logout' do
session[:user_id] = nil
redirect '/'
end
get '/auth/failure' do
erb "<h1>Authentication Failed:</h1><h3>message:<h3> <pre>#{params}</pre>"
end
get '/auth/:provider/deauthorized' do
erb "#{params[:provider]} has deauthorized this app."
end
get '/protected' do
throw(:halt, [401, "Not authorized\n"]) unless session[:authenticated]
erb "<pre>#{request.env['omniauth.auth'].to_json}</pre><hr>
<a href='/logout'>Logout</a>"
end
end
########## don't know what this is #############
SinatraApp.run! if __FILE__ == $0
Disclaimer: I don't know Datamapper, but this should get you going.
There needs to be a way to associate a note with a user. This needs a table in the database, some would call it users_notes, personally I prefer users_rel_notes, (perhaps Datamapper has a convention for this… YMMV). Anyway, the table will have a minimum of 2 columns - the user's id, and the note id. You don't need a separate table as I wrote before (I'm lacking a bit of sleep, sorry!), that would be for a many to many relationship where a user could have several notes and a note could be associated with several users. For what you have, where only the owner of a note has access to it, it requires a one to many relationship. You could add a column to the notes table to store the user id.
Then, in the User class, add an association to the Note class, it's a one to many association and in Datamapper that's a has n, e.g.
has n, :notes
Now when you have a user instance, you can (probably) call the notes for that user via:
user.notes
I see you have the helper current_user defined, so if someone is logged on you could call current_user.notes to get back all the notes for the logged in user.
Remember, when you add a note to make sure you add a record to the association table, (probably, read the link) via user.notes << my_new_note.
The session is the information you keep around to identify the user and any other little bits of info that you may recurrently need. The likelyhood is, you're just storing an id for the user, or the facebook token that identifies them, and then during a request, if it's needed then you'll look inside the cookie, grab the id, look up that user by the id and get a user instance. Session info can be stored in several ways, most often cookies but you can use anything you would use to store any other data.

Resources