merb-auth 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +64 -0
- data/Rakefile +47 -0
- data/app/controllers/application.rb +4 -0
- data/app/controllers/controller_mixin.rb +150 -0
- data/app/controllers/users.rb +41 -0
- data/app/helpers/application_helper.rb +64 -0
- data/app/views/layout/merb_auth.html.erb +47 -0
- data/app/views/users/login.html.erb +31 -0
- data/app/views/users/signup.html.erb +29 -0
- data/lib/merb-auth/adapter/activerecord.rb +52 -0
- data/lib/merb-auth/adapter/datamapper.rb +78 -0
- data/lib/merb-auth/merbtasks.rb +166 -0
- data/lib/merb-auth/model.rb +80 -0
- data/lib/merb-auth/slicetasks.rb +18 -0
- data/lib/merb-auth.rb +72 -0
- data/public/stylesheets/master.css +157 -0
- data/spec/controllers/router_spec.rb +29 -0
- data/spec/controllers/session_spec.rb +87 -0
- data/spec/controllers/users_spec.rb +41 -0
- data/spec/controllers/view_helper_spec.rb +27 -0
- data/spec/merb-auth_spec.rb +52 -0
- data/spec/models/ar_user_spec.rb +20 -0
- data/spec/models/dm_user_spec.rb +22 -0
- data/spec/models/shared_user_spec.rb +251 -0
- data/spec/spec_helper.rb +42 -0
- metadata +98 -0
@@ -0,0 +1,166 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :merb_auth do
|
3
|
+
|
4
|
+
desc "Install MerbAuth"
|
5
|
+
task :install => [:preflight, :setup_directories, :copy_assets, :migrate]
|
6
|
+
|
7
|
+
desc "Test for any dependencies"
|
8
|
+
task :preflight do # see slicetasks.rb
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Setup directories"
|
12
|
+
task :setup_directories do
|
13
|
+
puts "Creating directories for host application"
|
14
|
+
MerbAuth.mirrored_components.each do |type|
|
15
|
+
if File.directory?(MerbAuth.dir_for(type))
|
16
|
+
if !File.directory?(dst_path = MerbAuth.app_dir_for(type))
|
17
|
+
relative_path = dst_path.relative_path_from(Merb.root)
|
18
|
+
puts "- creating directory :#{type} #{File.basename(Merb.root) / relative_path}"
|
19
|
+
mkdir_p(dst_path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Copy stub files to host application"
|
26
|
+
task :stubs do
|
27
|
+
puts "Copying stubs for MerbAuth - resolves any collisions"
|
28
|
+
copied, preserved = MerbAuth.mirror_stubs!
|
29
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
30
|
+
copied.each { |f| puts "- copied #{f}" }
|
31
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Copy stub files and views to host application"
|
35
|
+
task :patch => [ "stubs", "freeze:views" ]
|
36
|
+
|
37
|
+
desc "Copy public assets to host application"
|
38
|
+
task :copy_assets do
|
39
|
+
puts "Copying assets for MerbAuth - resolves any collisions"
|
40
|
+
copied, preserved = MerbAuth.mirror_public!
|
41
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
42
|
+
copied.each { |f| puts "- copied #{f}" }
|
43
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Migrate the database"
|
47
|
+
task :migrate do # see slicetasks.rb
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Freeze MerbAuth into your app (only merb-auth/app)"
|
51
|
+
task :freeze => [ "freeze:app" ]
|
52
|
+
|
53
|
+
namespace :freeze do
|
54
|
+
|
55
|
+
desc "Freezes MerbAuth by installing the gem into application/gems using merb-freezer"
|
56
|
+
task :gem do
|
57
|
+
begin
|
58
|
+
Object.const_get(:Freezer).freeze(ENV["GEM"] || "merb-auth", ENV["UPDATE"], ENV["MODE"] || 'rubygems')
|
59
|
+
rescue NameError
|
60
|
+
puts "! dependency 'merb-freezer' missing"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "Freezes MerbAuth by copying all files from merb-auth/app to your application"
|
65
|
+
task :app do
|
66
|
+
puts "Copying all merb-auth/app files to your application - resolves any collisions"
|
67
|
+
copied, preserved = MerbAuth.mirror_app!
|
68
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
69
|
+
copied.each { |f| puts "- copied #{f}" }
|
70
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "Freeze all views into your application for easy modification"
|
74
|
+
task :views do
|
75
|
+
puts "Copying all view templates to your application - resolves any collisions"
|
76
|
+
copied, preserved = MerbAuth.mirror_files_for :view
|
77
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
78
|
+
copied.each { |f| puts "- copied #{f}" }
|
79
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "Freeze all models into your application for easy modification"
|
83
|
+
task :models do
|
84
|
+
puts "Copying all models to your application - resolves any collisions"
|
85
|
+
copied, preserved = MerbAuth.mirror_files_for :model
|
86
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
87
|
+
copied.each { |f| puts "- copied #{f}" }
|
88
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "Freezes MerbAuth as a gem and copies over merb-auth/app"
|
92
|
+
task :app_with_gem => [:gem, :app]
|
93
|
+
|
94
|
+
desc "Freezes MerbAuth by unpacking all files into your application"
|
95
|
+
task :unpack do
|
96
|
+
puts "Unpacking MerbAuth files to your application - resolves any collisions"
|
97
|
+
copied, preserved = MerbAuth.unpack_slice!
|
98
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
99
|
+
copied.each { |f| puts "- copied #{f}" }
|
100
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "Run slice specs within the host application context"
|
106
|
+
task :spec => [ "spec:explain", "spec:default" ]
|
107
|
+
|
108
|
+
namespace :spec do
|
109
|
+
|
110
|
+
slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
111
|
+
|
112
|
+
task :explain do
|
113
|
+
puts "\nNote: By running MerbAuth specs inside the application context any\n" +
|
114
|
+
"overrides could break existing specs. This isn't always a problem,\n" +
|
115
|
+
"especially in the case of views. Use these spec tasks to check how\n" +
|
116
|
+
"well your application conforms to the original slice implementation."
|
117
|
+
end
|
118
|
+
|
119
|
+
Spec::Rake::SpecTask.new('default') do |t|
|
120
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
121
|
+
t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
|
122
|
+
end
|
123
|
+
|
124
|
+
desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
|
125
|
+
Spec::Rake::SpecTask.new('model') do |t|
|
126
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
127
|
+
if(ENV['MODEL'])
|
128
|
+
t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
|
129
|
+
else
|
130
|
+
t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
desc "Run all controller specs, run a spec for a specific Controller with CONTROLLER=MyController"
|
135
|
+
Spec::Rake::SpecTask.new('controller') do |t|
|
136
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
137
|
+
if(ENV['CONTROLLER'])
|
138
|
+
t.spec_files = Dir["#{slice_root}/spec/controllers/**/#{ENV['CONTROLLER']}_spec.rb"].sort
|
139
|
+
else
|
140
|
+
t.spec_files = Dir["#{slice_root}/spec/controllers/**/*_spec.rb"].sort
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
desc "Run all view specs, run specs for a specific controller (and view) with CONTROLLER=MyController (VIEW=MyView)"
|
145
|
+
Spec::Rake::SpecTask.new('view') do |t|
|
146
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
147
|
+
if(ENV['CONTROLLER'] and ENV['VIEW'])
|
148
|
+
t.spec_files = Dir["#{slice_root}/spec/views/**/#{ENV['CONTROLLER']}/#{ENV['VIEW']}*_spec.rb"].sort
|
149
|
+
elsif(ENV['CONTROLLER'])
|
150
|
+
t.spec_files = Dir["#{slice_root}/spec/views/**/#{ENV['CONTROLLER']}/*_spec.rb"].sort
|
151
|
+
else
|
152
|
+
t.spec_files = Dir["#{slice_root}/spec/views/**/*_spec.rb"].sort
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
desc "Run all specs and output the result in html"
|
157
|
+
Spec::Rake::SpecTask.new('html') do |t|
|
158
|
+
t.spec_opts = ["--format", "html"]
|
159
|
+
t.libs = ['lib', 'server/lib' ]
|
160
|
+
t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module MerbAuth
|
4
|
+
def self.use_adapter(adapter)
|
5
|
+
require File.join(File.dirname(__FILE__), "adapter", adapter.to_s)
|
6
|
+
end
|
7
|
+
|
8
|
+
module BaseModel
|
9
|
+
def self.included(base)
|
10
|
+
base.send(:include, InstanceMethods)
|
11
|
+
base.send(:extend, ClassMethods)
|
12
|
+
|
13
|
+
base.class_eval do
|
14
|
+
attr_accessor :password, :password_confirmation
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
def authenticated?(password)
|
20
|
+
crypted_password == encrypt(password)
|
21
|
+
end
|
22
|
+
|
23
|
+
# before filter
|
24
|
+
def encrypt_password
|
25
|
+
return if password.blank?
|
26
|
+
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{username}--") if new_record?
|
27
|
+
self.crypted_password = encrypt(password)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Encrypts the password with the user salt
|
31
|
+
def encrypt(password)
|
32
|
+
self.class.encrypt(password, salt)
|
33
|
+
end
|
34
|
+
|
35
|
+
def remember_token?
|
36
|
+
remember_token_expires_at && Date.today < remember_token_expires_at
|
37
|
+
end
|
38
|
+
|
39
|
+
def remember_me_until(time)
|
40
|
+
self.remember_token_expires_at = time
|
41
|
+
self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
|
42
|
+
save
|
43
|
+
end
|
44
|
+
|
45
|
+
def remember_me_for(days)
|
46
|
+
remember_me_until (Date.today + days)
|
47
|
+
end
|
48
|
+
|
49
|
+
# These create and unset the fields required for remembering users between browser closes
|
50
|
+
# Default of 2 weeks
|
51
|
+
def remember_me
|
52
|
+
remember_me_for(14)
|
53
|
+
end
|
54
|
+
|
55
|
+
def forget_me
|
56
|
+
self.remember_token_expires_at = nil
|
57
|
+
self.remember_token = nil
|
58
|
+
self.save
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
def password_required?
|
63
|
+
crypted_password.blank? || !password.blank?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
module ClassMethods
|
68
|
+
# Encrypts some data with the salt.
|
69
|
+
def encrypt(password, salt)
|
70
|
+
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
71
|
+
end
|
72
|
+
|
73
|
+
# Authenticates a user by their username and unencrypted password. Returns the user or nil.
|
74
|
+
def authenticate(username, password)
|
75
|
+
u = find_by_username(username) # need to get the salt
|
76
|
+
u && u.authenticated?(password) ? u : nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :merb_auth do
|
3
|
+
# add your own tasks here
|
4
|
+
|
5
|
+
# implement this to test for structural/code dependencies
|
6
|
+
# like certain directories or availability of other files
|
7
|
+
desc "Test for any dependencies"
|
8
|
+
task :preflight do
|
9
|
+
end
|
10
|
+
|
11
|
+
# implement this to perform any database related setup steps
|
12
|
+
desc "Migrate the database"
|
13
|
+
task :migrate do
|
14
|
+
MerbAuth::User.create_db_table
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
data/lib/merb-auth.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
if defined?(Merb::Plugins)
|
2
|
+
|
3
|
+
load_dependency 'merb-slices'
|
4
|
+
load_dependency 'merb_helpers'
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), 'merb-auth', 'model')
|
7
|
+
|
8
|
+
Merb::Plugins.add_rakefiles "merb-auth/merbtasks", "merb-auth/slicetasks"
|
9
|
+
|
10
|
+
# Register the Slice for the current host application
|
11
|
+
Merb::Slices::register(__FILE__)
|
12
|
+
|
13
|
+
# Slice configuration - set this in a before_app_loads callback.
|
14
|
+
# By default a Slice uses its own layout.
|
15
|
+
Merb::Slices::config[:merb_auth][:layout] ||= :merb_auth
|
16
|
+
|
17
|
+
# Use a short name for the user model class. Set this to false to use MerbAuth::User
|
18
|
+
Merb::Slices::config[:merb_auth][:user_class_alias] = "User"
|
19
|
+
|
20
|
+
# All Slice code is expected to be namespaced inside a module
|
21
|
+
module MerbAuth
|
22
|
+
# Slice metadata
|
23
|
+
self.description = "MerbAuth is an user authentication Merb slice!"
|
24
|
+
self.version = "0.1.0"
|
25
|
+
self.author = "ctran@pragmaquest.com"
|
26
|
+
|
27
|
+
# Stub classes loaded hook - runs before LoadClasses BootLoader
|
28
|
+
# right after a slice's classes have been loaded internally.
|
29
|
+
def self.loaded
|
30
|
+
MerbAuth.use_adapter(Merb.orm_generator_scope) if Merb.orm_generator_scope != :merb_default
|
31
|
+
Object.const_set(Merb::Slices::config[:merb_auth][:user_class_alias], MerbAuth::User) if Merb::Slices::config[:merb_auth][:user_class_alias]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Initialization hook - runs before AfterAppLoads BootLoader
|
35
|
+
def self.init
|
36
|
+
end
|
37
|
+
|
38
|
+
# Activation hook - runs after AfterAppLoads BootLoader
|
39
|
+
def self.activate
|
40
|
+
end
|
41
|
+
|
42
|
+
# Deactivation hook - triggered by Merb::Slices.deactivate(Bar)
|
43
|
+
def self.deactivate
|
44
|
+
end
|
45
|
+
|
46
|
+
# Setup routes inside the host application
|
47
|
+
#
|
48
|
+
# @param scope<Merb::Router::Behaviour>
|
49
|
+
# Routes will be added within this scope (namespace). In fact, any
|
50
|
+
# router behaviour is a valid namespace, so you can attach
|
51
|
+
# routes at any level of your router setup.
|
52
|
+
def self.setup_router(scope)
|
53
|
+
scope.match('/login').to(:controller => 'users', :action => 'login').name(:login)
|
54
|
+
scope.match('/logout').to(:controller => 'users', :action => 'logout').name(:logout)
|
55
|
+
scope.match('/signup').to(:controller => 'users', :action => 'signup').name(:signup)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Setup the slice layout for MerbAuth
|
60
|
+
#
|
61
|
+
# Use MerbAuth.push_path and MerbAuth.push_app_path
|
62
|
+
# to set paths to merb-auth-level and app-level paths. Example:
|
63
|
+
#
|
64
|
+
# MerbAuth.push_path(:application, MerbAuth.root)
|
65
|
+
# MerbAuth.push_app_path(:application, Merb.root / 'slices' / 'merb-auth')
|
66
|
+
# ...
|
67
|
+
#
|
68
|
+
# Any component path that hasn't been set will default to MerbAuth.root
|
69
|
+
#
|
70
|
+
# Or just call setup_default_structure! to setup a basic Merb MVC structure.
|
71
|
+
MerbAuth.setup_default_structure!
|
72
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
body {
|
2
|
+
font-family: Arial, Verdana, sans-serif;
|
3
|
+
font-size: 14px;
|
4
|
+
background-color: #fff;
|
5
|
+
color: #000;
|
6
|
+
background-color: #fff;
|
7
|
+
}
|
8
|
+
|
9
|
+
html {
|
10
|
+
height: 100%;
|
11
|
+
}
|
12
|
+
|
13
|
+
hr {
|
14
|
+
border: 0px;
|
15
|
+
color: #ccc;
|
16
|
+
background-color: #cdcdcd;
|
17
|
+
height: 1px;
|
18
|
+
width: 100%;
|
19
|
+
text-align: left;
|
20
|
+
}
|
21
|
+
|
22
|
+
h1, h2, h3 {
|
23
|
+
color: #003399;
|
24
|
+
color: #09579A;
|
25
|
+
background-color: #fff;
|
26
|
+
font-family: Arial, Verdana, sans-serif;
|
27
|
+
font-weight: 300;
|
28
|
+
}
|
29
|
+
|
30
|
+
h1 {
|
31
|
+
font-size: 20px;
|
32
|
+
}
|
33
|
+
h2 {
|
34
|
+
font-size: 15px;
|
35
|
+
}
|
36
|
+
h3 {
|
37
|
+
font-size: 15px;
|
38
|
+
}
|
39
|
+
|
40
|
+
p {
|
41
|
+
line-height: 20px;
|
42
|
+
padding: 5px;
|
43
|
+
}
|
44
|
+
|
45
|
+
a, a:hover {
|
46
|
+
color: #0033CC;
|
47
|
+
background-color: #fff;
|
48
|
+
text-decoration: underline;
|
49
|
+
}
|
50
|
+
|
51
|
+
#container {
|
52
|
+
width: 95%;
|
53
|
+
text-align: left;
|
54
|
+
background-color: #fff;
|
55
|
+
margin-right: auto;
|
56
|
+
margin-left: auto;
|
57
|
+
}
|
58
|
+
|
59
|
+
#header-container {
|
60
|
+
width: 100%;
|
61
|
+
padding-top: 15px;
|
62
|
+
}
|
63
|
+
|
64
|
+
#header-container h1, #header-container h2 {
|
65
|
+
margin-left: 6px;
|
66
|
+
margin-bottom: 6px;
|
67
|
+
}
|
68
|
+
|
69
|
+
#main-container {
|
70
|
+
padding: 15px;
|
71
|
+
min-height: 400px;
|
72
|
+
}
|
73
|
+
|
74
|
+
#footer-container {
|
75
|
+
clear: both;
|
76
|
+
font-size: 12px;
|
77
|
+
font-family: Verdana, Arial, sans-serif;
|
78
|
+
}
|
79
|
+
|
80
|
+
#main-container ul {
|
81
|
+
margin-left: 3.0em;
|
82
|
+
}
|
83
|
+
|
84
|
+
.right {
|
85
|
+
float: right;
|
86
|
+
font-size: 100%;
|
87
|
+
margin-top: 5px;
|
88
|
+
color: #999;
|
89
|
+
background-color: #fff;
|
90
|
+
}
|
91
|
+
.left {
|
92
|
+
float: left;
|
93
|
+
font-size: 100%;
|
94
|
+
margin-top: 5px;
|
95
|
+
color: #999;
|
96
|
+
background-color: #fff;
|
97
|
+
}
|
98
|
+
|
99
|
+
a#google_signup {
|
100
|
+
background:transparent url(/images/btn-google-signup.png) no-repeat scroll 0% 0%;
|
101
|
+
color:#FFFFFF;
|
102
|
+
display:block;
|
103
|
+
font-size:14px;
|
104
|
+
height:67px;
|
105
|
+
line-height:22px;
|
106
|
+
margin:0pt;
|
107
|
+
padding:0px;
|
108
|
+
text-align:center;
|
109
|
+
text-decoration:none;
|
110
|
+
width:151px;
|
111
|
+
}
|
112
|
+
|
113
|
+
a#google_signup div {
|
114
|
+
padding-top:8px;
|
115
|
+
}
|
116
|
+
|
117
|
+
#openid_url, .openid_link {
|
118
|
+
background:#FFFFFF url(/images/openid-login.gif) no-repeat scroll 2px 50%;
|
119
|
+
padding-left:25px;
|
120
|
+
width:14.25em;
|
121
|
+
}
|
122
|
+
|
123
|
+
/** Form inputs **/
|
124
|
+
label {
|
125
|
+
font-weight: bold;
|
126
|
+
display: block;
|
127
|
+
}
|
128
|
+
|
129
|
+
#remember_me {
|
130
|
+
margin: 2px 5px 0px 0px;
|
131
|
+
float: left;
|
132
|
+
}
|
133
|
+
|
134
|
+
div.error {
|
135
|
+
background-color:#FCECEC;
|
136
|
+
}
|
137
|
+
|
138
|
+
div.error ul {
|
139
|
+
padding-bottom:20px;
|
140
|
+
}
|
141
|
+
|
142
|
+
.error {
|
143
|
+
background-color:#FCECEC;
|
144
|
+
}
|
145
|
+
|
146
|
+
.error h2 {
|
147
|
+
background: #CC0000 no-repeat scroll left center;
|
148
|
+
border-color: #CC9999;
|
149
|
+
color:#FFFFFF;
|
150
|
+
border:1px solid #CCCCCC;
|
151
|
+
font-size:14px;
|
152
|
+
padding:5px 5px 5px 10px;
|
153
|
+
}
|
154
|
+
|
155
|
+
.error li {
|
156
|
+
color: #CC0000;
|
157
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
|
2
|
+
|
3
|
+
prefix = 'merb-auth'
|
4
|
+
|
5
|
+
describe "Router.add_slice(:MerbAuth)" do
|
6
|
+
include Merb::Test::Rspec::RouteMatchers
|
7
|
+
|
8
|
+
before(:all) do
|
9
|
+
Merb::Router.prepare { |r| r.add_slice(:MerbAuth) } if standalone?
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should have named routes for :login, :logout and :signup" do
|
13
|
+
[:login, :logout, :signup].each do |name|
|
14
|
+
url(name).should == "/#{prefix}/#{name}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should route /#{prefix}/login to Session#new" do
|
19
|
+
request_to("/#{prefix}/login", :get).should route_to(MerbAuth::Users, :login)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should route /#{prefix}/login via post to Session#create" do
|
23
|
+
request_to("/#{prefix}/login", :post).should route_to(MerbAuth::Users, :login)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should route /#{prefix}/logout via delete to Session#destroy" do
|
27
|
+
request_to("/#{prefix}/logout", :delete).should route_to(MerbAuth::Users, :logout)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe "Session Controller", "index action" do
|
4
|
+
before(:all) do
|
5
|
+
Merb::Router.prepare { |r| r.add_slice(:MerbAuth) } if standalone?
|
6
|
+
end
|
7
|
+
|
8
|
+
def login_with(params = {}, &blk)
|
9
|
+
dispatch_to(MerbAuth::Users, :login, params, {:request_method => 'post'}, &blk)
|
10
|
+
end
|
11
|
+
|
12
|
+
def logout(&blk)
|
13
|
+
dispatch_to(MerbAuth::Users, :logout, {}, {}, &blk)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'logins and redirects' do
|
17
|
+
controller = login_with(:username => 'quentin', :password => 'test') {|c|
|
18
|
+
c.should_receive(:verify_login).with('quentin', 'test').and_return(mock("User", :id => 1, :name => 'quentin'))
|
19
|
+
}
|
20
|
+
|
21
|
+
controller.session[:user].should_not be_nil
|
22
|
+
controller.session[:user].should == 1
|
23
|
+
controller.should redirect_to("/")
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'fails login and does not redirect' do
|
27
|
+
controller = login_with(:username => 'quentin', :password => 'bad password') {|c|
|
28
|
+
c.should_receive(:verify_login).with('quentin', 'bad password').and_return(nil)
|
29
|
+
}
|
30
|
+
controller.session[:user].should be_nil
|
31
|
+
controller.should be_successful
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'logs out' do
|
35
|
+
controller = logout {|c|
|
36
|
+
c.stub!(:current_user).and_return(mock("User", :forget_me => true))
|
37
|
+
}
|
38
|
+
controller.session[:user].should be_nil
|
39
|
+
controller.should redirect
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'remembers me' do
|
43
|
+
controller = login_with(:username => 'quentin', :password => 'test', :remember_me => "1") {|c|
|
44
|
+
user = mock("User", :id => 1)
|
45
|
+
c.should_receive(:verify_login).with('quentin', 'test').and_return(user)
|
46
|
+
user.should_receive(:remember_me)
|
47
|
+
user.should_receive(:remember_token).and_return("abc123")
|
48
|
+
user.should_receive(:remember_token_expires_at).and_return(Date.today)
|
49
|
+
}
|
50
|
+
controller.cookies["auth_token"].should_not be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'does not remember me' do
|
54
|
+
controller = login_with(:username => 'quentin', :password => 'test', :remember_me => "0") {|c|
|
55
|
+
user = mock("User", :id => 1)
|
56
|
+
c.should_receive(:verify_login).with('quentin', 'test').and_return(user)
|
57
|
+
}
|
58
|
+
controller.cookies["auth_token"].should be_nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'deletes token on logout' do
|
62
|
+
controller = logout {|c| controller.stub!(:current_user).and_return(@quentin) }
|
63
|
+
controller.cookies["auth_token"].should == nil
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
it 'logs in with cookie' do
|
68
|
+
controller = login_with do |c|
|
69
|
+
c.request.env[Merb::Const::HTTP_COOKIE] = "auth_token=abc123"
|
70
|
+
c.should_receive(:verify_login).and_return(nil)
|
71
|
+
user = mock("User", :id => 1, :remember_token? => true)
|
72
|
+
c.should_receive(:find_user_by_remember_token).with('abc123').and_return(user)
|
73
|
+
user.should_receive(:remember_me)
|
74
|
+
user.should_receive(:remember_token).and_return("abc123")
|
75
|
+
user.should_receive(:remember_token_expires_at).and_return(Date.today)
|
76
|
+
end
|
77
|
+
controller.should be_logged_in
|
78
|
+
end
|
79
|
+
|
80
|
+
def auth_token(token)
|
81
|
+
CGI::Cookie.new('name' => 'auth_token', 'value' => token)
|
82
|
+
end
|
83
|
+
|
84
|
+
def cookie_for(user)
|
85
|
+
auth_token user.remember_token
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
|
2
|
+
|
3
|
+
def user_hash(options = {})
|
4
|
+
{ 'name' => 'ctran',
|
5
|
+
'username' => "ctran",
|
6
|
+
'email' => "ctran@example.com",
|
7
|
+
'password' => "sekret",
|
8
|
+
'password_confirmation' => "sekret"}.merge(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe MerbAuth::Users do
|
12
|
+
before(:all) do
|
13
|
+
require 'dm-core'
|
14
|
+
DataMapper.setup(:default, 'sqlite3::memory:')
|
15
|
+
MerbAuth.use_adapter(:datamapper)
|
16
|
+
|
17
|
+
Merb::Router.prepare { |r| r.add_slice(:MerbAuth) } if standalone?
|
18
|
+
end
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
@user = mock("User", user_hash)
|
22
|
+
MerbAuth::User.should_receive(:new).with(user_hash).and_return(@user)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'allows signup and redirect to /' do
|
26
|
+
@user.should_receive(:save).and_return(true)
|
27
|
+
|
28
|
+
controller = dispatch_to(MerbAuth::Users, :signup, {'merb_auth::user' => user_hash }, {:request_method => 'post'})
|
29
|
+
controller.assigns(:user).should == @user
|
30
|
+
controller.should redirect_to('/')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'reject signup and render errors in template' do
|
34
|
+
@user.should_receive(:save).and_return(false)
|
35
|
+
controller = dispatch_to(MerbAuth::Users, :signup, {'merb_auth::user' => user_hash}, {:request_method => 'post'}) {|c|
|
36
|
+
c.should_receive(:render)
|
37
|
+
}
|
38
|
+
controller.assigns(:user).should == @user
|
39
|
+
controller.should respond_successfully
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
module MerbAuth
|
4
|
+
class Main < Application
|
5
|
+
def index
|
6
|
+
'index'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "MerbAuth::Main (controller)" do
|
12
|
+
before :all do
|
13
|
+
Merb::Router.prepare { |r| r.add_slice(:MerbAuth) } if standalone?
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have helper methods for dealing with public paths" do
|
17
|
+
controller = dispatch_to(MerbAuth::Main, :index)
|
18
|
+
controller.public_path_for(:image).should == "/slices/merb-auth/images"
|
19
|
+
controller.public_path_for(:javascript).should == "/slices/merb-auth/javascripts"
|
20
|
+
controller.public_path_for(:stylesheet).should == "/slices/merb-auth/stylesheets"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should have a slice-specific _template_root" do
|
24
|
+
MerbAuth::Main._template_root.should == MerbAuth.dir_for(:view)
|
25
|
+
MerbAuth::Main._template_root.should == MerbAuth::Application._template_root
|
26
|
+
end
|
27
|
+
end
|