sinatra-authentication 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +6 -1
- data/lib/models/abstract_user.rb +39 -1
- data/lib/models/datamapper_user.rb +2 -1
- data/lib/models/dm_adapter.rb +15 -4
- data/lib/models/mm_adapter.rb +21 -9
- data/lib/models/mongoid_adapter.rb +67 -0
- data/lib/models/mongoid_user.rb +41 -0
- data/lib/models/mongomapper_user.rb +4 -8
- data/lib/models/rufus_tokyo_user.rb +48 -19
- data/lib/models/sequel_adapter.rb +68 -0
- data/lib/models/sequel_user.rb +53 -0
- data/lib/models/tc_adapter.rb +20 -2
- data/lib/sinatra-authentication.rb +35 -23
- data/lib/sinatra-authentication/models.rb +5 -0
- data/lib/views/edit.haml +1 -1
- data/lib/views/login.haml +3 -3
- data/lib/views/signup.haml +4 -4
- data/readme.markdown +53 -3
- data/sinatra-authentication-0.3.2.gem +0 -0
- data/sinatra-authentication.gemspec +86 -60
- data/spec/run_all_specs.rb +8 -0
- data/spec/unit/dm_model_spec.rb +3 -0
- data/spec/unit/mm_model_spec.rb +3 -0
- data/spec/unit/mongoid_model_spec.rb +3 -0
- data/spec/unit/sequel_model_spec.rb +10 -0
- data/spec/unit/tc_model_spec.rb +3 -0
- data/spec/unit/user_specs.rb +119 -0
- data/test/lib/dm_app.rb +1 -0
- data/test/lib/dm_extend_app.rb +1 -0
- data/test/lib/extend_views/edit.haml +4 -0
- data/test/lib/extend_views/signup.haml +4 -4
- data/test/lib/helper.rb +4 -0
- data/test/lib/mm_app.rb +16 -15
- data/test/lib/mongoid_app.rb +29 -0
- data/test/lib/sequel_app.rb +22 -0
- data/test/lib/tc_app.rb +2 -0
- data/test/mongoid_test.rb +5 -0
- data/test/mongomapper_test.rb +36 -35
- data/test/sequel_test.rb +5 -0
- metadata +40 -24
- data/.gitignore +0 -4
data/Rakefile
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
+
#require 'spec/rake/spectask'
|
3
4
|
|
4
5
|
begin
|
5
6
|
require 'jeweler'
|
6
7
|
|
7
8
|
Jeweler::Tasks.new do |gemspec|
|
8
9
|
gemspec.name = 'sinatra-authentication'
|
9
|
-
gemspec.version = '0.
|
10
|
+
gemspec.version = '0.4.0'
|
10
11
|
gemspec.description = "Simple authentication plugin for sinatra."
|
11
12
|
gemspec.summary = "Simple authentication plugin for sinatra."
|
12
13
|
gemspec.homepage = "http://github.com/maxjustus/sinatra-authentication"
|
@@ -36,3 +37,7 @@ Rake::TestTask.new do |t|
|
|
36
37
|
t.verbose = true
|
37
38
|
end
|
38
39
|
|
40
|
+
#desc 'Run all specs'
|
41
|
+
#Spec::Rake::SpecTask.new('specs') do |t|
|
42
|
+
# t.spec_files = FileList['spec/**/*.rb']
|
43
|
+
#end
|
data/lib/models/abstract_user.rb
CHANGED
@@ -10,6 +10,12 @@ elsif Object.const_defined?("Rufus") && Rufus.const_defined?("Tokyo")
|
|
10
10
|
elsif Object.const_defined?("MongoMapper")
|
11
11
|
require Pathname(__FILE__).dirname.expand_path + "mongomapper_user.rb"
|
12
12
|
require Pathname(__FILE__).dirname.expand_path + "mm_adapter.rb"
|
13
|
+
elsif Object.const_defined?("Sequel")
|
14
|
+
require Pathname(__FILE__).dirname.expand_path + "sequel_user.rb"
|
15
|
+
require Pathname(__FILE__).dirname.expand_path + "sequel_adapter.rb"
|
16
|
+
elsif Object.const_defined?("Mongoid")
|
17
|
+
require Pathname(__FILE__).dirname.expand_path + "mongoid_user.rb"
|
18
|
+
require Pathname(__FILE__).dirname.expand_path + "mongoid_adapter.rb"
|
13
19
|
end
|
14
20
|
|
15
21
|
class User
|
@@ -19,8 +25,12 @@ class User
|
|
19
25
|
include TcAdapter
|
20
26
|
elsif Object.const_defined?("MongoMapper")
|
21
27
|
include MmAdapter
|
28
|
+
elsif Object.const_defined?("Sequel")
|
29
|
+
include SequelAdapter
|
30
|
+
elsif Object.const_defined?("Mongoid")
|
31
|
+
include MongoidAdapter
|
22
32
|
else
|
23
|
-
throw "you need to require either 'dm-core', 'mongo_mapper', or 'rufus-tokyo' for sinatra-authentication to work"
|
33
|
+
throw "you need to require either 'dm-core', 'mongo_mapper', 'sequel', 'mongoid', or 'rufus-tokyo' for sinatra-authentication to work"
|
24
34
|
end
|
25
35
|
|
26
36
|
def initialize(interfacing_class_instance)
|
@@ -38,6 +48,10 @@ class User
|
|
38
48
|
nil
|
39
49
|
end
|
40
50
|
|
51
|
+
def db_instance
|
52
|
+
@instance
|
53
|
+
end
|
54
|
+
|
41
55
|
protected
|
42
56
|
|
43
57
|
def self.encrypt(pass, salt)
|
@@ -51,4 +65,28 @@ class User
|
|
51
65
|
1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
|
52
66
|
return newpass
|
53
67
|
end
|
68
|
+
|
69
|
+
#def self.page_limit
|
70
|
+
# 20
|
71
|
+
#end
|
72
|
+
|
73
|
+
#def self.page_offset(page = 0)
|
74
|
+
# page.to_i * self.page_limit
|
75
|
+
#end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Hash
|
79
|
+
def stringify
|
80
|
+
inject({}) do |options, (key, value)|
|
81
|
+
options[key.to_s] = value.to_s
|
82
|
+
options
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def stringify!
|
87
|
+
each do |key, value|
|
88
|
+
delete(key)
|
89
|
+
store(key.to_s, value.to_s)
|
90
|
+
end
|
91
|
+
end
|
54
92
|
end
|
@@ -5,7 +5,8 @@ class DmUser
|
|
5
5
|
property :email, String, :length => (5..40), :unique => true, :format => :email_address
|
6
6
|
property :hashed_password, String
|
7
7
|
property :salt, String
|
8
|
-
|
8
|
+
#Was DateTime should be DateTime?
|
9
|
+
property :created_at, Time
|
9
10
|
property :permission_level, Integer, :default => 1
|
10
11
|
if Sinatra.const_defined?('FacebookObject')
|
11
12
|
property :fb_uid, String
|
data/lib/models/dm_adapter.rb
CHANGED
@@ -6,8 +6,8 @@ module DmAdapter
|
|
6
6
|
|
7
7
|
module ClassMethods
|
8
8
|
#pass all args to this
|
9
|
-
def all
|
10
|
-
result = DmUser.all(
|
9
|
+
def all
|
10
|
+
result = DmUser.all(:order => [:created_at.desc])
|
11
11
|
result.collect {|instance| self.new instance}
|
12
12
|
end
|
13
13
|
|
@@ -22,13 +22,13 @@ module DmAdapter
|
|
22
22
|
def set(attributes)
|
23
23
|
user = DmUser.new attributes
|
24
24
|
user.save
|
25
|
-
user
|
25
|
+
self.new user
|
26
26
|
end
|
27
27
|
|
28
28
|
def set!(attributes)
|
29
29
|
user = DmUser.new attributes
|
30
30
|
user.save!
|
31
|
-
user
|
31
|
+
self.new user
|
32
32
|
end
|
33
33
|
|
34
34
|
def delete(pk)
|
@@ -38,8 +38,19 @@ module DmAdapter
|
|
38
38
|
end
|
39
39
|
|
40
40
|
module InstanceMethods
|
41
|
+
def valid
|
42
|
+
@instance.valid?
|
43
|
+
end
|
44
|
+
|
45
|
+
def errors
|
46
|
+
@instance.errors.collect do |k,v|
|
47
|
+
"#{k} #{v}"
|
48
|
+
end.join(', ')
|
49
|
+
end
|
50
|
+
|
41
51
|
def update(attributes)
|
42
52
|
@instance.update attributes
|
53
|
+
#self
|
43
54
|
end
|
44
55
|
|
45
56
|
def method_missing(meth, *args, &block)
|
data/lib/models/mm_adapter.rb
CHANGED
@@ -6,7 +6,7 @@ module MmAdapter
|
|
6
6
|
|
7
7
|
module ClassMethods
|
8
8
|
def all
|
9
|
-
result = MmUser.all
|
9
|
+
result = MmUser.all(:order => 'created_at desc')
|
10
10
|
result.collect {|instance| self.new instance}
|
11
11
|
end
|
12
12
|
|
@@ -19,32 +19,44 @@ module MmAdapter
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def set(attributes)
|
22
|
-
puts attributes.inspect
|
22
|
+
#puts attributes.inspect
|
23
23
|
user = MmUser.new attributes
|
24
|
-
|
25
|
-
|
26
|
-
user.save
|
27
|
-
user
|
24
|
+
user.id = nil unless user.save
|
25
|
+
self.new user
|
28
26
|
end
|
29
27
|
|
30
28
|
def set!(attributes)
|
31
29
|
user = MmUser.new attributes
|
32
|
-
user.save
|
33
|
-
user
|
30
|
+
user.save(:validate => false)
|
31
|
+
self.new user
|
34
32
|
end
|
35
33
|
|
36
34
|
def delete(pk)
|
37
|
-
user =
|
35
|
+
user = MmUser.first(:id => pk)
|
36
|
+
#returns nil on success. Is this correct? Will it return something else on failure?
|
38
37
|
user.destroy
|
38
|
+
user.destroyed?
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
module InstanceMethods
|
43
|
+
def valid
|
44
|
+
@instance.valid?
|
45
|
+
end
|
46
|
+
|
43
47
|
def update(attributes)
|
44
48
|
@instance.update_attributes attributes
|
45
49
|
@instance.save
|
46
50
|
end
|
47
51
|
|
52
|
+
def saved
|
53
|
+
@instance.valid?
|
54
|
+
end
|
55
|
+
|
56
|
+
def errors
|
57
|
+
@instance.errors.full_messages.join(', ')
|
58
|
+
end
|
59
|
+
|
48
60
|
def method_missing(meth, *args, &block)
|
49
61
|
#cool I just found out * on an array turns the array into a list of args for a function
|
50
62
|
@instance.send(meth, *args, &block)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module MongoidAdapter
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
base.class_eval { include MongoidAdapter::InstanceMethods }
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def all
|
9
|
+
result = MongoidUser.criteria.order_by([[:created_at, :desc]])
|
10
|
+
result.collect {|instance| self.new instance }
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(hash)
|
14
|
+
if user = MongoidUser.first(:conditions => hash)
|
15
|
+
self.new user
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(attributes)
|
22
|
+
#puts attributes.inspect
|
23
|
+
user = MongoidUser.new attributes
|
24
|
+
#puts user.inspect
|
25
|
+
#puts user.to_json
|
26
|
+
user.save
|
27
|
+
self.new user
|
28
|
+
end
|
29
|
+
|
30
|
+
def set!(attributes)
|
31
|
+
user = MongoidUser.new attributes
|
32
|
+
user.save(:validate => false)
|
33
|
+
self.new user
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete(pk)
|
37
|
+
user = MongoidUser.find(pk)
|
38
|
+
#returns nil on success. Is this correct? Will it return something else on failure?
|
39
|
+
user.destroy
|
40
|
+
user.destroyed?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module InstanceMethods
|
45
|
+
def valid
|
46
|
+
@instance.valid?
|
47
|
+
end
|
48
|
+
|
49
|
+
def update(attributes)
|
50
|
+
@instance.update_attributes(attributes)
|
51
|
+
@instance.save
|
52
|
+
end
|
53
|
+
|
54
|
+
def saved
|
55
|
+
@instance.valid?
|
56
|
+
end
|
57
|
+
|
58
|
+
def errors
|
59
|
+
@instance.errors.full_messages.join(', ')
|
60
|
+
end
|
61
|
+
|
62
|
+
def method_missing(meth, *args, &block)
|
63
|
+
#cool I just found out * on an array turns the array into a list of args for a function
|
64
|
+
@instance.send(meth, *args, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class MongoidUser
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Timestamps
|
4
|
+
field :email
|
5
|
+
field :hashed_password
|
6
|
+
field :salt
|
7
|
+
field :permission_level, :type => Integer, :default => 1
|
8
|
+
if Sinatra.const_defined?('FacebookObject')
|
9
|
+
field :fb_uid
|
10
|
+
end
|
11
|
+
|
12
|
+
# Validations
|
13
|
+
validates_uniqueness_of :email
|
14
|
+
validates_format_of :email, :with => /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
|
15
|
+
validates_presence_of :password
|
16
|
+
validates_confirmation_of :password
|
17
|
+
|
18
|
+
#attr_protected :_id, :salt
|
19
|
+
|
20
|
+
attr_accessor :password, :password_confirmation
|
21
|
+
|
22
|
+
def password=(pass)
|
23
|
+
@password = pass
|
24
|
+
self.salt = User.random_string(10) if !self.salt
|
25
|
+
self.hashed_password = User.encrypt(@password, self.salt)
|
26
|
+
end
|
27
|
+
|
28
|
+
def admin?
|
29
|
+
self.permission_level == -1 || self.id == 1
|
30
|
+
end
|
31
|
+
|
32
|
+
def site_admin?
|
33
|
+
self.id == 1
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def method_missing(m, *args)
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
class MmUser
|
2
2
|
include MongoMapper::Document
|
3
3
|
|
4
|
-
|
4
|
+
email_regexp = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
|
5
|
+
key :email, String, :unique => true, :format => email_regexp
|
5
6
|
key :hashed_password, String
|
6
7
|
key :salt, String
|
7
8
|
key :permission_level, Integer, :default => 1
|
@@ -12,13 +13,8 @@ class MmUser
|
|
12
13
|
timestamps!
|
13
14
|
|
14
15
|
attr_accessor :password, :password_confirmation
|
15
|
-
|
16
|
-
|
17
|
-
#doesn't behave correctly, I'm not even sure why I did this.
|
18
|
-
|
19
|
-
#validates_presence_of :password_confirmation, :unless => Proc.new { |t| t.hashed_password }
|
20
|
-
#validates_presence_of :password, :unless => Proc.new { |t| t.hashed_password }
|
21
|
-
#validates_is_confirmed :password
|
16
|
+
validates_presence_of :password, :allow_blank => true
|
17
|
+
validates_confirmation_of :password
|
22
18
|
|
23
19
|
def password=(pass)
|
24
20
|
@password = pass
|
@@ -1,4 +1,5 @@
|
|
1
1
|
class TcUser
|
2
|
+
attr_accessor :errors
|
2
3
|
#include RufusOrm
|
3
4
|
|
4
5
|
#custom_attribute :salt
|
@@ -29,8 +30,13 @@ class TcUser
|
|
29
30
|
#and then after I've called all the setters, I store that class variable into the database.
|
30
31
|
#or, I do all of this on the instance level, and have a save method.
|
31
32
|
|
32
|
-
def initialize(attributes)
|
33
|
+
def initialize(attributes, errors = [])
|
33
34
|
@attributes = attributes
|
35
|
+
if @attributes['created_at']
|
36
|
+
@attributes['created_at'] = Time.parse(@attributes['created_at'])
|
37
|
+
end
|
38
|
+
|
39
|
+
@errors = errors
|
34
40
|
end
|
35
41
|
|
36
42
|
def self.query(&block)
|
@@ -58,26 +64,46 @@ class TcUser
|
|
58
64
|
#or maybe just write a little method that makes hash merger look a little cleaner
|
59
65
|
pk = attributes.delete(:pk) if attributes[:pk]
|
60
66
|
|
67
|
+
errors = []
|
68
|
+
|
61
69
|
email_regexp = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
attributes = attributes.stringify
|
71
|
+
|
72
|
+
unless attributes['email'] =~ email_regexp
|
73
|
+
errors << 'Email is invalid'
|
74
|
+
end
|
75
|
+
|
76
|
+
if attributes['password'] != attributes.delete('password_confirmation') && attributes['password'] != nil
|
77
|
+
errors << "Passwords don't match"
|
78
|
+
end
|
79
|
+
|
80
|
+
if attributes['password'] != nil && attributes['password'].length == 0
|
81
|
+
errors << "You need to provide a password"
|
82
|
+
end
|
83
|
+
|
84
|
+
if errors.length == 0
|
85
|
+
password = attributes.delete('password')
|
86
|
+
if !attributes['salt']
|
87
|
+
salt = User.random_string(10)
|
88
|
+
attributes.merge!({'salt' => salt})
|
89
|
+
attributes.merge!('hashed_password' => User.encrypt(password, salt))
|
90
|
+
end
|
91
|
+
permission_level = attributes['permission_level'] ? attributes['permission_level'] : '1'
|
92
|
+
attributes.merge!('permission_level' => permission_level)
|
93
|
+
if attributes['created_at']
|
94
|
+
attributes.merge!('created_at_i' => Time.parse(attributes['created_at']).to_i)
|
95
|
+
else
|
96
|
+
attributes.merge!('created_at' => Time.now.to_s)
|
97
|
+
attributes.merge!('created_at_i' => Time.now.to_i.to_s)
|
73
98
|
end
|
74
99
|
|
75
100
|
existing_user = TcUser.query do |q|
|
76
101
|
q.add 'email', :streq, attributes['email']
|
77
102
|
end[0]
|
78
103
|
|
79
|
-
if existing_user && existing_user[
|
80
|
-
|
104
|
+
if existing_user && existing_user[:pk] != pk
|
105
|
+
errors << "Email is already taken"
|
106
|
+
return self.new(attributes, errors)
|
81
107
|
else
|
82
108
|
connection = TcUserTable.new
|
83
109
|
pk ||= connection.genuid.to_s
|
@@ -87,10 +113,10 @@ class TcUser
|
|
87
113
|
#might not need this in newer version of rufus
|
88
114
|
result.merge!({:pk => pk})
|
89
115
|
connection.close
|
90
|
-
self.new(result)
|
116
|
+
self.new(result, errors)
|
91
117
|
end
|
92
118
|
else
|
93
|
-
|
119
|
+
self.new(attributes, errors)
|
94
120
|
end
|
95
121
|
end
|
96
122
|
|
@@ -110,8 +136,11 @@ class TcUser
|
|
110
136
|
end
|
111
137
|
|
112
138
|
def update(attributes)
|
139
|
+
self.errors = []
|
113
140
|
new_attributes = @attributes.merge(attributes)
|
114
|
-
TcUser.set(new_attributes)
|
141
|
+
updated_user = TcUser.set(new_attributes)
|
142
|
+
self.errors = updated_user.errors
|
143
|
+
updated_user.errors.length < 1
|
115
144
|
end
|
116
145
|
|
117
146
|
def [](key)
|
@@ -133,11 +162,11 @@ class TcUser
|
|
133
162
|
end
|
134
163
|
|
135
164
|
def admin?
|
136
|
-
|
137
|
-
@attributes['permission_level'] == '-1' || @attributes['permission_level'] == '-2'
|
165
|
+
@attributes['permission_level'] == '-1' || site_admin?
|
138
166
|
end
|
139
167
|
|
140
168
|
def site_admin?
|
169
|
+
#-2 is the site admin
|
141
170
|
@attributes['permission_level'] == '-2'
|
142
171
|
end
|
143
172
|
|