Skip to content

Instantly share code, notes, and snippets.

@mamantoha
Created October 26, 2011 14:35
Show Gist options
  • Save mamantoha/1316534 to your computer and use it in GitHub Desktop.
Save mamantoha/1316534 to your computer and use it in GitHub Desktop.
Warden integration with Sinatra and DataMapper(w/ BCryptHash) - http://ruby-ua.blogspot.com/2011/10/user-datamapper-bcrypthash-warden.html
# -*- encoding: utf-8 -*-
require 'sinatra'
require 'slim'
require 'warden'
require 'dm-core'
require 'dm-migrations'
require 'dm-timestamps'
require 'dm-validations'
require 'dm-types'
DataMapper::Logger.new(STDOUT, :debug)
DataMapper.setup(:default, "sqlite:///#{Dir.pwd}/project.db")
Slim::Engine.set_default_options :pretty => true
# Model and database table to hold administrator's usernames and passwords
#
class User
include DataMapper::Resource
property :id, Serial
property :name, String
property :password, BCryptHash
timestamps :at
attr_accessor :password_confirmation
validates_presence_of :name
validates_uniqueness_of :name
validates_confirmation_of :password
validates_with_method :password_non_blank
# Public class method than returns a user object if the caller supplies the correct name and password
#
def self.authenticate(name, password)
user = first(:name => name)
if user
if user.password != password
user = nil
end
end
user
end
private
def password_non_blank
if password_confirmation.nil? || password_confirmation.empty?
[ false, 'Missing password']
else
true
end
end
end
# finalize them after declaring all of the models
DataMapper.finalize
# wipes out existing data
DataMapper.auto_upgrade!
# create test user
#User.create(:name => 'user', :password => 'qwerty')
### Rack Setup
#
use Rack::Session::Cookie, :secret => "bla-bla-bla"
use Warden::Manager do |manager|
manager.default_strategies :password
manager.failure_app = FailureApp.new
end
###
### Session Setup
# Tell Warden how to serialize the user in and out of the session.
#
Warden::Manager.serialize_into_session do |user|
puts '[INFO] serialize into session'
user.id
end
Warden::Manager.serialize_from_session do |id|
puts '[INFO] serialize from session'
User.get(id)
end
###
### Declare Some Strategies
#
Warden::Strategies.add(:password) do
def valid?
puts '[INFO] password strategy valid?'
params['username'] || params['password']
end
def authenticate!
puts '[INFO] password strategy authenticate'
u = User.authenticate(params['username'], params['password'])
u.nil? ? fail!('Could not login in') : success!(u)
end
end
###
class FailureApp
def call(env)
uri = env['REQUEST_URI']
puts "failure #{env['REQUEST_METHOD']} #{uri}"
end
end
get '/' do
redirect '/login' unless env['warden'].user
slim :index
end
get '/login/?' do
slim :login
end
post '/login/?' do
if env['warden'].authenticate
redirect '/'
else
redirect '/login'
end
end
get '/logout/?' do
env['warden'].logout
redirect '/'
end
# index
get '/users/?' do
@users = User.all(:order => [ :name.asc ])
slim :'/users/index'
end
# new
get '/users/new' do
@user = User.new
slim :'/users/new'
end
# show
get '/users/:id' do
@user = User.first(params[:id])
slim :'/users/show'
end
# edit
get '/users/:id/edit' do
@user = User.first(params[:id])
end
# create
post '/users' do
@user = User.create(:name => params[:name], :password => params[:password], :password_confirmation => params[:password_confirmation])
if @user.save
redirect '/users'
else
slim :'/users/new'
end
end
# update
put '/users/:id' do
@user = User.first(params[:id])
if @user.update(:name => params[:name], :password => params[:password], :password_confirmation => params[:password_confirmation])
redirect '/users'
else
slim :'/users/edit'
end
end
# destroy
delete '/users/:id' do
@user = User.first(params[:id])
@user.destroy!
redirect '/users'
end
p Welcome, #{env['warden'].user.name}
a href='/logout' Log out
form action='/login' method='post'
ul
li#username
label Username:
br
input name='username' type='text'
li#password
label Password:
br
input name='password' type='password'
input type='submit' value='Log in'
h1 Listing users
table
tr
th Name
- @users.each do |user|
tr
td= user.name
td
a href="/users/#{user.id}" Show
td
a href="/users/#{user.id}/edit" Edit
br
a href="/users/new" New User
form method='post' action='/users'
fieldset
legend Enter User Details
div
label Name:
input type='text' name='name'
div
label Password:
input type='password' name='password'
div
label Confirm:
input type='password' name='password_confirmation'
input type='submit' value='Add User'
diff --git a/test1.rb b/test2.rb
index d4e5d66..d901d7a 100644
--- a/test1.rb
+++ b/test2.rb
@@ -8,7 +8,7 @@ require 'dm-core'
require 'dm-migrations'
require 'dm-timestamps'
require 'dm-validations'
-require 'digest/sha1'
+require 'dm-types'
DataMapper::Logger.new(STDOUT, :debug)
DataMapper.setup(:default, "sqlite:///#{Dir.pwd}/project.db")
@@ -22,8 +22,7 @@ class User
property :id, Serial
property :name, String
- property :hashed_password, String
- property :salt, String
+ property :password, BCryptHash
timestamps :at
@@ -40,44 +39,23 @@ class User
def self.authenticate(name, password)
user = first(:name => name)
if user
- expected_password = encrypted_password(password, user.salt)
- if user.hashed_password != expected_password
+ if user.password != password
user = nil
end
end
user
end
- def password
- @password
- end
-
- def password=(pwd)
- @password = pwd
- return if pwd.empty?
- create_new_salt
- self.hashed_password = User.encrypted_password(self.password, self.salt)
- end
-
private
def password_non_blank
- if hashed_password.nil? || hashed_password.empty?
+ if password_confirmation.nil? || password_confirmation.empty?
[ false, 'Missing password']
else
true
end
end
- def create_new_salt
- self.salt = self.object_id.to_s + rand.to_s
- end
-
- def self.encrypted_password(password, salt)
- string_to_hash = password + "wibble" + salt
- Digest::SHA1.hexdigest(string_to_hash)
- end
-
end
# finalize them after declaring all of the models
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment