sinatra-authentication 0.3.2 → 0.4.0

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.
Files changed (41) hide show
  1. data/Rakefile +6 -1
  2. data/lib/models/abstract_user.rb +39 -1
  3. data/lib/models/datamapper_user.rb +2 -1
  4. data/lib/models/dm_adapter.rb +15 -4
  5. data/lib/models/mm_adapter.rb +21 -9
  6. data/lib/models/mongoid_adapter.rb +67 -0
  7. data/lib/models/mongoid_user.rb +41 -0
  8. data/lib/models/mongomapper_user.rb +4 -8
  9. data/lib/models/rufus_tokyo_user.rb +48 -19
  10. data/lib/models/sequel_adapter.rb +68 -0
  11. data/lib/models/sequel_user.rb +53 -0
  12. data/lib/models/tc_adapter.rb +20 -2
  13. data/lib/sinatra-authentication.rb +35 -23
  14. data/lib/sinatra-authentication/models.rb +5 -0
  15. data/lib/views/edit.haml +1 -1
  16. data/lib/views/login.haml +3 -3
  17. data/lib/views/signup.haml +4 -4
  18. data/readme.markdown +53 -3
  19. data/sinatra-authentication-0.3.2.gem +0 -0
  20. data/sinatra-authentication.gemspec +86 -60
  21. data/spec/run_all_specs.rb +8 -0
  22. data/spec/unit/dm_model_spec.rb +3 -0
  23. data/spec/unit/mm_model_spec.rb +3 -0
  24. data/spec/unit/mongoid_model_spec.rb +3 -0
  25. data/spec/unit/sequel_model_spec.rb +10 -0
  26. data/spec/unit/tc_model_spec.rb +3 -0
  27. data/spec/unit/user_specs.rb +119 -0
  28. data/test/lib/dm_app.rb +1 -0
  29. data/test/lib/dm_extend_app.rb +1 -0
  30. data/test/lib/extend_views/edit.haml +4 -0
  31. data/test/lib/extend_views/signup.haml +4 -4
  32. data/test/lib/helper.rb +4 -0
  33. data/test/lib/mm_app.rb +16 -15
  34. data/test/lib/mongoid_app.rb +29 -0
  35. data/test/lib/sequel_app.rb +22 -0
  36. data/test/lib/tc_app.rb +2 -0
  37. data/test/mongoid_test.rb +5 -0
  38. data/test/mongomapper_test.rb +36 -35
  39. data/test/sequel_test.rb +5 -0
  40. metadata +40 -24
  41. 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.3.2'
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
@@ -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
- property :created_at, DateTime
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
@@ -6,8 +6,8 @@ module DmAdapter
6
6
 
7
7
  module ClassMethods
8
8
  #pass all args to this
9
- def all(*args)
10
- result = DmUser.all(*args)
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)
@@ -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
- puts user.inspect
25
- puts user.to_json
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 = User.first(:id => pk)
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
- key :email, String, :length => (5..40), :unique => true
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
- #protected equievelant? :protected => true doesn't exist in dm 0.10.0
16
- #protected :id, :salt
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
- if attributes['email'] =~ email_regexp
63
- if attributes['password'] == attributes.delete('password_confirmation') && attributes['password'] != nil
64
- password = attributes.delete('password')
65
- attributes.merge!({'salt' => User.random_string(10)}) if !attributes['salt']
66
- attributes.merge!('hashed_password' => User.encrypt(password, attributes['salt']))
67
- permission_level = attributes['permission_level'] ? attributes['permission_level'] : '1'
68
- attributes.merge!('permission_level' => permission_level)
69
- unless attributes['created_at']
70
- attributes.merge!('created_at' => Time.now.to_s)
71
- attributes.merge!('created_at_i' => Time.now.to_i.to_s)
72
- end
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['pk'] != attributes['pk']
80
- return false
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
- false
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
- #-2 is the site admin
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