dancroak-clearance-admin 0.0.1

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.
@@ -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
+