shingara-sinatra-authentication 0.0.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.
@@ -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
+