maxjustus-sinatra-authentication 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.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2009-04-06
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest ADDED
@@ -0,0 +1,12 @@
1
+ lib/models/user.rb
2
+ lib/views/signup.haml
3
+ lib/views/edit.haml
4
+ lib/views/login.haml
5
+ lib/views/index.haml
6
+ lib/views/show.haml
7
+ lib/sinatra-authentication.rb
8
+ History.txt
9
+ Rakefile
10
+ readme.rdoc
11
+ TODO
12
+ Manifest
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('sinatra-authentication', '0.0.1') do |p|
6
+ p.description = "Simple authentication plugin for sinatra."
7
+ p.url = "http://github.com/maxjustus/sinatra-authentication"
8
+ p.author = "Max Justus Spransy"
9
+ p.email = "maxjustus@gmail.com"
10
+ p.ignore_pattern = []
11
+ p.development_dependencies = ["sinatra", "dm-core", "dm-timestamps", "dm-validations"]
12
+
13
+ end
14
+
15
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/TODO ADDED
@@ -0,0 +1,11 @@
1
+ TODO:
2
+ - write rake task which creates first admin user
3
+ after writing this, change the User.permission_level default value to 0
4
+
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
+ - ?implement a session store which isn't cookie based
10
+ - turn on sessions unless they're already on
11
+ - randomize the session key on every installation
@@ -0,0 +1,50 @@
1
+ class User
2
+ include DataMapper::Resource
3
+
4
+ attr_accessor :password, :password_confirmation
5
+
6
+ property :id, Serial, :protected => true
7
+ property :email, String, :key => true, :nullable => false, :length => (5..40), :unique => true, :format => :email_address
8
+ property :hashed_password, String
9
+ property :salt, String, :protected => true, :nullable => false
10
+ property :created_at, DateTime
11
+ property :permission_level, Integer, :default => -1
12
+
13
+ validates_present :password_confirmation, :unless => Proc.new { |t| t.hashed_password }
14
+ validates_present :password, :unless => Proc.new { |t| t.hashed_password }
15
+ validates_is_confirmed :password
16
+
17
+ def self.authenticate(email, pass)
18
+ current_user = first(:email => email)
19
+ return nil if current_user.nil?
20
+ return current_user if User.encrypt(pass, current_user.salt) == current_user.hashed_password
21
+ nil
22
+ end
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
32
+ end
33
+ protected
34
+
35
+ def self.encrypt(pass, salt)
36
+ Digest::SHA1.hexdigest(pass+salt)
37
+ end
38
+
39
+ def self.random_string(len)
40
+ #generate a random password consisting of strings and digits
41
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
42
+ newpass = ""
43
+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
44
+ return newpass
45
+ end
46
+
47
+ def method_missing(m, *args)
48
+ return false
49
+ end
50
+ end
@@ -0,0 +1,210 @@
1
+ require 'sinatra/base'
2
+ require 'dm-core'
3
+ require 'dm-timestamps'
4
+ require 'dm-validations'
5
+ require Pathname(__FILE__).dirname.expand_path + "models/user"
6
+
7
+ module SinatraAuthentication
8
+ VERSION = "0.0.1"
9
+ end
10
+
11
+ module Sinatra
12
+ module LilAuthentication
13
+ def self.registered(app)
14
+ #INVESTIGATE
15
+ #the possibility of sinatra having an array of view_paths to load from
16
+ #PROBLEM
17
+ #sinatra 9.1.1 doesn't have multiple view capability anywhere
18
+ #so to get around I have to do it totally manually by
19
+ #loading the view from this path into a string and rendering it
20
+ set :lil_authentication_view_path, Pathname(__FILE__).dirname.expand_path + "views/"
21
+
22
+ #TODO write captain sinatra developer man and inform him that the documentation
23
+ #conserning the writing of extensions is somewhat outdaded/incorrect.
24
+ #you do not need to to do self.get/self.post when writing an extension
25
+ #In fact, it doesn't work. You have to use the plain old sinatra DSL
26
+
27
+ get '/users' do
28
+ @users = User.all
29
+ if @users != []
30
+ haml get_view_as_string("index.haml"), :layout => use_layout?
31
+ else
32
+ redirect '/signup'
33
+ end
34
+ end
35
+
36
+ get '/users/:id' do
37
+ login_required
38
+
39
+ #INVESTIGATE
40
+ #
41
+ #WHY THE HECK WON'T GET RETURN ANYTHING?
42
+ #if I user User.get(params[:id]) it returns nil for some inexplicable reason
43
+ @user = User.first(:id => params[:id])
44
+ haml get_view_as_string("show.haml"), :layout => use_layout?
45
+ end
46
+
47
+ #convenience for ajax but maybe entirely stupid and unnecesary
48
+ get '/logged_in' do
49
+ if session[:user]
50
+ "true"
51
+ else
52
+ "false"
53
+ end
54
+ end
55
+
56
+ get '/login' do
57
+ haml get_view_as_string("login.haml"), :layout => use_layout?
58
+ end
59
+
60
+ post '/login' do
61
+ if user = User.authenticate(params[:email], params[:password])
62
+ session[:user] = user.id
63
+ redirect '/'
64
+ else
65
+ redirect '/login'
66
+ end
67
+ end
68
+
69
+ get '/logout' do
70
+ session[:user] = nil
71
+ @message = "in case it weren't obvious, you've logged out"
72
+ redirect '/'
73
+ end
74
+
75
+ get '/signup' do
76
+ haml get_view_as_string("signup.haml"), :layout => use_layout?
77
+ end
78
+
79
+ post '/signup' do
80
+ @user = User.new(params[:user])
81
+ if @user.save
82
+ session[:user] = @user.id
83
+ redirect '/'
84
+ else
85
+ session[:flash] = "failure!"
86
+ redirect '/'
87
+ end
88
+ end
89
+
90
+ get '/users/:id/edit' do
91
+ login_required
92
+ redirect "/users" unless current_user.admin? || current_user == params[:id]
93
+
94
+ @user = User.first(:id => params[:id])
95
+ haml get_view_as_string("edit.haml"), :layout => use_layout?
96
+ end
97
+
98
+ post '/users/:id/edit' do
99
+ login_required
100
+ redirect "/users" unless current_user.admin? || current_user == params[:id]
101
+
102
+ user = User.first(:id => params[:id])
103
+ user_attributes = params[:user]
104
+ if params[:user][:password] == ""
105
+ user_attributes.delete("password")
106
+ user_attributes.delete("password_confirmation")
107
+ end
108
+
109
+ if user.update_attributes(user_attributes)
110
+ redirect "/users/#{user.id}"
111
+ else
112
+ throw user.errors
113
+ end
114
+ end
115
+
116
+ get '/users/:id/delete' do
117
+ login_required
118
+ redirect "/users" unless current_user.admin? || current_user == params[:id]
119
+
120
+ user = User.first(:id => params[:id])
121
+ user.destroy
122
+ session[:flash] = "way to go, you deleted a user"
123
+ redirect '/'
124
+ end
125
+ end
126
+
127
+
128
+ end
129
+
130
+ module Helpers
131
+ def login_required
132
+ if session[:user]
133
+ return true
134
+ else
135
+ session[:return_to] = request.fullpath
136
+ redirect '/login'
137
+ return false
138
+ end
139
+ end
140
+
141
+ def current_user
142
+ # TODO
143
+ # considering returning a user like object with a permission method if not logged in
144
+ if session[:user]
145
+ User.first(:id => session[:user])
146
+ else
147
+ GuestUser.new
148
+ end
149
+ end
150
+
151
+ def logged_in?
152
+ !!session[:user]
153
+ end
154
+
155
+ def use_layout?
156
+ !request.xhr?
157
+ end
158
+
159
+ #BECAUSE sinatra 9.1.1 can't load views from different paths properly
160
+ def get_view_as_string(filename)
161
+ view = options.lil_authentication_view_path + filename
162
+ data = ""
163
+ f = File.open(view, "r")
164
+ f.each_line do |line|
165
+ data += line
166
+ end
167
+ return data
168
+ end
169
+
170
+ def render_login_logout(html_attributes = {:class => ""})
171
+ css_classes = html_attributes.delete(:class)
172
+ parameters = ''
173
+ html_attributes.each_pair do |attribute, value|
174
+ parameters += "#{attribute}=\"#{value}\" "
175
+ end
176
+
177
+ result = "<div id='sinatra-authentication-login-logout' >"
178
+ if logged_in?
179
+ logout_parameters = html_attributes
180
+ # a tad janky?
181
+ logout_parameters.delete(:rel)
182
+ result += "<a href='/logout' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>logout</a> "
183
+ result += "<a href='/users/#{current_user.id}/edit' class='#{css_classes} sinatra-authentication-edit' #{parameters}>edit account</a>"
184
+ else
185
+ result += "<a href='/login' class='#{css_classes} sinatra-authentication-login' #{parameters}>login</a> "
186
+ result += "<a href='/signup' class='#{css_classes} sinatra-authentication-signup' #{parameters}>signup</a>"
187
+ end
188
+
189
+ result += "</div>"
190
+ end
191
+ end
192
+
193
+ register LilAuthentication
194
+ end
195
+
196
+ class GuestUser
197
+ def guest?
198
+ true
199
+ end
200
+
201
+ def permission_level
202
+ 0
203
+ end
204
+
205
+ # current_user.admin? returns false. current_user.has_a_baby? returns false.
206
+ # (which is a bit of an assumption I suppose)
207
+ def method_missing(m, *args)
208
+ return false
209
+ end
210
+ end
@@ -0,0 +1,21 @@
1
+ #sinatra_authentication
2
+ %h1
3
+ Editing
4
+ = @user.email
5
+ %form{:action => "/users/#{@user.id}/edit", :method => "post"}
6
+ %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
7
+ new password
8
+ %br
9
+ %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
10
+ confirm
11
+ -# don't render permission field if admin and editing yourself so you don't shoot yourself in the foot
12
+ - if current_user.admin? && current_user.id != @user.id
13
+ %br
14
+ %select{ :id => "permission_level", :name => "user[permission_level]" }
15
+ %option{:value => -1}
16
+ admin
17
+ %option{:value => 1}
18
+ authenticated user
19
+ permission level
20
+ %br
21
+ %input{ :value => "update", :type => "submit" }
@@ -0,0 +1,19 @@
1
+ #sinatra_authentication
2
+ %h1 Users
3
+ %table
4
+ %tr
5
+ %th email
6
+ - if current_user.admin?
7
+ %th permission level
8
+ - @users.each do |user|
9
+ %tr
10
+ %td= user.email
11
+ - if current_user.admin?
12
+ %td= user.permission_level
13
+ %td
14
+ %a{:href => "/users/#{user.id}"} show
15
+ - if current_user.admin?
16
+ %td
17
+ %a{:href => "/users/#{user.id}/edit"} edit
18
+ %td
19
+ %a{:href => "/users/#{user.id}/delete", :onclick => "return confirm('you sure?')"} delete
@@ -0,0 +1,10 @@
1
+ #sinatra_authentication
2
+ %h1 Login
3
+ %form{:action => "/login", :method => "post"}
4
+ %input{:id => "user_email", :name => "email", :size => 30, :type => "text"}
5
+ email
6
+ %br
7
+ %input{:id => "user_password", :name => "password", :size => 30, :type => "password"}
8
+ password
9
+ %br
10
+ %input{:value => "login", :type => "submit"}
@@ -0,0 +1,5 @@
1
+ #sinatra_authentication
2
+ %h1= @user.email
3
+ - if current_user.admin?
4
+ %h2 permission level
5
+ = @user.permission_level
@@ -0,0 +1,13 @@
1
+ #sinatra_authentication
2
+ %h1 Signup
3
+ %form{:action => "/signup", :method => "post"}
4
+ %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text" }
5
+ email
6
+ %br
7
+ %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
8
+ password
9
+ %br
10
+ %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
11
+ confirm
12
+ %br
13
+ %input{ :value => "sign up", :type => "submit" }
data/readme.rdoc ADDED
@@ -0,0 +1,71 @@
1
+ a little sinatra gem that implements user authentication
2
+
3
+ INSTALLATION:
4
+
5
+ in your sinatra app simply include "sinatra-authentication" and turn on session storage
6
+ with a super secret key, like so:
7
+
8
+ include "sinatra-authentication"
9
+
10
+ use Rack::Session::Cookie, :secret => 'A1 sauce 1s so good you should use 1t on a11 yr st34ksssss'
11
+
12
+ DEFAULT ROUTES
13
+
14
+ - get '/login'
15
+ - get '/logout'
16
+ - get '/signup'
17
+ - get/post '/users'
18
+ - get '/users/:id'
19
+ - get/post '/users/:id/edit'
20
+ - get '/users/:id/delete'
21
+
22
+ If you fetch any of the user pages using ajax, they will automatically render without a layout
23
+
24
+ HELPER METHODS
25
+
26
+ This plugin provides the following helper methods for your sinatra app:
27
+
28
+ - login_required
29
+ which you place at the beginning of any routes you want to be protected
30
+ - current_user
31
+ - logged_in?
32
+ - render_login_logout(html_attributes)
33
+ Which renders login/logout and singup/edit account links.
34
+ If you pass a hash of html parameters to render_login_logout all the links will get set to them.
35
+ Which useful for if you're using some sort of lightbox
36
+
37
+ SIMPLE PERMISSIONS
38
+
39
+ By default the user class includes a method called admin? which simply checks
40
+ if user.permission_level == -1.
41
+
42
+ you can take advantage of this method in your views or controllers by calling
43
+ current_user.admin?
44
+ i.e.
45
+
46
+ - if current_user.admin?
47
+ %a{:href => "/adminey_link_route_thing"} do something adminey
48
+
49
+ (these view examples are in HAML, by the way)
50
+
51
+ You can also extend the user class with any convenience methods for determining permissions.
52
+ i.e.
53
+
54
+ #somewhere in the murky depths of your sinatra app
55
+ class User
56
+ def peasant?
57
+ self.permission_level == 0
58
+ end
59
+ end
60
+
61
+ then in your views you can do
62
+
63
+ - if current_user.peasant?
64
+ %h1 hello peasant!
65
+ %p Welcome to the caste system! It's very depressing.
66
+
67
+ if no one is logged in, current_user returns a GuestUser instance, which responds to current_user.guest?
68
+ with true, current_user.permission_level with 0 and any other method calls with false
69
+
70
+ This makes some view logic easier since you don't always have to check if the user is logged in,
71
+ although a logged_in? helper method is still provided
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{sinatra-authentication}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Max Justus Spransy"]
9
+ s.date = %q{2009-04-21}
10
+ s.description = %q{Simple authentication plugin for sinatra.}
11
+ s.email = %q{maxjustus@gmail.com}
12
+ s.extra_rdoc_files = ["lib/models/user.rb", "lib/views/signup.haml", "lib/views/edit.haml", "lib/views/login.haml", "lib/views/index.haml", "lib/views/show.haml", "lib/sinatra-authentication.rb", "TODO"]
13
+ s.files = ["lib/models/user.rb", "lib/views/signup.haml", "lib/views/edit.haml", "lib/views/login.haml", "lib/views/index.haml", "lib/views/show.haml", "lib/sinatra-authentication.rb", "History.txt", "Rakefile", "readme.rdoc", "TODO", "Manifest", "sinatra-authentication.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/maxjustus/sinatra-authentication}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Sinatra-authentication", "--main", "readme.rdoc"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{sinatra-authentication}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{Simple authentication plugin for sinatra.}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_development_dependency(%q<sinatra>, [">= 0"])
28
+ s.add_development_dependency(%q<dm-core>, [">= 0"])
29
+ s.add_development_dependency(%q<dm-timestamps>, [">= 0"])
30
+ s.add_development_dependency(%q<dm-validations>, [">= 0"])
31
+ else
32
+ s.add_dependency(%q<sinatra>, [">= 0"])
33
+ s.add_dependency(%q<dm-core>, [">= 0"])
34
+ s.add_dependency(%q<dm-timestamps>, [">= 0"])
35
+ s.add_dependency(%q<dm-validations>, [">= 0"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<sinatra>, [">= 0"])
39
+ s.add_dependency(%q<dm-core>, [">= 0"])
40
+ s.add_dependency(%q<dm-timestamps>, [">= 0"])
41
+ s.add_dependency(%q<dm-validations>, [">= 0"])
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maxjustus-sinatra-authentication
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Max Justus Spransy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-21 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sinatra
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: dm-core
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: dm-timestamps
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: dm-validations
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description: Simple authentication plugin for sinatra.
56
+ email: maxjustus@gmail.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - lib/models/user.rb
63
+ - lib/views/signup.haml
64
+ - lib/views/edit.haml
65
+ - lib/views/login.haml
66
+ - lib/views/index.haml
67
+ - lib/views/show.haml
68
+ - lib/sinatra-authentication.rb
69
+ - TODO
70
+ files:
71
+ - lib/models/user.rb
72
+ - lib/views/signup.haml
73
+ - lib/views/edit.haml
74
+ - lib/views/login.haml
75
+ - lib/views/index.haml
76
+ - lib/views/show.haml
77
+ - lib/sinatra-authentication.rb
78
+ - History.txt
79
+ - Rakefile
80
+ - readme.rdoc
81
+ - TODO
82
+ - Manifest
83
+ - sinatra-authentication.gemspec
84
+ has_rdoc: true
85
+ homepage: http://github.com/maxjustus/sinatra-authentication
86
+ post_install_message:
87
+ rdoc_options:
88
+ - --line-numbers
89
+ - --inline-source
90
+ - --title
91
+ - Sinatra-authentication
92
+ - --main
93
+ - readme.rdoc
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ version:
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: "1.2"
107
+ version:
108
+ requirements: []
109
+
110
+ rubyforge_project: sinatra-authentication
111
+ rubygems_version: 1.2.0
112
+ signing_key:
113
+ specification_version: 2
114
+ summary: Simple authentication plugin for sinatra.
115
+ test_files: []
116
+