sinatra-authentication-oran 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +33 -0
- data/History.txt +4 -0
- data/Manifest +26 -0
- data/Rakefile +43 -0
- data/TODO +53 -0
- data/UNLICENSE +24 -0
- data/example/database.yml +3 -0
- data/example/dm_extend_app.rb +26 -0
- data/example/dm_sinbook.rb +56 -0
- data/example/extend_views/edit.haml +42 -0
- data/example/extend_views/index.haml +31 -0
- data/example/extend_views/login.haml +21 -0
- data/example/extend_views/show.haml +9 -0
- data/example/extend_views/signup.haml +30 -0
- data/example/mm_app.rb +22 -0
- data/example/tc_app.rb +16 -0
- data/example/tc_sinbook.rb +62 -0
- data/lib/models/abstract_user.rb +95 -0
- data/lib/models/activerecord_user.rb +56 -0
- data/lib/models/ar_adapter.rb +66 -0
- data/lib/models/datamapper_user.rb +43 -0
- data/lib/models/dm_adapter.rb +61 -0
- data/lib/models/mm_adapter.rb +65 -0
- data/lib/models/mongoid_adapter.rb +67 -0
- data/lib/models/mongoid_user.rb +42 -0
- data/lib/models/mongomapper_user.rb +38 -0
- data/lib/models/rufus_tokyo_user.rb +206 -0
- data/lib/models/sequel_adapter.rb +68 -0
- data/lib/models/sequel_user.rb +53 -0
- data/lib/models/tc_adapter.rb +101 -0
- data/lib/sinatra-authentication-o.rb +305 -0
- data/lib/sinatra-authentication.rb +305 -0
- data/lib/sinatra-authentication/models.rb +5 -0
- data/lib/views/edit.haml +45 -0
- data/lib/views/edit.slim +46 -0
- data/lib/views/index.haml +29 -0
- data/lib/views/index.slim +30 -0
- data/lib/views/login.haml +24 -0
- data/lib/views/login.slim +25 -0
- data/lib/views/show.haml +9 -0
- data/lib/views/show.slim +10 -0
- data/lib/views/signup.haml +28 -0
- data/lib/views/signup.slim +33 -0
- data/readme.markdown +295 -0
- data/sinatra-authentication-oran.gemspec +137 -0
- data/spec/run_all_specs.rb +8 -0
- data/spec/unit/ar_model_spec.rb +3 -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/activerecord_test.rb +5 -0
- data/test/datamapper_test.rb +5 -0
- data/test/lib/ar_app.rb +27 -0
- data/test/lib/dm_app.rb +20 -0
- data/test/lib/dm_extend_app.rb +27 -0
- data/test/lib/dm_sinbook.rb +54 -0
- data/test/lib/extend_views/edit.haml +46 -0
- data/test/lib/extend_views/index.haml +31 -0
- data/test/lib/extend_views/login.haml +21 -0
- data/test/lib/extend_views/show.haml +9 -0
- data/test/lib/extend_views/signup.haml +29 -0
- data/test/lib/helper.rb +16 -0
- data/test/lib/mm_app.rb +24 -0
- data/test/lib/mongoid_app.rb +28 -0
- data/test/lib/sequel_app.rb +21 -0
- data/test/lib/tc_app.rb +17 -0
- data/test/lib/tc_sinbook.rb +61 -0
- data/test/mongoid_test.rb +5 -0
- data/test/mongomapper_test.rb +40 -0
- data/test/route_tests.rb +29 -0
- data/test/rufus_tokyo_test.rb +5 -0
- data/test/sequel_test.rb +5 -0
- metadata +220 -0
@@ -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.where(hash).first
|
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,42 @@
|
|
1
|
+
class MongoidUser
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Timestamps
|
4
|
+
field :email
|
5
|
+
field :name
|
6
|
+
field :hashed_password
|
7
|
+
field :salt
|
8
|
+
field :permission_level, :type => Integer, :default => 1
|
9
|
+
if Sinatra.const_defined?('FacebookObject')
|
10
|
+
field :fb_uid
|
11
|
+
end
|
12
|
+
|
13
|
+
# Validations
|
14
|
+
validates_uniqueness_of :email ,:name
|
15
|
+
validates_format_of :email, :with => /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
|
16
|
+
validates_presence_of :name ,:email
|
17
|
+
validates_confirmation_of :password
|
18
|
+
|
19
|
+
#attr_protected :_id, :salt
|
20
|
+
|
21
|
+
attr_accessor :password, :password_confirmation
|
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,38 @@
|
|
1
|
+
class MmUser
|
2
|
+
include MongoMapper::Document
|
3
|
+
|
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
|
6
|
+
key :hashed_password, String
|
7
|
+
key :salt, String
|
8
|
+
key :permission_level, Integer, :default => 1
|
9
|
+
if Sinatra.const_defined?('FacebookObject')
|
10
|
+
key :fb_uid, String
|
11
|
+
end
|
12
|
+
|
13
|
+
timestamps!
|
14
|
+
|
15
|
+
attr_accessor :password, :password_confirmation
|
16
|
+
validates_presence_of :password, :allow_blank => true
|
17
|
+
validates_confirmation_of :password
|
18
|
+
|
19
|
+
def password=(pass)
|
20
|
+
@password = pass
|
21
|
+
self.salt = User.random_string(10) if !self.salt
|
22
|
+
self.hashed_password = User.encrypt(@password, self.salt)
|
23
|
+
end
|
24
|
+
|
25
|
+
def admin?
|
26
|
+
self.permission_level == -1 || self.id == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def site_admin?
|
30
|
+
self.id == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def method_missing(m, *args)
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
class TcUser
|
2
|
+
attr_accessor :errors
|
3
|
+
#include RufusOrm
|
4
|
+
|
5
|
+
#custom_attribute :salt
|
6
|
+
#custom_attribute :hashed_password
|
7
|
+
#custom_attribute :hashed_permission_level
|
8
|
+
#custom_attribute :created_at
|
9
|
+
#custom_attribute :created_at_i
|
10
|
+
|
11
|
+
#attribute method?
|
12
|
+
#if I'm gonna write all this, I might as well create a tinyyyy
|
13
|
+
#orm, that's more just like a way to define custom attributes for cabinets
|
14
|
+
#something worth noting though is that even datamapper defines custom
|
15
|
+
#attributes by allowing the developer to override setter methods.
|
16
|
+
#and it just calls all the setter methods defined in the model.
|
17
|
+
#the only trouble with this route is it assumes a predefined schema.
|
18
|
+
#and thus it knows what setter methods to call.
|
19
|
+
#I would write a class method that allows you to declare attributes like
|
20
|
+
#attribute :salt, with an optional block (which gets passed a hash of attributes)
|
21
|
+
#if a block isn't defined, it looks in the class for a salt=(attributes) function, calls it and marges the
|
22
|
+
#result into the hash going into the database, like 'attributes.merge{"salt" => result}'
|
23
|
+
#so my 'set' method or whatever I choose to call it, has to somehow look through each
|
24
|
+
#declared attribute, call the method associated with it, and merge the result into the hash going
|
25
|
+
#into the database.
|
26
|
+
#
|
27
|
+
#but what if I don't want an attribute passed in to be stored into the database? What if I just want to
|
28
|
+
#create a virtual attribute, for declaring other attributes?
|
29
|
+
#I might create a class variable that I store all the attributes in, and the I can get to it from any setter,
|
30
|
+
#and then after I've called all the setters, I store that class variable into the database.
|
31
|
+
#or, I do all of this on the instance level, and have a save method.
|
32
|
+
|
33
|
+
def initialize(attributes, errors = [])
|
34
|
+
@attributes = attributes
|
35
|
+
if @attributes['created_at']
|
36
|
+
@attributes['created_at'] = Time.parse(@attributes['created_at'])
|
37
|
+
end
|
38
|
+
|
39
|
+
@errors = errors
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.query(&block)
|
43
|
+
connection = TcUserTable.new
|
44
|
+
result_set = connection.query(&block)
|
45
|
+
output = result_set.collect { |result_hash| TcUser.new(result_hash) }
|
46
|
+
connection.close
|
47
|
+
output
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.get(key)
|
51
|
+
connection = TcUserTable.new
|
52
|
+
result = connection[key]
|
53
|
+
connection.close
|
54
|
+
if result
|
55
|
+
self.new(result.merge({:pk => key}))
|
56
|
+
else
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.set(attributes)
|
62
|
+
#this way of validating is real crap, replace it with Validator maybe
|
63
|
+
#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
|
64
|
+
#or maybe just write a little method that makes hash merger look a little cleaner
|
65
|
+
pk = attributes.delete(:pk) if attributes[:pk]
|
66
|
+
|
67
|
+
errors = []
|
68
|
+
|
69
|
+
email_regexp = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
|
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)
|
98
|
+
end
|
99
|
+
|
100
|
+
existing_user = TcUser.query do |q|
|
101
|
+
q.add 'email', :streq, attributes['email']
|
102
|
+
end[0]
|
103
|
+
|
104
|
+
if existing_user && existing_user[:pk] != pk
|
105
|
+
errors << "Email is already taken"
|
106
|
+
return self.new(attributes, errors)
|
107
|
+
else
|
108
|
+
connection = TcUserTable.new
|
109
|
+
pk ||= connection.genuid.to_s
|
110
|
+
#site admin if their first
|
111
|
+
attributes.merge!({'permission_level' => '-2'}) if pk == '1'
|
112
|
+
result = connection[pk] = attributes
|
113
|
+
#might not need this in newer version of rufus
|
114
|
+
result.merge!({:pk => pk})
|
115
|
+
connection.close
|
116
|
+
self.new(result, errors)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
self.new(attributes, errors)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.set!(attributes)
|
124
|
+
connection = TcUserTable.new
|
125
|
+
pk = connection.genuid.to_s
|
126
|
+
result = connection[pk] = attributes
|
127
|
+
result.merge!({:pk => pk})
|
128
|
+
connection.close
|
129
|
+
self.new(result)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.delete(pk)
|
133
|
+
connection = TcUserTable.new
|
134
|
+
connection.delete(pk)
|
135
|
+
connection.close
|
136
|
+
end
|
137
|
+
|
138
|
+
def update(attributes)
|
139
|
+
self.errors = []
|
140
|
+
new_attributes = @attributes.merge(attributes)
|
141
|
+
updated_user = TcUser.set(new_attributes)
|
142
|
+
self.errors = updated_user.errors
|
143
|
+
updated_user.errors.length < 1
|
144
|
+
end
|
145
|
+
|
146
|
+
def [](key)
|
147
|
+
@attributes[key]
|
148
|
+
end
|
149
|
+
|
150
|
+
#saves to database and returns self
|
151
|
+
def []=(key, value)
|
152
|
+
@attributes[key] = value
|
153
|
+
#change so that it sets the attributes and then you call save to save to the database?
|
154
|
+
connection = TcUserTable.new
|
155
|
+
connection[@attributes[:pk]] = @attributes.merge!({key => value})
|
156
|
+
connection.close
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
160
|
+
def id
|
161
|
+
@attributes[:pk]
|
162
|
+
end
|
163
|
+
|
164
|
+
def admin?
|
165
|
+
@attributes['permission_level'] == '-1' || site_admin?
|
166
|
+
end
|
167
|
+
|
168
|
+
def site_admin?
|
169
|
+
#-2 is the site admin
|
170
|
+
@attributes['permission_level'] == '-2'
|
171
|
+
end
|
172
|
+
|
173
|
+
#from hash extension for making hashes like javascript objects
|
174
|
+
def method_missing(meth,*args)
|
175
|
+
if /=$/=~(meth=meth.id2name) then
|
176
|
+
self[meth[0...-1]] = (args.length<2 ? args[0] : args)
|
177
|
+
elsif @attributes[meth]
|
178
|
+
@attributes[meth]
|
179
|
+
else
|
180
|
+
false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
if Rufus::Tokyo.const_defined?('Table')
|
186
|
+
class TokyoTableDad < Rufus::Tokyo::Table
|
187
|
+
end
|
188
|
+
elsif Rufus::Edo.const_defined?('Table')
|
189
|
+
class TokyoTableDad < Rufus::Edo::Table
|
190
|
+
end
|
191
|
+
else
|
192
|
+
throw 'wtf?'
|
193
|
+
end
|
194
|
+
|
195
|
+
class TcUserTable < TokyoTableDad
|
196
|
+
@@path = false
|
197
|
+
def initialize
|
198
|
+
#make this path configurable somehow
|
199
|
+
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
|
200
|
+
super(@@path + '/users.tct')
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.cabinet_path=(path)
|
204
|
+
@@path = path
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SequelAdapter
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
base.class_eval { include SequelAdapter::InstanceMethods }
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def all
|
9
|
+
result = SequelUser.order(:created_at.desc).all
|
10
|
+
result.collect {|instance| self.new instance}
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(hash)
|
14
|
+
if user = SequelUser.first(hash)
|
15
|
+
self.new user
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(attributes)
|
22
|
+
user = SequelUser.new attributes
|
23
|
+
if user.valid?
|
24
|
+
user.save
|
25
|
+
#false
|
26
|
+
end
|
27
|
+
|
28
|
+
self.new user
|
29
|
+
end
|
30
|
+
|
31
|
+
def set!(attributes)
|
32
|
+
user = SequelUser.new attributes
|
33
|
+
user.save!
|
34
|
+
self.new user
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(pk)
|
38
|
+
user = SequelUser.first(:id => pk)
|
39
|
+
user.destroy
|
40
|
+
!user.exists?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module InstanceMethods
|
45
|
+
def errors
|
46
|
+
@instance.errors.full_messages.join(', ')
|
47
|
+
end
|
48
|
+
|
49
|
+
def valid
|
50
|
+
@instance.valid?
|
51
|
+
end
|
52
|
+
|
53
|
+
def update(attributes)
|
54
|
+
@instance.set attributes
|
55
|
+
if @instance.valid?
|
56
|
+
@instance.save
|
57
|
+
true
|
58
|
+
else
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_missing(meth, *args, &block)
|
64
|
+
#cool I just found out * on an array turns the array into a list of args for a function
|
65
|
+
@instance.send(meth, *args, &block)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
unless DB.table_exists? :sequel_users
|
2
|
+
DB.create_table :sequel_users do
|
3
|
+
primary_key :id
|
4
|
+
String :email, :unique => true
|
5
|
+
String :hashed_password
|
6
|
+
String :salt
|
7
|
+
DateTime :created_at
|
8
|
+
Integer :permission_level, :default => 1
|
9
|
+
if Sinatra.const_defined?('FacebookObject')
|
10
|
+
String :fb_uid
|
11
|
+
end
|
12
|
+
|
13
|
+
#check{{char_length(email)=>5..40}}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class SequelUser < Sequel::Model
|
18
|
+
attr_writer :password_confirmation
|
19
|
+
plugin :validation_helpers
|
20
|
+
plugin :timestamps, :create => :created_at
|
21
|
+
|
22
|
+
def validate
|
23
|
+
super
|
24
|
+
email_regexp = /(\A(\s*)\Z)|(\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z)/i
|
25
|
+
validates_format email_regexp, :email
|
26
|
+
validates_presence :email
|
27
|
+
validates_unique :email
|
28
|
+
validates_presence :password if new?
|
29
|
+
errors.add :passwords, ' don\'t match' unless @password == @password_confirmation
|
30
|
+
#validate equality?
|
31
|
+
end
|
32
|
+
#TODO validate format of email
|
33
|
+
|
34
|
+
def password=(pass)
|
35
|
+
@password = pass
|
36
|
+
self.salt = User.random_string(10) if !self.salt
|
37
|
+
self.hashed_password = User.encrypt(@password, self.salt)
|
38
|
+
end
|
39
|
+
|
40
|
+
def admin?
|
41
|
+
self.permission_level == -1 || self.id == 1
|
42
|
+
end
|
43
|
+
|
44
|
+
def site_admin?
|
45
|
+
self.id == 1
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def method_missing(m, *args)
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
end
|