sinatra-authentication-dmeiz 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/.gitignore +4 -0
  2. data/History.txt +4 -0
  3. data/Manifest +26 -0
  4. data/Rakefile +38 -0
  5. data/TODO +53 -0
  6. data/example/dm_extend_app.rb +26 -0
  7. data/example/dm_sinbook.rb +56 -0
  8. data/example/extend_views/edit.haml +42 -0
  9. data/example/extend_views/index.haml +31 -0
  10. data/example/extend_views/login.haml +21 -0
  11. data/example/extend_views/show.haml +9 -0
  12. data/example/extend_views/signup.haml +30 -0
  13. data/example/mm_app.rb +22 -0
  14. data/example/tc_app.rb +16 -0
  15. data/example/tc_sinbook.rb +62 -0
  16. data/lib/models/abstract_user.rb +54 -0
  17. data/lib/models/datamapper_user.rb +42 -0
  18. data/lib/models/dm_adapter.rb +50 -0
  19. data/lib/models/mm_adapter.rb +53 -0
  20. data/lib/models/mongomapper_user.rb +42 -0
  21. data/lib/models/rufus_tokyo_user.rb +177 -0
  22. data/lib/models/tc_adapter.rb +83 -0
  23. data/lib/sinatra-authentication.rb +290 -0
  24. data/lib/views/edit.haml +43 -0
  25. data/lib/views/index.haml +29 -0
  26. data/lib/views/login.haml +22 -0
  27. data/lib/views/show.haml +9 -0
  28. data/lib/views/signup.haml +26 -0
  29. data/readme.markdown +238 -0
  30. data/sinatra-authentication.gemspec +119 -0
  31. data/test/datamapper_test.rb +5 -0
  32. data/test/lib/dm_app.rb +20 -0
  33. data/test/lib/dm_extend_app.rb +27 -0
  34. data/test/lib/dm_sinbook.rb +55 -0
  35. data/test/lib/extend_views/edit.haml +42 -0
  36. data/test/lib/extend_views/index.haml +31 -0
  37. data/test/lib/extend_views/login.haml +21 -0
  38. data/test/lib/extend_views/show.haml +9 -0
  39. data/test/lib/extend_views/signup.haml +29 -0
  40. data/test/lib/helper.rb +9 -0
  41. data/test/lib/mm_app.rb +24 -0
  42. data/test/lib/tc_app.rb +16 -0
  43. data/test/lib/tc_sinbook.rb +62 -0
  44. data/test/mongomapper_test.rb +39 -0
  45. data/test/route_tests.rb +29 -0
  46. data/test/rufus_tokyo_test.rb +5 -0
  47. metadata +212 -0
@@ -0,0 +1,290 @@
1
+ require 'sinatra/base'
2
+ require 'pathname'
3
+ require Pathname(__FILE__).dirname.expand_path + "models/abstract_user"
4
+
5
+ module Sinatra
6
+ module LilAuthentication
7
+ def self.registered(app)
8
+ #INVESTIGATE
9
+ #the possibility of sinatra having an array of view_paths to load from
10
+ #PROBLEM
11
+ #sinatra 9.1.1 doesn't have multiple view capability anywhere
12
+ #so to get around I have to do it totally manually by
13
+ #loading the view from this path into a string and rendering it
14
+ set :sinatra_authentication_view_path, Pathname(__FILE__).dirname.expand_path + "views/"
15
+
16
+ get '/users' do
17
+ login_required
18
+ redirect "/" unless current_user.admin?
19
+
20
+ @users = User.all
21
+ if @users != []
22
+ haml get_view_as_string("index.haml"), :layout => use_layout?
23
+ else
24
+ redirect '/signup'
25
+ end
26
+ end
27
+
28
+ get '/users/:id' do
29
+ login_required
30
+
31
+ @user = User.get(:id => params[:id])
32
+ haml get_view_as_string("show.haml"), :layout => use_layout?
33
+ end
34
+
35
+ #convenience for ajax but maybe entirely stupid and unnecesary
36
+ get '/logged_in' do
37
+ if session[:user]
38
+ "true"
39
+ else
40
+ "false"
41
+ end
42
+ end
43
+
44
+ get '/login' do
45
+ haml get_view_as_string("login.haml"), :layout => use_layout?
46
+ end
47
+
48
+ post '/login' do
49
+ if user = User.authenticate(params[:email], params[:password])
50
+ session[:user] = user.id
51
+
52
+ if Rack.const_defined?('Flash')
53
+ flash[:notice] = "Login successful."
54
+ end
55
+
56
+ if session[:return_to]
57
+ redirect_url = session[:return_to]
58
+ session[:return_to] = false
59
+ redirect redirect_url
60
+ else
61
+ redirect '/'
62
+ end
63
+ else
64
+ if Rack.const_defined?('Flash')
65
+ flash[:notice] = "The email or password you entered is incorrect."
66
+ end
67
+ redirect '/login'
68
+ end
69
+ end
70
+
71
+ get '/logout' do
72
+ session[:user] = nil
73
+ if Rack.const_defined?('Flash')
74
+ flash[:notice] = "Logout successful."
75
+ end
76
+ redirect '/'
77
+ end
78
+
79
+ get '/signup' do
80
+ haml get_view_as_string("signup.haml"), :layout => use_layout?
81
+ end
82
+
83
+ post '/signup' do
84
+ @user = User.set(params[:user])
85
+ if @user && @user.id
86
+ session[:user] = @user.id
87
+ if Rack.const_defined?('Flash')
88
+ flash[:notice] = "Account created."
89
+ end
90
+ redirect '/'
91
+ else
92
+ if Rack.const_defined?('Flash')
93
+ flash[:notice] = 'There were some problems creating your account. Please be sure you\'ve entered all your information correctly.'
94
+ end
95
+ redirect '/signup'
96
+ end
97
+ end
98
+
99
+ get '/users/:id/edit' do
100
+ login_required
101
+ redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
102
+ @user = User.get(:id => params[:id])
103
+ haml get_view_as_string("edit.haml"), :layout => use_layout?
104
+ end
105
+
106
+ post '/users/:id/edit' do
107
+ login_required
108
+ redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
109
+
110
+ user = User.get(:id => params[:id])
111
+ user_attributes = params[:user]
112
+ if params[:user][:password] == ""
113
+ user_attributes.delete("password")
114
+ user_attributes.delete("password_confirmation")
115
+ end
116
+
117
+ if user.update(user_attributes)
118
+ if Rack.const_defined?('Flash')
119
+ flash[:notice] = 'Account updated.'
120
+ end
121
+ redirect '/'
122
+ else
123
+ if Rack.const_defined?('Flash')
124
+ flash[:notice] = 'Whoops, looks like there were some problems with your updates.'
125
+ end
126
+ redirect "/users/#{user.id}/edit"
127
+ end
128
+ end
129
+
130
+ get '/users/:id/delete' do
131
+ login_required
132
+ redirect "/users" unless current_user.admin? || current_user.id.to_s == params[:id]
133
+
134
+ if User.delete(params[:id])
135
+ if Rack.const_defined?('Flash')
136
+ flash[:notice] = "User deleted."
137
+ end
138
+ else
139
+ if Rack.const_defined?('Flash')
140
+ flash[:notice] = "Deletion failed."
141
+ end
142
+ end
143
+ redirect '/'
144
+ end
145
+
146
+
147
+ if Sinatra.const_defined?('FacebookObject')
148
+ get '/connect' do
149
+ if fb[:user]
150
+ if current_user.class != GuestUser
151
+ user = current_user
152
+ else
153
+ user = User.get(:fb_uid => fb[:user])
154
+ end
155
+
156
+ if user
157
+ if !user.fb_uid || user.fb_uid != fb[:user]
158
+ user.update :fb_uid => fb[:user]
159
+ end
160
+ session[:user] = user.id
161
+ else
162
+ user = User.set!(:fb_uid => fb[:user])
163
+ session[:user] = user.id
164
+ end
165
+ end
166
+ redirect '/'
167
+ end
168
+
169
+ get '/receiver' do
170
+ %[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
171
+ <html xmlns="http://www.w3.org/1999/xhtml" >
172
+ <body>
173
+ <script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js" type="text/javascript"></script>
174
+ </body>
175
+ </html>]
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ module Helpers
182
+ def login_required
183
+ #not as efficient as checking the session. but this inits the fb_user if they are logged in
184
+ if current_user.class != GuestUser
185
+ return true
186
+ else
187
+ session[:return_to] = request.fullpath
188
+ redirect '/login'
189
+ return false
190
+ end
191
+ end
192
+
193
+ def current_user
194
+ if session[:user]
195
+ User.get(:id => session[:user])
196
+ else
197
+ GuestUser.new
198
+ end
199
+ end
200
+
201
+ def logged_in?
202
+ !!session[:user]
203
+ end
204
+
205
+ def use_layout?
206
+ !request.xhr?
207
+ end
208
+
209
+ #BECAUSE sinatra 9.1.1 can't load views from different paths properly
210
+ def get_view_as_string(filename)
211
+ view = options.sinatra_authentication_view_path + filename
212
+ data = ""
213
+ f = File.open(view, "r")
214
+ f.each_line do |line|
215
+ data += line
216
+ end
217
+ return data
218
+ end
219
+
220
+ def render_login_logout(html_attributes = {:class => ""})
221
+ css_classes = html_attributes.delete(:class)
222
+ parameters = ''
223
+ html_attributes.each_pair do |attribute, value|
224
+ parameters += "#{attribute}=\"#{value}\" "
225
+ end
226
+
227
+ result = "<div id='sinatra-authentication-login-logout' >"
228
+ if logged_in?
229
+ logout_parameters = html_attributes
230
+ # a tad janky?
231
+ logout_parameters.delete(:rel)
232
+ result += "<a href='/users/#{current_user.id}/edit' class='#{css_classes} sinatra-authentication-edit' #{parameters}>Edit account</a> "
233
+ if Sinatra.const_defined?('FacebookObject')
234
+ if fb[:user]
235
+ result += "<a href='javascript:FB.Connect.logoutAndRedirect(\"/logout\");' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>Logout</a>"
236
+ else
237
+ result += "<a href='/logout' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>Logout</a>"
238
+ end
239
+ else
240
+ result += "<a href='/logout' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>Logout</a>"
241
+ end
242
+ else
243
+ result += "<a href='/signup' class='#{css_classes} sinatra-authentication-signup' #{parameters}>Signup</a> "
244
+ result += "<a href='/login' class='#{css_classes} sinatra-authentication-login' #{parameters}>Login</a>"
245
+ end
246
+
247
+ result += "</div>"
248
+ end
249
+
250
+ if Sinatra.const_defined?('FacebookObject')
251
+ def render_facebook_connect_link(text = 'Login using facebook', options = {:size => 'small'})
252
+ if options[:size] == 'small'
253
+ size = 'Small'
254
+ elsif options[:size] == 'medium'
255
+ size = 'Medium'
256
+ elsif options[:size] == 'large'
257
+ size = 'Large'
258
+ elsif options[:size] == 'xlarge'
259
+ size = 'BigPun'
260
+ else
261
+ size = 'Small'
262
+ end
263
+
264
+ %[<a href="#" onclick="FB.Connect.requireSession(function(){document.location = '/connect';}); return false;" class="fbconnect_login_button FBConnectButton FBConnectButton_#{size}">
265
+ <span id="RES_ID_fb_login_text" class="FBConnectButton_Text">
266
+ #{text}
267
+ </span>
268
+ </a>]
269
+ end
270
+ end
271
+ end
272
+
273
+ register LilAuthentication
274
+ end
275
+
276
+ class GuestUser
277
+ def guest?
278
+ true
279
+ end
280
+
281
+ def permission_level
282
+ 0
283
+ end
284
+
285
+ # current_user.admin? returns false. current_user.has_a_baby? returns false.
286
+ # (which is a bit of an assumption I suppose)
287
+ def method_missing(m, *args)
288
+ return false
289
+ end
290
+ end
@@ -0,0 +1,43 @@
1
+ #sinatra_authentication
2
+ - if Rack.const_defined?('Flash')
3
+ #sinatra_authentication_flash= flash[:notice]
4
+ %h1
5
+ Edit
6
+ - if @user.id == current_user.id
7
+ account
8
+ - else
9
+ - if @user.email
10
+ = @user.email
11
+ - elsif @user.fb_uid
12
+ <fb:name uid=#{@user.fb_uid} linked='false' />
13
+ - else
14
+ account
15
+ %form{:action => "/users/#{@user.id}/edit", :method => "post"}
16
+ .field
17
+ .label
18
+ %label{:for => "user_email"} Email
19
+ %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text", :value => @user.email }
20
+ .field
21
+ .label
22
+ %label{:for => "user_password"} New password
23
+ %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
24
+ .field
25
+ .label
26
+ %label{:for => "user_password_confirmation"} Confirm
27
+ %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
28
+ -# don't render permission field if admin and editing yourself so you don't shoot yourself in the foot
29
+ - if current_user.admin? && current_user.id != @user.id
30
+ .field
31
+ .label
32
+ %label{:for => 'permission_level'} Permission level
33
+ %select{ :id => "permission_level", :name => "user[permission_level]" }
34
+ %option{:value => -1, :selected => @user.admin?}
35
+ Admin
36
+ %option{:value => 1, :selected => @user.permission_level == 1}
37
+ Authenticated user
38
+ .buttons
39
+ %input{ :value => "Update", :type => "submit" }
40
+ - if Sinatra.const_defined?('FacebookObject')
41
+ - unless @user.fb_uid
42
+ |
43
+ = render_facebook_connect_link('Link account with Facebook')
@@ -0,0 +1,29 @@
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
+ <fb:name uid=#{user.fb_uid} />
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
@@ -0,0 +1,22 @@
1
+ #sinatra_authentication
2
+ - if Rack.const_defined?('Flash')
3
+ #sinatra_authentication_flash= flash[:notice]
4
+ %h1.page_title Login
5
+ %form{:action => "/login", :method => "post"}
6
+ .field
7
+ .label
8
+ %label{:for => "user_email'"} Email
9
+ %input{:id => "user_email", :name => "email", :size => 30, :type => "text"}
10
+ .field
11
+ .label
12
+ %label{:for => "user_password"} Password
13
+ %input{:id => "user_password", :name => "password", :size => 30, :type => "password"}
14
+ .buttons
15
+ %input{:value => "login", :type => "submit"}
16
+ %a{:href => "/signup", :class => 'sinatra_authentication_link'}
17
+ Signup
18
+ - if Sinatra.const_defined?('FacebookObject')
19
+ .third_party_signup
20
+ %h3.section_title One click login:
21
+ .login_link.facebook_login
22
+ = render_facebook_connect_link('Login using facebook', :size => 'large')
@@ -0,0 +1,9 @@
1
+ #sinatra_authentication
2
+ %h1.page_title
3
+ - if @user.email
4
+ = @user.email
5
+ - elsif @user.fb_uid
6
+ <fb:name uid=#{@user.fb_uid} linked='false' />
7
+ - if current_user.admin?
8
+ %h2 permission level
9
+ = @user.permission_level
@@ -0,0 +1,26 @@
1
+ #sinatra_authentication
2
+ - if Rack.const_defined?('Flash')
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_password_confirmation"} Confirm Password
17
+ %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
18
+ .buttons
19
+ %input{ :value => "Create account", :type => "submit" }
20
+ %a{:href => "/login", :class => 'sinatra_authentication_link'}
21
+ Login
22
+ - if Sinatra.const_defined?('FacebookObject')
23
+ .third_party_signup
24
+ %h3.section_title One click signup:
25
+ .login_link.facebook_login
26
+ = render_facebook_connect_link('Signup using facebook', :size => 'large')
data/readme.markdown ADDED
@@ -0,0 +1,238 @@
1
+ ### A little sinatra gem that implements user authentication, with support for datamapper, mongomapper and rufus-tokyo
2
+
3
+ ## INSTALLATION:
4
+
5
+ in your sinatra app simply require either "dm-core", "rufus-tokyo" 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 '/reciever'
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]
60
+
61
+ -# somewhere in a haml view:
62
+ = flash[:notice]
63
+
64
+ ## HELPER METHODS:
65
+
66
+ This plugin provides the following helper methods for your sinatra app:
67
+
68
+ * login_required
69
+ > which you place at the beginning of any routes you want to be protected
70
+ * current_user
71
+ * logged_in?
72
+ * render_login_logout(html_attributes)
73
+ > Which renders login/logout and singup/edit account links.
74
+ If you pass a hash of html parameters to render_login_logout all the links will get set to them.
75
+ Which is useful for if you're using some sort of lightbox
76
+
77
+ ## SIMPLE PERMISSIONS:
78
+
79
+ By default the user class includes a method called admin? which simply checks
80
+ if user.permission_level == -1.
81
+
82
+ you can take advantage of this method in your views or controllers by calling
83
+ current_user.admin?
84
+ i.e.
85
+
86
+ - if current_user.admin?
87
+ %a{:href => "/adminey_link_route_thing"} do something adminey
88
+
89
+ (these view examples are in HAML, by the way)
90
+
91
+ You can also extend the user class with any convenience methods for determining permissions.
92
+ i.e.
93
+
94
+ #somewhere in the murky depths of your sinatra app
95
+ class User
96
+ def peasant?
97
+ self.permission_level == 0
98
+ end
99
+ end
100
+
101
+ then in your views you can do
102
+
103
+ - if current_user.peasant?
104
+ %h1 hello peasant!
105
+ %p Welcome to the caste system! It's very depressing.
106
+
107
+ if no one is logged in, current_user returns a GuestUser instance, which responds to current_user.guest?
108
+ with true, current_user.permission_level with 0 and any other method calls with false
109
+
110
+ This makes some view logic easier since you don't always have to check if the user is logged in,
111
+ although a logged_in? helper method is still provided
112
+
113
+ ## RUFUS TOKYO
114
+
115
+ 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].
116
+ if you wanna set an attribute, you can do something like current_user["has_a_dog"] = true
117
+ and if you want to open a connection with the cabinet directly, you can do something like
118
+
119
+ user_connection = TcUser.new
120
+ users_with_gmail = user_connection.query do |q|
121
+ q.add 'email', :strinc, 'gmail'
122
+ end
123
+ user_connection.close
124
+
125
+ ## FACEBOOK
126
+
127
+ # at present, sinatra authentication supports sinbook for interacting with the facebook api.
128
+
129
+ If you want to allow users to login using facebook, just require 'sinbook' before requiring 'sinatra-authentication'.
130
+ The routes '/reciever' and '/connect' will be added. as well as connect links on the login and edit account pages.
131
+ You'll still have to include and initialize the facebook connect javascript in your layout yourself, like so:
132
+
133
+ (This example layout assumes you're using sinbook)
134
+
135
+ !!!
136
+ %head
137
+ %title Welcome to my Facebook Connect website!
138
+ %script{:type => 'text/javascript', :src => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US'}
139
+ %body
140
+ = yield
141
+ :javascript
142
+ FB.init("#{fb.api_key}", "/receiver")
143
+
144
+ Just remember to specify '/reciever' as the path to the xd-receiver file in your call to 'FB.init'.
145
+
146
+ The render_login_logout helper 'logout' link will log the user out of facebook and your app.
147
+
148
+ I've also included a little helper method 'render_facebook_connect_link' for rendering the facebook connect link with the correct 'onconnect' javascript callback.
149
+ The callback redirects to '/connect'.
150
+ This is important because the way I've implemented facebook connect support is by pinging '/connect' after the user
151
+ successfully connects with facebook.
152
+
153
+ If you choose to render the connect button yourself, be sure to have the 'onconnect' callback include "window.location = '/connect'".
154
+
155
+ '/connect' redirects to '/' on completion.
156
+
157
+ The 'render_facebook_connect_link' helper uses html instead of fbml, so ajax requests to '/login' or "/users/#{user.id}/edit"
158
+ will render the connect link without you needing to parse any fbml.
159
+
160
+ If the user is already logged into the app and connects with facebook via the user edit page,
161
+ it adds their fb_uid to their profile in the database,
162
+ which will allow them to log in using their email and password, OR their facebook account.
163
+
164
+ If they aren't already logged in to the app through the normal login form,
165
+ it creates a new user in the database without an email address or password.
166
+ They can later add this data by going to "/users/#{current_user.id}/edit",
167
+ which will allow them to log in using their email address and password, OR their facebook account.
168
+
169
+ ## OVERRIDING DEFAULT VIEWS
170
+
171
+ Right now if you're going to override sinatra-authentication's views, you have to override all of them.
172
+ This is something I hope to change in a future release.
173
+
174
+ To override the default view path do something like this:
175
+
176
+ set :sinatra_authentication_view_path, Pathname(__FILE__).dirname.expand_path + "my_views/"
177
+
178
+ And then the views you'll need to define are:
179
+
180
+ * show.haml
181
+ * index.haml
182
+ * signup.haml
183
+ * login.haml
184
+ * edit.haml
185
+
186
+ The signup and edit form fields are named so they pass a hash called 'user' to the server:
187
+
188
+ %input{:name => "user[email]", :size => 30, :type => "text", :value => @user.email}
189
+ %input{:name => "user[password]", :size => 30, :type => "password"}
190
+ %input{:name => "user[password_confirmation]", :size => 30, :type => "password"}
191
+
192
+ %select{:name => "user[permission_level]"}
193
+ %option{:value => -1, :selected => @user.admin?}
194
+ Admin
195
+ %option{:value => 1, :selected => @user.permission_level == 1}
196
+ Authenticated user
197
+
198
+ 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.
199
+
200
+ The login form fields just pass a field called email and a field called password:
201
+
202
+ %input{:name => "email", :size => 30, :type => "text"}
203
+ %input{:name => "password", :size => 30, :type => "password"}
204
+
205
+ To add methods or properties to the User class, you have to access the underlying database user class, like so:
206
+
207
+ class DmUser
208
+ property :name, String
209
+ property :has_dog, Boolean, :default => false
210
+ end
211
+
212
+ And then to access/update your newly defined attributes you use the User class:
213
+
214
+ current_user.name
215
+ current_user.has_dog
216
+
217
+ current_user.update({:has_dog => true})
218
+
219
+ new_user = User.set({:email => 'max@max.com' :password => 'hi', :password_confirmation => 'hi', :name => 'Max', :has_dog => false})
220
+
221
+ User.all(:has_dog => true).each do |user|
222
+ user.update({has_dog => false})
223
+ end
224
+
225
+ User.all(:has_dog => false).each do |user|
226
+ user.delete
227
+ end
228
+
229
+ the User class passes additional method calls along to the interfacing database class, so calls to Datamapper/Mongomapper/RufusTokyo functions should work as expected.
230
+
231
+ The database user classes are named as follows:
232
+
233
+ * for Datamapper:
234
+ > DmUser
235
+ * for Rufus Tokyo:
236
+ > TcUser
237
+ * for Mongomapper:
238
+ > MmUser