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.
- data/.gitignore +4 -0
- data/History.txt +4 -0
- data/Manifest +26 -0
- data/Rakefile +38 -0
- data/TODO +53 -0
- data/example/dm_extend_app.rb +26 -0
- data/example/dm_sinbook.rb +56 -0
- data/example/extend_views/edit.haml +42 -0
- data/example/extend_views/index.haml +31 -0
- data/example/extend_views/login.haml +21 -0
- data/example/extend_views/show.haml +9 -0
- data/example/extend_views/signup.haml +30 -0
- data/example/mm_app.rb +22 -0
- data/example/tc_app.rb +16 -0
- data/example/tc_sinbook.rb +62 -0
- data/lib/models/abstract_user.rb +54 -0
- data/lib/models/datamapper_user.rb +42 -0
- data/lib/models/dm_adapter.rb +50 -0
- data/lib/models/mm_adapter.rb +53 -0
- data/lib/models/mongomapper_user.rb +42 -0
- data/lib/models/rufus_tokyo_user.rb +177 -0
- data/lib/models/tc_adapter.rb +83 -0
- data/lib/sinatra-authentication.rb +290 -0
- data/lib/views/edit.haml +43 -0
- data/lib/views/index.haml +29 -0
- data/lib/views/login.haml +22 -0
- data/lib/views/show.haml +9 -0
- data/lib/views/signup.haml +26 -0
- data/readme.markdown +238 -0
- data/sinatra-authentication.gemspec +119 -0
- data/test/datamapper_test.rb +5 -0
- data/test/lib/dm_app.rb +20 -0
- data/test/lib/dm_extend_app.rb +27 -0
- data/test/lib/dm_sinbook.rb +55 -0
- data/test/lib/extend_views/edit.haml +42 -0
- data/test/lib/extend_views/index.haml +31 -0
- data/test/lib/extend_views/login.haml +21 -0
- data/test/lib/extend_views/show.haml +9 -0
- data/test/lib/extend_views/signup.haml +29 -0
- data/test/lib/helper.rb +9 -0
- data/test/lib/mm_app.rb +24 -0
- data/test/lib/tc_app.rb +16 -0
- data/test/lib/tc_sinbook.rb +62 -0
- data/test/mongomapper_test.rb +39 -0
- data/test/route_tests.rb +29 -0
- data/test/rufus_tokyo_test.rb +5 -0
- 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
|
data/lib/views/edit.haml
ADDED
@@ -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')
|
data/lib/views/show.haml
ADDED
@@ -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
|