dancroak-clearance-admin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,100 @@
1
+ h1. Clearance
2
+
3
+ Admin feature for Clearance authentication
4
+
5
+ "We have clearance, Clarence.":http://www.youtube.com/v/mNRXJEE3Nz8
6
+
7
+ h2. Features
8
+
9
+ * does one thing
10
+ * built on top of "clearance":http://github.com/thoughtbot/clearance
11
+ * modules, not a generator
12
+ * gem, not a plugin
13
+ * tests included using "shoulda":http://thoughtbot.com/projects/shoulda & "factory_girl":http://thoughtbot.com/projects/factory_girl
14
+
15
+ h2. Gem installation (Rails 2.1+)
16
+
17
+ Specify the gem dependency in your config/environment.rb file:
18
+
19
+ Rails::Initializer.run do |config|
20
+ # ...
21
+ config.gem 'dancroak-clearance-admin', :lib => 'clearance-admin', :source => "http://gems.github.com"
22
+ end
23
+
24
+ Then:
25
+
26
+ rake gems:install
27
+ rake gems:unpack
28
+
29
+ h2. Tests
30
+
31
+ The tests use "Shoulda":http://thoughtbot.com/projects/shoulda and "Factory Girl":http://thoughtbot.com/projects/factory_girl. Update your User Factory:
32
+
33
+ Factory.define :user do |user|
34
+ user.email { Factory.next :email }
35
+ user.password 'sekrit'
36
+ user.password_confirmation 'sekrit'
37
+ user.admin false
38
+ end
39
+
40
+ In test/unit/user_test.rb:
41
+
42
+ class UserTest < Test::Unit::TestCase
43
+ include Clearance::UserTest
44
+ include Clearance::Admin::UserTest
45
+ end
46
+
47
+ h2. Schema
48
+
49
+ Write a migration:
50
+
51
+ add_column :users, :admin, :boolean
52
+
53
+ h2. User Model
54
+
55
+ In app/models/user.rb:
56
+
57
+ class User < ActiveRecord::Base
58
+ include Clearance::Model
59
+ include Clearance::Admin::Model
60
+ end
61
+
62
+ h2. Controllers
63
+
64
+ In app/controllers/admin/users_controller.rb:
65
+
66
+ class Admin::UsersController < ActionController::Base
67
+ include Clearance::Admin::UsersController
68
+ end
69
+
70
+ h2. Routes
71
+
72
+ map.namespace :admin do |admin|
73
+ admin.resources :users
74
+ end
75
+
76
+ h2. Views
77
+
78
+ In app/views/admin/users/new.html.erb
79
+
80
+ <% form_for :session, :url => session_path do |f| %>
81
+ <div class="group">
82
+ <%= f.label :email %>
83
+ <%= f.text_field :email %>
84
+ </div>
85
+ <div class="group">
86
+ <%= f.label :password %>
87
+ <%= f.password_field :password %>
88
+ </div>
89
+ <div class="checkbox">
90
+ <%= f.check_box :remember_me %>
91
+ <%= f.label :remember_me %>
92
+ </div>
93
+ <div class="group buttons">
94
+ <%= f.submit 'Login', :disable_with => 'Please wait...' %>
95
+ </div>
96
+ <% end %>
97
+
98
+ h2. Authors
99
+
100
+ * Dan Croak
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "clearance-admin"
3
+ s.version = "0.0.1"
4
+ s.date = "2008-09-28"
5
+ s.summary = "Simple admin function for Rails authentication."
6
+ s.email = "dcroak@thoughtbot.com"
7
+ s.homepage = "http://github.com/dancroak/clearance-admin"
8
+ s.description = "Simple admin function for Rails authentication."
9
+ s.authors = ["Dan Croak"]
10
+ s.files = ["README.textile",
11
+ "clearance-admin.gemspec",
12
+ "lib/clearance-admin.rb",
13
+ "lib/clearance-admin/app/models/admin/model.rb",
14
+ "lib/clearance-admin/test/units/admin/user_test.rb",
15
+ "lib/clearance-admin/app/controllers/admin/users_controller.rb",
16
+ "lib/clearance-admin/test/functionals/admin/users_controller_test.rb"]
17
+ end
@@ -0,0 +1,4 @@
1
+ require 'clearance-admin/app/controllers/admin/users_controller'
2
+ require 'clearance-admin/app/models/admin/model'
3
+ require 'clearance-admin/test/functionals/admin/users_controller_test'
4
+ require 'clearance-admin/test/units/admin/user_test'
@@ -0,0 +1,83 @@
1
+ module Clearance
2
+ module Admin
3
+ module UsersController
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ before_filter :authenticate, :except => [:new, :create]
8
+ before_filter :redirect_to_root, :only => [:new, :create], :if => :logged_in?
9
+ before_filter :ensure_user_is_accessing_self, :only => [:edit, :update, :show]
10
+
11
+ filter_parameter_logging :password
12
+
13
+ include InstanceMethods
14
+
15
+ private
16
+ include PrivateInstanceMethods
17
+ end
18
+ end
19
+
20
+ module InstanceMethods
21
+ def index
22
+ @users = User.find :all
23
+ end
24
+
25
+ def new
26
+ @user = User.new
27
+ end
28
+
29
+ def show
30
+ @user = User.find params[:id]
31
+ end
32
+
33
+ def create
34
+ @user = User.new params[:user]
35
+ if @user.save
36
+ current_user = @user
37
+ flash[:notice] = "User created and logged in."
38
+ redirect_back_or root_url
39
+ else
40
+ render :action => "new"
41
+ end
42
+ end
43
+
44
+ def edit
45
+ @user = User.find params[:id]
46
+ end
47
+
48
+ def update
49
+ @user = User.find params[:id]
50
+
51
+ if @user.update_attributes params[:user]
52
+ flash[:notice] = "User updated."
53
+ redirect_back_or root_url
54
+ else
55
+ render :action => "edit"
56
+ end
57
+ end
58
+
59
+ def destroy
60
+ @user = User.find params[:id]
61
+ @user.destroy
62
+ redirect_to root_url
63
+ end
64
+ end
65
+
66
+ module PrivateInstanceMethods
67
+ def ensure_user_is_accessing_self
68
+ return if current_user and current_user.respond_to?(:admin?) and current_user.admin?
69
+ deny_access('You cannot edit that user.', :redirect => root_url) unless current_user.id.to_i == params[:id].to_i
70
+ end
71
+
72
+ def url_after_create
73
+ root_url
74
+ end
75
+
76
+ def url_after_update
77
+ root_url
78
+ end
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,80 @@
1
+ module Clearance
2
+ module Admin
3
+ module Model
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+
8
+ attr_accessible :email, :password, :password_confirmation
9
+ attr_accessor :password, :password_confirmation
10
+
11
+ validates_presence_of :email
12
+ validates_presence_of :password, :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
31
+ user && user.authenticated?(password) ? user : nil
32
+ end
33
+ end
34
+
35
+ module InstanceMethods
36
+ def authenticated?(password)
37
+ crypted_password == encrypt(password)
38
+ end
39
+
40
+ def encrypt(password)
41
+ Digest::SHA1.hexdigest "--#{salt}--#{password}--"
42
+ end
43
+
44
+ def remember_token?
45
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
46
+ end
47
+
48
+ def remember_me!
49
+ remember_me_until 2.weeks.from_now.utc
50
+ end
51
+
52
+ def remember_me_until(time)
53
+ self.update_attribute :remember_token_expires_at, time
54
+ self.update_attribute :remember_token, encrypt("#{email}--#{remember_token_expires_at}")
55
+ end
56
+
57
+ def forget_me!
58
+ self.update_attribute :remember_token_expires_at, nil
59
+ self.update_attribute :remember_token, nil
60
+ end
61
+ end
62
+
63
+ module ProtectedInstanceMethods
64
+ def initialize_salt
65
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
66
+ end
67
+
68
+ def encrypt_password
69
+ return if password.blank?
70
+ self.crypted_password = encrypt(password)
71
+ end
72
+
73
+ def password_required?
74
+ crypted_password.blank? || !password.blank?
75
+ end
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,115 @@
1
+ module Clearance
2
+ module Admin
3
+ module UsersControllerTest
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ public_context do
8
+
9
+ context "on GET to /users/new" do
10
+ setup { get :new }
11
+ should_respond_with :success
12
+ should_render_template :new
13
+ should_not_set_the_flash
14
+ should_have_form :action => "users_path",
15
+ :method => :post,
16
+ :fields => { :email => :text,
17
+ :password => :password,
18
+ :password_confirmation => :password }
19
+ end
20
+
21
+ context "on POST to /users" do
22
+ setup do
23
+ post :create, :user => {
24
+ :email => Factory.next(:email),
25
+ :password => 'skerit',
26
+ :password_confirmation => 'skerit'
27
+ }
28
+ end
29
+
30
+ should_set_the_flash_to /created/i
31
+ should_redirect_to "@controller.send(:url_after_create)"
32
+ should_assign_to :user
33
+ should_change 'User.count', :by => 1
34
+ end
35
+
36
+ should_deny_access_on "get :edit, :id => 1", :redirect => "login_url"
37
+ should_deny_access_on "put :update, :id => 1", :redirect => "login_url"
38
+ should_deny_access_on "get :show, :id => 1", :redirect => "login_url"
39
+ should_deny_access_on "delete :destroy, :id => 1", :redirect => "login_url"
40
+
41
+ end
42
+
43
+ logged_in_user_context do
44
+
45
+ should_deny_access_on "get :new"
46
+ should_deny_access_on "post :create, :user => {}"
47
+ should_filter :password
48
+
49
+ context "viewing their account" do
50
+ context "on GET to /users/:id/show" do
51
+ setup { get :show, :id => @user.to_param }
52
+ should_respond_with :success
53
+ should_render_template :show
54
+ should_not_set_the_flash
55
+
56
+ should 'assign to @user' do
57
+ assert_equal @user, assigns(:user)
58
+ end
59
+ end
60
+
61
+ should_deny_access_on "delete :destroy, :id => @user.to_param"
62
+
63
+ context "on GET to /users/:id/edit" do
64
+ setup { get :edit, :id => @user.to_param }
65
+
66
+ should_respond_with :success
67
+ should_render_template :edit
68
+ should_not_set_the_flash
69
+ should_assign_to :user
70
+ should_have_form :action => "user_path(@user)",
71
+ :method => :put,
72
+ :fields => { :email => :text,
73
+ :password => :password,
74
+ :password_confirmation => :password }
75
+ end
76
+
77
+ context "on PUT to /users/:id" do
78
+ setup do
79
+ put :update,
80
+ :id => @user.to_param,
81
+ :user => { :email => "none@example.com" }
82
+ end
83
+ should_set_the_flash_to /updated/i
84
+ should_redirect_to "@controller.send(:url_after_update)"
85
+ should_assign_to :user
86
+ should "update the user's attributes" do
87
+ assert_equal "none@example.com", assigns(:user).email
88
+ end
89
+ end
90
+
91
+ context "on PUT to /users/:id with invalid attributes" do
92
+ setup { put :update, :id => @user.to_param, :user => {:email => ''} }
93
+ should_not_set_the_flash
94
+ should_assign_to :user
95
+ should_render_template 'edit'
96
+ should "display errors" do
97
+ assert_select '#errorExplanation'
98
+ end
99
+ end
100
+ end
101
+
102
+ context "trying to access another user's account" do
103
+ setup { @user = Factory :user }
104
+
105
+ should_deny_access_on "get :show, :id => @user.to_param", :flash => /cannot edit/i
106
+ should_deny_access_on "get :edit, :id => @user.to_param", :flash => /cannot edit/i
107
+ should_deny_access_on "put :update, :id => @user.to_param, :user => {}", :flash => /cannot edit/i
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,186 @@
1
+ module Clearance
2
+ module UserTest
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 = 'mysekrit'
39
+ @salt = 'salt'
40
+ User.any_instance.stubs(:initialize_salt)
41
+ @user = Factory(:user, :password => @password, :password_confirmation => @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, @password
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 false' 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 false' 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
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dancroak-clearance-admin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dan Croak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-09-28 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Simple admin function for Rails authentication.
17
+ email: dcroak@thoughtbot.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README.textile
26
+ - clearance-admin.gemspec
27
+ - lib/clearance-admin.rb
28
+ - lib/clearance-admin/app/models/admin/model.rb
29
+ - lib/clearance-admin/test/units/admin/user_test.rb
30
+ - lib/clearance-admin/app/controllers/admin/users_controller.rb
31
+ - lib/clearance-admin/test/functionals/admin/users_controller_test.rb
32
+ has_rdoc: false
33
+ homepage: http://github.com/dancroak/clearance-admin
34
+ post_install_message:
35
+ rdoc_options: []
36
+
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ requirements: []
52
+
53
+ rubyforge_project:
54
+ rubygems_version: 1.2.0
55
+ signing_key:
56
+ specification_version: 2
57
+ summary: Simple admin function for Rails authentication.
58
+ test_files: []
59
+