merb-auth 0.1.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.
- 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
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 PragmaQuest Inc
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
MerbAuth
|
2
|
+
=========
|
3
|
+
|
4
|
+
An user authentication slice for Merb, with support for ActiveRecord and DataMapper.
|
5
|
+
See http://github.com/ctran/merb-skel for an example application that uses this slice
|
6
|
+
|
7
|
+
== Installation:
|
8
|
+
|
9
|
+
> gem install merb
|
10
|
+
> gem install merb-slices
|
11
|
+
|
12
|
+
To use activerecord adapter
|
13
|
+
> gem install activerecord
|
14
|
+
> gem merb_activerecord
|
15
|
+
|
16
|
+
To use datamapper adapter
|
17
|
+
> gem install datamapper
|
18
|
+
> gem install merb_datamapper
|
19
|
+
|
20
|
+
=== config/init.rb
|
21
|
+
|
22
|
+
# add the slice as a regular dependency
|
23
|
+
dependency 'merb-auth'
|
24
|
+
|
25
|
+
=== config/router.rb
|
26
|
+
|
27
|
+
# This will add the following routes /login, /logout and /signup
|
28
|
+
r.slice(:MerbAuth)
|
29
|
+
|
30
|
+
# This will add the following routes /merb-auth/login, /merb-auth/logout and /merb-auth/signup
|
31
|
+
r.add_slice(:MerbAuth)
|
32
|
+
|
33
|
+
# This will add the following routes /user/login, /user/logout and /user/signup
|
34
|
+
r.add_slice(:MerbAuth, 'user') # same as :path => 'user'
|
35
|
+
|
36
|
+
=== Normally you should also run the following rake task:
|
37
|
+
> rake slices:merb_auth:install
|
38
|
+
|
39
|
+
== Customization/Overrides
|
40
|
+
|
41
|
+
By default, the user model class MerbAuth::User is aliased to User.
|
42
|
+
|
43
|
+
You can also put your application-level overrides in:
|
44
|
+
|
45
|
+
host-app/slices/merb-auth/app - controllers, models, views ...
|
46
|
+
|
47
|
+
Templates are located in this order:
|
48
|
+
|
49
|
+
1. host-app/slices/merb-auth/app/views/*
|
50
|
+
2. gems/merb-auth/app/views/*
|
51
|
+
3. host-app/app/views/*
|
52
|
+
|
53
|
+
You can use the host application's layout by configuring the
|
54
|
+
merb-auth slice in a before_app_loads block:
|
55
|
+
|
56
|
+
Merb::Slices.config[:merb_auth] = { :layout => :application }
|
57
|
+
|
58
|
+
By default :merb_auth is used. If you need to override
|
59
|
+
stylesheets or javascripts, just specify your own files in your layout
|
60
|
+
instead/in addition to the ones supplied (if any) in
|
61
|
+
host-app/public/slices/merb-auth.
|
62
|
+
|
63
|
+
In any case don't edit those files directly as they may be clobbered any time
|
64
|
+
rake merb_auth:install is run.
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'merb-core/version'
|
5
|
+
require 'merb-core/test/tasks/spectasks'
|
6
|
+
|
7
|
+
PLUGIN = "merb-auth"
|
8
|
+
NAME = "merb-auth"
|
9
|
+
AUTHOR = "Cuong Tran"
|
10
|
+
EMAIL = "ctran@pragmaquest.com"
|
11
|
+
HOMEPAGE = "http://merb-slices.rubyforge.org/merb-auth/"
|
12
|
+
SUMMARY = "Merb Slice that provides user authentication"
|
13
|
+
SLICE_VERSION = "0.1.0"
|
14
|
+
|
15
|
+
spec = Gem::Specification.new do |s|
|
16
|
+
s.name = NAME
|
17
|
+
s.version = SLICE_VERSION
|
18
|
+
s.platform = Gem::Platform::RUBY
|
19
|
+
s.has_rdoc = true
|
20
|
+
s.extra_rdoc_files = ["README", "LICENSE"]
|
21
|
+
s.summary = SUMMARY
|
22
|
+
s.description = s.summary
|
23
|
+
s.author = AUTHOR
|
24
|
+
s.email = EMAIL
|
25
|
+
s.homepage = HOMEPAGE
|
26
|
+
s.add_dependency('merb-slices', '>= 0.9.4')
|
27
|
+
s.require_path = 'lib'
|
28
|
+
s.files = %w(LICENSE README Rakefile) + Dir.glob("{lib,spec,app,public}/**/*")
|
29
|
+
end
|
30
|
+
|
31
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
32
|
+
pkg.gem_spec = spec
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Install Foo as a gem"
|
36
|
+
task :install => [:package] do
|
37
|
+
sh %{sudo gem install pkg/#{NAME}-#{SLICE_VERSION} --no-update-sources --local}
|
38
|
+
end
|
39
|
+
|
40
|
+
namespace :jruby do
|
41
|
+
|
42
|
+
desc "Run :package and install the resulting .gem with jruby"
|
43
|
+
task :install => :package do
|
44
|
+
sh %{#{SUDO} jruby -S gem install pkg/#{NAME}-#{VERSION}.gem --no-rdoc --no-ri}
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module MerbAuth
|
2
|
+
module ControllerMixin
|
3
|
+
protected
|
4
|
+
# Returns true or false if the user is logged in.
|
5
|
+
# Preloads @current_user with the user model if they're logged in.
|
6
|
+
def logged_in?
|
7
|
+
current_user != :false
|
8
|
+
end
|
9
|
+
|
10
|
+
# Accesses the current user from the session. Set it to :false if login fails
|
11
|
+
# so that future calls do not hit the database.
|
12
|
+
def current_user
|
13
|
+
@current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie || :false)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Store the given user in the session.
|
17
|
+
def current_user=(new_user)
|
18
|
+
session[:user] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.id
|
19
|
+
@current_user = new_user
|
20
|
+
end
|
21
|
+
|
22
|
+
# Check if the user is authorized
|
23
|
+
#
|
24
|
+
# Override this method in your controllers if you want to restrict access
|
25
|
+
# to only a few actions or if you want to check if the user
|
26
|
+
# has the correct rights.
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# # only allow nonbobs
|
31
|
+
# def authorized?
|
32
|
+
# current_user.username != "bob"
|
33
|
+
# end
|
34
|
+
def authorized?
|
35
|
+
logged_in?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Filter method to enforce a login requirement.
|
39
|
+
#
|
40
|
+
# To require logins for all actions, use this in your controllers:
|
41
|
+
#
|
42
|
+
# before_filter :login_required
|
43
|
+
#
|
44
|
+
# To require logins for specific actions, use this in your controllers:
|
45
|
+
#
|
46
|
+
# before_filter :login_required, :only => [ :edit, :update ]
|
47
|
+
#
|
48
|
+
# To skip this in a subclassed controller:
|
49
|
+
#
|
50
|
+
# skip_before_filter :login_required
|
51
|
+
#
|
52
|
+
def login_required
|
53
|
+
authorized? || throw(:halt, :access_denied)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Redirect as appropriate when an access request fails.
|
57
|
+
#
|
58
|
+
# The default HTML action is to redirect to the login screen.
|
59
|
+
#
|
60
|
+
# The default XML action is to render the text Couldn't authenticate you.
|
61
|
+
# To provide this response wrapped in XML, make sure to specify an
|
62
|
+
# XML layout, such as /app/views/layouts/application.xml.builder.
|
63
|
+
#
|
64
|
+
# Override this method in your controllers if you want to have special
|
65
|
+
# behavior in case the user is not authorized
|
66
|
+
# to access the requested action. For example, a popup window might
|
67
|
+
# simply close itself.
|
68
|
+
def access_denied
|
69
|
+
case content_type
|
70
|
+
when :html
|
71
|
+
store_location
|
72
|
+
redirect url(:login)
|
73
|
+
when :xml
|
74
|
+
headers["Status"] = "Unauthorized"
|
75
|
+
headers["WWW-Authenticate"] = %(Basic realm="Web Password")
|
76
|
+
self.status = 401
|
77
|
+
render "Couldn't authenticate you"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Store the URI of the current request in the session.
|
82
|
+
#
|
83
|
+
# We can return to this location by calling #redirect_back_or_default.
|
84
|
+
def store_location
|
85
|
+
session[:return_to] = request.uri
|
86
|
+
end
|
87
|
+
|
88
|
+
# Redirect to the URI stored by the most recent store_location call or
|
89
|
+
# to the passed default.
|
90
|
+
def redirect_back_or_default(default)
|
91
|
+
loc = session[:return_to] || default
|
92
|
+
session[:return_to] = nil
|
93
|
+
redirect loc
|
94
|
+
end
|
95
|
+
|
96
|
+
# Inclusion hook to make #current_user and #logged_in?
|
97
|
+
# available as ActionView helper methods.
|
98
|
+
# def self.included(base)
|
99
|
+
# base.send :helper_method, :current_user, :logged_in?
|
100
|
+
# end
|
101
|
+
|
102
|
+
# Called from #current_user. First attempt to login by the user id stored in the session.
|
103
|
+
def login_from_session
|
104
|
+
self.current_user = find_user_by_id(session[:user]) if session[:user]
|
105
|
+
end
|
106
|
+
|
107
|
+
# Called from #current_user. Now, attempt to login by basic authentication information.
|
108
|
+
def login_from_basic_auth
|
109
|
+
username, passwd = get_auth_data
|
110
|
+
self.current_user = verify_login(username, passwd) if username && passwd
|
111
|
+
end
|
112
|
+
|
113
|
+
# Called from #current_user. Finaly, attempt to login by an expiring token in the cookie.
|
114
|
+
def login_from_cookie
|
115
|
+
user = cookies[:auth_token] && find_user_by_remember_token(cookies[:auth_token])
|
116
|
+
if user && user.remember_token?
|
117
|
+
user.remember_me
|
118
|
+
cookies[:auth_token] = { :value => user.remember_token, :expires => Time.parse(user.remember_token_expires_at.strftime) }
|
119
|
+
self.current_user = user
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def reset_session
|
124
|
+
session.data.each{|k,v| session.data.delete(k)}
|
125
|
+
end
|
126
|
+
|
127
|
+
protected
|
128
|
+
def verify_login(username, password)
|
129
|
+
MerbAuth::User.authenticate(username, password)
|
130
|
+
end
|
131
|
+
|
132
|
+
def find_user_by_id(user_id)
|
133
|
+
MerbAuth::User.find_by_id(user_id)
|
134
|
+
end
|
135
|
+
|
136
|
+
def find_user_by_remember_token(token)
|
137
|
+
MerbAuth::User.find_by_remember_token(token)
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
@@http_auth_headers = %w(Authorization HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION)
|
142
|
+
|
143
|
+
# gets BASIC auth info
|
144
|
+
def get_auth_data
|
145
|
+
auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
|
146
|
+
auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
|
147
|
+
return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class MerbAuth::Users < MerbAuth::Application
|
2
|
+
provides :html
|
3
|
+
|
4
|
+
skip_before :login_required
|
5
|
+
|
6
|
+
def login
|
7
|
+
if request.post?
|
8
|
+
self.current_user = verify_login(params[:username], params[:password])
|
9
|
+
if logged_in?
|
10
|
+
if params[:remember_me] == "1"
|
11
|
+
self.current_user.remember_me
|
12
|
+
cookies[:auth_token] = {
|
13
|
+
:value => self.current_user.remember_token,
|
14
|
+
:expires => Time.parse(current_user.remember_token_expires_at.strftime)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
return redirect_back_or_default('/')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
render
|
22
|
+
end
|
23
|
+
|
24
|
+
def logout
|
25
|
+
self.current_user.forget_me if logged_in?
|
26
|
+
cookies.delete :auth_token
|
27
|
+
reset_session
|
28
|
+
redirect_back_or_default('/')
|
29
|
+
end
|
30
|
+
|
31
|
+
def signup
|
32
|
+
cookies.delete :auth_token
|
33
|
+
@user = MerbAuth::User.new(params['merb_auth::user'] || {})
|
34
|
+
|
35
|
+
if request.post? && @user.save
|
36
|
+
return redirect_back_or_default('/')
|
37
|
+
end
|
38
|
+
|
39
|
+
render
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Merb
|
2
|
+
module MerbAuth
|
3
|
+
module ApplicationHelper
|
4
|
+
|
5
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
6
|
+
#
|
7
|
+
# @return <String>
|
8
|
+
# A path relative to the public directory, with added segments.
|
9
|
+
def image_path(*segments)
|
10
|
+
public_path_for(:image, *segments)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
14
|
+
#
|
15
|
+
# @return <String>
|
16
|
+
# A path relative to the public directory, with added segments.
|
17
|
+
def javascript_path(*segments)
|
18
|
+
public_path_for(:javascript, *segments)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
22
|
+
#
|
23
|
+
# @return <String>
|
24
|
+
# A path relative to the public directory, with added segments.
|
25
|
+
def stylesheet_path(*segments)
|
26
|
+
public_path_for(:stylesheet, *segments)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Construct a path relative to the public directory
|
30
|
+
#
|
31
|
+
# @param <Symbol> The type of component.
|
32
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
33
|
+
#
|
34
|
+
# @return <String>
|
35
|
+
# A path relative to the public directory, with added segments.
|
36
|
+
def public_path_for(type, *segments)
|
37
|
+
File.join(::MerbAuth.public_dir_for(type), *segments)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Construct an app-level path.
|
41
|
+
#
|
42
|
+
# @param <Symbol> The type of component.
|
43
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
44
|
+
#
|
45
|
+
# @return <String>
|
46
|
+
# A path within the host application, with added segments.
|
47
|
+
def app_path_for(type, *segments)
|
48
|
+
File.join(::MerbAuth.app_dir_for(type), *segments)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Construct a slice-level path.
|
52
|
+
#
|
53
|
+
# @param <Symbol> The type of component.
|
54
|
+
# @param *segments<Array[#to_s]> Path segments to append.
|
55
|
+
#
|
56
|
+
# @return <String>
|
57
|
+
# A path within the slice source (Gem), with added segments.
|
58
|
+
def slice_path_for(type, *segments)
|
59
|
+
File.join(::MerbAuth.dir_for(type), *segments)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
|
3
|
+
<head>
|
4
|
+
<title>Auth::Slice Sample Layout</title>
|
5
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
6
|
+
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.1/build/reset-fonts-grids/reset-fonts-grids.css">
|
7
|
+
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.1/build/base/base-min.css">
|
8
|
+
<link href="<%= public_path_for :stylesheet, 'master.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all"/>
|
9
|
+
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
|
10
|
+
<script type="text/javascript">
|
11
|
+
(function($) {
|
12
|
+
$(document).ready(function() {
|
13
|
+
$(":text:visible:enabled:first").focus();
|
14
|
+
});
|
15
|
+
})(jQuery);
|
16
|
+
</script>
|
17
|
+
</head>
|
18
|
+
<body>
|
19
|
+
<div id="container">
|
20
|
+
<div id="header-container">
|
21
|
+
<span style="float:right;margin-right:50px">
|
22
|
+
<% if logged_in? %>
|
23
|
+
Welcome <%= current_user.name %>
|
24
|
+
| <a href="<%= url(:logout) %>">Sign out</a>
|
25
|
+
<% else %>
|
26
|
+
<a href="<%= url(:signup) %>">Join now</a>
|
27
|
+
| <a href="<%= url(:login) %>">Sign in</a>
|
28
|
+
<% end %>
|
29
|
+
</span>
|
30
|
+
<h1>Sample MerbAuth App</h1>
|
31
|
+
<hr />
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div id="main-container">
|
35
|
+
<%= catch_content :for_layout %>
|
36
|
+
</div>
|
37
|
+
|
38
|
+
<hr />
|
39
|
+
|
40
|
+
<div id="footer-container">
|
41
|
+
<div class="left"><%= __FILE__ %></div>
|
42
|
+
<div class="right">© 2008 PragmaQuest Inc. All rights reserved.</div>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</body>
|
46
|
+
</html>
|
47
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
<div>
|
3
|
+
<div class="form_description">
|
4
|
+
<h2>Member sign in</h2>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<form action="<%= url(:login) %>" method="post">
|
8
|
+
<% if request.post? -%>
|
9
|
+
<div class="error">
|
10
|
+
<h2>Incorrect username and password</h2>
|
11
|
+
</div>
|
12
|
+
<% end -%>
|
13
|
+
|
14
|
+
<p><label for="username">Username or e-mail</label>
|
15
|
+
<input type="text" name="username" value="<%= params[:username] %>"/></p>
|
16
|
+
|
17
|
+
<p><label for="password">Password</label>
|
18
|
+
<input type="password" name="password"/></p>
|
19
|
+
|
20
|
+
<p><input type="checkbox" name='remember_me' id='remember_me' value="1"/>
|
21
|
+
<label id='remember_me_lb' for="remember_me">Keep me signed in</label></p>
|
22
|
+
|
23
|
+
<p><input type="submit" value="Sign in"/> <span style="margin-left:20px"><a href="#">Forgot password?</a></span></p>
|
24
|
+
</form>
|
25
|
+
|
26
|
+
<form action="#" method="post" style="display:none">
|
27
|
+
<p><label for="openid_url">OpenID</label>
|
28
|
+
<input type="text" value="" size="40" id="openid_url" name="openid_url"/> <small>(e.g. http://username.myopenid.com)</small>
|
29
|
+
</p>
|
30
|
+
</form>
|
31
|
+
</div>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
<div>
|
3
|
+
<div class="form_description">
|
4
|
+
<h2>Sign up here (it's absolutely free)</h2>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<% form_for @user, :action => url(:signup) do %>
|
8
|
+
<%= error_messages_for :user, lambda{|err| "<li>#{err.join('<br/>')}</li>"} %>
|
9
|
+
|
10
|
+
<p>
|
11
|
+
<%= text_control :name, :label => "Name" %>
|
12
|
+
</p>
|
13
|
+
<p>
|
14
|
+
<%= text_control :username, :label => "Username" %>
|
15
|
+
</p>
|
16
|
+
<p>
|
17
|
+
<%= text_control :email, :label => "E-mail" %>
|
18
|
+
</p>
|
19
|
+
<p>
|
20
|
+
<%= password_control :password, :label => "Password" %>
|
21
|
+
</p>
|
22
|
+
<p>
|
23
|
+
<%= password_control :password_confirmation, :label => "Password Confirmation" %>
|
24
|
+
</p>
|
25
|
+
<p>
|
26
|
+
<%= submit_button "Sign up" %> <span style="margin-left:20px">Already a member? <a href="<%= url(:login) %>">Sign in here</a></span>
|
27
|
+
</p>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module MerbAuth
|
2
|
+
module Adapter
|
3
|
+
module Activerecord
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
include MerbAuth::BaseModel
|
7
|
+
extend ClassMethods
|
8
|
+
|
9
|
+
validates_presence_of :name
|
10
|
+
validates_presence_of :username
|
11
|
+
validates_presence_of :email
|
12
|
+
validates_length_of :username, :within => 3..40
|
13
|
+
validates_length_of :password, :within => 4..40, :if => :password_required?
|
14
|
+
validates_presence_of :password_confirmation, :if => :password_required?
|
15
|
+
validates_confirmation_of :password, :if => :password_required?
|
16
|
+
validates_uniqueness_of :username, :case_sensitive => false, :if => lambda { |u| !u.username.blank? }
|
17
|
+
validates_uniqueness_of :email, :case_sensitive => false, :if => lambda { |u| !u.email.blank? }
|
18
|
+
|
19
|
+
before_save :encrypt_password
|
20
|
+
|
21
|
+
def username=(value)
|
22
|
+
self[:username] = value.downcase if value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def drop_db_table
|
29
|
+
self.connection.drop_table("users")
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_db_table
|
33
|
+
self.connection.create_table("users") do |t|
|
34
|
+
t.string :name, :limit => 40, :null => false
|
35
|
+
t.string :username, :limit => 40, :null => false
|
36
|
+
t.string :email, :null => false
|
37
|
+
t.string :crypted_password, :limit => 40
|
38
|
+
t.string :salt, :limit => 40, :null => false
|
39
|
+
t.string :remember_token
|
40
|
+
t.date :remember_token_expires_at
|
41
|
+
t.timestamps
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
MerbAuth.send(:remove_const, :User) if defined? MerbAuth::User
|
50
|
+
class MerbAuth::User < ActiveRecord::Base
|
51
|
+
include MerbAuth::Adapter::Activerecord
|
52
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-validations'
|
3
|
+
require 'dm-timestamps'
|
4
|
+
require 'dm-aggregates'
|
5
|
+
|
6
|
+
module MerbAuth
|
7
|
+
module Adapter
|
8
|
+
module Datamapper
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
include ::DataMapper::Resource
|
12
|
+
include MerbAuth::BaseModel
|
13
|
+
extend ClassMethods
|
14
|
+
|
15
|
+
storage_names[:default] = 'users'
|
16
|
+
|
17
|
+
property :id, Integer, :serial => true
|
18
|
+
property :name, String, :length => 3..40, :nullable => false
|
19
|
+
property :username, String, :length => 3..40, :nullable => false
|
20
|
+
property :email, String, :format => :email_address, :nullable => false
|
21
|
+
property :crypted_password, String, :length => 40
|
22
|
+
property :salt, String, :length => 40
|
23
|
+
property :remember_token_expires_at, Date
|
24
|
+
property :remember_token, String
|
25
|
+
property :created_at, DateTime
|
26
|
+
property :updated_at, DateTime
|
27
|
+
|
28
|
+
validates_is_unique :username, :email
|
29
|
+
validates_length :password, :in => 4..40, :if => :password_required?
|
30
|
+
validates_is_confirmed :password, :groups => :create
|
31
|
+
|
32
|
+
before :save, :encrypt_password
|
33
|
+
|
34
|
+
def username=(value)
|
35
|
+
attribute_set(:username, value.downcase) if value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module ClassMethods
|
41
|
+
def create_db_table
|
42
|
+
self.auto_migrate!
|
43
|
+
end
|
44
|
+
|
45
|
+
def drop_db_table
|
46
|
+
self.repository do |r|
|
47
|
+
r.adapter.destroy_model_storage(r, self)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_by_id(id)
|
52
|
+
MerbAuth::User.first(:id => id)
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_by_remember_token(rt)
|
56
|
+
MerbAuth::User.first(:remember_token => rt)
|
57
|
+
end
|
58
|
+
|
59
|
+
def find_by_username(username)
|
60
|
+
if MerbAuth::User.properties[:activated_at]
|
61
|
+
MerbAuth::User.first(:username => username, :activated_at.not => nil)
|
62
|
+
else
|
63
|
+
MerbAuth::User.first(:username => username)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_by_activiation_code(activation_code)
|
68
|
+
MerbAuth::User.first(:activation_code => activation_code)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
MerbAuth.send(:remove_const, :User) if defined? MerbAuth::User
|
76
|
+
class MerbAuth::User
|
77
|
+
include MerbAuth::Adapter::Datamapper
|
78
|
+
end
|