├── .gitignore
├── Gemfile
├── History.txt
├── Manifest
├── Rakefile
├── TODO
├── UNLICENSE
├── example
├── database.yml
├── dm_extend_app.rb
├── dm_sinbook.rb
├── extend_views
│ ├── edit.haml
│ ├── index.haml
│ ├── login.haml
│ ├── show.haml
│ └── signup.haml
├── mm_app.rb
├── tc_app.rb
└── tc_sinbook.rb
├── lib
├── models
│ ├── abstract_user.rb
│ ├── activerecord_user.rb
│ ├── ar_adapter.rb
│ ├── datamapper_user.rb
│ ├── dm_adapter.rb
│ ├── mm_adapter.rb
│ ├── mongoid_adapter.rb
│ ├── mongoid_user.rb
│ ├── mongomapper_user.rb
│ ├── rufus_tokyo_user.rb
│ ├── sequel_adapter.rb
│ ├── sequel_user.rb
│ └── tc_adapter.rb
├── sinatra-authentication.rb
├── sinatra-authentication
│ └── models.rb
└── views
│ ├── edit.haml
│ ├── index.haml
│ ├── login.haml
│ ├── show.haml
│ └── signup.haml
├── readme.markdown
├── sinatra-authentication-0.3.2.gem
├── sinatra-authentication-0.4.2.gem
├── sinatra-authentication.gemspec
├── spec
├── run_all_specs.rb
└── unit
│ ├── ar_model_spec.rb
│ ├── dm_model_spec.rb
│ ├── mm_model_spec.rb
│ ├── mongoid_model_spec.rb
│ ├── sequel_model_spec.rb
│ ├── tc_model_spec.rb
│ └── user_specs.rb
└── test
├── activerecord_test.rb
├── datamapper_test.rb
├── lib
├── ar_app.rb
├── dm_app.rb
├── dm_extend_app.rb
├── dm_sinbook.rb
├── extend_views
│ ├── edit.haml
│ ├── index.haml
│ ├── login.haml
│ ├── show.haml
│ └── signup.haml
├── helper.rb
├── mm_app.rb
├── mongoid_app.rb
├── sequel_app.rb
├── tc_app.rb
└── tc_sinbook.rb
├── mongoid_test.rb
├── mongomapper_test.rb
├── route_tests.rb
├── rufus_tokyo_test.rb
└── sequel_test.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | pkg/
2 | *.swp
3 | *.db
4 | *.tct
5 | .bundle
6 | Gemfile.lock
7 | vendor/bundle
8 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source :rubygems
2 | gemspec # include gems from gemspec
3 |
4 | group :test do
5 | gem 'rake'
6 | gem 'rspec'
7 | end
8 |
9 | ##############################################
10 | # Other Dependancies
11 | # - Uncomment as needed.
12 | ##############################################
13 | #
14 | # HAML
15 | # gem 'haml'
16 | #
17 | # Mongoid
18 | # gem 'mongoid'
19 | #
20 | # MongoMapper
21 | # gem 'mongo_mapper'
22 | #
23 | # Active Record
24 | # gem 'activerecord'
25 | #
26 | # SQLite3
27 | # gem 'sqlite3'
28 | #
29 | # Sequel
30 | # gem 'sequel'
31 | #
32 | # Rufus Tokyo
33 | # gem 'rufus-tokyo'
34 |
--------------------------------------------------------------------------------
/History.txt:
--------------------------------------------------------------------------------
1 | == 0.0.1 2009-04-06
2 |
3 | * 1 major enhancement:
4 | * Initial release
5 |
--------------------------------------------------------------------------------
/Manifest:
--------------------------------------------------------------------------------
1 | History.txt
2 | Manifest
3 | Rakefile
4 | TODO
5 | lib/models/abstract_user.rb
6 | lib/models/datamapper_user.rb
7 | lib/models/dm_adapter.rb
8 | lib/models/mongomapper_user.rb
9 | lib/models/mm_adapter.rb
10 | lib/models/rufus_tokyo_user.rb
11 | lib/models/tc_adapter.rb
12 | lib/sinatra-authentication.rb
13 | lib/views/edit.haml
14 | lib/views/index.haml
15 | lib/views/login.haml
16 | lib/views/show.haml
17 | lib/views/signup.haml
18 | readme.markdown
19 | test/datamapper_test.rb
20 | test/lib/dm_app.rb
21 | test/lib/helper.rb
22 | test/lib/tc_app.rb
23 | test/lib/test.db
24 | test/lib/users.tct
25 | test/route_tests.rb
26 | test/rufus_tokyo_test.rb
27 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'rake'
3 | #require 'spec/rake/spectask'
4 |
5 | begin
6 | require 'jeweler'
7 |
8 | Jeweler::Tasks.new do |gemspec|
9 | gemspec.name = 'sinatra-authentication'
10 | gemspec.version = '0.4.1'
11 | gemspec.description = "Simple authentication plugin for sinatra."
12 | gemspec.summary = "Simple authentication plugin for sinatra."
13 | gemspec.homepage = "http://github.com/maxjustus/sinatra-authentication"
14 | gemspec.author = "Max Justus Spransy"
15 | gemspec.email = "maxjustus@gmail.com"
16 | gemspec.add_dependency "sinatra"
17 | gemspec.add_dependency "dm-core"
18 | gemspec.add_dependency "dm-migrations"
19 | gemspec.add_dependency "dm-validations"
20 | gemspec.add_dependency "dm-timestamps"
21 | gemspec.add_dependency "rufus-tokyo"
22 | gemspec.add_dependency "sinbook"
23 | gemspec.add_dependency "rack-flash3"
24 | end
25 | Jeweler::GemcutterTasks.new
26 | rescue LoadError
27 | puts "Jeweler (or a dependency) not available. Install it first!"
28 | end
29 |
30 | Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
31 |
32 | require 'rake/testtask'
33 |
34 | Rake::TestTask.new do |t|
35 | t.libs << "test"
36 | t.test_files = FileList['test/activerecord_test.rb']
37 | t.verbose = true
38 | end
39 |
40 | #desc 'Run all specs'
41 | #Spec::Rake::SpecTask.new('specs') do |t|
42 | # t.spec_files = FileList['spec/**/*.rb']
43 | #end
44 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | TODO:
2 | ensure all calls to new in adaptors are equivelant to "find or create by"
3 | - this is done in the Tc adaptor, and essentially works in Dm adaptor
4 | because I validate the uniqueness of email
5 | implement some way to allow for the creation of users with different
6 | permission levels in an untamperable manner. Perhaps with some secret key
7 | that must be sent in the form
8 | - look at other permissions systems for some feature ideas
9 | - add a config method that you pass a hash for configuring it's behavior
10 | - secret signup urls
11 | - account activation through email
12 |
13 | - ?implement a session store which isn't cookie based
14 | - turn on sessions unless they're already on
15 | - randomize the session key on every installation?
16 | - clean up adapters
17 | - write simple attribute declaration method for TcUser
18 | - condense the adapters down to the simplest solution that could possibly work
19 | - right now it's like I have two seperate goals, both which are important
20 | one is to write a simple ORM for rufus tokyo, the other is to create a simple adapter
21 | for different database backends. I think it would be better if I made the datamapper adapter more abstract
22 | and the api simpler, and then changed the way the controllers and views work to interface with the more abstract and simpler adapter.
23 | maybe make the adapter class called UserAdapter, and then TkUser and DmUser become User. All my controller method calls go to UserAdapter
24 | but then for people wanting to talk to the model, they just use User, since they aren't dealing with multiple backends and thus don't need
25 | a creepy adapter.
26 |
27 | - make site admin work the same for dm and tc, because I like how permission == -2 is site admin, then you could set a user as site admin instead of it being limited to the first user created
28 | and they wouldn't be deletable.
29 | or maybe I just make a heirarchy for that so users with lower permissions can't delete users with higher.
30 | just remember, this is supposed to be a simple authentication solution
31 |
32 |
33 | - for the User adapter
34 | - add pagination to all
35 | - serious cleanup of rufus_tokyo_user.rb
36 | - add virtual attribute declaration
37 | - add validations
38 | - remove the object syntax method_missing? and stick to hash accessors?
39 | - or rather then use method missing, dynamically create class methods based in the contents of the hash?
40 | - or create a validator tool for hashes. hash.valid?
41 | - change User to AbstractUser and DmUser and TcUser to User
42 |
43 | - add error messages for failed logins and stuff
44 |
45 | - PROBLEM the way I have method missing working right now, it doesn't behave the same way I've documented, since I use it for attributes
46 | - throw configuration errors on startup
47 | - investigate why sinatra_auth doesn't seem to work unless it's the last thing required..
48 |
49 | - add facebook connect
50 | - using facebooker and frankie
51 | - using the same method as datamapper vs tokyo in that the functionality is only included if the libraries are required
52 | before sinatra_auth is.
53 | - when a user signs in using facebook and doesn't have an email specified, an email field is included in the edit form.
54 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/example/database.yml:
--------------------------------------------------------------------------------
1 | db:
2 | adapter: sqlite3
3 | database: test.db
4 |
--------------------------------------------------------------------------------
/example/dm_extend_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'haml'
4 | require 'dm-core'
5 | require 'dm-migrations'
6 | require 'rack-flash'
7 | require 'sinatra-authentication'
8 |
9 | class DmUser
10 | property :name, String
11 | end
12 |
13 | DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/test.db")
14 | DataMapper.auto_migrate!
15 |
16 | set :sinatra_authentication_view_path, Pathname(__FILE__).dirname.expand_path + "extend_views/"
17 | use Rack::Session::Cookie, :secret => "heyhihello"
18 | use Rack::Flash
19 |
20 | set :environment, 'development'
21 | set :public, 'public'
22 | set :views, 'views'
23 |
24 | get '/' do
25 | haml "= render_login_logout", :layout => :layout
26 | end
27 |
--------------------------------------------------------------------------------
/example/dm_sinbook.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'haml'
4 | require 'sinbook'
5 | require 'dm-core'
6 | require 'dm-migrations'
7 | require 'sinatra-authentication'
8 |
9 | facebook do
10 | api_key 'aa2db1b96cb7b57f0c5b1d4d3d8f0a22'
11 | secret '21d94ee63969ae3b3f833689838ca00f'
12 | app_id 48652736613
13 | url 'peoplewithjetpacks.com:4568/'
14 | callback 'peoplewithjetpacks.com:4568/'
15 | end
16 |
17 | set :port, 4568
18 |
19 | DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/test.db")
20 | DataMapper.auto_migrate!
21 |
22 | use Rack::Session::Cookie, :secret => "heyhihello"
23 |
24 | set :environment, 'development'
25 | set :public, 'public'
26 | set :views, 'views'
27 |
28 | get '/' do
29 | haml :main
30 | end
31 |
32 | get '/test' do
33 | login_required
34 | 'hihihi'
35 | end
36 |
37 | __END__
38 |
39 | @@ layout
40 | %html{:xmlns=>"http://www.w3.org/1999/xhtml", :'xmlns:fb'=>"http://www.facebook.com/2008/fbml"}
41 | %head
42 | %title Welcome to my Facebook Connect website!
43 | %script{:type => 'text/javascript', :src => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US'}
44 | %body
45 | = render_login_logout
46 | = yield
47 | :javascript
48 | FB.init("#{fb.api_key}", "/receiver")
49 |
50 | @@ main
51 | - if fb[:user]
52 | Hi,
53 | %fb:profile-pic{:uid => fb[:user]}
54 | %fb:name{:uid => fb[:user], :useyou => 'false', :firstnameonly => 'true'}
55 | !
56 |
57 |
--------------------------------------------------------------------------------
/example/extend_views/edit.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | #sinatra_authentication_flash= flash[:notice]
3 | %h1
4 | Edit
5 | - if @user.id == current_user.id
6 | account
7 | - else
8 | - if @user.email
9 | = @user.email
10 | - elsif @user.fb_uid
11 |
12 | - else
13 | account
14 | %form{:action => "/users/#{@user.id}/edit", :method => "post"}
15 | .field
16 | .label
17 | %label{:for => "user_email"} Email
18 | %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text", :value => @user.email }
19 | .field
20 | .label
21 | %label{:for => "user_password"} New password
22 | %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
23 | .field
24 | .label
25 | %label{:for => "user_password_confirmation"} Confirm
26 | %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
27 | -# don't render permission field if admin and editing yourself so you don't shoot yourself in the foot
28 | - if current_user.admin? && current_user.id != @user.id
29 | .field
30 | .label
31 | %label{:for => 'permission_level'} Permission level
32 | %select{ :id => "permission_level", :name => "user[permission_level]" }
33 | %option{:value => -1, :selected => @user.admin?}
34 | Admin
35 | %option{:value => 1, :selected => @user.permission_level == 1}
36 | Authenticated user
37 | .buttons
38 | %input{ :value => "Update", :type => "submit" }
39 | - if Sinatra.const_defined?('FacebookObject')
40 | - unless @user.fb_uid
41 | |
42 | = render_facebook_connect_link('Link account with Facebook')
43 |
--------------------------------------------------------------------------------
/example/extend_views/index.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | %h1.page_title Users
3 | %table
4 | %tr
5 | %th
6 | - if current_user.admin?
7 | %th permission level
8 | - @users.each do |user|
9 | %tr
10 | %td
11 | - if user.email
12 | = user.email
13 | - elsif user.fb_uid
14 |
15 | - else
16 | "user #{user.id}"
17 | - if current_user.admin?
18 | %td= user.permission_level
19 | %td
20 | = user.name
21 | %td
22 | %a{:href => "/users/#{user.id}"} show
23 | - if current_user.admin?
24 | %td
25 | %a{:href => "/users/#{user.id}/edit"} edit
26 | %td
27 | -# this doesn't work for tk
28 | - if !user.site_admin?
29 | %a{:href => "/users/#{user.id}/delete", :onclick => "return confirm('you sure?')"} delete
30 | - else
31 | site admin
32 |
--------------------------------------------------------------------------------
/example/extend_views/login.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | #sinatra_authentication_flash= flash[:notice]
3 | %h1.page_title Login
4 | %form{:action => "/login", :method => "post"}
5 | .field
6 | .label
7 | %label{:for => "user_email'"} Email
8 | %input{:id => "user_email", :name => "email", :size => 30, :type => "text"}
9 | .field
10 | .label
11 | %label{:for => "user_password"} Password
12 | %input{:id => "user_password", :name => "password", :size => 30, :type => "password"}
13 | .buttons
14 | %input{:value => "login", :type => "submit"}
15 | %a{:href => "/signup", :class => 'sinatra_authentication_link'}
16 | Signup
17 | - if Sinatra.const_defined?('FacebookObject')
18 | .third_party_signup
19 | %h3.section_title One click login:
20 | .login_link.facebook_login
21 | = render_facebook_connect_link('Login using facebook', :size => 'large')
22 |
--------------------------------------------------------------------------------
/example/extend_views/show.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | %h1.page_title
3 | - if @user.email
4 | = @user.email
5 | - elsif @user.fb_uid
6 |
7 | - if current_user.admin?
8 | %h2 permission level
9 | = @user.permission_level
10 |
--------------------------------------------------------------------------------
/example/extend_views/signup.haml:
--------------------------------------------------------------------------------
1 | %h1 This view is overridden
2 | #sinatra_authentication
3 | #sinatra_authentication_flash= flash[:notice]
4 | %h1.page_title Signup
5 | %form{:action => "/signup", :method => "post"}
6 | .field
7 | .label
8 | %label{:for => "user_email"} Email
9 | %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text" }
10 | .field
11 | .label
12 | %label{:for => "user_password"} Password
13 | %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
14 | .field
15 | .label
16 | %label{:for => "user_name"} Name
17 | %input{ :id => "user_name", :name => "user[name]", :size => 30, :type => "text" }
18 | .field
19 | .label
20 | %label{:for => "user_password_confirmation"} Confirm Password
21 | %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
22 | .buttons
23 | %input{ :value => "Create account", :type => "submit" }
24 | %a{:href => "/login", :class => 'sinatra_authentication_link'}
25 | Login
26 | - if Sinatra.const_defined?('FacebookObject')
27 | .third_party_signup
28 | %h3.section_title One click signup:
29 | .login_link.facebook_login
30 | = render_facebook_connect_link('Signup using facebook', :size => 'large')
31 |
--------------------------------------------------------------------------------
/example/mm_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra/base'
3 | require 'haml'
4 | require 'mongo_mapper'
5 | require 'sinatra-authentication'
6 |
7 | logger = Logger.new($stdout)
8 | MongoMapper.connection = Mongo::Connection.new('db.mongohq.com', 27017, :logger => logger)
9 | MongoMapper.database = "fdbk"
10 | MongoMapper.database.authenticate(ENV['mongohq_user'], ENV['mongohq_pass'])
11 |
12 | class TestApp < Sinatra::Base
13 | use Rack::Session::Cookie, :secret => "heyhihello"
14 |
15 | set :environment, 'development'
16 | set :public, 'public'
17 | set :views, 'views'
18 |
19 | get '/' do
20 | haml "= render_login_logout", :layout => :layout
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/example/tc_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'haml'
4 | require 'rufus/tokyo'
5 | require 'sinatra-authentication'
6 |
7 | use Rack::Session::Cookie, :secret => "heyhihello"
8 | TcUserTable.cabinet_path = File.dirname(__FILE__)
9 |
10 | set :environment, 'development'
11 | set :public, 'public'
12 | set :views, 'views'
13 |
14 | get '/' do
15 | haml "= render_login_logout", :layout => :layout
16 | end
17 |
--------------------------------------------------------------------------------
/example/tc_sinbook.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'haml'
3 | require 'sinbook'
4 | require 'rufus/tokyo'
5 | require 'sinatra'
6 | require 'sinatra-authentication'
7 |
8 | use Rack::Session::Cookie, :secret => "heyhihello"
9 | TcUserTable.cabinet_path = File.dirname(__FILE__)
10 |
11 | facebook do
12 | api_key 'aa2db1b96cb7b57f0c5b1d4d3d8f0a22'
13 | secret '21d94ee63969ae3b3f833689838ca00f'
14 | app_id 48652736613
15 | url 'peoplewithjetpacks.com:4568/'
16 | callback 'peoplewithjetpacks.com:4568/'
17 | end
18 |
19 | set :port, 4568
20 |
21 | get '/' do
22 | haml :main
23 | end
24 |
25 | get '/test' do
26 | login_required
27 | 'hihihi'
28 | end
29 |
30 | __END__
31 |
32 | @@ layout
33 | %html{:xmlns=>"http://www.w3.org/1999/xhtml", :'xmlns:fb'=>"http://www.facebook.com/2008/fbml"}
34 | %head
35 | %title Welcome to my Facebook Connect website!
36 | %script{:type => 'text/javascript', :src => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US'}
37 | %script{:type => 'text/javascript', :src => 'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'}
38 | :javascript
39 | $(document).ready(function(){
40 | /* test facebook crap works with ajax */
41 | $('.sinatra-authentication-login').click(function(){
42 | $.get($(this).attr('href'), {}, function(data){
43 | $('#test_box').html(data);
44 | });
45 | return false;
46 | });
47 | });
48 | %body
49 | = render_login_logout
50 | = yield
51 | :javascript
52 | FB.init("#{fb.api_key}", "/receiver")
53 | #test_box
54 |
55 | @@ main
56 | - if fb[:user]
57 | Hi,
58 | %fb:profile-pic{:uid => fb[:user]}
59 | %fb:name{:uid => fb[:user], :useyou => 'false', :firstnameonly => 'true'}
60 | !
61 | %br/
62 |
63 |
--------------------------------------------------------------------------------
/lib/models/abstract_user.rb:
--------------------------------------------------------------------------------
1 | current_path = File.expand_path("..", __FILE__)
2 | if Object.const_defined?("DataMapper")
3 | #require 'dm-core'
4 | require 'dm-timestamps'
5 | require 'dm-validations'
6 | require File.join(current_path, 'datamapper_user')
7 | require File.join(current_path, 'dm_adapter')
8 | elsif Object.const_defined?("Rufus") && Rufus.const_defined?("Tokyo")
9 | require File.join(current_path, 'rufus_tokyo_user')
10 | require File.join(current_path, 'tc_adapter')
11 | elsif Object.const_defined?("MongoMapper")
12 | require File.join(current_path, 'mongomapper_user')
13 | require File.join(current_path, 'mm_adapter')
14 | elsif Object.const_defined?("Sequel")
15 | require File.join(current_path, 'sequel_user')
16 | require File.join(current_path, 'sequel_adapter')
17 | elsif Object.const_defined?("Mongoid")
18 | require File.join(current_path, 'mongoid_user')
19 | require File.join(current_path, 'mongoid_adapter')
20 | end
21 |
22 | class User
23 | if Object.const_defined?("DataMapper")
24 | include DmAdapter
25 | elsif Object.const_defined?("Rufus") && Rufus.const_defined?("Tokyo")
26 | include TcAdapter
27 | elsif Object.const_defined?("MongoMapper")
28 | include MmAdapter
29 | elsif Object.const_defined?("Sequel")
30 | include SequelAdapter
31 | elsif Object.const_defined?("Mongoid")
32 | include MongoidAdapter
33 | elsif Object.const_defined?("ActiveRecord")
34 | include ArAdapter
35 | else
36 | throw "you need to require either 'dm-core', 'mongo_mapper', 'sequel', 'mongoid', or 'rufus-tokyo' for sinatra-authentication to work"
37 | end
38 |
39 | def initialize(interfacing_class_instance)
40 | @instance = interfacing_class_instance
41 | end
42 |
43 | def id
44 | @instance.id
45 | end
46 |
47 | def self.authenticate(email, pass)
48 | current_user = get(:email => email)
49 | return nil if current_user.nil?
50 | return current_user if User.encrypt(pass, current_user.salt) == current_user.hashed_password
51 | nil
52 | end
53 |
54 | def db_instance
55 | @instance
56 | end
57 |
58 | protected
59 |
60 | def self.encrypt(pass, salt)
61 | Digest::SHA1.hexdigest(pass+salt)
62 | end
63 |
64 | def self.random_string(len)
65 | #generate a random password consisting of strings and digits
66 | chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
67 | newpass = ""
68 | 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
69 | return newpass
70 | end
71 |
72 | #def self.page_limit
73 | # 20
74 | #end
75 |
76 | #def self.page_offset(page = 0)
77 | # page.to_i * self.page_limit
78 | #end
79 | end
80 |
81 | class Hash
82 | def stringify
83 | inject({}) do |options, (key, value)|
84 | options[key.to_s] = value.to_s
85 | options
86 | end
87 | end
88 |
89 | def stringify!
90 | each do |key, value|
91 | delete(key)
92 | store(key.to_s, value.to_s)
93 | end
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/lib/models/activerecord_user.rb:
--------------------------------------------------------------------------------
1 | unless ActiveRecord::Base.connection.table_exists?("ar_users")
2 | class CreateArUsers < ActiveRecord::Migration
3 | def self.up
4 | create_table :ar_users do |t|
5 | t.string :email
6 | t.string :hashed_password
7 | t.string :salt
8 | t.integer :permission_level
9 | t.string :fb_uid
10 |
11 | t.timestamps
12 | end
13 |
14 | add_index :ar_users, :email, :unique => true
15 | end
16 |
17 | def self.down
18 | remove_index :ar_users, :email
19 | drop_table :ar_users
20 | end
21 | end
22 |
23 | CreateArUsers.up
24 | end
25 |
26 | #require 'logger'
27 | #ActiveRecord::Base.logger = Logger.new(STDOUT)
28 |
29 | class ArUser < ActiveRecord::Base
30 |
31 | attr_accessor :password, :password_confirmation
32 |
33 | validates_format_of :email, :with => /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
34 | validates_uniqueness_of :email
35 | validates_presence_of :password_confirmation, :unless => Proc.new { |t| t.hashed_password }
36 | validates_presence_of :password, :unless => Proc.new { |t| t.hashed_password }
37 | validates_confirmation_of :password
38 |
39 | def password=(pass)
40 | @password = pass
41 | self.salt = User.random_string(10) if !self.salt
42 | self.hashed_password = User.encrypt(@password, self.salt)
43 | end
44 |
45 | def admin?
46 | self.permission_level == -1 || self.id == 1
47 | end
48 |
49 | def site_admin?
50 | self.id == 1
51 | end
52 |
53 | def to_ary
54 | self.attributes.values
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/models/ar_adapter.rb:
--------------------------------------------------------------------------------
1 | module ArAdapter
2 | def self.included(base)
3 | base.extend ClassMethods
4 | base.class_eval { include ArAdapter::InstanceMethods }
5 | end
6 |
7 | module ClassMethods
8 | #pass all args to this
9 | def all
10 | result = ArUser.all(:order => ["created_at DESC"])
11 | result.collect {|instance| self.new instance}
12 | end
13 |
14 | def get(hash)
15 | if user = ArUser.where(hash).first
16 | self.new user
17 | else
18 | nil
19 | end
20 | end
21 |
22 | def set(attributes)
23 | user = ArUser.new attributes
24 | user.save
25 | self.new user
26 | end
27 |
28 | def set!(attributes)
29 | user = ArUser.new attributes
30 | user.save!
31 | self.new user
32 | end
33 |
34 | def delete(pk)
35 | user = ArUser.first(pk).first
36 | user.destroy
37 | user.destroyed?
38 | end
39 | end
40 |
41 | module InstanceMethods
42 | def valid
43 | @instance.valid?
44 | end
45 |
46 | def errors
47 | @instance.errors.collect do |k,v|
48 | "#{k} #{v}"
49 | end.join(', ')
50 | end
51 |
52 | def update(attributes)
53 | @instance.update_attributes attributes
54 | #self
55 | end
56 |
57 | def saved
58 | @instance.valid?
59 | end
60 |
61 | def method_missing(meth, *args, &block)
62 | #cool I just found out * on an array turns the array into a list of args for a function
63 | @instance.send(meth, *args, &block)
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/lib/models/datamapper_user.rb:
--------------------------------------------------------------------------------
1 | class DmUser
2 | include DataMapper::Resource
3 |
4 | property :id, Serial
5 | property :email, String, :length => (5..40), :unique => true, :format => :email_address
6 | property :hashed_password, String
7 | property :salt, String
8 | #Was DateTime should be DateTime?
9 | property :created_at, Time
10 | property :permission_level, Integer, :default => 1
11 | if Sinatra.const_defined?('FacebookObject')
12 | property :fb_uid, String
13 | end
14 |
15 | attr_accessor :password, :password_confirmation
16 | #protected equievelant? :protected => true doesn't exist in dm 0.10.0
17 | #protected :id, :salt
18 | #doesn't behave correctly, I'm not even sure why I did this.
19 |
20 | validates_presence_of :password_confirmation, :unless => Proc.new { |t| t.hashed_password }
21 | validates_presence_of :password, :unless => Proc.new { |t| t.hashed_password }
22 | validates_confirmation_of :password
23 |
24 | def password=(pass)
25 | @password = pass
26 | self.salt = User.random_string(10) if !self.salt
27 | self.hashed_password = User.encrypt(@password, self.salt)
28 | end
29 |
30 | def admin?
31 | self.permission_level == -1 || self.id == 1
32 | end
33 |
34 | def site_admin?
35 | self.id == 1
36 | end
37 |
38 | protected
39 |
40 | def method_missing(m, *args)
41 | return false
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/models/dm_adapter.rb:
--------------------------------------------------------------------------------
1 | module DmAdapter
2 | def self.included(base)
3 | base.extend ClassMethods
4 | base.class_eval { include DmAdapter::InstanceMethods }
5 | end
6 |
7 | module ClassMethods
8 | #pass all args to this
9 | def all
10 | result = DmUser.all(:order => [:created_at.desc])
11 | result.collect {|instance| self.new instance}
12 | end
13 |
14 | def get(hash)
15 | if user = DmUser.first(hash)
16 | self.new user
17 | else
18 | nil
19 | end
20 | end
21 |
22 | def set(attributes)
23 | user = DmUser.new attributes
24 | user.save
25 | self.new user
26 | end
27 |
28 | def set!(attributes)
29 | user = DmUser.new attributes
30 | user.save!
31 | self.new user
32 | end
33 |
34 | def delete(pk)
35 | user = DmUser.first(:id => pk)
36 | user.destroy
37 | end
38 | end
39 |
40 | module InstanceMethods
41 | def valid
42 | @instance.valid?
43 | end
44 |
45 | def errors
46 | @instance.errors.collect do |k,v|
47 | "#{k} #{v}"
48 | end.join(', ')
49 | end
50 |
51 | def update(attributes)
52 | @instance.update attributes
53 | #self
54 | end
55 |
56 | def method_missing(meth, *args, &block)
57 | #cool I just found out * on an array turns the array into a list of args for a function
58 | @instance.send(meth, *args, &block)
59 | end
60 | end
61 | end
62 |
--------------------------------------------------------------------------------
/lib/models/mm_adapter.rb:
--------------------------------------------------------------------------------
1 | module MmAdapter
2 | def self.included(base)
3 | base.extend ClassMethods
4 | base.class_eval { include MmAdapter::InstanceMethods }
5 | end
6 |
7 | module ClassMethods
8 | def all
9 | result = MmUser.all(:order => 'created_at desc')
10 | result.collect {|instance| self.new instance}
11 | end
12 |
13 | def get(hash)
14 | if user = MmUser.first(hash)
15 | self.new user
16 | else
17 | nil
18 | end
19 | end
20 |
21 | def set(attributes)
22 | #puts attributes.inspect
23 | user = MmUser.new attributes
24 | user.id = nil unless user.save
25 | self.new user
26 | end
27 |
28 | def set!(attributes)
29 | user = MmUser.new attributes
30 | user.save(:validate => false)
31 | self.new user
32 | end
33 |
34 | def delete(pk)
35 | user = MmUser.first(:id => pk)
36 | #returns nil on success. Is this correct? Will it return something else on failure?
37 | user.destroy
38 | user.destroyed?
39 | end
40 | end
41 |
42 | module InstanceMethods
43 | def valid
44 | @instance.valid?
45 | end
46 |
47 | def update(attributes)
48 | @instance.update_attributes attributes
49 | @instance.save
50 | end
51 |
52 | def saved
53 | @instance.valid?
54 | end
55 |
56 | def errors
57 | @instance.errors.full_messages.join(', ')
58 | end
59 |
60 | def method_missing(meth, *args, &block)
61 | #cool I just found out * on an array turns the array into a list of args for a function
62 | @instance.send(meth, *args, &block)
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/lib/models/mongoid_adapter.rb:
--------------------------------------------------------------------------------
1 | module MongoidAdapter
2 | def self.included(base)
3 | base.extend ClassMethods
4 | base.class_eval { include MongoidAdapter::InstanceMethods }
5 | end
6 |
7 | module ClassMethods
8 | def all
9 | result = MongoidUser.criteria.order_by([[:created_at, :desc]])
10 | result.collect {|instance| self.new instance }
11 | end
12 |
13 | def get(hash)
14 | if user = MongoidUser.where(hash).first
15 | self.new user
16 | else
17 | nil
18 | end
19 | end
20 |
21 | def set(attributes)
22 | #puts attributes.inspect
23 | user = MongoidUser.new attributes
24 | #puts user.inspect
25 | #puts user.to_json
26 | user.save
27 | self.new user
28 | end
29 |
30 | def set!(attributes)
31 | user = MongoidUser.new attributes
32 | user.save(:validate => false)
33 | self.new user
34 | end
35 |
36 | def delete(pk)
37 | user = MongoidUser.find(pk)
38 | #returns nil on success. Is this correct? Will it return something else on failure?
39 | user.destroy
40 | user.destroyed?
41 | end
42 | end
43 |
44 | module InstanceMethods
45 | def valid
46 | @instance.valid?
47 | end
48 |
49 | def update(attributes)
50 | @instance.update_attributes(attributes)
51 | @instance.save
52 | end
53 |
54 | def saved
55 | @instance.valid?
56 | end
57 |
58 | def errors
59 | @instance.errors.full_messages.join(', ')
60 | end
61 |
62 | def method_missing(meth, *args, &block)
63 | #cool I just found out * on an array turns the array into a list of args for a function
64 | @instance.send(meth, *args, &block)
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/lib/models/mongoid_user.rb:
--------------------------------------------------------------------------------
1 | class MongoidUser
2 | include Mongoid::Document
3 | include Mongoid::Timestamps
4 | field :email
5 | field :hashed_password
6 | field :salt
7 | field :permission_level, :type => Integer, :default => 1
8 | if Sinatra.const_defined?('FacebookObject')
9 | field :fb_uid
10 | end
11 |
12 | # Validations
13 | validates_uniqueness_of :email
14 | validates_format_of :email, :with => /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
15 | validates_presence_of :password
16 | validates_confirmation_of :password
17 |
18 | #attr_protected :_id, :salt
19 |
20 | attr_accessor :password, :password_confirmation
21 |
22 | def password=(pass)
23 | @password = pass
24 | self.salt = User.random_string(10) if !self.salt
25 | self.hashed_password = User.encrypt(@password, self.salt)
26 | end
27 |
28 | def admin?
29 | self.permission_level == -1 || self.id == 1
30 | end
31 |
32 | def site_admin?
33 | self.id == 1
34 | end
35 |
36 | protected
37 |
38 | def method_missing(m, *args)
39 | return false
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/models/mongomapper_user.rb:
--------------------------------------------------------------------------------
1 | class MmUser
2 | include MongoMapper::Document
3 |
4 | email_regexp = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
5 | key :email, String, :unique => true, :format => email_regexp
6 | key :hashed_password, String
7 | key :salt, String
8 | key :permission_level, Integer, :default => 1
9 | if Sinatra.const_defined?('FacebookObject')
10 | key :fb_uid, String
11 | end
12 |
13 | timestamps!
14 |
15 | attr_accessor :password, :password_confirmation
16 | validates_presence_of :password, :allow_blank => true
17 | validates_confirmation_of :password
18 |
19 | def password=(pass)
20 | @password = pass
21 | self.salt = User.random_string(10) if !self.salt
22 | self.hashed_password = User.encrypt(@password, self.salt)
23 | end
24 |
25 | def admin?
26 | self.permission_level == -1 || self.id == 1
27 | end
28 |
29 | def site_admin?
30 | self.id == 1
31 | end
32 |
33 | protected
34 |
35 | def method_missing(m, *args)
36 | return false
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/lib/models/rufus_tokyo_user.rb:
--------------------------------------------------------------------------------
1 | class TcUser
2 | attr_accessor :errors
3 | #include RufusOrm
4 |
5 | #custom_attribute :salt
6 | #custom_attribute :hashed_password
7 | #custom_attribute :hashed_permission_level
8 | #custom_attribute :created_at
9 | #custom_attribute :created_at_i
10 |
11 | #attribute method?
12 | #if I'm gonna write all this, I might as well create a tinyyyy
13 | #orm, that's more just like a way to define custom attributes for cabinets
14 | #something worth noting though is that even datamapper defines custom
15 | #attributes by allowing the developer to override setter methods.
16 | #and it just calls all the setter methods defined in the model.
17 | #the only trouble with this route is it assumes a predefined schema.
18 | #and thus it knows what setter methods to call.
19 | #I would write a class method that allows you to declare attributes like
20 | #attribute :salt, with an optional block (which gets passed a hash of attributes)
21 | #if a block isn't defined, it looks in the class for a salt=(attributes) function, calls it and marges the
22 | #result into the hash going into the database, like 'attributes.merge{"salt" => result}'
23 | #so my 'set' method or whatever I choose to call it, has to somehow look through each
24 | #declared attribute, call the method associated with it, and merge the result into the hash going
25 | #into the database.
26 | #
27 | #but what if I don't want an attribute passed in to be stored into the database? What if I just want to
28 | #create a virtual attribute, for declaring other attributes?
29 | #I might create a class variable that I store all the attributes in, and the I can get to it from any setter,
30 | #and then after I've called all the setters, I store that class variable into the database.
31 | #or, I do all of this on the instance level, and have a save method.
32 |
33 | def initialize(attributes, errors = [])
34 | @attributes = attributes
35 | if @attributes['created_at']
36 | @attributes['created_at'] = Time.parse(@attributes['created_at'])
37 | end
38 |
39 | @errors = errors
40 | end
41 |
42 | def self.query(&block)
43 | connection = TcUserTable.new
44 | result_set = connection.query(&block)
45 | output = result_set.collect { |result_hash| TcUser.new(result_hash) }
46 | connection.close
47 | output
48 | end
49 |
50 | def self.get(key)
51 | connection = TcUserTable.new
52 | result = connection[key]
53 | connection.close
54 | if result
55 | self.new(result.merge({:pk => key}))
56 | else
57 | false
58 | end
59 | end
60 |
61 | def self.set(attributes)
62 | #this way of validating is real crap, replace it with Validator maybe
63 | #and maybe replace all this hash merging with setters for the various attributes that update @attributes, and then I can call save to store to the database
64 | #or maybe just write a little method that makes hash merger look a little cleaner
65 | pk = attributes.delete(:pk) if attributes[:pk]
66 |
67 | errors = []
68 |
69 | email_regexp = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
70 | attributes = attributes.stringify
71 |
72 | unless attributes['email'] =~ email_regexp
73 | errors << 'Email is invalid'
74 | end
75 |
76 | if attributes['password'] != attributes.delete('password_confirmation') && attributes['password'] != nil
77 | errors << "Passwords don't match"
78 | end
79 |
80 | if attributes['password'] != nil && attributes['password'].length == 0
81 | errors << "You need to provide a password"
82 | end
83 |
84 | if errors.length == 0
85 | password = attributes.delete('password')
86 | if !attributes['salt']
87 | salt = User.random_string(10)
88 | attributes.merge!({'salt' => salt})
89 | attributes.merge!('hashed_password' => User.encrypt(password, salt))
90 | end
91 | permission_level = attributes['permission_level'] ? attributes['permission_level'] : '1'
92 | attributes.merge!('permission_level' => permission_level)
93 | if attributes['created_at']
94 | attributes.merge!('created_at_i' => Time.parse(attributes['created_at']).to_i)
95 | else
96 | attributes.merge!('created_at' => Time.now.to_s)
97 | attributes.merge!('created_at_i' => Time.now.to_i.to_s)
98 | end
99 |
100 | existing_user = TcUser.query do |q|
101 | q.add 'email', :streq, attributes['email']
102 | end[0]
103 |
104 | if existing_user && existing_user[:pk] != pk
105 | errors << "Email is already taken"
106 | return self.new(attributes, errors)
107 | else
108 | connection = TcUserTable.new
109 | pk ||= connection.genuid.to_s
110 | #site admin if their first
111 | attributes.merge!({'permission_level' => '-2'}) if pk == '1'
112 | result = connection[pk] = attributes
113 | #might not need this in newer version of rufus
114 | result.merge!({:pk => pk})
115 | connection.close
116 | self.new(result, errors)
117 | end
118 | else
119 | self.new(attributes, errors)
120 | end
121 | end
122 |
123 | def self.set!(attributes)
124 | connection = TcUserTable.new
125 | pk = connection.genuid.to_s
126 | result = connection[pk] = attributes
127 | result.merge!({:pk => pk})
128 | connection.close
129 | self.new(result)
130 | end
131 |
132 | def self.delete(pk)
133 | connection = TcUserTable.new
134 | connection.delete(pk)
135 | connection.close
136 | end
137 |
138 | def update(attributes)
139 | self.errors = []
140 | new_attributes = @attributes.merge(attributes)
141 | updated_user = TcUser.set(new_attributes)
142 | self.errors = updated_user.errors
143 | updated_user.errors.length < 1
144 | end
145 |
146 | def [](key)
147 | @attributes[key]
148 | end
149 |
150 | #saves to database and returns self
151 | def []=(key, value)
152 | @attributes[key] = value
153 | #change so that it sets the attributes and then you call save to save to the database?
154 | connection = TcUserTable.new
155 | connection[@attributes[:pk]] = @attributes.merge!({key => value})
156 | connection.close
157 | self
158 | end
159 |
160 | def id
161 | @attributes[:pk]
162 | end
163 |
164 | def admin?
165 | @attributes['permission_level'] == '-1' || site_admin?
166 | end
167 |
168 | def site_admin?
169 | #-2 is the site admin
170 | @attributes['permission_level'] == '-2'
171 | end
172 |
173 | #from hash extension for making hashes like javascript objects
174 | def method_missing(meth,*args)
175 | if /=$/=~(meth=meth.id2name) then
176 | self[meth[0...-1]] = (args.length<2 ? args[0] : args)
177 | elsif @attributes[meth]
178 | @attributes[meth]
179 | else
180 | false
181 | end
182 | end
183 | end
184 |
185 | if Rufus::Tokyo.const_defined?('Table')
186 | class TokyoTableDad < Rufus::Tokyo::Table
187 | end
188 | elsif Rufus::Edo.const_defined?('Table')
189 | class TokyoTableDad < Rufus::Edo::Table
190 | end
191 | else
192 | throw 'wtf?'
193 | end
194 |
195 | class TcUserTable < TokyoTableDad
196 | @@path = false
197 | def initialize
198 | #make this path configurable somehow
199 | raise "you need to define a path for the user cabinet to be stored at, like so: TcUserTable.cabinet_path = 'folder/where/you/wanna/store/your/database'" unless @@path
200 | super(@@path + '/users.tct')
201 | end
202 |
203 | def self.cabinet_path=(path)
204 | @@path = path
205 | end
206 | end
207 |
--------------------------------------------------------------------------------
/lib/models/sequel_adapter.rb:
--------------------------------------------------------------------------------
1 | module SequelAdapter
2 | def self.included(base)
3 | base.extend ClassMethods
4 | base.class_eval { include SequelAdapter::InstanceMethods }
5 | end
6 |
7 | module ClassMethods
8 | def all
9 | result = SequelUser.order(:created_at.desc).all
10 | result.collect {|instance| self.new instance}
11 | end
12 |
13 | def get(hash)
14 | if user = SequelUser.first(hash)
15 | self.new user
16 | else
17 | nil
18 | end
19 | end
20 |
21 | def set(attributes)
22 | user = SequelUser.new attributes
23 | if user.valid?
24 | user.save
25 | #false
26 | end
27 |
28 | self.new user
29 | end
30 |
31 | def set!(attributes)
32 | user = SequelUser.new attributes
33 | user.save!
34 | self.new user
35 | end
36 |
37 | def delete(pk)
38 | user = SequelUser.first(:id => pk)
39 | user.destroy
40 | !user.exists?
41 | end
42 | end
43 |
44 | module InstanceMethods
45 | def errors
46 | @instance.errors.full_messages.join(', ')
47 | end
48 |
49 | def valid
50 | @instance.valid?
51 | end
52 |
53 | def update(attributes)
54 | @instance.set attributes
55 | if @instance.valid?
56 | @instance.save
57 | true
58 | else
59 | false
60 | end
61 | end
62 |
63 | def method_missing(meth, *args, &block)
64 | #cool I just found out * on an array turns the array into a list of args for a function
65 | @instance.send(meth, *args, &block)
66 | end
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/lib/models/sequel_user.rb:
--------------------------------------------------------------------------------
1 | unless DB.table_exists? :sequel_users
2 | DB.create_table :sequel_users do
3 | primary_key :id
4 | String :email, :unique => true
5 | String :hashed_password
6 | String :salt
7 | DateTime :created_at
8 | Integer :permission_level, :default => 1
9 | if Sinatra.const_defined?('FacebookObject')
10 | String :fb_uid
11 | end
12 |
13 | #check{{char_length(email)=>5..40}}
14 | end
15 | end
16 |
17 | class SequelUser < Sequel::Model
18 | attr_writer :password_confirmation
19 | plugin :validation_helpers
20 | plugin :timestamps, :create => :created_at
21 |
22 | def validate
23 | super
24 | email_regexp = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
25 | validates_format email_regexp, :email
26 | validates_presence :email
27 | validates_unique :email
28 | validates_presence :password if new?
29 | errors.add :passwords, ' don\'t match' unless @password == @password_confirmation
30 | #validate equality?
31 | end
32 | #TODO validate format of email
33 |
34 | def password=(pass)
35 | @password = pass
36 | self.salt = User.random_string(10) if !self.salt
37 | self.hashed_password = User.encrypt(@password, self.salt)
38 | end
39 |
40 | def admin?
41 | self.permission_level == -1 || self.id == 1
42 | end
43 |
44 | def site_admin?
45 | self.id == 1
46 | end
47 |
48 | protected
49 |
50 | def method_missing(m, *args)
51 | return false
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/models/tc_adapter.rb:
--------------------------------------------------------------------------------
1 | module TcAdapter
2 | def self.included(base)
3 | base.extend ClassMethods
4 | base.class_eval {
5 | include TcAdapter::InstanceMethods
6 | alias :class_id :id
7 | }
8 | end
9 |
10 | module ClassMethods
11 | #TODO add pagination
12 | def all
13 | result = TcUser.query do |q|
14 | q.order_by 'created_at_i', :numdesc
15 | end
16 |
17 | #these will be the same for all adapters, they should be defined in the user class, and these methods should have a different name?
18 | result.collect {|instance| self.new instance }
19 | end
20 |
21 | def get(hash)
22 | if hash[:id]
23 | pk = hash[:id]
24 | if pk.length > 0
25 | result = TcUser.get(pk)
26 | else
27 | nil
28 | end
29 | else
30 | result = TcUser.query do |q|
31 | hash.each do |key, value|
32 | q.add key.to_s, :streq, value.to_s
33 | end
34 | end[0]
35 | end
36 | #elsif hash[:email]
37 | # result = TcUser.query do |q|
38 | # q.add 'email', :streq, hash[:email]
39 | # end[0]
40 | #the zero is because this returns an array but get should return the first result
41 | #end
42 |
43 | if result
44 | self.new result
45 | else
46 | nil
47 | end
48 | end
49 |
50 | def set(attributes)
51 | user = TcUser.query do |q|
52 | q.add 'email', :streq, attributes['email']
53 | end
54 |
55 | if user == [] #no user
56 | self.new TcUser.set(attributes)
57 | else
58 | if attributes['email'].length == 0
59 | error = 'You need to provide an email address'
60 | else
61 | error = 'That email is already taken'
62 | end
63 |
64 | TcUser.new(attributes, error)
65 | end
66 | end
67 |
68 | def set!(attributes)
69 | self.new TcUser.set!(attributes)
70 | end
71 |
72 | def delete(pk)
73 | #true or false
74 | !!TcUser.delete(pk)
75 | end
76 | end
77 |
78 | module InstanceMethods
79 | def update(attributes)
80 | @instance.update attributes
81 | end
82 |
83 | def errors
84 | @instance.errors.join(', ')
85 | end
86 |
87 | def valid
88 | @instance.errors.length == 0
89 | end
90 |
91 | def method_missing(meth, *args, &block)
92 | #cool I just found out * on an array turn the array into a list of args for a function
93 | @instance.send(meth, *args, &block)
94 | end
95 |
96 | #this was the only thing that didn't get passed on to method_missing because this is a method of object doh
97 | def id
98 | @instance.id
99 | end
100 | end
101 | end
102 |
--------------------------------------------------------------------------------
/lib/sinatra-authentication.rb:
--------------------------------------------------------------------------------
1 | require 'sinatra/base'
2 | require File.expand_path("../models/abstract_user", __FILE__)
3 |
4 | module Sinatra
5 | module SinatraAuthentication
6 | def self.registered(app)
7 | #INVESTIGATE
8 | #the possibility of sinatra having an array of view_paths to load from
9 | #PROBLEM
10 | #sinatra 9.1.1 doesn't have multiple view capability anywhere
11 | #so to get around I have to do it totally manually by
12 | #loading the view from this path into a string and rendering it
13 | app.set :sinatra_authentication_view_path, File.expand_path('../views/', __FILE__)
14 | unless defined?(settings.template_engine)
15 | app.set :template_engine, :haml
16 | end
17 |
18 | app.get '/users/?' do
19 | login_required
20 | redirect "/" unless current_user.admin?
21 |
22 | @users = User.all
23 | if @users != []
24 | send settings.template_engine, get_view_as_string("index.#{settings.template_engine}"), :layout => use_layout?
25 | else
26 | redirect '/signup'
27 | end
28 | end
29 |
30 | app.get '/users/:id/?' do
31 | login_required
32 |
33 | if params[:id].to_i != current_user.id and !current_user.admin?
34 | redirect "/"
35 | end
36 | @user = User.get(:id => params[:id])
37 | send settings.template_engine, get_view_as_string("show.#{settings.template_engine}"), :layout => use_layout?
38 | end
39 |
40 | #convenience for ajax but maybe entirely stupid and unnecesary
41 | app.get '/logged_in' do
42 | if session[:user]
43 | "true"
44 | else
45 | "false"
46 | end
47 | end
48 |
49 | app.get '/login/?' do
50 | if session[:user]
51 | redirect '/'
52 | else
53 | send settings.template_engine, get_view_as_string("login.#{settings.template_engine}"), :layout => use_layout?
54 | end
55 | end
56 |
57 | app.post '/login/?' do
58 | if user = User.authenticate(params[:email], params[:password])
59 | session[:user] = user.id
60 |
61 | if Rack.const_defined?('Flash')
62 | flash[:notice] = "Login successful."
63 | end
64 |
65 | if session[:return_to]
66 | redirect_url = session[:return_to]
67 | session[:return_to] = false
68 | redirect redirect_url
69 | else
70 | redirect '/'
71 | end
72 | else
73 | if Rack.const_defined?('Flash')
74 | flash[:error] = "The email or password you entered is incorrect."
75 | end
76 | redirect '/login'
77 | end
78 | end
79 |
80 | app.get '/logout/?' do
81 | session[:user] = nil
82 | if Rack.const_defined?('Flash')
83 | flash[:notice] = "Logout successful."
84 | end
85 | return_to = ( session[:return_to] ? session[:return_to] : '/' )
86 | redirect return_to
87 | end
88 |
89 | app.get '/signup/?' do
90 | if session[:user]
91 | redirect '/'
92 | else
93 | send settings.template_engine, get_view_as_string("signup.#{settings.template_engine}"), :layout => use_layout?
94 | end
95 | end
96 |
97 | app.post '/signup/?' do
98 | @user = User.set(params[:user])
99 | if @user.valid && @user.id
100 | session[:user] = @user.id
101 | if Rack.const_defined?('Flash')
102 | flash[:notice] = "Account created."
103 | end
104 | redirect '/'
105 | else
106 | if Rack.const_defined?('Flash')
107 | flash[:error] = "There were some problems creating your account: #{@user.errors}."
108 | end
109 | redirect '/signup?' + hash_to_query_string(params['user'])
110 | end
111 | end
112 |
113 | app.get '/users/:id/edit/?' do
114 | login_required
115 | redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
116 | @user = User.get(:id => params[:id])
117 | send settings.template_engine, get_view_as_string("edit.#{settings.template_engine}"), :layout => use_layout?
118 | end
119 |
120 | app.post '/users/:id/edit/?' do
121 | login_required
122 | redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
123 |
124 | user = User.get(:id => params[:id])
125 | user_attributes = params[:user]
126 | if params[:user][:password] == ""
127 | user_attributes.delete("password")
128 | user_attributes.delete("password_confirmation")
129 | end
130 |
131 | if user.update(user_attributes)
132 | if Rack.const_defined?('Flash')
133 | flash[:notice] = 'Account updated.'
134 | end
135 | redirect '/'
136 | else
137 | if Rack.const_defined?('Flash')
138 | flash[:error] = "Whoops, looks like there were some problems with your updates: #{user.errors}."
139 | end
140 | redirect "/users/#{user.id}/edit?" + hash_to_query_string(user_attributes)
141 | end
142 | end
143 |
144 | app.get '/users/:id/delete/?' do
145 | login_required
146 | redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
147 |
148 | if User.delete(params[:id])
149 | if Rack.const_defined?('Flash')
150 | flash[:notice] = "User deleted."
151 | end
152 | else
153 | if Rack.const_defined?('Flash')
154 | flash[:error] = "Deletion failed."
155 | end
156 | end
157 | redirect '/'
158 | end
159 |
160 |
161 | if Sinatra.const_defined?('FacebookObject')
162 | app.get '/connect/?' do
163 | if fb[:user]
164 | if current_user.class != GuestUser
165 | user = current_user
166 | else
167 | user = User.get(:fb_uid => fb[:user])
168 | end
169 |
170 | if user
171 | if !user.fb_uid || user.fb_uid != fb[:user]
172 | user.update :fb_uid => fb[:user]
173 | end
174 | session[:user] = user.id
175 | else
176 | user = User.set!(:fb_uid => fb[:user])
177 | session[:user] = user.id
178 | end
179 | end
180 | redirect '/'
181 | end
182 |
183 | app.get '/receiver' do
184 | %[
185 |
186 |
187 |
188 |
189 | ]
190 | end
191 | end
192 | end
193 | end
194 |
195 | module Helpers
196 | def hash_to_query_string(hash)
197 | hash.collect {|k,v| "#{k}=#{v}"}.join('&')
198 | end
199 |
200 | def login_required
201 | #not as efficient as checking the session. but this inits the fb_user if they are logged in
202 | user = current_user
203 | if user && user.class != GuestUser
204 | return true
205 | else
206 | session[:return_to] = request.fullpath
207 | redirect '/login'
208 | return false
209 | end
210 | end
211 |
212 | def current_user
213 | if session[:user]
214 | User.get(:id => session[:user])
215 | else
216 | GuestUser.new
217 | end
218 | end
219 |
220 | def logged_in?
221 | !!session[:user]
222 | end
223 |
224 | def use_layout?
225 | !request.xhr?
226 | end
227 |
228 | #BECAUSE sinatra 9.1.1 can't load views from different paths properly
229 | def get_view_as_string(filename)
230 | view = File.join(settings.sinatra_authentication_view_path, filename)
231 | data = ""
232 | f = File.open(view, "r")
233 | f.each_line do |line|
234 | data += line
235 | end
236 | return data
237 | end
238 |
239 | def render_login_logout(html_attributes = {:class => ""})
240 | css_classes = html_attributes.delete(:class)
241 | parameters = ''
242 | html_attributes.each_pair do |attribute, value|
243 | parameters += "#{attribute}=\"#{value}\" "
244 | end
245 |
246 | result = ""
247 | if logged_in?
248 | logout_parameters = html_attributes
249 | # a tad janky?
250 | logout_parameters.delete(:rel)
251 | result += "
Edit account "
252 | if Sinatra.const_defined?('FacebookObject')
253 | if fb[:user]
254 | result += "
Logout"
255 | else
256 | result += "
Logout"
257 | end
258 | else
259 | result += "
Logout"
260 | end
261 | else
262 | result += "
Signup "
263 | result += "
Login"
264 | end
265 |
266 | result += "
"
267 | end
268 |
269 | if Sinatra.const_defined?('FacebookObject')
270 | def render_facebook_connect_link(text = 'Login using facebook', options = {:size => 'small'})
271 | if options[:size] == 'small'
272 | size = 'Small'
273 | elsif options[:size] == 'medium'
274 | size = 'Medium'
275 | elsif options[:size] == 'large'
276 | size = 'Large'
277 | elsif options[:size] == 'xlarge'
278 | size = 'BigPun'
279 | else
280 | size = 'Small'
281 | end
282 |
283 | %[
284 |
285 | #{text}
286 |
287 | ]
288 | end
289 | end
290 | end
291 |
292 | register SinatraAuthentication
293 | end
294 |
295 | class GuestUser
296 | def guest?
297 | true
298 | end
299 |
300 | def permission_level
301 | 0
302 | end
303 |
304 | # current_user.admin? returns false. current_user.has_a_baby? returns false.
305 | # (which is a bit of an assumption I suppose)
306 | def method_missing(m, *args)
307 | return false
308 | end
309 | end
310 |
--------------------------------------------------------------------------------
/lib/sinatra-authentication/models.rb:
--------------------------------------------------------------------------------
1 | # Allows you to use the models from cron scripts etc
2 | if !Object.const_defined?("Sinatra")
3 | class Sinatra;end # To please the testing for Sinatra.const_defined?('FacebookObject')
4 | require File.expand_path(File.join(__FILE__,'../../models/') + 'abstract_user.rb')
5 | end
--------------------------------------------------------------------------------
/lib/views/edit.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | - if Rack.const_defined?('Flash') && flash[:notice]
3 | #sinatra_authentication_flash= flash[:notice]
4 | - if Rack.const_defined?('Flash') && flash[:error]
5 | #sinatra_authentication_flash= flash[:error]
6 | %h1
7 | Edit
8 | - if @user.id == current_user.id
9 | account
10 | - else
11 | - if @user.email
12 | = @user.email
13 | - elsif @user.fb_uid
14 |
15 | - else
16 | account
17 | %form{:action => "/users/#{@user.id}/edit", :method => "post"}
18 | .field
19 | .label
20 | %label{:for => "user_email"} Email
21 | %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text", :value => @user.email }
22 | .field
23 | .label
24 | %label{:for => "user_password"} New password
25 | %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
26 | .field
27 | .label
28 | %label{:for => "user_password_confirmation"} Confirm
29 | %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
30 | -# don't render permission field if admin and editing yourself so you don't shoot yourself in the foot
31 | - if current_user.admin? && current_user.id != @user.id
32 | .field
33 | .label
34 | %label{:for => 'permission_level'} Permission level
35 | %select{ :id => "permission_level", :name => "user[permission_level]" }
36 | %option{:value => -1, :selected => @user.admin?}
37 | Admin
38 | %option{:value => 1, :selected => @user.permission_level == 1}
39 | Authenticated user
40 | .buttons
41 | %input{ :value => "Update", :type => "submit" }
42 | - if Sinatra.const_defined?('FacebookObject')
43 | - unless @user.fb_uid
44 | |
45 | = render_facebook_connect_link('Link account with Facebook')
46 |
--------------------------------------------------------------------------------
/lib/views/index.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | %h1.page_title Users
3 | %table
4 | %tr
5 | %th
6 | - if current_user.admin?
7 | %th permission level
8 | - @users.each do |user|
9 | %tr
10 | %td
11 | - if user.email
12 | = user.email
13 | - elsif user.fb_uid
14 |
15 | - else
16 | "user #{user.id}"
17 | - if current_user.admin?
18 | %td= user.permission_level
19 | %td
20 | %a{:href => "/users/#{user.id}"} show
21 | - if current_user.admin?
22 | %td
23 | %a{:href => "/users/#{user.id}/edit"} edit
24 | %td
25 | -# this doesn't work for tk
26 | - if !user.site_admin?
27 | %a{:href => "/users/#{user.id}/delete", :onclick => "return confirm('you sure?')"} delete
28 | - else
29 | site admin
30 |
--------------------------------------------------------------------------------
/lib/views/login.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | - if Rack.const_defined?('Flash') && flash[:notice]
3 | #sinatra_authentication_flash= flash[:notice]
4 | - if Rack.const_defined?('Flash') && flash[:error]
5 | #sinatra_authentication_flash= flash[:error]
6 | %h1.page_title Login
7 | %form{:action => "/login", :method => "post"}
8 | .field
9 | .label
10 | %label{:for => "user_email'"} Email
11 | %input{:id => "user_email", :name => "email", :size => 30, :type => "text"}
12 | .field
13 | .label
14 | %label{:for => "user_password"} Password
15 | %input{:id => "user_password", :name => "password", :size => 30, :type => "password"}
16 | .buttons
17 | %input{:value => "Login", :type => "submit"}
18 | %a{:href => "/signup", :class => 'sinatra_authentication_link'}
19 | Sign up
20 | - if Sinatra.const_defined?('FacebookObject')
21 | .third_party_signup
22 | %h3.section_title One click login:
23 | .login_link.facebook_login
24 | = render_facebook_connect_link('Login using facebook', :size => 'large')
25 |
--------------------------------------------------------------------------------
/lib/views/show.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | %h1.page_title
3 | - if @user.email
4 | = @user.email
5 | - elsif @user.fb_uid
6 |
7 | - if current_user.admin?
8 | %h2 permission level
9 | = @user.permission_level
10 |
--------------------------------------------------------------------------------
/lib/views/signup.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | - if Rack.const_defined?('Flash') && flash[:notice]
3 | #sinatra_authentication_flash= flash[:notice]
4 | - if Rack.const_defined?('Flash') && flash[:error]
5 | #sinatra_authentication_flash= flash[:error]
6 | %h1.page_title Sign Up
7 | %form{:action => "/signup", :method => "post"}
8 | .field
9 | .label
10 | %label{:for => "user_email"} Email
11 | %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text" , :value => params[:email]}
12 | .field
13 | .label
14 | %label{:for => "user_password"} Password
15 | %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
16 | .field
17 | .label
18 | %label{:for => "user_password_confirmation"} Confirm Password
19 | %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
20 | .buttons
21 | %input{ :value => "Sign up", :type => "submit" }
22 | %a{:href => "/login", :class => 'sinatra_authentication_link'}
23 | Login
24 | - if Sinatra.const_defined?('FacebookObject')
25 | .third_party_signup
26 | %h3.section_title One click signup:
27 | .login_link.facebook_login
28 | = render_facebook_connect_link('Signup using facebook', :size => 'large')
29 |
--------------------------------------------------------------------------------
/readme.markdown:
--------------------------------------------------------------------------------
1 | ### A little sinatra gem that implements user authentication, with support for Datamapper, Mongomapper, Mongoid, Sequel and Rufus-Tokyo
2 |
3 | ## INSTALLATION:
4 |
5 | in your sinatra app simply require either "dm-core", 'sequel', 'rufus-tokyo', 'mongoid' or "mongo_mapper", "digest/sha1", 'rack-flash' (if you want flash messages) and then "sinatra-authentication" and turn on session storage
6 | with a super secret key, like so:
7 |
8 | require "dm-core"
9 | #for using auto_migrate!
10 | require "dm-migrations"
11 | require "digest/sha1"
12 | require 'rack-flash'
13 | require "sinatra-authentication"
14 |
15 | use Rack::Session::Cookie, :secret => 'A1 sauce 1s so good you should use 1t on a11 yr st34ksssss'
16 | #if you want flash messages
17 | use Rack::Flash
18 |
19 | If you're using rufus-tokyo, you also need to set the database path for Users. like so:
20 |
21 | require "rufus_tokyo"
22 | require "digest/sha1"
23 | require 'rack-flash'
24 | require "sinatra-authentication"
25 |
26 | #Setting the database path for Users
27 | TcUserTable.cabinet_path = File.dirname(__FILE__) + 'folder/where/you/wanna/store/your/database'
28 |
29 | use Rack::Session::Cookie, :secret => 'A1 sauce 1s so good you should use 1t on a11 yr st34ksssss'
30 | #if you want flash messages
31 | use Rack::Flash
32 |
33 | ## DEFAULT ROUTES:
34 |
35 | * get '/login'
36 | * get '/logout'
37 | * get '/signup'
38 | * get/post '/users'
39 | * get '/users/:id'
40 | * get/post '/users/:id/edit'
41 | * get '/users/:id/delete'
42 |
43 | If you fetch any of the user pages using ajax, they will automatically render without a layout
44 |
45 | ## ADDITIONAL ROUTES WHEN USING SINBOOK FOR FACEBOOK INTEGRATION:
46 |
47 | * get '/receiver'
48 | * get '/connect'
49 |
50 | ## FLASH MESSAGES
51 |
52 | Flash messages are implemented using rack-flash. To set them up add this to your code:
53 |
54 | require 'rack-flash'
55 |
56 | #be sure and do this after after 'use Rack:Session:Cookie...'
57 | use Rack::Flash
58 |
59 | And then sinatra-authentication related flash messages will be made available through flash[:notice] (successes) and flash[:error] (failures)
60 |
61 | -# somewhere in a haml view:
62 | = flash[:notice]
63 | = flash[:error]
64 |
65 | ## HELPER METHODS:
66 |
67 | This plugin provides the following helper methods for your sinatra app:
68 |
69 | * login_required
70 | > which you place at the beginning of any routes you want to be protected
71 | * current_user
72 | * logged_in?
73 | * render_login_logout(html_attributes)
74 | > Which renders login/logout and singup/edit account links.
75 | If you pass a hash of html parameters to render_login_logout all the links will get set to them.
76 | Which is useful for if you're using some sort of lightbox
77 |
78 | ## SIMPLE PERMISSIONS:
79 |
80 | By default the user class includes a method called admin? which simply checks
81 | if user.permission_level == -1.
82 |
83 | you can take advantage of this method in your views or controllers by calling
84 | current_user.admin?
85 | i.e.
86 |
87 | - if current_user.admin?
88 | %a{:href => "/adminey_link_route_thing"} do something adminey
89 |
90 | (these view examples are in HAML, by the way)
91 |
92 | You can also extend the user class with any convenience methods for determining permissions.
93 | i.e.
94 |
95 | #somewhere in the murky depths of your sinatra app
96 | class User
97 | def peasant?
98 | self.permission_level == 0
99 | end
100 | end
101 |
102 | then in your views you can do
103 |
104 | - if current_user.peasant?
105 | %h1 hello peasant!
106 | %p Welcome to the caste system! It's very depressing.
107 |
108 | if no one is logged in, current_user returns a GuestUser instance, which responds to current_user.guest?
109 | with true, current_user.permission_level with 0 and any other method calls with false
110 |
111 | This makes some view logic easier since you don't always have to check if the user is logged in,
112 | although a logged_in? helper method is still provided
113 |
114 | ## RUFUS TOKYO
115 |
116 | when using rufus-tokyo, current_user returns a hash, so to get the primary key of the current_user you would do current_user[:pk].
117 | if you wanna set an attribute, you can do something like current_user["has_a_dog"] = true
118 | and if you want to open a connection with the cabinet directly, you can do something like
119 |
120 | user_connection = TcUser.new
121 | users_with_gmail = user_connection.query do |q|
122 | q.add 'email', :strinc, 'gmail'
123 | end
124 | user_connection.close
125 |
126 | ## FACEBOOK
127 |
128 | # at present, sinatra authentication supports sinbook for interacting with the facebook api.
129 |
130 | If you want to allow users to login using facebook, just require 'sinbook' before requiring 'sinatra-authentication'.
131 | The routes '/receiver' and '/connect' will be added. as well as connect links on the login and edit account pages.
132 | You'll still have to include and initialize the facebook connect javascript in your layout yourself, like so:
133 |
134 | (This example layout assumes you're using sinbook)
135 |
136 | !!!
137 | %head
138 | %title Welcome to my Facebook Connect website!
139 | %script{:type => 'text/javascript', :src => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US'}
140 | %body
141 | = yield
142 | :javascript
143 | FB.init("#{fb.api_key}", "/receiver")
144 |
145 | Just remember to specify '/receiver' as the path to the xd-receiver file in your call to 'FB.init'.
146 |
147 | The render_login_logout helper 'logout' link will log the user out of facebook and your app.
148 |
149 | I've also included a little helper method 'render_facebook_connect_link' for rendering the facebook connect link with the correct 'onconnect' javascript callback.
150 | The callback redirects to '/connect'.
151 | This is important because the way I've implemented facebook connect support is by pinging '/connect' after the user
152 | successfully connects with facebook.
153 |
154 | If you choose to render the connect button yourself, be sure to have the 'onconnect' callback include "window.location = '/connect'".
155 |
156 | '/connect' redirects to '/' on completion.
157 |
158 | The 'render_facebook_connect_link' helper uses html instead of fbml, so ajax requests to '/login' or "/users/#{user.id}/edit"
159 | will render the connect link without you needing to parse any fbml.
160 |
161 | If the user is already logged into the app and connects with facebook via the user edit page,
162 | it adds their fb_uid to their profile in the database,
163 | which will allow them to log in using their email and password, OR their facebook account.
164 |
165 | If they aren't already logged in to the app through the normal login form,
166 | it creates a new user in the database without an email address or password.
167 | They can later add this data by going to "/users/#{current_user.id}/edit",
168 | which will allow them to log in using their email address and password, OR their facebook account.
169 |
170 | ## OVERRIDING DEFAULT VIEWS
171 |
172 | Right now if you're going to override sinatra-authentication's views, you have to override all of them.
173 | This is something I hope to change in a future release.
174 |
175 | To override the default view path do something like this:
176 |
177 | set :sinatra_authentication_view_path, Pathname(__FILE__).dirname.expand_path + "my_views/"
178 |
179 | And then the views you'll need to define are:
180 |
181 | * show.haml
182 | * index.haml
183 | * signup.haml
184 | * login.haml
185 | * edit.haml
186 |
187 | To override haml, set template_engine in your Sinatra App:
188 |
189 | configure do
190 | set :template_engine, :erb # for example
191 | end
192 |
193 | The signup and edit form fields are named so they pass a hash called 'user' to the server:
194 |
195 | %input{:name => "user[email]", :size => 30, :type => "text", :value => @user.email}
196 | %input{:name => "user[password]", :size => 30, :type => "password"}
197 | %input{:name => "user[password_confirmation]", :size => 30, :type => "password"}
198 |
199 | %select{:name => "user[permission_level]"}
200 | %option{:value => -1, :selected => @user.admin?}
201 | Admin
202 | %option{:value => 1, :selected => @user.permission_level == 1}
203 | Authenticated user
204 |
205 | if you add attributes to the User class and pass them in the user hash your new attributes will be set along with the others.
206 |
207 | The login form fields just pass a field called email and a field called password:
208 |
209 | %input{:name => "email", :size => 30, :type => "text"}
210 | %input{:name => "password", :size => 30, :type => "password"}
211 |
212 | To add methods or properties to the User class, you have to access the underlying database user class, like so:
213 |
214 | class DmUser
215 | property :name, String
216 | property :has_dog, Boolean, :default => false
217 | end
218 |
219 | And then to access/update your newly defined attributes you use the User class:
220 |
221 | current_user.name
222 | current_user.has_dog
223 |
224 | current_user.update({:has_dog => true})
225 |
226 | new_user = User.set({:email => 'max@max.com' :password => 'hi', :password_confirmation => 'hi', :name => 'Max', :has_dog => false})
227 |
228 | User.all(:has_dog => true).each do |user|
229 | user.update({has_dog => false})
230 | end
231 |
232 | User.all(:has_dog => false).each do |user|
233 | user.delete
234 | end
235 |
236 | the User class passes additional method calls along to the interfacing database class, so most calls to Datamapper/Sequel/Mongomapper/RufusTokyo functions should work as expected.
237 |
238 | If you need to get associations on current_user from the underlying ORM use current_user.db_instance, take this case for example:
239 | class Brain
240 | include DataMapper::Resource
241 | property :type, String
242 | property :iq, Integer
243 | end
244 |
245 | class DmUser
246 | has n, :brains
247 | end
248 |
249 | get '/' do
250 | @user_brains = current_user.db_instance.brains
251 | end
252 |
253 | The database user classes are named as follows:
254 |
255 | * for Datamapper:
256 | > DmUser
257 | * for Sequel:
258 | > SequelUser
259 | * for Rufus Tokyo:
260 | > TcUser
261 | * for Mongoid:
262 | > MongoidUser
263 | * for Mongomapper:
264 | > MmUser
265 |
266 | ## Deprecations
267 | * All database adapters now store created_at as a Time object.
268 |
269 | ## Known issues
270 | * First user in database is not properly recognized as site admin
271 | > Proposed fix: add site_admin_email option when initialization functionality is added
272 |
273 | ## Roadmap
274 |
275 | * Move database adapter initialization, along with auto configuration of sinbook and rack flash functionality into a Sinatra::SinatraAuthentication.init(args) method
276 | * Refactor/redesign database adapter interface, make User class AbstractUser and all ORM user classes User, with corresponding specs
277 | * Remove Facebook connect support and add support for Omniauth
278 | * Provide a method for overriding specific views, and/or specifying your own form partial, (passed an instance of User)
279 | * Add Remember me (forever) checkbox to login form
280 | * Add next url parameter support for login/signup
281 | * Add verb selection on configuration (Sign in / Log in)
282 | * Provide optional support through init method for inclusion of username
283 | > Where login form accepts either email or username (through the same field)
284 | * Add email functionality
285 | > Confirmation emails
286 | > Forgotten password emails
287 | * Look into what might be neccesary to allow for logging in using Ajax
288 |
289 | ## Maybe
290 |
291 | * Allow passing custom database attributes into init method, also dynamically altering corresponding signup and user edit views. (potentially leaky abstraction)
292 | > As an alternative, create a generic interface for accessing database row names through the various ORMs.
293 | > So when users alter their User schemas, I can make my views 'Just Work'.
294 | * Add HTTP basic auth support
295 | * Add pluggable OAuth consumer/provider support
296 |
297 | ## License
298 |
299 | This software is released under the Unlicense. See the UNLICENSE file in this repository or http://unlicense.org for details.
300 |
--------------------------------------------------------------------------------
/sinatra-authentication-0.3.2.gem:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maxjustus/sinatra-authentication/cb919a7cc01631924d7b830bab41f85f54a7305d/sinatra-authentication-0.3.2.gem
--------------------------------------------------------------------------------
/sinatra-authentication-0.4.2.gem:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maxjustus/sinatra-authentication/cb919a7cc01631924d7b830bab41f85f54a7305d/sinatra-authentication-0.4.2.gem
--------------------------------------------------------------------------------
/sinatra-authentication.gemspec:
--------------------------------------------------------------------------------
1 | # Generated by jeweler
2 | # DO NOT EDIT THIS FILE DIRECTLY
3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4 | # -*- encoding: utf-8 -*-
5 |
6 | Gem::Specification.new do |s|
7 | s.name = %q{sinatra-authentication}
8 | s.version = "0.4.2"
9 |
10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11 | s.authors = ["Max Justus Spransy"]
12 | s.date = %q{2010-12-14}
13 | s.description = %q{Simple authentication plugin for sinatra.}
14 | s.email = %q{maxjustus@gmail.com}
15 | s.extra_rdoc_files = [
16 | "TODO"
17 | ]
18 | s.files = [
19 | "History.txt",
20 | "Manifest",
21 | "Rakefile",
22 | "TODO",
23 | "example/dm_extend_app.rb",
24 | "example/dm_sinbook.rb",
25 | "example/extend_views/edit.haml",
26 | "example/extend_views/index.haml",
27 | "example/extend_views/login.haml",
28 | "example/extend_views/show.haml",
29 | "example/extend_views/signup.haml",
30 | "example/mm_app.rb",
31 | "example/tc_app.rb",
32 | "example/tc_sinbook.rb",
33 | "lib/models/abstract_user.rb",
34 | "lib/models/datamapper_user.rb",
35 | "lib/models/dm_adapter.rb",
36 | "lib/models/mm_adapter.rb",
37 | "lib/models/mongoid_adapter.rb",
38 | "lib/models/mongoid_user.rb",
39 | "lib/models/mongomapper_user.rb",
40 | "lib/models/rufus_tokyo_user.rb",
41 | "lib/models/sequel_adapter.rb",
42 | "lib/models/sequel_user.rb",
43 | "lib/models/tc_adapter.rb",
44 | "lib/sinatra-authentication.rb",
45 | "lib/sinatra-authentication/models.rb",
46 | "lib/views/edit.haml",
47 | "lib/views/index.haml",
48 | "lib/views/login.haml",
49 | "lib/views/show.haml",
50 | "lib/views/signup.haml",
51 | "readme.markdown",
52 | "sinatra-authentication-0.3.2.gem",
53 | "sinatra-authentication.gemspec",
54 | "spec/run_all_specs.rb",
55 | "spec/unit/dm_model_spec.rb",
56 | "spec/unit/mm_model_spec.rb",
57 | "spec/unit/mongoid_model_spec.rb",
58 | "spec/unit/sequel_model_spec.rb",
59 | "spec/unit/tc_model_spec.rb",
60 | "spec/unit/user_specs.rb",
61 | "test/datamapper_test.rb",
62 | "test/lib/dm_app.rb",
63 | "test/lib/dm_extend_app.rb",
64 | "test/lib/dm_sinbook.rb",
65 | "test/lib/extend_views/edit.haml",
66 | "test/lib/extend_views/index.haml",
67 | "test/lib/extend_views/login.haml",
68 | "test/lib/extend_views/show.haml",
69 | "test/lib/extend_views/signup.haml",
70 | "test/lib/helper.rb",
71 | "test/lib/mm_app.rb",
72 | "test/lib/mongoid_app.rb",
73 | "test/lib/sequel_app.rb",
74 | "test/lib/tc_app.rb",
75 | "test/lib/tc_sinbook.rb",
76 | "test/mongoid_test.rb",
77 | "test/mongomapper_test.rb",
78 | "test/route_tests.rb",
79 | "test/rufus_tokyo_test.rb",
80 | "test/sequel_test.rb"
81 | ]
82 | s.homepage = %q{http://github.com/maxjustus/sinatra-authentication}
83 | s.require_paths = ["lib"]
84 | s.rubygems_version = %q{1.3.7}
85 | s.summary = %q{Simple authentication plugin for sinatra.}
86 | s.test_files = [
87 | "spec/run_all_specs.rb",
88 | "spec/unit/dm_model_spec.rb",
89 | "spec/unit/mm_model_spec.rb",
90 | "spec/unit/mongoid_model_spec.rb",
91 | "spec/unit/sequel_model_spec.rb",
92 | "spec/unit/tc_model_spec.rb",
93 | "spec/unit/user_specs.rb",
94 | "test/datamapper_test.rb",
95 | "test/lib/dm_app.rb",
96 | "test/lib/dm_extend_app.rb",
97 | "test/lib/dm_sinbook.rb",
98 | "test/lib/helper.rb",
99 | "test/lib/mm_app.rb",
100 | "test/lib/mongoid_app.rb",
101 | "test/lib/sequel_app.rb",
102 | "test/lib/tc_app.rb",
103 | "test/lib/tc_sinbook.rb",
104 | "test/mongoid_test.rb",
105 | "test/mongomapper_test.rb",
106 | "test/route_tests.rb",
107 | "test/rufus_tokyo_test.rb",
108 | "test/sequel_test.rb"
109 | ]
110 |
111 | if s.respond_to? :specification_version then
112 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
113 | s.specification_version = 3
114 |
115 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
116 | s.add_runtime_dependency(%q, [">= 0"])
117 | s.add_runtime_dependency(%q, [">= 0"])
118 | s.add_runtime_dependency(%q, [">= 0"])
119 | s.add_runtime_dependency(%q, [">= 0"])
120 | s.add_runtime_dependency(%q, [">= 0"])
121 | s.add_runtime_dependency(%q, [">= 0"])
122 | s.add_runtime_dependency(%q, [">= 0"])
123 | s.add_runtime_dependency(%q, [">= 0"])
124 | else
125 | s.add_dependency(%q, [">= 0"])
126 | s.add_dependency(%q, [">= 0"])
127 | s.add_dependency(%q, [">= 0"])
128 | s.add_dependency(%q, [">= 0"])
129 | s.add_dependency(%q, [">= 0"])
130 | s.add_dependency(%q, [">= 0"])
131 | s.add_dependency(%q, [">= 0"])
132 | s.add_dependency(%q, [">= 0"])
133 | end
134 | else
135 | s.add_dependency(%q, [">= 0"])
136 | s.add_dependency(%q, [">= 0"])
137 | s.add_dependency(%q, [">= 0"])
138 | s.add_dependency(%q, [">= 0"])
139 | s.add_dependency(%q, [">= 0"])
140 | s.add_dependency(%q, [">= 0"])
141 | s.add_dependency(%q, [">= 0"])
142 | s.add_dependency(%q, [">= 0"])
143 | end
144 | end
145 |
146 |
--------------------------------------------------------------------------------
/spec/run_all_specs.rb:
--------------------------------------------------------------------------------
1 | #!/bin/ruby
2 |
3 | specs = ['sequel_model', 'dm_model', 'mm_model', 'tc_model', 'mongoid_model']
4 | specs.each do |spec|
5 | spec_path = File.dirname(__FILE__) + "/unit/#{spec}_spec.rb"
6 | @output = %x[rspec #{spec_path}]
7 | puts @output
8 | end
9 |
--------------------------------------------------------------------------------
/spec/unit/ar_model_spec.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), '../../test/lib/ar_app')
2 | require File.join(File.dirname(__FILE__), '../../test/lib/helper')
3 | require File.join(File.dirname(__FILE__), 'user_specs')
4 |
--------------------------------------------------------------------------------
/spec/unit/dm_model_spec.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), '../../test/lib/dm_app')
2 | require File.join(File.dirname(__FILE__), '../../test/lib/helper')
3 | require File.join(File.dirname(__FILE__), 'user_specs')
4 |
--------------------------------------------------------------------------------
/spec/unit/mm_model_spec.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), '../../test/lib/mm_app')
2 | require File.join(File.dirname(__FILE__), '../../test/lib/helper')
3 | require File.join(File.dirname(__FILE__), 'user_specs')
4 |
--------------------------------------------------------------------------------
/spec/unit/mongoid_model_spec.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), '../../test/lib/mongoid_app')
2 | require File.join(File.dirname(__FILE__), '../../test/lib/helper')
3 | require File.join(File.dirname(__FILE__), '/user_specs')
4 |
--------------------------------------------------------------------------------
/spec/unit/sequel_model_spec.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), '../../test/lib/sequel_app')
2 | require File.join(File.dirname(__FILE__), '../../test/lib/helper')
3 | require File.join(File.dirname(__FILE__), 'user_specs')
4 |
5 | describe User do
6 | describe '#get' do
7 | it 'Should pass arguments to the underlying ORM' do
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/unit/tc_model_spec.rb:
--------------------------------------------------------------------------------
1 | require File.join(File.dirname(__FILE__), '../../test/lib/tc_app')
2 | require File.join(File.dirname(__FILE__), '../../test/lib/helper')
3 | require File.join(File.dirname(__FILE__), 'user_specs')
4 |
--------------------------------------------------------------------------------
/spec/unit/user_specs.rb:
--------------------------------------------------------------------------------
1 | describe User do
2 | before :each do
3 | User.all.each do |u|
4 | User.delete(u.id)
5 | end
6 |
7 | @user = User.set(TestHelper.gen_user_for_model)
8 | end
9 |
10 | describe 'instance' do
11 | describe '#update' do
12 | before :each do
13 | @user.update(:password => 'password', :password_confirmation => 'password')
14 | end
15 |
16 | it 'Returns an instance if User' do
17 | @user.class.should == User
18 | end
19 |
20 | it 'Should return true on success' do
21 | @user.update(:email => 'max@max.com', :password => 'hi', :password_confirmation => 'hi').should == true
22 | @user.update(:email => 'max@max.com').should == true
23 | end
24 |
25 | it 'Does not allow duplicate email addresses on update' do
26 | @user_two = User.set(:email => 'steve@steve.com', :password => 'hi', :password_confirmation => 'hi')
27 | @user.update(:email => 'steve@steve.com').should == false
28 | #@user.class.should == User
29 | end
30 |
31 | it 'Should say saved is false when invalid' do
32 | @user.update(:password => 'paz')
33 | @user.saved.should == false
34 | end
35 | end
36 |
37 | describe '#errors' do
38 | it 'Returns a string of errors' do
39 | @user.update(:password => 'paz')
40 | @user.errors.class.should == String && @user.errors.length.should > 0
41 | end
42 | end
43 |
44 | describe '#valid' do
45 | it 'Should return false when invalid' do
46 | @user.update(:password => 'hi')
47 | @user.valid.should == false
48 | end
49 |
50 | it 'Should validate format of email' do
51 | @user.update(:email => 'meeewewe!!')
52 | @user.valid.should == false
53 | end
54 |
55 | it 'Should return true when valid' do
56 | @user.valid.should == true
57 | end
58 | end
59 | end
60 |
61 | describe '#all' do
62 | it 'Should return an array of instances of User' do
63 | User.all[0].class.should == User
64 | end
65 |
66 | it 'Should return users in descending order of creation time' do
67 | @newest_user = User.set(:email => 'hey@hi.com', :password => 'hihihi', :password_confirmation => 'hihihi', :created_at => Time.now + 1)
68 | User.all[0].email.should == @newest_user.email
69 | end
70 |
71 | #it 'Should accept a page argument in a hash and limit results to 20 by default' do
72 | # 40.times do |n|
73 | # @newest_user = User.set(:email => "#{n}hey@hi.com", :password => 'hihihi', :password_confirmation => 'hihihi', :created_at => Time.now + n)
74 | # end
75 | # users = User.all(:page => 0)
76 | # users[0].email.should == @newest_user.email
77 | # users.size.should == 20
78 | # User.all(:page => 1)[0].email.should == "19hey@hi.com"
79 | #end
80 | end
81 |
82 | describe '#set' do
83 | it 'Returns an instance if User' do
84 | @user.class.should == User
85 | end
86 |
87 | it 'Timestamps user with Time' do
88 | @user.created_at.class.should == Time
89 | end
90 |
91 | it 'Should set first user in database as site admin' do
92 | @user.site_admin?.should == true
93 | end
94 |
95 | #it 'Should say saved is true' do
96 | # @user.saved.should == true
97 | #end
98 | end
99 |
100 | describe '#set!' do
101 | before :each do
102 | @user = User.set!({:password => 'hi'})
103 | end
104 |
105 | it 'Returns an instance if User' do
106 | @user.class.should == User
107 | end
108 |
109 | #it 'Should say saved is true' do
110 | # @user.saved.should == true
111 | #end
112 | end
113 |
114 | describe '#delete' do
115 | it 'Returns a boolean' do
116 | User.delete(@user.id).should == true
117 | end
118 | end
119 | end
120 |
--------------------------------------------------------------------------------
/test/activerecord_test.rb:
--------------------------------------------------------------------------------
1 | require 'lib/ar_app'
2 | require 'lib/helper'
3 | require 'test/unit'
4 | require 'rack/test'
5 | require 'route_tests'
6 |
--------------------------------------------------------------------------------
/test/datamapper_test.rb:
--------------------------------------------------------------------------------
1 | require 'lib/dm_app'
2 | require 'lib/helper'
3 | require 'test/unit'
4 | require 'rack/test'
5 | require 'route_tests'
6 |
--------------------------------------------------------------------------------
/test/lib/ar_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'active_record'
4 | require 'rack-flash'
5 |
6 | ActiveRecord::Base.establish_connection(
7 | :adapter => "sqlite3",
8 | :database => "#{Dir.pwd}/test.db"
9 | )
10 |
11 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
12 |
13 | use Rack::Session::Cookie, :secret => "heyhihello"
14 | use Rack::Flash
15 |
16 | set :environment, 'development'
17 | set :public, 'public'
18 | set :views, 'views'
19 |
20 | get '/' do
21 | send TEMPLATE, "= render_login_logout", :layout => :layout
22 | end
23 |
24 | __END__
25 |
26 | @@ layout
27 | = yield
28 |
--------------------------------------------------------------------------------
/test/lib/dm_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'dm-core'
4 | require 'dm-migrations'
5 | require 'rack-flash'
6 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
7 |
8 | DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/test.db")
9 | DataMapper.auto_migrate!
10 |
11 | use Rack::Session::Cookie, :secret => "heyhihello"
12 | use Rack::Flash
13 |
14 | set :environment, 'development'
15 | set :public, 'public'
16 | set :views, 'views'
17 |
18 | get '/' do
19 | send TEMPLATE, "= render_login_logout", :layout => :layout
20 | end
21 |
--------------------------------------------------------------------------------
/test/lib/dm_extend_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'dm-core'
4 | require 'dm-migrations'
5 | require 'rack-flash'
6 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
7 |
8 |
9 | class DmUser
10 | property :name, String
11 | end
12 |
13 | DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/test.db")
14 | DataMapper.auto_migrate!
15 |
16 | set :sinatra_authentication_view_path, Pathname(__FILE__).dirname.expand_path + "extend_views/"
17 | use Rack::Session::Cookie, :secret => "heyhihello"
18 | use Rack::Flash
19 |
20 | set :environment, 'development'
21 | set :public, 'public'
22 | set :views, 'views'
23 |
24 | get '/' do
25 | puts User.all(:name => 'max')
26 | send TEMPLATE, "= render_login_logout", :layout => :layout
27 | end
28 |
--------------------------------------------------------------------------------
/test/lib/dm_sinbook.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'sinbook'
4 | require 'dm-core'
5 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
6 |
7 | facebook do
8 | api_key 'aa2db1b96cb7b57f0c5b1d4d3d8f0a22'
9 | secret '21d94ee63969ae3b3f833689838ca00f'
10 | app_id 48652736613
11 | url 'peoplewithjetpacks.com:4568/'
12 | callback 'peoplewithjetpacks.com:4568/'
13 | end
14 |
15 | set :port, 4568
16 |
17 | DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/test.db")
18 | DataMapper.auto_migrate!
19 |
20 | use Rack::Session::Cookie, :secret => "heyhihello"
21 |
22 | set :environment, 'development'
23 | set :public, 'public'
24 | set :views, 'views'
25 |
26 | get '/' do
27 | send TEMPLATE, :main
28 | end
29 |
30 | get '/test' do
31 | login_required
32 | 'hihihi'
33 | end
34 |
35 | __END__
36 |
37 | @@ layout
38 | %html{:xmlns=>"http://www.w3.org/1999/xhtml", :'xmlns:fb'=>"http://www.facebook.com/2008/fbml"}
39 | %head
40 | %title Welcome to my Facebook Connect website!
41 | %script{:type => 'text/javascript', :src => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US'}
42 | %body
43 | = render_login_logout
44 | = yield
45 | :javascript
46 | FB.init("#{fb.api_key}", "/receiver")
47 |
48 | @@ main
49 | - if fb[:user]
50 | Hi,
51 | %fb:profile-pic{:uid => fb[:user]}
52 | %fb:name{:uid => fb[:user], :useyou => 'false', :firstnameonly => 'true'}
53 | !
54 |
55 |
--------------------------------------------------------------------------------
/test/lib/extend_views/edit.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | #sinatra_authentication_flash= session[:flash]
3 | %h1
4 | Edit
5 | - if @user.id == current_user.id
6 | account
7 | - else
8 | - if @user.email
9 | = @user.email
10 | - elsif @user.fb_uid
11 |
12 | - else
13 | account
14 | %form{:action => "/users/#{@user.id}/edit", :method => "post"}
15 | .field
16 | .label
17 | %label{:for => "user_email"} Email
18 | %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text", :value => @user.email }
19 | .field
20 | .label
21 | %label{:for => "user_email"} Name
22 | %input{ :id => "user_email", :name => "user[name]", :size => 30, :type => "text", :value => @user.name }
23 | .field
24 | .label
25 | %label{:for => "user_password"} New password
26 | %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
27 | .field
28 | .label
29 | %label{:for => "user_password_confirmation"} Confirm
30 | %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
31 | -# don't render permission field if admin and editing yourself so you don't shoot yourself in the foot
32 | - if current_user.admin? && current_user.id != @user.id
33 | .field
34 | .label
35 | %label{:for => 'permission_level'} Permission level
36 | %select{ :id => "permission_level", :name => "user[permission_level]" }
37 | %option{:value => -1, :selected => @user.admin?}
38 | Admin
39 | %option{:value => 1, :selected => @user.permission_level == 1}
40 | Authenticated user
41 | .buttons
42 | %input{ :value => "Update", :type => "submit" }
43 | - if Sinatra.const_defined?('FacebookObject')
44 | - unless @user.fb_uid
45 | |
46 | = render_facebook_connect_link('Link account with Facebook')
47 |
--------------------------------------------------------------------------------
/test/lib/extend_views/index.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | %h1.page_title Users
3 | %table
4 | %tr
5 | %th
6 | - if current_user.admin?
7 | %th permission level
8 | - @users.each do |user|
9 | %tr
10 | %td
11 | - if user.email
12 | = user.email
13 | - elsif user.fb_uid
14 |
15 | - else
16 | "user #{user.id}"
17 | - if current_user.admin?
18 | %td= user.permission_level
19 | %td
20 | = user.name
21 | %td
22 | %a{:href => "/users/#{user.id}"} show
23 | - if current_user.admin?
24 | %td
25 | %a{:href => "/users/#{user.id}/edit"} edit
26 | %td
27 | -# this doesn't work for tk
28 | - if !user.site_admin?
29 | %a{:href => "/users/#{user.id}/delete", :onclick => "return confirm('you sure?')"} delete
30 | - else
31 | site admin
32 |
--------------------------------------------------------------------------------
/test/lib/extend_views/login.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | #sinatra_authentication_flash= session[:flash]
3 | %h1.page_title Login
4 | %form{:action => "/login", :method => "post"}
5 | .field
6 | .label
7 | %label{:for => "user_email'"} Email
8 | %input{:id => "user_email", :name => "email", :size => 30, :type => "text"}
9 | .field
10 | .label
11 | %label{:for => "user_password"} Password
12 | %input{:id => "user_password", :name => "password", :size => 30, :type => "password"}
13 | .buttons
14 | %input{:value => "login", :type => "submit"}
15 | %a{:href => "/signup", :class => 'sinatra_authentication_link'}
16 | Signup
17 | - if Sinatra.const_defined?('FacebookObject')
18 | .third_party_signup
19 | %h3.section_title One click login:
20 | .login_link.facebook_login
21 | = render_facebook_connect_link('Login using facebook', :size => 'large')
22 |
--------------------------------------------------------------------------------
/test/lib/extend_views/show.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | %h1.page_title
3 | - if @user.email
4 | = @user.email
5 | - elsif @user.fb_uid
6 |
7 | - if current_user.admin?
8 | %h2 permission level
9 | = @user.permission_level
10 |
--------------------------------------------------------------------------------
/test/lib/extend_views/signup.haml:
--------------------------------------------------------------------------------
1 | #sinatra_authentication
2 | #sinatra_authentication_flash= session[:flash]
3 | %h1.page_title Signup
4 | %form{:action => "/signup", :method => "post"}
5 | .field
6 | .label
7 | %label{:for => "user_email"} Email
8 | %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text" }
9 | .field
10 | .label
11 | %label{:for => "user_name"} Name
12 | %input{ :id => "user_name", :name => "user[name]", :size => 30, :type => "text" }
13 | .field
14 | .label
15 | %label{:for => "user_password"} Password
16 | %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
17 | .field
18 | .label
19 | %label{:for => "user_password_confirmation"} Confirm Password
20 | %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
21 | .buttons
22 | %input{ :value => "Create account", :type => "submit" }
23 | %a{:href => "/login", :class => 'sinatra_authentication_link'}
24 | Login
25 | - if Sinatra.const_defined?('FacebookObject')
26 | .third_party_signup
27 | %h3.section_title One click signup:
28 | .login_link.facebook_login
29 | = render_facebook_connect_link('Signup using facebook', :size => 'large')
30 |
--------------------------------------------------------------------------------
/test/lib/helper.rb:
--------------------------------------------------------------------------------
1 | require 'haml'
2 | TEMPLATE = :haml
3 |
4 | class TestHelper
5 | def self.gen_user
6 | {'user[email]' => 'yodawg@gmail.com', 'user[password]' => 'password', 'user[password_confirmation]' => 'password'}
7 | end
8 |
9 | def self.gen_user_for_model
10 | {:email => 'yodawg@gmail.com', :password => 'password', :password_confirmation => 'password'}
11 | end
12 | end
13 |
14 | def app
15 | Sinatra::Application
16 | end
17 |
--------------------------------------------------------------------------------
/test/lib/mm_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'rack-flash'
4 | require 'mongo_mapper'
5 |
6 | #logger = Logger.new($stdout)
7 | #MongoMapper.connection = Mongo::Connection.new('db.mongohq.com', 27017, :logger => logger)
8 | #MongoMapper.database = "fdbk"
9 | #MongoMapper.database.authenticate(ENV['mongohq_user'], ENV['mongohq_pass'])
10 |
11 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
12 |
13 | MongoMapper.database = "sinatraauthtest"
14 |
15 | use Rack::Session::Cookie, :secret => "heyhihello"
16 | use Rack::Flash
17 |
18 | set :environment, 'development'
19 | set :public_folder, 'public'
20 | set :views, 'views'
21 |
22 | get '/' do
23 | send TEMPLATE, "= render_login_logout", :layout => :layout
24 | end
25 |
--------------------------------------------------------------------------------
/test/lib/mongoid_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'rack-flash'
4 | require 'mongoid'
5 |
6 | #logger = Logger.new($stdout)
7 | #MongoMapper.connection = Mongo::Connection.new('db.mongohq.com', 27017, :logger => logger)
8 | #MongoMapper.database = "fdbk"
9 | #MongoMapper.database.authenticate(ENV['mongohq_user'], ENV['mongohq_pass'])
10 |
11 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
12 |
13 | Mongoid.configure do |config|
14 | name = "sinatraauth_test"
15 | host = "localhost"
16 | config.master = Mongo::Connection.new.db(name)
17 | end
18 |
19 | use Rack::Session::Cookie, :secret => "heyhihello"
20 | use Rack::Flash
21 |
22 | set :environment, 'development'
23 | set :public, 'public'
24 | set :views, 'views'
25 |
26 | get '/' do
27 | send TEMPLATE, "= render_login_logout", :layout => :layout
28 | end
29 |
--------------------------------------------------------------------------------
/test/lib/sequel_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'sequel'
4 | require 'rack-flash'
5 |
6 | #DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/test.db")
7 | #DataMapper.auto_migrate!
8 | DB = Sequel.sqlite(:database => 'test.db')
9 |
10 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
11 |
12 | use Rack::Session::Cookie, :secret => "heyhihello"
13 | use Rack::Flash
14 |
15 | set :environment, 'development'
16 | set :public, 'public'
17 | set :views, 'views'
18 |
19 | get '/' do
20 | send TEMPLATE, "= render_login_logout", :layout => :layout
21 | end
22 |
--------------------------------------------------------------------------------
/test/lib/tc_app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'rufus/tokyo'
4 | require 'rack-flash'
5 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
6 |
7 | use Rack::Session::Cookie, :secret => "heyhihello"
8 | use Rack::Flash
9 | TcUserTable.cabinet_path = File.dirname(__FILE__)
10 |
11 | set :environment, 'development'
12 | set :public, 'public'
13 | set :views, 'views'
14 |
15 | get '/' do
16 | send TEMPLATE, "= render_login_logout", :layout => :layout
17 | end
18 |
--------------------------------------------------------------------------------
/test/lib/tc_sinbook.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinbook'
3 | require 'rufus/tokyo'
4 | require 'sinatra'
5 | require File.join(File.dirname(__FILE__), '../../lib/sinatra-authentication')
6 |
7 | use Rack::Session::Cookie, :secret => "heyhihello"
8 | TcUserTable.cabinet_path = File.dirname(__FILE__)
9 |
10 | facebook do
11 | api_key 'aa2db1b96cb7b57f0c5b1d4d3d8f0a22'
12 | secret '21d94ee63969ae3b3f833689838ca00f'
13 | app_id 48652736613
14 | url 'peoplewithjetpacks.com:4568/'
15 | callback 'peoplewithjetpacks.com:4568/'
16 | end
17 |
18 | set :port, 4568
19 |
20 | get '/' do
21 | send TEMPLATE, :main
22 | end
23 |
24 | get '/test' do
25 | login_required
26 | 'hihihi'
27 | end
28 |
29 | __END__
30 |
31 | @@ layout
32 | %html{:xmlns=>"http://www.w3.org/1999/xhtml", :'xmlns:fb'=>"http://www.facebook.com/2008/fbml"}
33 | %head
34 | %title Welcome to my Facebook Connect website!
35 | %script{:type => 'text/javascript', :src => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US'}
36 | %script{:type => 'text/javascript', :src => 'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'}
37 | :javascript
38 | $(document).ready(function(){
39 | /* test facebook crap works with ajax */
40 | $('.sinatra-authentication-login').click(function(){
41 | $.get($(this).attr('href'), {}, function(data){
42 | $('#test_box').html(data);
43 | });
44 | return false;
45 | });
46 | });
47 | %body
48 | = render_login_logout
49 | = yield
50 | :javascript
51 | FB.init("#{fb.api_key}", "/receiver")
52 | #test_box
53 |
54 | @@ main
55 | - if fb[:user]
56 | Hi,
57 | %fb:profile-pic{:uid => fb[:user]}
58 | %fb:name{:uid => fb[:user], :useyou => 'false', :firstnameonly => 'true'}
59 | !
60 | %br/
61 |
62 |
--------------------------------------------------------------------------------
/test/mongoid_test.rb:
--------------------------------------------------------------------------------
1 | require 'lib/mongoid_app'
2 | require 'lib/helper'
3 | require 'test/unit'
4 | require 'rack/test'
5 | require 'route_tests'
6 |
--------------------------------------------------------------------------------
/test/mongomapper_test.rb:
--------------------------------------------------------------------------------
1 | require 'lib/mm_app'
2 | require 'lib/helper'
3 | require 'test/unit'
4 | require 'rack/test'
5 | require 'route_tests'
6 |
7 | #Test::Unit::TestCase.send :include, Rack::Test::Methods
8 |
9 | #class SinatraAuthMongoMapperTest < Test::Unit::TestCase
10 | # include Rack::Test::Methods
11 | #
12 | # def app
13 | # TestApp
14 | # end
15 | #
16 | # def setup
17 | # post '/signup', TestHelper.gen_user
18 | # follow_redirect!
19 | # get '/logout'
20 | # end
21 | #
22 | # def test_should_login
23 | # post '/login', {'email' => TestHelper.gen_user['user[email]'], 'password' => TestHelper.gen_user['user[password]']}
24 | # follow_redirect!
25 | #
26 | # assert_equal 'http://example.org/', last_request.url
27 | # #assert cookie_jar['user']
28 | # assert last_request.env['rack.session'][:user]
29 | # assert last_response.ok?
30 | # end
31 | #
32 | # def test_should_logout
33 | # post '/login', {'email' => TestHelper.gen_user['user[email]'], 'password' => TestHelper.gen_user['user[password]']}
34 | # get '/logout'
35 | # follow_redirect!
36 | #
37 | # assert !last_request.env['rack.session'][:user]
38 | # assert_equal 'http://example.org/', last_request.url
39 | # end
40 | #end
41 |
--------------------------------------------------------------------------------
/test/route_tests.rb:
--------------------------------------------------------------------------------
1 | Test::Unit::TestCase.send :include, Rack::Test::Methods
2 |
3 | class SinatraAuthDataMapperTest < Test::Unit::TestCase
4 |
5 | def setup
6 | post '/signup', TestHelper.gen_user
7 | follow_redirect!
8 | get '/logout'
9 | end
10 |
11 | def test_should_login
12 | post '/login', {'email' => TestHelper.gen_user['user[email]'], 'password' => TestHelper.gen_user['user[password]']}
13 | follow_redirect!
14 |
15 | assert_equal 'http://example.org/', last_request.url
16 | #assert cookie_jar['user']
17 | assert last_request.env['rack.session'][:user]
18 | assert last_response.ok?
19 | end
20 |
21 | def test_should_logout
22 | post '/login', {'email' => TestHelper.gen_user['user[email]'], 'password' => TestHelper.gen_user['user[password]']}
23 | get '/logout'
24 | follow_redirect!
25 |
26 | assert !last_request.env['rack.session'][:user]
27 | assert_equal 'http://example.org/', last_request.url
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/test/rufus_tokyo_test.rb:
--------------------------------------------------------------------------------
1 | require 'lib/tc_app'
2 | require 'lib/helper'
3 | require 'test/unit'
4 | require 'rack/test'
5 | require 'route_tests'
6 |
--------------------------------------------------------------------------------
/test/sequel_test.rb:
--------------------------------------------------------------------------------
1 | require 'lib/sequel_app'
2 | require 'lib/helper'
3 | require 'test/unit'
4 | require 'rack/test'
5 | require 'route_tests'
6 |
--------------------------------------------------------------------------------