sinatra-authentication-nedludd 0.0.1

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.
@@ -0,0 +1,44 @@
1
+ if Object.const_defined?("DataMapper")
2
+ #require 'dm-core'
3
+ require 'dm-timestamps'
4
+ require 'dm-validations'
5
+ require Pathname(__FILE__).dirname.expand_path + "datamapper_user.rb"
6
+ require Pathname(__FILE__).dirname.expand_path + "dm_adapter.rb"
7
+ end
8
+
9
+ class User
10
+ if Object.const_defined?("DataMapper")
11
+ include DmAdapter
12
+ else
13
+ throw "you need to require 'dm-core' for sinatra-authentication to work"
14
+ end
15
+
16
+ def initialize(interfacing_class_instance)
17
+ @instance = interfacing_class_instance
18
+ end
19
+
20
+ def id
21
+ @instance.id
22
+ end
23
+
24
+ def self.authenticate(email, pass)
25
+ current_user = get(:email => email)
26
+ return nil if current_user.nil?
27
+ return current_user if User.encrypt(pass, current_user.salt) == current_user.hashed_password
28
+ nil
29
+ end
30
+
31
+ protected
32
+
33
+ def self.encrypt(pass, salt)
34
+ Digest::SHA1.hexdigest(pass+salt)
35
+ end
36
+
37
+ def self.random_string(len)
38
+ #generate a random password consisting of strings and digits
39
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
40
+ newpass = ""
41
+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
42
+ return newpass
43
+ end
44
+ end
@@ -0,0 +1,39 @@
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
+ property :created_at, DateTime
9
+ property :permission_level, Integer, :default => 1
10
+
11
+ attr_accessor :password, :password_confirmation
12
+ #protected equievelant? :protected => true doesn't exist in dm 0.10.0
13
+ #protected :id, :salt
14
+ #doesn't behave correctly, I'm not even sure why I did this.
15
+
16
+ validates_presence_of :password_confirmation, :unless => Proc.new { |t| t.hashed_password }
17
+ validates_presence_of :password, :unless => Proc.new { |t| t.hashed_password }
18
+ validates_confirmation_of :password
19
+
20
+ def password=(pass)
21
+ @password = pass
22
+ self.salt = User.random_string(10) if !self.salt
23
+ self.hashed_password = User.encrypt(@password, self.salt)
24
+ end
25
+
26
+ def admin?
27
+ self.permission_level == -1 || self.id == 1
28
+ end
29
+
30
+ def site_admin?
31
+ self.id == 1
32
+ end
33
+
34
+ protected
35
+
36
+ def method_missing(m, *args)
37
+ return false
38
+ end
39
+ end
@@ -0,0 +1,50 @@
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(*args)
10
+ result = DmUser.all(*args)
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
+ user
26
+ end
27
+
28
+ def set!(attributes)
29
+ user = DmUser.new attributes
30
+ user.save!
31
+ 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 update(attributes)
42
+ @instance.update attributes
43
+ end
44
+
45
+ def method_missing(meth, *args, &block)
46
+ #cool I just found out * on an array turns the array into a list of args for a function
47
+ @instance.send(meth, *args, &block)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,227 @@
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
+ end
146
+ end
147
+
148
+ module Helpers
149
+ def login_required
150
+ #not as efficient as checking the session. but this inits the fb_user if they are logged in
151
+ if current_user.class != GuestUser
152
+ return true
153
+ else
154
+ session[:return_to] = request.fullpath
155
+ redirect '/login'
156
+ return false
157
+ end
158
+ end
159
+
160
+ def current_user
161
+ if session[:user]
162
+ User.get(:id => session[:user])
163
+ else
164
+ GuestUser.new
165
+ end
166
+ end
167
+
168
+ def logged_in?
169
+ !!session[:user]
170
+ end
171
+
172
+ def use_layout?
173
+ !request.xhr?
174
+ end
175
+
176
+ #BECAUSE sinatra 9.1.1 can't load views from different paths properly
177
+ def get_view_as_string(filename)
178
+ view = options.sinatra_authentication_view_path + filename
179
+ data = ""
180
+ f = File.open(view, "r")
181
+ f.each_line do |line|
182
+ data += line
183
+ end
184
+ return data
185
+ end
186
+
187
+ def render_login_logout(html_attributes = {:class => ""})
188
+ css_classes = html_attributes.delete(:class)
189
+ parameters = ''
190
+ html_attributes.each_pair do |attribute, value|
191
+ parameters += "#{attribute}=\"#{value}\" "
192
+ end
193
+
194
+ result = "<div id='sinatra-authentication-login-logout' >"
195
+ if logged_in?
196
+ logout_parameters = html_attributes
197
+ # a tad janky?
198
+ logout_parameters.delete(:rel)
199
+ result += "<a href='/users/#{current_user.id}/edit' class='#{css_classes} sinatra-authentication-edit' #{parameters}>Edit account</a> "
200
+ result += "<a href='/logout' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>Logout</a>"
201
+ else
202
+ result += "<a href='/signup' class='#{css_classes} sinatra-authentication-signup' #{parameters}>Signup</a> "
203
+ result += "<a href='/login' class='#{css_classes} sinatra-authentication-login' #{parameters}>Login</a>"
204
+ end
205
+
206
+ result += "</div>"
207
+ end
208
+ end
209
+
210
+ register LilAuthentication
211
+ end
212
+
213
+ class GuestUser
214
+ def guest?
215
+ true
216
+ end
217
+
218
+ def permission_level
219
+ 0
220
+ end
221
+
222
+ # current_user.admin? returns false. current_user.has_a_baby? returns false.
223
+ # (which is a bit of an assumption I suppose)
224
+ def method_missing(m, *args)
225
+ return false
226
+ end
227
+ end
@@ -0,0 +1,38 @@
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
+ - else
12
+ account
13
+ %form{:action => "/users/#{@user.id}/edit", :method => "post"}
14
+ .field
15
+ .label
16
+ %label{:for => "user_email"} Email
17
+ %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text", :value => @user.email }
18
+ .field
19
+ .label
20
+ %label{:for => "user_password"} New password
21
+ %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
22
+ .field
23
+ .label
24
+ %label{:for => "user_password_confirmation"} Confirm
25
+ %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
26
+ -# don't render permission field if admin and editing yourself so you don't shoot yourself in the foot
27
+ - if current_user.admin? && current_user.id != @user.id
28
+ .field
29
+ .label
30
+ %label{:for => 'permission_level'} Permission level
31
+ %select{ :id => "permission_level", :name => "user[permission_level]" }
32
+ %option{:value => -1, :selected => @user.admin?}
33
+ Admin
34
+ %option{:value => 1, :selected => @user.permission_level == 1}
35
+ Authenticated user
36
+ .buttons
37
+ %input{ :value => "Update", :type => "submit" }
38
+
@@ -0,0 +1,27 @@
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
+ - else
14
+ "user #{user.id}"
15
+ - if current_user.admin?
16
+ %td= user.permission_level
17
+ %td
18
+ %a{:href => "/users/#{user.id}"} show
19
+ - if current_user.admin?
20
+ %td
21
+ %a{:href => "/users/#{user.id}/edit"} edit
22
+ %td
23
+ -# this doesn't work for tk
24
+ - if !user.site_admin?
25
+ %a{:href => "/users/#{user.id}/delete", :onclick => "return confirm('you sure?')"} delete
26
+ - else
27
+ site admin
@@ -0,0 +1,18 @@
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
+