sinatra-authentication-dmeiz 0.3.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.
Files changed (47) hide show
  1. data/.gitignore +4 -0
  2. data/History.txt +4 -0
  3. data/Manifest +26 -0
  4. data/Rakefile +38 -0
  5. data/TODO +53 -0
  6. data/example/dm_extend_app.rb +26 -0
  7. data/example/dm_sinbook.rb +56 -0
  8. data/example/extend_views/edit.haml +42 -0
  9. data/example/extend_views/index.haml +31 -0
  10. data/example/extend_views/login.haml +21 -0
  11. data/example/extend_views/show.haml +9 -0
  12. data/example/extend_views/signup.haml +30 -0
  13. data/example/mm_app.rb +22 -0
  14. data/example/tc_app.rb +16 -0
  15. data/example/tc_sinbook.rb +62 -0
  16. data/lib/models/abstract_user.rb +54 -0
  17. data/lib/models/datamapper_user.rb +42 -0
  18. data/lib/models/dm_adapter.rb +50 -0
  19. data/lib/models/mm_adapter.rb +53 -0
  20. data/lib/models/mongomapper_user.rb +42 -0
  21. data/lib/models/rufus_tokyo_user.rb +177 -0
  22. data/lib/models/tc_adapter.rb +83 -0
  23. data/lib/sinatra-authentication.rb +290 -0
  24. data/lib/views/edit.haml +43 -0
  25. data/lib/views/index.haml +29 -0
  26. data/lib/views/login.haml +22 -0
  27. data/lib/views/show.haml +9 -0
  28. data/lib/views/signup.haml +26 -0
  29. data/readme.markdown +238 -0
  30. data/sinatra-authentication.gemspec +119 -0
  31. data/test/datamapper_test.rb +5 -0
  32. data/test/lib/dm_app.rb +20 -0
  33. data/test/lib/dm_extend_app.rb +27 -0
  34. data/test/lib/dm_sinbook.rb +55 -0
  35. data/test/lib/extend_views/edit.haml +42 -0
  36. data/test/lib/extend_views/index.haml +31 -0
  37. data/test/lib/extend_views/login.haml +21 -0
  38. data/test/lib/extend_views/show.haml +9 -0
  39. data/test/lib/extend_views/signup.haml +29 -0
  40. data/test/lib/helper.rb +9 -0
  41. data/test/lib/mm_app.rb +24 -0
  42. data/test/lib/tc_app.rb +16 -0
  43. data/test/lib/tc_sinbook.rb +62 -0
  44. data/test/mongomapper_test.rb +39 -0
  45. data/test/route_tests.rb +29 -0
  46. data/test/rufus_tokyo_test.rb +5 -0
  47. metadata +212 -0
@@ -0,0 +1,54 @@
1
+ if Object.const_defined?("DataMapper")
2
+ #require 'dm-core'
3
+ require 'dm-timestamps'
4
+ require 'dm-validations'
5
+ require Pathname(__FILE__).dirname.expand_path + "datamapper_user.rb"
6
+ require Pathname(__FILE__).dirname.expand_path + "dm_adapter.rb"
7
+ elsif Object.const_defined?("Rufus") && Rufus.const_defined?("Tokyo")
8
+ require Pathname(__FILE__).dirname.expand_path + "rufus_tokyo_user.rb"
9
+ require Pathname(__FILE__).dirname.expand_path + "tc_adapter.rb"
10
+ elsif Object.const_defined?("MongoMapper")
11
+ require Pathname(__FILE__).dirname.expand_path + "mongomapper_user.rb"
12
+ require Pathname(__FILE__).dirname.expand_path + "mm_adapter.rb"
13
+ end
14
+
15
+ class User
16
+ if Object.const_defined?("DataMapper")
17
+ include DmAdapter
18
+ elsif Object.const_defined?("Rufus")
19
+ include TcAdapter
20
+ elsif Object.const_defined?("MongoMapper")
21
+ include MmAdapter
22
+ else
23
+ throw "you need to require either 'dm-core', 'mongo_mapper', or 'rufus-tokyo' for sinatra-authentication to work"
24
+ end
25
+
26
+ def initialize(interfacing_class_instance)
27
+ @instance = interfacing_class_instance
28
+ end
29
+
30
+ def id
31
+ @instance.id
32
+ end
33
+
34
+ def self.authenticate(email, pass)
35
+ current_user = get(:email => email)
36
+ return nil if current_user.nil?
37
+ return current_user if User.encrypt(pass, current_user.salt) == current_user.hashed_password
38
+ nil
39
+ end
40
+
41
+ protected
42
+
43
+ def self.encrypt(pass, salt)
44
+ Digest::SHA1.hexdigest(pass+salt)
45
+ end
46
+
47
+ def self.random_string(len)
48
+ #generate a random password consisting of strings and digits
49
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
50
+ newpass = ""
51
+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
52
+ return newpass
53
+ end
54
+ end
@@ -0,0 +1,42 @@
1
+ class DmUser
2
+ include DataMapper::Resource
3
+
4
+ property :id, Serial
5
+ property :email, String, :length => (5..40), :unique => true, :format => :email_address
6
+ property :hashed_password, String
7
+ property :salt, String
8
+ property :created_at, DateTime
9
+ property :permission_level, Integer, :default => 1
10
+ if Sinatra.const_defined?('FacebookObject')
11
+ property :fb_uid, String
12
+ end
13
+
14
+ 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_confirmation_of :password
22
+
23
+ def password=(pass)
24
+ @password = pass
25
+ self.salt = User.random_string(10) if !self.salt
26
+ self.hashed_password = User.encrypt(@password, self.salt)
27
+ end
28
+
29
+ def admin?
30
+ self.permission_level == -1 || self.id == 1
31
+ end
32
+
33
+ def site_admin?
34
+ self.id == 1
35
+ end
36
+
37
+ protected
38
+
39
+ def method_missing(m, *args)
40
+ return false
41
+ end
42
+ end
@@ -0,0 +1,50 @@
1
+ module DmAdapter
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ base.class_eval { include DmAdapter::InstanceMethods }
5
+ end
6
+
7
+ module ClassMethods
8
+ #pass all args to this
9
+ def all(*args)
10
+ result = DmUser.all(*args)
11
+ result.collect {|instance| self.new instance}
12
+ end
13
+
14
+ def get(hash)
15
+ if user = DmUser.first(hash)
16
+ self.new user
17
+ else
18
+ nil
19
+ end
20
+ end
21
+
22
+ def set(attributes)
23
+ user = DmUser.new attributes
24
+ user.save
25
+ user
26
+ end
27
+
28
+ def set!(attributes)
29
+ user = DmUser.new attributes
30
+ user.save!
31
+ user
32
+ end
33
+
34
+ def delete(pk)
35
+ user = DmUser.first(:id => pk)
36
+ user.destroy
37
+ end
38
+ end
39
+
40
+ module InstanceMethods
41
+ def update(attributes)
42
+ @instance.update attributes
43
+ end
44
+
45
+ def method_missing(meth, *args, &block)
46
+ #cool I just found out * on an array turns the array into a list of args for a function
47
+ @instance.send(meth, *args, &block)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,53 @@
1
+ module MmAdapter
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ base.class_eval { include MmAdapter::InstanceMethods }
5
+ end
6
+
7
+ module ClassMethods
8
+ def all
9
+ result = MmUser.all
10
+ result.collect {|instance| self.new instance}
11
+ end
12
+
13
+ def get(hash)
14
+ if user = MmUser.first(hash)
15
+ self.new user
16
+ else
17
+ nil
18
+ end
19
+ end
20
+
21
+ def set(attributes)
22
+ puts attributes.inspect
23
+ user = MmUser.new attributes
24
+ puts user.inspect
25
+ puts user.to_json
26
+ user.id = nil unless user.save
27
+ user
28
+ end
29
+
30
+ def set!(attributes)
31
+ user = MmUser.new attributes
32
+ user.save!
33
+ user
34
+ end
35
+
36
+ def delete(pk)
37
+ user = User.first(:id => pk)
38
+ user.destroy
39
+ end
40
+ end
41
+
42
+ module InstanceMethods
43
+ def update(attributes)
44
+ @instance.update_attributes attributes
45
+ @instance.save
46
+ end
47
+
48
+ def method_missing(meth, *args, &block)
49
+ #cool I just found out * on an array turns the array into a list of args for a function
50
+ @instance.send(meth, *args, &block)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,42 @@
1
+ class MmUser
2
+ include MongoMapper::Document
3
+
4
+ key :email, String, :length => (5..40), :unique => true
5
+ key :hashed_password, String
6
+ key :salt, String
7
+ key :permission_level, Integer, :default => 1
8
+ if Sinatra.const_defined?('FacebookObject')
9
+ key :fb_uid, String
10
+ end
11
+
12
+ timestamps!
13
+
14
+ 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
22
+
23
+ def password=(pass)
24
+ @password = pass
25
+ self.salt = User.random_string(10) if !self.salt
26
+ self.hashed_password = User.encrypt(@password, self.salt)
27
+ end
28
+
29
+ def admin?
30
+ self.permission_level == -1 || self.id == 1
31
+ end
32
+
33
+ def site_admin?
34
+ self.id == 1
35
+ end
36
+
37
+ protected
38
+
39
+ def method_missing(m, *args)
40
+ return false
41
+ end
42
+ end
@@ -0,0 +1,177 @@
1
+ class TcUser
2
+ #include RufusOrm
3
+
4
+ #custom_attribute :salt
5
+ #custom_attribute :hashed_password
6
+ #custom_attribute :hashed_permission_level
7
+ #custom_attribute :created_at
8
+ #custom_attribute :created_at_i
9
+
10
+ #attribute method?
11
+ #if I'm gonna write all this, I might as well create a tinyyyy
12
+ #orm, that's more just like a way to define custom attributes for cabinets
13
+ #something worth noting though is that even datamapper defines custom
14
+ #attributes by allowing the developer to override setter methods.
15
+ #and it just calls all the setter methods defined in the model.
16
+ #the only trouble with this route is it assumes a predefined schema.
17
+ #and thus it knows what setter methods to call.
18
+ #I would write a class method that allows you to declare attributes like
19
+ #attribute :salt, with an optional block (which gets passed a hash of attributes)
20
+ #if a block isn't defined, it looks in the class for a salt=(attributes) function, calls it and marges the
21
+ #result into the hash going into the database, like 'attributes.merge{"salt" => result}'
22
+ #so my 'set' method or whatever I choose to call it, has to somehow look through each
23
+ #declared attribute, call the method associated with it, and merge the result into the hash going
24
+ #into the database.
25
+ #
26
+ #but what if I don't want an attribute passed in to be stored into the database? What if I just want to
27
+ #create a virtual attribute, for declaring other attributes?
28
+ #I might create a class variable that I store all the attributes in, and the I can get to it from any setter,
29
+ #and then after I've called all the setters, I store that class variable into the database.
30
+ #or, I do all of this on the instance level, and have a save method.
31
+
32
+ def initialize(attributes)
33
+ @attributes = attributes
34
+ end
35
+
36
+ def self.query(&block)
37
+ connection = TcUserTable.new
38
+ result_set = connection.query(&block)
39
+ output = result_set.collect { |result_hash| TcUser.new(result_hash) }
40
+ connection.close
41
+ output
42
+ end
43
+
44
+ def self.get(key)
45
+ connection = TcUserTable.new
46
+ result = connection[key]
47
+ connection.close
48
+ if result
49
+ self.new(result.merge({:pk => key}))
50
+ else
51
+ false
52
+ end
53
+ end
54
+
55
+ def self.set(attributes)
56
+ #this way of validating is real crap, replace it with Validator maybe
57
+ #and maybe replace all this hash merging with setters for the various attributes that update @attributes, and then I can call save to store to the database
58
+ #or maybe just write a little method that makes hash merger look a little cleaner
59
+ pk = attributes.delete(:pk) if attributes[:pk]
60
+
61
+ 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
73
+ end
74
+
75
+ existing_user = TcUser.query do |q|
76
+ q.add 'email', :streq, attributes['email']
77
+ end[0]
78
+
79
+ if existing_user && existing_user['pk'] != attributes['pk']
80
+ return false
81
+ else
82
+ connection = TcUserTable.new
83
+ pk ||= connection.genuid.to_s
84
+ #site admin if their first
85
+ attributes.merge!({'permission_level' => '-2'}) if pk == '1'
86
+ result = connection[pk] = attributes
87
+ #might not need this in newer version of rufus
88
+ result.merge!({:pk => pk})
89
+ connection.close
90
+ self.new(result)
91
+ end
92
+ else
93
+ false
94
+ end
95
+ end
96
+
97
+ def self.set!(attributes)
98
+ connection = TcUserTable.new
99
+ pk = connection.genuid.to_s
100
+ result = connection[pk] = attributes
101
+ result.merge!({:pk => pk})
102
+ connection.close
103
+ self.new(result)
104
+ end
105
+
106
+ def self.delete(pk)
107
+ connection = TcUserTable.new
108
+ connection.delete(pk)
109
+ connection.close
110
+ end
111
+
112
+ def update(attributes)
113
+ new_attributes = @attributes.merge(attributes)
114
+ TcUser.set(new_attributes)
115
+ end
116
+
117
+ def [](key)
118
+ @attributes[key]
119
+ end
120
+
121
+ #saves to database and returns self
122
+ def []=(key, value)
123
+ @attributes[key] = value
124
+ #change so that it sets the attributes and then you call save to save to the database?
125
+ connection = TcUserTable.new
126
+ connection[@attributes[:pk]] = @attributes.merge!({key => value})
127
+ connection.close
128
+ self
129
+ end
130
+
131
+ def id
132
+ @attributes[:pk]
133
+ end
134
+
135
+ def admin?
136
+ #-2 is the site admin
137
+ @attributes['permission_level'] == '-1' || @attributes['permission_level'] == '-2'
138
+ end
139
+
140
+ def site_admin?
141
+ @attributes['permission_level'] == '-2'
142
+ end
143
+
144
+ #from hash extension for making hashes like javascript objects
145
+ def method_missing(meth,*args)
146
+ if /=$/=~(meth=meth.id2name) then
147
+ self[meth[0...-1]] = (args.length<2 ? args[0] : args)
148
+ elsif @attributes[meth]
149
+ @attributes[meth]
150
+ else
151
+ false
152
+ end
153
+ end
154
+ end
155
+
156
+ if Rufus::Tokyo.const_defined?('Table')
157
+ class TokyoTableDad < Rufus::Tokyo::Table
158
+ end
159
+ elsif Rufus::Edo.const_defined?('Table')
160
+ class TokyoTableDad < Rufus::Edo::Table
161
+ end
162
+ else
163
+ throw 'wtf?'
164
+ end
165
+
166
+ class TcUserTable < TokyoTableDad
167
+ @@path = false
168
+ def initialize
169
+ #make this path configurable somehow
170
+ raise "you need to define a path for the user cabinet to be stored at, like so: TcUserTable.cabinet_path = 'folder/where/you/wanna/store/your/database'" unless @@path
171
+ super(@@path + '/users.tct')
172
+ end
173
+
174
+ def self.cabinet_path=(path)
175
+ @@path = path
176
+ end
177
+ end
@@ -0,0 +1,83 @@
1
+ module TcAdapter
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ base.class_eval {
5
+ include TcAdapter::InstanceMethods
6
+ alias :class_id :id
7
+ }
8
+ end
9
+
10
+ module ClassMethods
11
+ #TODO add pagination
12
+ def all
13
+ result = TcUser.query do |q|
14
+ q.order_by 'created_at_i', :numdesc
15
+ end
16
+
17
+ #these will be the same for all adapters, they should be defined in the user class, and these methods should have a different name?
18
+ result.collect {|instance| self.new instance }
19
+ end
20
+
21
+ def get(hash)
22
+ if hash[:id]
23
+ pk = hash[:id]
24
+ result = TcUser.get(pk)
25
+ else
26
+ result = TcUser.query do |q|
27
+ hash.each do |key, value|
28
+ q.add key.to_s, :streq, value.to_s
29
+ end
30
+ end[0]
31
+ end
32
+ #elsif hash[:email]
33
+ # result = TcUser.query do |q|
34
+ # q.add 'email', :streq, hash[:email]
35
+ # end[0]
36
+ #the zero is because this returns an array but get should return the first result
37
+ #end
38
+
39
+ if result
40
+ self.new result
41
+ else
42
+ nil
43
+ end
44
+ end
45
+
46
+ def set(attributes)
47
+ user = TcUser.query do |q|
48
+ q.add 'email', :streq, attributes['email']
49
+ end
50
+
51
+ if user == [] #no user
52
+ self.new TcUser.set(attributes)
53
+ else
54
+ false
55
+ end
56
+ end
57
+
58
+ def set!(attributes)
59
+ self.new TcUser.set!(attributes)
60
+ end
61
+
62
+ def delete(pk)
63
+ #true or false
64
+ !!TcUser.delete(pk)
65
+ end
66
+ end
67
+
68
+ module InstanceMethods
69
+ def update(attributes)
70
+ @instance.update attributes
71
+ end
72
+
73
+ def method_missing(meth, *args, &block)
74
+ #cool I just found out * on an array turn the array into a list of args for a function
75
+ @instance.send(meth, *args, &block)
76
+ end
77
+
78
+ #this was the only thing that didn't get passed on to method_missing because this is a method of object doh
79
+ def id
80
+ @instance.id
81
+ end
82
+ end
83
+ end