dancroak-clearance 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile ADDED
@@ -0,0 +1,78 @@
1
+ h1. Clearance
2
+
3
+ Simple, complete Rails authentication.
4
+
5
+ "We have clearance, Clarence.":http://www.youtube.com/v/mNRXJEE3Nz8
6
+
7
+ h2. Features
8
+
9
+ * email & password
10
+ * modules, not a generator
11
+ * gem, not a plugin
12
+ * should & factory_girl tests included
13
+
14
+ h2. Schema
15
+
16
+ Change your User model so it has these attributes.
17
+
18
+ change_table(:users) do |t|
19
+ t.column :email, :string
20
+ t.column :crypted_password, :string, :limit => 40
21
+ t.column :salt, :string, :limit => 40
22
+ t.column :remember_token, :string
23
+ t.column :remember_token_expires_at, :datetime
24
+ end
25
+
26
+ add_index :users, [:email, :password]
27
+
28
+ h2. Model
29
+
30
+ In app/models/user.rb:
31
+
32
+ include Clearance::Model
33
+
34
+ h2. Controllers
35
+
36
+ In app/controllers/application_controller.rb:
37
+
38
+ include Clearance::ApplicationController
39
+
40
+ In app/controllers/sessions_controller.rb:
41
+
42
+ include Clearance::SessionsController
43
+
44
+ In app/controllers/users_controller.rb:
45
+
46
+ include Clearance::UsersController
47
+
48
+ h2. Routes
49
+
50
+ map.login '/login', :controller => 'sessions', :action => 'new'
51
+ map.logout '/logout', :controller => 'sessions', :action => 'destroy'
52
+ map.resource :session
53
+
54
+ h2. Tests
55
+
56
+ The tests use Shoulda and Factory Girl.
57
+
58
+ In test/test_helper.rb:
59
+
60
+ include Clearance::TestHelper
61
+
62
+ In test/unit/user_test.rb:
63
+
64
+ include Clearance::UnitTest
65
+
66
+ In test/functional/sessions_controller_test.rb:
67
+
68
+ include Clearance::SessionsControllerTest
69
+
70
+ In test/functional/users_controller_test.rb:
71
+
72
+ include Clearance::UsersControllerTest
73
+
74
+ h2. Authors
75
+
76
+ * thoughtbot, inc.
77
+ * Dan Croak
78
+ * Josh Nichols
data/clearance.gemspec ADDED
@@ -0,0 +1,11 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "clearance"
3
+ s.version = "0.1"
4
+ s.date = "2008-09-06"
5
+ s.summary = "Simple, complete Rails authentication."
6
+ s.email = "dcroak@thoughtbot.com"
7
+ s.homepage = "http://github.com/dancroak/clearance"
8
+ s.description = "Simple, complete Rails authentication scheme."
9
+ s.authors = ["thoughtbot, inc.", "Dan Croak", "Josh Nichols"]
10
+ s.files = ["README.textile", "clearance.gemspec", "lib/clearance.rb", "lib/clearance/application_controller.rb", "lib/clearance/model.rb", "lib/clearance/sessions_controller.rb", "lib/clearance/sessions_controller_test.rb", "lib/clearance/test_helper.rb", "lib/clearance/unit_test.rb", "lib/clearance/users_controller.rb", "lib/clearance/users_controller_test.rb"]
11
+ end
data/lib/clearance.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'clearance/application_controller'
2
+ require 'clearance/sessions_controller'
3
+ require 'clearance/users_controller'
4
+ require 'clearance/model'
5
+ require 'clearance/test_helper'
6
+ require 'clearance/sessions_controller_test'
7
+ require 'clearance/users_controller_test'
8
+ require 'clearance/unit_test'
@@ -0,0 +1,62 @@
1
+ module Clearance
2
+ module ApplicationController
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ attr_accessor :current_user
7
+ helper_method :current_user
8
+
9
+ include InstanceMethods
10
+
11
+ protected
12
+ include ProtectedInstanceMethods
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+ def current_user
18
+ @current_user ||= (user_from_session || user_from_cookie)
19
+ end
20
+ end
21
+
22
+ module ProtectedInstanceMethods
23
+ def authenticate
24
+ deny_access if current_user.nil?
25
+ end
26
+
27
+ def user_from_session
28
+ User.find_by_id(session[:user_id])
29
+ end
30
+
31
+ def user_from_cookie
32
+ user = User.find_by_remember_token(cookies[:auth_token]) if cookies[:auth_token]
33
+ user && user.remember_token? ? user : nil
34
+ end
35
+
36
+ def login(user)
37
+ create_session_for user
38
+ @current_user = user
39
+ end
40
+
41
+ def create_session_for(user)
42
+ session[:user_id] = user.id if user
43
+ end
44
+
45
+ def redirect_back_or(default)
46
+ session[:return_to] ? redirect_to(session[:return_to]) : redirect_to(default)
47
+ session[:return_to] = nil
48
+ end
49
+
50
+ def store_location
51
+ session[:return_to] = request.request_uri
52
+ end
53
+
54
+ def deny_access(flash_message = nil, opts = {})
55
+ opts[:redirect] ||= login_url
56
+ store_location
57
+ flash[:error] = flash_message if flash_message
58
+ redirect_to opts[:redirect]
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,84 @@
1
+ module Clearance
2
+ module Model
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+
7
+ attr_accessible :email, :password, :password_confirmation
8
+ attr_accessor :password, :password_confirmation
9
+
10
+ validates_presence_of :email
11
+ validates_presence_of :password, :if => :password_required?
12
+ validates_length_of :password, :within => 3..40, :if => :password_required?
13
+ validates_confirmation_of :password, :if => :password_required?
14
+ validates_uniqueness_of :email
15
+
16
+ before_save :initialize_salt, :encrypt_password
17
+
18
+ extend ClassMethods
19
+ include InstanceMethods
20
+
21
+ protected
22
+
23
+ include ProtectedInstanceMethods
24
+
25
+ end
26
+ end
27
+
28
+ module ClassMethods
29
+ def authenticate(email, password)
30
+ user = find_by_email(email) # need to get the salt
31
+ user && user.authenticated?(password) ? user : nil
32
+ end
33
+
34
+ def authenticate_via_auth_token(token)
35
+ return nil if token.blank?
36
+ find_by_auth_token(token)
37
+ end
38
+ end
39
+
40
+ module InstanceMethods
41
+ def authenticated?(password)
42
+ crypted_password == encrypt(password)
43
+ end
44
+
45
+ def encrypt(password)
46
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
47
+ end
48
+
49
+ def remember_token?
50
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
51
+ end
52
+
53
+ def remember_me!
54
+ remember_me_until 2.weeks.from_now.utc
55
+ end
56
+
57
+ def remember_me_until(time)
58
+ self.update_attribute :remember_token_expires_at, time
59
+ self.update_attribute :remember_token, encrypt("#{email}--#{remember_token_expires_at}")
60
+ end
61
+
62
+ def forget_me!
63
+ self.update_attribute :remember_token_expires_at, nil
64
+ self.update_attribute :remember_token, nil
65
+ end
66
+ end
67
+
68
+ module ProtectedInstanceMethods
69
+ def initialize_salt
70
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
71
+ end
72
+
73
+ def encrypt_password
74
+ return if password.blank?
75
+ self.crypted_password = encrypt(password)
76
+ end
77
+
78
+ def password_required?
79
+ crypted_password.blank? || !password.blank?
80
+ end
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,66 @@
1
+ module Clearance
2
+ module SessionsController
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ skip_before_filter :authenticate
7
+ protect_from_forgery :except => :create
8
+ filter_parameter_logging :password
9
+
10
+ include InstanceMethods
11
+
12
+ protected
13
+ include ProtectedInstanceMethods
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def create
19
+ remember_me = params[:session][:remember_me] if params[:session]
20
+ login_via_password(params[:session][:email], params[:session][:password], remember_me)
21
+ end
22
+
23
+ def destroy
24
+ forget current_user
25
+ reset_session
26
+ flash[:notice] = 'You have been logged out.'
27
+ redirect_to login_url
28
+ end
29
+ end
30
+
31
+ module ProtectedInstanceMethods
32
+ def login_via_password(email, password, remember_me)
33
+ user = User.authenticate(email, password)
34
+ if login(user)
35
+ create_session_for(user)
36
+ remember(user) if remember_me == '1'
37
+ login_successful
38
+ else
39
+ login_failure
40
+ end
41
+ end
42
+
43
+ def login_successful
44
+ flash[:notice] = 'Logged in successfully'
45
+ redirect_back_or root_url
46
+ end
47
+
48
+ def login_failure(message = "Bad email or password.")
49
+ flash.now[:notice] = message
50
+ render :action => :new
51
+ end
52
+
53
+ def remember(user)
54
+ user.remember_me!
55
+ cookies[:auth_token] = { :value => user.remember_token,
56
+ :expires => user.remember_token_expires_at }
57
+ end
58
+
59
+ def forget(user)
60
+ user.forget_me! if user
61
+ cookies.delete :auth_token
62
+ end
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,84 @@
1
+ module Clearance
2
+ module SessionsControllerTest
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ context "Given a user" do
7
+ setup do
8
+ @user = Factory(:user)
9
+ end
10
+
11
+ should_filter :password
12
+
13
+ # context "on GET to /sessions/new" do
14
+ # setup { get :new }
15
+ #
16
+ # should_respond_with :success
17
+ # should_render_template :new
18
+ # should_not_set_the_flash
19
+ # should "render a login form" do
20
+ # assert_select "form[action=/session]" do
21
+ # assert_select "input[type=text][name=?]", "session[email]"
22
+ # assert_select "input[type=password][name=?]", "session[password]"
23
+ # assert_select "input[type=checkbox][name=?]", "session[remember_me]"
24
+ # end
25
+ # end
26
+ # end
27
+
28
+ context "a POST to #create with good credentials" do
29
+ setup do
30
+ post :create, :session => { :email => @user.email, :password => @user.password }
31
+ end
32
+
33
+ should_set_the_flash_to /success/i
34
+ should_redirect_to 'root_url'
35
+ end
36
+
37
+ context "a POST to #create with bad credentials" do
38
+ setup do
39
+ post :create, :session => { :email => @user.email, :password => "bad value" }
40
+ end
41
+
42
+ should_set_the_flash_to /bad/i
43
+ should_render_template :new
44
+ end
45
+
46
+ end
47
+
48
+ context "While logged out" do
49
+ setup { logout }
50
+
51
+ context "logging out again" do
52
+ setup { delete :destroy }
53
+ should_redirect_to "login_url"
54
+ end
55
+ end
56
+
57
+ logged_in_user_context do
58
+ context "a DELETE to #destroy without a cookie" do
59
+ setup { delete :destroy }
60
+
61
+ should_set_the_flash_to(/logged out/i)
62
+ should_redirect_to "login_url"
63
+ end
64
+
65
+ context 'a DELETE to #destroy with a cookie' do
66
+ setup do
67
+ cookies['auth_token'] = CGI::Cookie.new 'token', 'value'
68
+ delete :destroy
69
+ end
70
+
71
+ should 'delete the cookie' do
72
+ assert cookies['auth_token'].empty?
73
+ end
74
+
75
+ should 'delete the remember me token in users table' do
76
+ assert_nil @current_user.reload.remember_token
77
+ assert_nil @current_user.reload.remember_token_expires_at
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,75 @@
1
+ module Clearance
2
+ module TestHelper
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ include InstanceMethods
7
+ extend ClassMethods
8
+ end
9
+ end
10
+
11
+ module InstanceMethods
12
+ def login_as(user = nil)
13
+ user ||= Factory(:user)
14
+ @request.session[:user_id] = user.id
15
+ return user
16
+ end
17
+
18
+ def logout
19
+ @request.session[:user_id] = nil
20
+ end
21
+ end
22
+
23
+ module ClassMethods
24
+ def should_deny_access_on(command, opts = {})
25
+ opts[:redirect] ||= "login_url"
26
+
27
+ context "on #{command}" do
28
+ setup { eval command }
29
+ should_redirect_to opts[:redirect]
30
+ if opts[:flash]
31
+ should_set_the_flash_to opts[:flash]
32
+ else
33
+ should_not_set_the_flash
34
+ end
35
+ end
36
+ end
37
+
38
+ def should_filter(*keys)
39
+ keys.each do |key|
40
+ should "filter #{key}" do
41
+ assert @controller.respond_to?(:filter_parameters),
42
+ "The key #{key} is not filtered"
43
+ filtered = @controller.send(:filter_parameters, {key.to_s => key.to_s})
44
+ assert_equal '[FILTERED]', filtered[key.to_s],
45
+ "The key #{key} is not filtered"
46
+ end
47
+ end
48
+ end
49
+
50
+ def should_have_user_form
51
+ should "have the user form" do
52
+ assert_select "form" do
53
+ %w(name email openid_url).each do |field|
54
+ assert_select "input[type=text][name=?]", "user[#{field}]"
55
+ end
56
+ %w(password password_confirmation).each do |field|
57
+ assert_select "input[type=password][name=?]", "user[#{field}]"
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def logged_in_user_context(user_name = nil, &blk)
64
+ context "When logged in as a user" do
65
+ setup do
66
+ user = user_name ? instance_variable_get("@#{user_name}") : Factory(:user)
67
+ assert @current_user = login_as(user)
68
+ end
69
+ merge_block(&blk)
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,186 @@
1
+ module Clearance
2
+ module UnitTest
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ should_require_attributes :email, :password
7
+
8
+ should "require password validation on create" do
9
+ user = Factory.build(:user, :password => 'blah', :password_confirmation => 'boogidy')
10
+ assert !user.save
11
+ assert_match(/confirmation/i, user.errors.on(:password))
12
+ end
13
+
14
+ should "create a crypted_password on save" do
15
+ assert_not_nil Factory(:user, :crypted_password => nil).crypted_password
16
+ end
17
+
18
+ context 'updating a password' do
19
+ setup do
20
+ @user = Factory(:user)
21
+ assert_not_nil @user.crypted_password
22
+ @crypt = @user.crypted_password
23
+ assert_not_nil @user.salt
24
+ @salt = @user.salt
25
+ @user.password = 'a_new_password'
26
+ @user.password_confirmation = 'a_new_password'
27
+ assert @user.save
28
+ end
29
+
30
+ should 'update a crypted_password' do
31
+ @user.reload
32
+ assert @user.crypted_password != @crypt
33
+ end
34
+ end
35
+
36
+ context 'A user' do
37
+ setup do
38
+ @password = 'sekrit'
39
+ @salt = 'salt'
40
+ User.any_instance.stubs(:initialize_salt)
41
+ @user = Factory(:user, :password => @password, :salt => @salt)
42
+ end
43
+
44
+ should "require password validation on update" do
45
+ @user.update_attributes(:password => "blah", :password_confirmation => "boogidy")
46
+ assert !@user.save
47
+ assert_match(/confirmation/i, @user.errors.on(:password))
48
+ end
49
+
50
+ should_require_unique_attributes :email
51
+
52
+ context 'authenticating a user' do
53
+ context 'with good credentials' do
54
+ setup do
55
+ @result = User.authenticate @user.email, 'sekrit'
56
+ end
57
+
58
+ should 'return true' do
59
+ assert @result
60
+ end
61
+ end
62
+
63
+ context 'with bad credentials' do
64
+ setup do
65
+ @result = User.authenticate @user.email, 'horribly_wrong_password'
66
+ end
67
+
68
+ should 'return true' do
69
+ assert !@result
70
+ end
71
+ end
72
+ end
73
+
74
+ context 'authenticated?' do
75
+ context 'with good credentials' do
76
+ setup do
77
+ @result = @user.authenticated? @password
78
+ end
79
+
80
+ should 'return true' do
81
+ assert @result
82
+ end
83
+ end
84
+
85
+ context 'with bad credentials' do
86
+ setup do
87
+ @result = @user.authenticated? 'horribly_wrong_password'
88
+ end
89
+
90
+ should 'return true' do
91
+ assert !@result
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'encrypt' do
97
+ setup do
98
+ @crypted = @user.encrypt(@password)
99
+ @expected = Digest::SHA1.hexdigest("--#{@salt}--#{@password}--")
100
+ end
101
+
102
+ should 'create a Hash using SHA1 encryption' do
103
+ assert_equal @expected, @crypted
104
+ assert_not_equal @password, @crypted
105
+ end
106
+ end
107
+
108
+ context 'remember_me!' do
109
+ setup do
110
+ assert_nil @user.remember_token
111
+ assert_nil @user.remember_token_expires_at
112
+ @user.remember_me!
113
+ end
114
+
115
+ should 'set the remember token and expiration date' do
116
+ assert_not_nil @user.remember_token
117
+ assert_not_nil @user.remember_token_expires_at
118
+ end
119
+
120
+ should 'remember_token?' do
121
+ assert @user.remember_token?
122
+ end
123
+
124
+ context 'forget_me!' do
125
+ setup do
126
+ @user.forget_me!
127
+ end
128
+
129
+ should 'unset the remember token and expiration date' do
130
+ assert_nil @user.remember_token
131
+ assert_nil @user.remember_token_expires_at
132
+ end
133
+
134
+ should 'not remember_token?' do
135
+ assert ! @user.remember_token?
136
+ end
137
+ end
138
+ end
139
+
140
+ context 'remember_token?' do
141
+ context 'when token expires in the future' do
142
+ setup do
143
+ @user.update_attribute :remember_token_expires_at, 2.weeks.from_now.utc
144
+ end
145
+
146
+ should 'be true' do
147
+ assert @user.remember_token?
148
+ end
149
+ end
150
+
151
+ context 'when token expired' do
152
+ setup do
153
+ @user.update_attribute :remember_token_expires_at, 2.weeks.ago.utc
154
+ end
155
+
156
+ should 'be false' do
157
+ assert ! @user.remember_token?
158
+ end
159
+ end
160
+ end
161
+
162
+ context "User.authenticate with a valid email and password" do
163
+ setup do
164
+ @found_user = User.authenticate @user.email, @user.password
165
+ end
166
+
167
+ should "find that user" do
168
+ assert_equal @user, @found_user
169
+ end
170
+ end
171
+
172
+ context "When sent authenticate with an invalid email and password" do
173
+ setup do
174
+ @found_user = User.authenticate "not", "valid"
175
+ end
176
+
177
+ should "find nothing" do
178
+ assert_nil @found_user
179
+ end
180
+ end
181
+ end
182
+
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,22 @@
1
+ module Clearance
2
+ module UsersController
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ before_filter :authenticate
7
+ before_filter :ensure_user_is_accessing_self, :only => [:edit, :update, :show]
8
+
9
+ filter_parameter_logging :password
10
+ private
11
+ include PrivateInstanceMethods
12
+ end
13
+ end
14
+
15
+ module PrivateInstanceMethods
16
+ def ensure_user_is_accessing_self
17
+ deny_access 'You cannot edit that user.' unless current_user.id.to_i == params[:id].to_i
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,72 @@
1
+ module Clearance
2
+ module UsersControllerTest
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ logged_in_user_context do
7
+
8
+ should_deny_access_on "get :index"
9
+ should_deny_access_on "get :new"
10
+ should_deny_access_on "post :create, :user => {}"
11
+ should_filter :password
12
+
13
+ context "dealing with their own account" do
14
+ context "on GET to /users/:id/show" do
15
+ setup { get :show, :id => @user.to_param }
16
+ should_redirect_to "edit_user_url(@user)"
17
+ should_not_set_the_flash
18
+ end
19
+
20
+ should_deny_access_on "delete :destroy, :id => @user.to_param"
21
+
22
+ context "on GET to /users/:id/edit" do
23
+ setup { get :edit, :id => @user.to_param }
24
+
25
+ should_respond_with :success
26
+ should_render_template :edit
27
+ should_not_set_the_flash
28
+ should_assign_to :user
29
+ should_have_user_form
30
+ end
31
+
32
+ context "on PUT to /users/:id" do
33
+ setup do
34
+ put :update,
35
+ :id => @user.to_param,
36
+ :user => {:email => "none@example.com"}
37
+ end
38
+ should_set_the_flash_to /updated/i
39
+ should_redirect_to "root_url"
40
+ should_assign_to :user
41
+ should "update the user's attributes" do
42
+ assert_equal "none@example.com", assigns(:user).email
43
+ end
44
+ end
45
+
46
+ context "on PUT to /users/:id with invalid attributes" do
47
+ setup { put :update, :id => @user.to_param, :user => {:email => ''} }
48
+ should_not_set_the_flash
49
+ should_assign_to :user
50
+ should_render_template 'edit'
51
+ should "display errors" do
52
+ assert_select '#errorExplanation'
53
+ end
54
+ end
55
+ end
56
+
57
+ context "dealing with another user's account" do
58
+ setup do
59
+ @target_user = Factory(:user, :account => @user.account)
60
+ assert_equal @user.account, @target_user.account
61
+ end
62
+
63
+ should_deny_access_on "get :show, :id => @target_user.to_param", :flash => /cannot edit/i
64
+ should_deny_access_on "get :edit, :id => @target_user.to_param", :flash => /cannot edit/i
65
+ should_deny_access_on "put :update, :id => @target_user.to_param, :user => {}", :flash => /cannot edit/i
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ end
72
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dancroak-clearance
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - thoughtbot, inc.
8
+ - Dan Croak
9
+ - Josh Nichols
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2008-09-06 00:00:00 -07:00
15
+ default_executable:
16
+ dependencies: []
17
+
18
+ description: Simple, complete Rails authentication scheme.
19
+ email: dcroak@thoughtbot.com
20
+ executables: []
21
+
22
+ extensions: []
23
+
24
+ extra_rdoc_files: []
25
+
26
+ files:
27
+ - README.textile
28
+ - clearance.gemspec
29
+ - lib/clearance.rb
30
+ - lib/clearance/application_controller.rb
31
+ - lib/clearance/model.rb
32
+ - lib/clearance/sessions_controller.rb
33
+ - lib/clearance/sessions_controller_test.rb
34
+ - lib/clearance/test_helper.rb
35
+ - lib/clearance/unit_test.rb
36
+ - lib/clearance/users_controller.rb
37
+ - lib/clearance/users_controller_test.rb
38
+ has_rdoc: false
39
+ homepage: http://github.com/dancroak/clearance
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.2.0
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: Simple, complete Rails authentication.
64
+ test_files: []
65
+