sinatra-authentication-dmeiz 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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