Skip to content

Instantly share code, notes, and snippets.

@canton7
Last active September 10, 2018 03:16
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save canton7/1696927 to your computer and use it in GitHub Desktop.
Save canton7/1696927 to your computer and use it in GitHub Desktop.
Sinatra Auth in a Box
# Adapted from https://gist.github.com/1444494
# Things you need to do before using this lib:
# require 'sinatra/flash' (gem sinatra-flash)
# register Sinatra::Flash
# register Sinatra::AuthInABox
# enable :sessions optionally configuring your favourite sessions settings
# Have a User model, which has the methods self.authenticate(user, pass) and is_admin?
# In your forms (login, signup, etc) make sure you display flash[:errors]
# See the other files in the gist for Sequel/DataMapper models
module Sinatra
module AuthInABox
def auth_setup(params)
params.each do |k,v|
settings.authinabox[k] = v if settings.authinabox.has_key?(k)
end
end
def self.registered(app)
app.helpers Helpers
app.set :authinabox, {
:login_redirect => '/',
:logout_redirect => '/',
:signup_redirect => '/',
:login_url => '/login',
}
end
module Helpers
def login(params, options={})
options = {
:redirect => true, # Controls whether we redirect at all
:success_redirect => nil, # Override success redirect URL
:failure_redirect => nil, # Override failure redirect url
}.merge(options)
if user = User.authenticate(params[:username], params[:password])
session[:user] = user.id
redirect session[:login_return_to] || options[:success_redirect] || settings.authinabox[:login_redirect] if options[:redirect]
return user
else
flash[:errors] = "Login failed"
redirect options[:failure_redirect] || request.fullpath if options[:redirect]
return false
end
end
def logout(options={})
options = {
:redirect => true, # Whether we redirect at all
:redirect_to => nil, # Overrides where we redirect to
}.merge(options)
session[:user] = nil
redirect options[:redirect_to] || settings.authinabox[:logout_redirect] if options[:redirect]
end
def signup(params, options={})
options = {
:login => true, # Whether we login after creating the account
:redirect => true, # Controls whether we redirect at all
:success_redirect => nil, # Override where to redirect on success
:failure_redirect => nil, # Override where to redirect on failure
}.merge(options)
user = User.new(params)
if user.save
session[:user] = user.id if options[:login]
redirect options[:success_redirect] || settings.authinabox[:signup_redirect] if options[:redirect]
return user
else
flash[:errors] = '<ul><li>' << user.errors.full_messages.join('</li><li>') << '</li></ul>'
redirect options[:failure_redirect] || request.fullpath if options[:redirect]
return false
end
end
def login_required(options={})
options = {
:redirect => true, # Controls whether we redirect at all
:login_url => nil, # Overrides redirect if they aren't authenticated
}.merge(options)
return true if session[:user]
session[:login_return_to] = request.fullpath
redirect options[:login_url] || settings.authinabox[:login_url] if options[:redirect]
return false
end
def admin_required(options={})
options = {
:redirect => true, # Controls whether we redirect at all
:login_url => nil, # Overrides redirect if they aren't authenticated
:error_msg => 'You need to be an admin', # Flash text to set. False/nil to disable
}.merge(options)
unless session[:user]
session[:login_return_to] = request.fullpath
redirect options[:login_url] || settings.authinabox[:login_url] if options[:redirect]
return false
end
unless current_user.is_admin?
flash[:errors] = options[:error_msg] if options[:error_msg]
session[:login_return_to] = request.fullpath
redirect options[:login_url] || settings.authinabox[:login_url] if options[:redirect]
return false
end
return true
end
def current_user
return unless session[:user]
User.get(session[:user])
end
def is_admin?
current_user && current_user.is_admin?
end
end
end
register AuthInABox
end
# From bcrypt-ruby gem
require 'bcrypt'
class User
include DataMapper::Resource
attr_accessor :password, :password_confirmation
property :id, Serial
property :username, String, :required => true, :length => (2..32), :unique => true
property :password_hash, String, :length => 60
property :account_type, Enum[:standard, :admin], :required => true, :default => :standard
property :active, Boolean, :default => true
validates_presence_of :password
validates_presence_of :password_confirmation
validates_confirmation_of :password
def self.authenticate(username, pass)
user = first(:username => username)
return false unless user && user.active && BCrypt::Password.new(user.password_hash) == pass
user
end
def is_admin?
account_type == :admin
end
def password=(pass)
@password = pass
self.password_hash = BCrypt::Password.create(pass).to_s
end
end
Sequel.migration do
up do
create_table(:users, :ignore_index_errors=>true) do
primary_key :id
String :username, :size=>50, :null=>false
String :password_hash, :size=>60, :null=>false
String :account_type, :default=>'user'
TrueClass :active, :default=>true
index [:username], :unique=>true
end
end
down do
drop_table(:users)
end
end
# From bcrypt-ruby gem
require 'bcrypt'
class User < Sequel::Model
plugin :validation_helpers
attr_accessor :password, :password_confirmation
def self.authenticate(username, pass)
user = first(:username => username)
return false unless user && user.active && BCrypt::Password.new(user.password_hash) == pass
user
end
def is_admin?
account_type == 'admin'
end
def password=(pass)
@password = pass
self.password_hash = BCrypt::Password.create(pass).to_s
end
def validate
validates_presence [:username, :password, :password_confirmation]
validates_length_range 2..32, :username
errors.add(:password_confirmation, 'Password must match confirmation') unless password == password_confirmation
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment