shingara-sinatra-authentication 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2009-04-06
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -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
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('sinatra-authentication', '0.0.2') 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 || self.id == 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,206 @@
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
+ end
127
+
128
+ module Helpers
129
+ def login_required
130
+ if session[:user]
131
+ return true
132
+ else
133
+ session[:return_to] = request.fullpath
134
+ redirect '/login'
135
+ return false
136
+ end
137
+ end
138
+
139
+ def current_user
140
+ if session[:user]
141
+ User.first(:id => session[:user])
142
+ else
143
+ GuestUser.new
144
+ end
145
+ end
146
+
147
+ def logged_in?
148
+ !!session[:user]
149
+ end
150
+
151
+ def use_layout?
152
+ !request.xhr?
153
+ end
154
+
155
+ #BECAUSE sinatra 9.1.1 can't load views from different paths properly
156
+ def get_view_as_string(filename)
157
+ view = options.lil_authentication_view_path + filename
158
+ data = ""
159
+ f = File.open(view, "r")
160
+ f.each_line do |line|
161
+ data += line
162
+ end
163
+ return data
164
+ end
165
+
166
+ def render_login_logout(html_attributes = {:class => ""})
167
+ css_classes = html_attributes.delete(:class)
168
+ parameters = ''
169
+ html_attributes.each_pair do |attribute, value|
170
+ parameters += "#{attribute}=\"#{value}\" "
171
+ end
172
+
173
+ result = "<div id='sinatra-authentication-login-logout' >"
174
+ if logged_in?
175
+ logout_parameters = html_attributes
176
+ # a tad janky?
177
+ logout_parameters.delete(:rel)
178
+ result += "<a href='/users/#{current_user.id}/edit' class='#{css_classes} sinatra-authentication-edit' #{parameters}>edit account</a> "
179
+ result += "<a href='/logout' class='#{css_classes} sinatra-authentication-logout' #{logout_parameters}>logout</a>"
180
+ else
181
+ result += "<a href='/signup' class='#{css_classes} sinatra-authentication-signup' #{parameters}>signup</a> "
182
+ result += "<a href='/login' class='#{css_classes} sinatra-authentication-login' #{parameters}>login</a>"
183
+ end
184
+
185
+ result += "</div>"
186
+ end
187
+ end
188
+
189
+ register LilAuthentication
190
+ end
191
+
192
+ class GuestUser
193
+ def guest?
194
+ true
195
+ end
196
+
197
+ def permission_level
198
+ 0
199
+ end
200
+
201
+ # current_user.admin? returns false. current_user.has_a_baby? returns false.
202
+ # (which is a bit of an assumption I suppose)
203
+ def method_missing(m, *args)
204
+ return false
205
+ end
206
+ 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, :selected => @user.admin?}
16
+ admin
17
+ %option{:value => 1, :selected => @user.permission_level == 1}
18
+ authenticated user
19
+ permission level
20
+ %br
21
+ %input{ :value => "update", :type => "submit" }
@@ -0,0 +1,22 @@
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
+ - if user.id != 1
20
+ %a{:href => "/users/#{user.id}/delete", :onclick => "return confirm('you sure?')"} delete
21
+ - else
22
+ site admin
@@ -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" }
@@ -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
+ require "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.2"
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-22}
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: shingara-sinatra-authentication
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Max Justus Spransy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-22 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
+