authcan_easyroller 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/.gitignore +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +217 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/app/controllers/authcan_easyroller_controller.rb +79 -0
- data/app/controllers/user_sessions_controller.rb +35 -0
- data/app/controllers/users_controller.rb +60 -0
- data/app/helpers/authcan_easyroller_helper.rb +36 -0
- data/app/models/ability.rb +41 -0
- data/app/models/user.rb +54 -0
- data/app/models/user_session.rb +3 -0
- data/app/views/user_sessions/new.html.erb +27 -0
- data/app/views/users/_form.html.erb +23 -0
- data/app/views/users/edit.html.erb +13 -0
- data/app/views/users/index.html.erb +35 -0
- data/app/views/users/new.html.erb +11 -0
- data/app/views/users/show.html.erb +39 -0
- data/authcan_easyroller.gemspec +70 -0
- data/config/routes.rb +4 -0
- data/example/ability.rb +41 -0
- data/example/application.html.erb +40 -0
- data/example/main.css +41 -0
- data/lib/authcan_easyroller.rb +20 -0
- data/test/helper.rb +10 -0
- data/test/test_authcan_easyroller.rb +7 -0
- metadata +100 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Topher Fangio
|
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.rdoc
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
== Rails 3 Notes
|
2
|
+
|
3
|
+
I wanted to put this at the top so that nobody misses it. There are currently some bugs due to
|
4
|
+
Rails 3 still being in beta. As I come across these bugs, I will add them below and any workaround
|
5
|
+
if available.
|
6
|
+
|
7
|
+
* Bug #3928[https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/3928] - process_parameter_filter throws an exception on array parameters
|
8
|
+
|
9
|
+
[Status:] Resolved in source control, awaiting next beta release.
|
10
|
+
[Workaround:] The code to enable parameter filtering in `lib/authcan_easyroller.rb` has been commented out until the next beta release.
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
== authcan_easyroller
|
15
|
+
|
16
|
+
This is a basic Rails engine utilizing Authlogic[http://github.com/binarylogic/authlogic],
|
17
|
+
CanCan[http://github.com/ryanb/cancan] and Easy Roles[http://github.com/platform45/easy_roles]
|
18
|
+
for simple Rails 3.x applications that need authentication and authorization.
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
== Basis for Creation
|
23
|
+
|
24
|
+
As a Rails developer, I find myself needing an elegant and easy-to-use solution for authentication
|
25
|
+
and authorization in the small web-apps that I create. They generally aren't linked together since
|
26
|
+
they are separate clients with completely different ideas and business models, but they both need
|
27
|
+
the same underlying code to manage their users.
|
28
|
+
|
29
|
+
I have used Authlogic for quite a while now and have found it to be very useful and a complete
|
30
|
+
solution for authentication. Adding a dash of functionality here and there is fairly easy and the
|
31
|
+
ability to extend it really drew me in (in fact, I helped develop some of the code behind
|
32
|
+
authlogic_ldap[http://github.com/topherfangio/authlogic_ldap] which I never got to finish but
|
33
|
+
still intend to do).
|
34
|
+
|
35
|
+
After watching the screencast[http://railscasts.com/episodes/192-authorization-with-cancan] by
|
36
|
+
Ryan Bates, I decided that CanCan was a great addition to my applications since it was easy to
|
37
|
+
use and cleanly separated the authorization from the model and view logic. The only other piece
|
38
|
+
was an easy way to manage roles soley in code, yet while storing the assignments in the database;
|
39
|
+
and I found this in Easy Roles.
|
40
|
+
|
41
|
+
Putting the three together, I now have a complete (albeit simple) authentication and authorization
|
42
|
+
solution that I can easily plugin into existing or new applications (and update in one spot).
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
== Future Enhancements
|
47
|
+
|
48
|
+
Before we get into the nitty-gritty of how to use it, I wanted to go ahead and mention the
|
49
|
+
planned enhancements so you can get a feel of where this project will go.
|
50
|
+
|
51
|
+
* Authlogic supports many different types of authentication without you needing to change
|
52
|
+
hardly anything, so I plan on modifying the code to allow for all of the possible authentication
|
53
|
+
schemes. Thus, I definitely plan on integrating OpenID, LDAP, Facebook Connect and OAuth. I may
|
54
|
+
or may not implement PAM, but we'll see.
|
55
|
+
* I plan on adding an easy password reset mechanism for users provided that you use an email column.
|
56
|
+
* I also plan to add the configuration option to turn on e-mail verification before users are granted
|
57
|
+
access to the system.
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
== Installation/Setup
|
62
|
+
|
63
|
+
Now that the code is a Rails 3 engine, installation is very simple; just install the gem and it's dependencies!
|
64
|
+
|
65
|
+
gem install rails authlogic cancan easy_roles authcan_easyroller
|
66
|
+
|
67
|
+
Next, create a migration for the users:
|
68
|
+
|
69
|
+
rails generate migration CreateUsers
|
70
|
+
|
71
|
+
Then, copy the following contents into that file making any changes you see fit:
|
72
|
+
|
73
|
+
class CreateUsers < ActiveRecord::Migration
|
74
|
+
def self.up
|
75
|
+
create_table :users do |t|
|
76
|
+
# Necessary Columns - These are required for AuthcanEasyroller to function properly
|
77
|
+
t.string :email, :null => false
|
78
|
+
t.string :crypted_password, :null => false
|
79
|
+
t.string :password_salt, :null => false
|
80
|
+
t.string :persistence_token, :null => false
|
81
|
+
t.string :single_access_token, :null => false
|
82
|
+
t.string :perishable_token, :null => false
|
83
|
+
t.integer :roles_mask, :null => false, :default => 0
|
84
|
+
|
85
|
+
# Magic Columns - You may leave any of the following out if you wish
|
86
|
+
t.integer :login_count, :null => false, :default => 0
|
87
|
+
t.integer :failed_login_count, :null => false, :default => 0
|
88
|
+
t.datetime :last_request_at
|
89
|
+
t.datetime :current_login_at
|
90
|
+
t.datetime :last_login_at
|
91
|
+
t.string :current_login_ip
|
92
|
+
t.string :last_login_ip
|
93
|
+
|
94
|
+
# Timestamp Columns - You should have these on every database table you create
|
95
|
+
t.timestamps
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.down
|
100
|
+
drop_table :users
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
Once saved, migrate your database by running
|
105
|
+
|
106
|
+
rake db:migrate
|
107
|
+
|
108
|
+
Next, copy the following files to their proper locations (feel free to edit them, these are just some basics to
|
109
|
+
help get you started). The <tt>rails.js</tt> file at the bottom of the list is the official Rails jQuery file available
|
110
|
+
at http://github.com/rails/jquery-ujs .
|
111
|
+
|
112
|
+
* ability.rb[http://github.com/topherfangio/authcan_easyroller/raw/master/examples/ability.rb] -> <<APPLICATION>>/app/models/ability.rb
|
113
|
+
* application.html.erb[http://github.com/topherfangio/authcan_easyroller/raw/master/examples/application.html.erb] -> <<APPLICATION>>/app/view/layouts/application.html.erb
|
114
|
+
* main.css[http://github.com/topherfangio/authcan_easyroller/raw/master/examples/main.css] -> <<APPLICATION>/public/stylesheets/main.css
|
115
|
+
* rails.js[http://github.com/rails/jquery-ujs/raw/master/src/rails.js] -> <<APPLICATION>/public/javascripts/rails.js
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
== Starting Your Engine
|
120
|
+
|
121
|
+
Once you have everything setup correctly, simply run the following to start your application, then visit http://localhost:3000
|
122
|
+
|
123
|
+
rails server
|
124
|
+
|
125
|
+
The application realizes that it has no users, and forces you to create one before you can continue
|
126
|
+
to any page. The first user is always created as an administrator and thus has privileges to create
|
127
|
+
new users, give themselves the "developer" role (or any/all roles) and do other adminy things. All
|
128
|
+
users created after this will be given the default role of "user".
|
129
|
+
|
130
|
+
Currently, the available roles are
|
131
|
+
|
132
|
+
1. Developer
|
133
|
+
2. Admin
|
134
|
+
3. Moderator
|
135
|
+
4. User
|
136
|
+
|
137
|
+
I'm working on a way to extend this functionality as a configuration option, or perhaps give it an
|
138
|
+
API so that you can create your own roles or modify the existing ones without having to delve into
|
139
|
+
the gem's code. Check back often to see where that stands if it is something you need.
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
== Usage
|
144
|
+
|
145
|
+
You can find out more by checking each specific project's documentation, but here is the gist:
|
146
|
+
|
147
|
+
A user's abilities are defined in <tt>app/models/ability.rb</tt>. I generally prefer to specify what
|
148
|
+
each role is allowed to do and then give a user all of the roles that they need instead of
|
149
|
+
saying that an admin can do everything that a moderator can do. This tends to keep the ability
|
150
|
+
model cleaner and your views don't change either. In addition, this allows you to assign roles
|
151
|
+
to a user for special circumstances. For instance, if you are writing a help desk app, you may
|
152
|
+
decide that one particular customer is really superb and should also have status update abilities
|
153
|
+
even though he has the customer role.
|
154
|
+
|
155
|
+
You define your abilities in each role's section. For instance, the moderator's role currently looks
|
156
|
+
like so:
|
157
|
+
|
158
|
+
# Moderator role abilities
|
159
|
+
if current_user.is_moderator?
|
160
|
+
end
|
161
|
+
|
162
|
+
If you wanted to let moderators manage users, you would simply call <tt>can</tt>
|
163
|
+
|
164
|
+
# Moderator role abilities
|
165
|
+
if current_user.is_moderator?
|
166
|
+
can :manage, User
|
167
|
+
end
|
168
|
+
|
169
|
+
You can also define your own abilities if they don't tie to a particular object, but you must pass a
|
170
|
+
<tt>nil</tt> object as the second argument to <tt>can?</tt> and you must specify all object types when
|
171
|
+
defining the ability. I'll get in touch with the developer to see if this can be a tad bit more streamlined.
|
172
|
+
|
173
|
+
# ability.rb
|
174
|
+
|
175
|
+
# Moderator role abilities
|
176
|
+
if current_user.is_moderator?
|
177
|
+
can :visit_woot_all_day, :all
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
# application.html.erb
|
182
|
+
|
183
|
+
<% if can? :visit_woot_all_day, nil %>
|
184
|
+
<%= link_to "Woot", "http://www.woot.com" %>
|
185
|
+
<% end %>
|
186
|
+
|
187
|
+
Once you have the roles and abilities setup, your views can check who has access by simply calling <tt>can?</tt>
|
188
|
+
|
189
|
+
if can? :create, Comment
|
190
|
+
...
|
191
|
+
end
|
192
|
+
|
193
|
+
You can also use <tt>cannot?</tt>
|
194
|
+
|
195
|
+
link_to "Export", export_users_url unless cannot? :create, Export
|
196
|
+
|
197
|
+
Using <tt>can?</tt> and <tt>cannot?</tt> is the preferred method of checking authorization privileges, however,
|
198
|
+
if you find a rare case that you need to limit access based on the role(s) that a user has, you can
|
199
|
+
always do the following:
|
200
|
+
|
201
|
+
if current_user.is_moderator? || current_user.is_admin?
|
202
|
+
...
|
203
|
+
end
|
204
|
+
|
205
|
+
However, the beauty of authcan_easyroller (more specifically the CanCan integration) is that you
|
206
|
+
don't have to. That is the exact purpose of the <tt>Ability</tt> class! Use it to your advantage and
|
207
|
+
make your life easier.
|
208
|
+
|
209
|
+
|
210
|
+
|
211
|
+
== Special Thanks
|
212
|
+
|
213
|
+
I would like to thank the creators of Authlogic, CanCan and Easy Roles for the effort that they put
|
214
|
+
into these plugins. Adding them together was relatively straightforward and easy and I hope that they
|
215
|
+
realize how much time this saves other developers!
|
216
|
+
|
217
|
+
Copyright (c) 2010 Topher Fangio, released under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "authcan_easyroller"
|
8
|
+
gem.summary = %Q{Rails 3 engine for user authentication/authorization utilizing Authlogic, CanCan and EasyRoles}
|
9
|
+
gem.description = %Q{This is a basic Rails engine utilizing Authlogic, CanCan and Easy Roles to create a starting point for simple Rails-based applications that need authentication and authorization. }
|
10
|
+
gem.email = "fangiotophia@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/topherfangio/authcan_easyroller"
|
12
|
+
gem.authors = ["Topher Fangio"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "authcan_easyroller #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class AuthcanEasyrollerController < ApplicationController
|
2
|
+
helper :all # include all helpers, all the time
|
3
|
+
protect_from_forgery # See ActionController::RequestForgeryProtection for details
|
4
|
+
|
5
|
+
# Scrub sensitive parameters from your log
|
6
|
+
helper_method :current_user_session, :current_user
|
7
|
+
|
8
|
+
rescue_from CanCan::AccessDenied do |exception|
|
9
|
+
flash[:error] = exception.message
|
10
|
+
redirect_back_or_default(root_url)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Ensure there is at least one user in the system before trying to do anything
|
14
|
+
before_filter :require_one_user
|
15
|
+
after_filter :store_location
|
16
|
+
|
17
|
+
private
|
18
|
+
def current_user_session
|
19
|
+
return @current_user_session if defined?(@current_user_session)
|
20
|
+
@current_user_session = UserSession.find
|
21
|
+
end
|
22
|
+
|
23
|
+
def current_user
|
24
|
+
if defined?(@current_user)
|
25
|
+
return @current_user
|
26
|
+
end
|
27
|
+
|
28
|
+
@current_user = current_user_session && current_user_session.user
|
29
|
+
|
30
|
+
if @current_user_session and @current_user_session.stale?
|
31
|
+
flash[:warning] = "You have been logged out due to an extended period of inactivity."
|
32
|
+
@current_user_session.destroy
|
33
|
+
end
|
34
|
+
|
35
|
+
return @current_user
|
36
|
+
end
|
37
|
+
|
38
|
+
def require_one_user
|
39
|
+
if User.count == 0 and not users_url.match(/#{request.request_uri}$/) and not new_user_url.match(/#{request.request_uri}$/)
|
40
|
+
flash[:error] = "No users yet, must create one to access the site."
|
41
|
+
|
42
|
+
redirect_to new_user_url
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def require_user
|
48
|
+
unless current_user
|
49
|
+
store_location
|
50
|
+
|
51
|
+
flash[:notice] = "You must be logged in to access this page."
|
52
|
+
redirect_to new_user_session_url
|
53
|
+
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def require_no_user
|
59
|
+
if current_user
|
60
|
+
store_location
|
61
|
+
|
62
|
+
flash[:notice] = "You must be logged out to access this page."
|
63
|
+
redirect_to user_url(current_user)
|
64
|
+
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def store_location
|
70
|
+
unless new_user_url.match(/#{request.request_uri}$/) or new_user_session_url.match(/#{request.request_uri}$/)
|
71
|
+
session[:return_to] = request.request_uri
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def redirect_back_or_default(default)
|
76
|
+
redirect_to(session[:return_to] || default)
|
77
|
+
session[:return_to] = nil
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class UserSessionsController < AuthcanEasyrollerController
|
2
|
+
before_filter :require_no_user, :only => [:new, :create]
|
3
|
+
before_filter :require_user, :only => :destroy
|
4
|
+
|
5
|
+
load_and_authorize_resource
|
6
|
+
|
7
|
+
def new
|
8
|
+
respond_to do |format|
|
9
|
+
if current_user
|
10
|
+
redirect_to :controll => 'users', :action => 'show', :id => current_user
|
11
|
+
else
|
12
|
+
@user_session = UserSession.new
|
13
|
+
|
14
|
+
format.html # new.html.erb
|
15
|
+
format.xml { render :xml => @user_session }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@user_session = UserSession.new(params[:user_session])
|
22
|
+
if @user_session.save
|
23
|
+
flash[:notice] = "Login successful!"
|
24
|
+
redirect_back_or_default user_url(@user_session.user)
|
25
|
+
else
|
26
|
+
render :action => :new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def destroy
|
31
|
+
current_user_session.destroy
|
32
|
+
flash[:notice] = "Logout successful!"
|
33
|
+
redirect_back_or_default '/'
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class UsersController < AuthcanEasyrollerController
|
2
|
+
|
3
|
+
before_filter :load_correct_user, :only => [:show, :edit, :update]
|
4
|
+
before_filter :require_user, :only => [:edit, :update]
|
5
|
+
|
6
|
+
load_and_authorize_resource
|
7
|
+
|
8
|
+
def index
|
9
|
+
@users = User.all
|
10
|
+
end
|
11
|
+
|
12
|
+
def new
|
13
|
+
@user = User.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def create
|
17
|
+
@user = User.new(params[:user])
|
18
|
+
if @user.save
|
19
|
+
flash[:notice] = "Account registered!"
|
20
|
+
redirect_back_or_default user_url(@user)
|
21
|
+
else
|
22
|
+
render :action => :new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def show
|
27
|
+
require_user if @user.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def edit
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
if @user.update_attributes(params[:user])
|
35
|
+
flash[:notice] = "Account updated!"
|
36
|
+
redirect_to user_url(@user)
|
37
|
+
else
|
38
|
+
render :action => :edit
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def destroy
|
43
|
+
@user = User.find(params[:id])
|
44
|
+
@user.destroy
|
45
|
+
|
46
|
+
respond_to do |format|
|
47
|
+
format.html { redirect_to(users_url) }
|
48
|
+
format.xml { head :ok }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def load_correct_user
|
54
|
+
if params[:id]
|
55
|
+
@user = User.find(params[:id])
|
56
|
+
else
|
57
|
+
@user = current_user
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module AuthcanEasyrollerHelper
|
2
|
+
def namify(object)
|
3
|
+
if object.present? and object.respond_to? :name
|
4
|
+
object.send(:name)
|
5
|
+
else
|
6
|
+
""
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def datify(object, format = :long)
|
11
|
+
object.to_s(format) unless object.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
def phonify(object)
|
15
|
+
if object.respond_to? :gsub
|
16
|
+
s = object.gsub(/[^0-9]/, '')
|
17
|
+
|
18
|
+
case s.length
|
19
|
+
when 11 then "#{s[0..0]} (#{s[1..3]}) #{s[4..6]}-#{s[7..10]}"
|
20
|
+
when 10 then "(#{s[0..2]}) #{s[3..5]}-#{s[6..9]}"
|
21
|
+
when 7 then "#{s[0..2]}-#{s[3..6]}"
|
22
|
+
else s
|
23
|
+
end
|
24
|
+
else
|
25
|
+
object.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def form_descriptor(message = nil)
|
30
|
+
raw "<br /><span class='form-descriptor-element'>#{message}</span>"
|
31
|
+
end
|
32
|
+
|
33
|
+
def link_separator
|
34
|
+
raw " | "
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Ability
|
2
|
+
include CanCan::Ability
|
3
|
+
|
4
|
+
def initialize(current_user)
|
5
|
+
can :read, :all
|
6
|
+
can :manage, UserSession
|
7
|
+
|
8
|
+
if current_user
|
9
|
+
# Abilities for someone with an account (does not necessarily have a "user" role)
|
10
|
+
can [:update, :destroy], User do |user|
|
11
|
+
user == current_user
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# User role abilities
|
16
|
+
if current_user.is_user?
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Moderator role abilities
|
21
|
+
if current_user.is_moderator?
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# Admin role abilities
|
26
|
+
if current_user.is_admin?
|
27
|
+
can :manage, :all
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# Developer role abilities
|
32
|
+
if current_user.is_developer?
|
33
|
+
can :manage, :all
|
34
|
+
end
|
35
|
+
else
|
36
|
+
can :create, User
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/app/models/user.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
|
3
|
+
# Constant variable storing roles in the system
|
4
|
+
ROLES_MASK = %w[developer admin moderator user]
|
5
|
+
|
6
|
+
HUMANIZED_COLUMNS = {
|
7
|
+
:email => 'E-mail'
|
8
|
+
}
|
9
|
+
|
10
|
+
|
11
|
+
# Authentication and Authorization
|
12
|
+
acts_as_authentic do |config|
|
13
|
+
config.logged_in_timeout = 1.hour
|
14
|
+
end
|
15
|
+
|
16
|
+
easy_roles :roles_mask, :method => :bitmask
|
17
|
+
|
18
|
+
|
19
|
+
# Ensure the user has the proper roles
|
20
|
+
before_create :assign_user_roles
|
21
|
+
|
22
|
+
|
23
|
+
# Validations
|
24
|
+
validates_each :roles do |record, attr, value|
|
25
|
+
begin
|
26
|
+
# If the roles changed, and it's not a new record whose only role is "user" and there is at least 1 user already in the system...
|
27
|
+
if record.roles_mask_changed? and not (record.new_record? and record.roles == ["user"]) and User.count > 0
|
28
|
+
session = UserSession.find
|
29
|
+
current_user = session.user unless session.nil?
|
30
|
+
|
31
|
+
record.errors.add attr, ' can only be modified by an administrator.' if (not current_user.is_developer? and not current_user.is_admin?)
|
32
|
+
end
|
33
|
+
rescue Authlogic::Session::Activation::NotActivatedError
|
34
|
+
# Do nothing, we are in a console session
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# Allows us to easily humanize our attributes in one location
|
40
|
+
# so we don't have to do it in every view
|
41
|
+
def self.human_attribute_name(attribute)
|
42
|
+
HUMANIZED_COLUMNS[attribute.to_sym] || super
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def assign_user_roles
|
47
|
+
self.roles += ["user"]
|
48
|
+
|
49
|
+
if User.count == 0
|
50
|
+
self.roles += ["admin"]
|
51
|
+
self.roles += ["moderator"]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<h1>Login</h1>
|
2
|
+
|
3
|
+
<% form_for @user_session, :url => user_session_path do |f| %>
|
4
|
+
<%= f.error_messages %>
|
5
|
+
|
6
|
+
<p>
|
7
|
+
<%= f.label :email %>
|
8
|
+
<%= f.text_field :email %>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<p>
|
12
|
+
<%= f.label :password %>
|
13
|
+
<%= f.password_field :password %>
|
14
|
+
</p>
|
15
|
+
|
16
|
+
<p>
|
17
|
+
<%= f.check_box :remember_me %><%= f.label :remember_me %>
|
18
|
+
</p>
|
19
|
+
|
20
|
+
<p>
|
21
|
+
<%= f.submit "Login" %>
|
22
|
+
</p>
|
23
|
+
|
24
|
+
<p>
|
25
|
+
Don't have an account yet? Why not <%= link_to "sign up", new_user_path %>!
|
26
|
+
</p>
|
27
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<%= form.hidden_field :id unless form.object.new_record? %>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<%= form.label :email, "E-mail" %>
|
5
|
+
<%= form.text_field :email %>
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
<%= form.label :password, form.object.new_record? ? "Password" : "Change Password" %>
|
10
|
+
<%= form.password_field :password %>
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
<%= form.label :password_confirmation, "Password Confirmation" %>
|
15
|
+
<%= form.password_field :password_confirmation %>
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<% if can? :manage, :roles %>
|
19
|
+
<p>
|
20
|
+
<%= form.label :roles %>
|
21
|
+
<%= form.select :roles, User::ROLES_MASK, {}, { :multiple => true, :size => 4 } %>
|
22
|
+
</p>
|
23
|
+
<% end %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<h3>Edit My Account</h3>
|
2
|
+
|
3
|
+
<% form_for @user do |f| %>
|
4
|
+
<%= f.error_messages %>
|
5
|
+
|
6
|
+
<%= render :partial => "form", :object => f %>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
<%= f.submit "Update" %> or <%= link_to 'return to list', users_path %>
|
10
|
+
</p>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<br /><%= link_to "My Profile", user_path(@user) %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<h3>Listing Users</h3>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<th>Email</th>
|
6
|
+
<th>Last Seen</th>
|
7
|
+
<th>Action</th>
|
8
|
+
</tr>
|
9
|
+
|
10
|
+
<% @users.each do |user| %>
|
11
|
+
<tr>
|
12
|
+
<td><%= user.email %></td>
|
13
|
+
<td><%= time_ago_in_words user.last_request_at %> ago</td>
|
14
|
+
|
15
|
+
<td>
|
16
|
+
<%= link_to 'Show', user %>
|
17
|
+
|
18
|
+
<% if can? :update, user %>
|
19
|
+
<%= link_separator %>
|
20
|
+
<%= link_to 'Edit', edit_user_path(user) %>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<% if can? :destroy, user %>
|
24
|
+
<%= link_separator %>
|
25
|
+
<%= link_to 'Delete', user, :confirm => 'Are you sure?', :method => :delete %>
|
26
|
+
<% end %>
|
27
|
+
</td>
|
28
|
+
</tr>
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
</table>
|
32
|
+
|
33
|
+
<br />
|
34
|
+
|
35
|
+
Not signed up? <%= link_to 'Register', new_user_path %> now!
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<h3>Showing User Information</h3>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<b>E-mail:</b>
|
5
|
+
<%=h @user.email %>
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
<b>Login Count:</b>
|
10
|
+
<%=h @user.login_count %>
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
<b>Last Request at:</b>
|
15
|
+
<%= datify @user.last_request_at %>
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<p>
|
19
|
+
<b>Last Login at:</b>
|
20
|
+
<%= datify @user.last_login_at %>
|
21
|
+
</p>
|
22
|
+
|
23
|
+
<p>
|
24
|
+
<b>Current Login at:</b>
|
25
|
+
<%= datify @user.current_login_at %>
|
26
|
+
</p>
|
27
|
+
|
28
|
+
<p>
|
29
|
+
<b>Last Login IP:</b>
|
30
|
+
<%=h @user.last_login_ip %>
|
31
|
+
</p>
|
32
|
+
|
33
|
+
<p>
|
34
|
+
<b>Current Login IP:</b>
|
35
|
+
<%=h @user.current_login_ip %>
|
36
|
+
</p>
|
37
|
+
|
38
|
+
|
39
|
+
<%= link_to 'Edit', edit_user_path(@user) %> or <%= link_to 'return to list', users_path %>
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{authcan_easyroller}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Topher Fangio"]
|
12
|
+
s.date = %q{2010-03-31}
|
13
|
+
s.description = %q{This is a basic Rails engine utilizing Authlogic, CanCan and Easy Roles to create a starting point for simple Rails-based applications that need authentication and authorization. }
|
14
|
+
s.email = %q{fangiotophia@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"app/controllers/authcan_easyroller_controller.rb",
|
26
|
+
"app/controllers/user_sessions_controller.rb",
|
27
|
+
"app/controllers/users_controller.rb",
|
28
|
+
"app/helpers/authcan_easyroller_helper.rb",
|
29
|
+
"app/models/ability.rb",
|
30
|
+
"app/models/user.rb",
|
31
|
+
"app/models/user_session.rb",
|
32
|
+
"app/views/user_sessions/new.html.erb",
|
33
|
+
"app/views/users/_form.html.erb",
|
34
|
+
"app/views/users/edit.html.erb",
|
35
|
+
"app/views/users/index.html.erb",
|
36
|
+
"app/views/users/new.html.erb",
|
37
|
+
"app/views/users/show.html.erb",
|
38
|
+
"authcan_easyroller.gemspec",
|
39
|
+
"config/routes.rb",
|
40
|
+
"example/ability.rb",
|
41
|
+
"example/application.html.erb",
|
42
|
+
"example/main.css",
|
43
|
+
"lib/authcan_easyroller.rb",
|
44
|
+
"test/helper.rb",
|
45
|
+
"test/test_authcan_easyroller.rb"
|
46
|
+
]
|
47
|
+
s.homepage = %q{http://github.com/topherfangio/authcan_easyroller}
|
48
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
49
|
+
s.require_paths = ["lib"]
|
50
|
+
s.rubygems_version = %q{1.3.6}
|
51
|
+
s.summary = %q{Rails 3 engine for user authentication/authorization utilizing Authlogic, CanCan and EasyRoles}
|
52
|
+
s.test_files = [
|
53
|
+
"test/helper.rb",
|
54
|
+
"test/test_authcan_easyroller.rb"
|
55
|
+
]
|
56
|
+
|
57
|
+
if s.respond_to? :specification_version then
|
58
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
59
|
+
s.specification_version = 3
|
60
|
+
|
61
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
62
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
65
|
+
end
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
data/config/routes.rb
ADDED
data/example/ability.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
class Ability
|
2
|
+
include CanCan::Ability
|
3
|
+
|
4
|
+
def initialize(current_user)
|
5
|
+
can :read, :all
|
6
|
+
can :manage, UserSession
|
7
|
+
|
8
|
+
if current_user
|
9
|
+
# Abilities for someone with an account (does not necessarily have a "user" role)
|
10
|
+
can [:update, :destroy], User do |user|
|
11
|
+
user == current_user
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# User role abilities
|
16
|
+
if current_user.is_user?
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Moderator role abilities
|
21
|
+
if current_user.is_moderator?
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# Admin role abilities
|
26
|
+
if current_user.is_admin?
|
27
|
+
can :manage, :all
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# Developer role abilities
|
32
|
+
if current_user.is_developer?
|
33
|
+
can :manage, :all
|
34
|
+
end
|
35
|
+
else
|
36
|
+
can :create, User
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<script src="http://www.google.com/jsapi"></script>
|
4
|
+
<script>google.load("jquery", "1.4");</script>
|
5
|
+
|
6
|
+
<%= stylesheet_link_tag 'main' %>
|
7
|
+
</head>
|
8
|
+
|
9
|
+
<body>
|
10
|
+
<div id='navigation'>
|
11
|
+
<%= link_to "Users", users_path %>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<div id='userland'>
|
15
|
+
<% if current_user %>
|
16
|
+
Welcome <%= current_user.email %>!
|
17
|
+
|
18
|
+
<%= link_to "My Account", user_path(current_user) %>
|
19
|
+
<%= link_separator %>
|
20
|
+
<%= link_to "Logout", '/user_sessions/destroy' %>
|
21
|
+
<% else %>
|
22
|
+
You are not currently
|
23
|
+
<%= link_to "logged in", new_user_session_path %>.
|
24
|
+
<% end %>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div id='flashes'>
|
28
|
+
<%= raw "<h5 class='flash error'>#{flash[:error]}</h5>" unless flash[:error].blank? %>
|
29
|
+
<%= raw "<h5 class='flash notice'>#{flash[:notice]}</h5>" unless flash[:notice].blank? %>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<div id='content'>
|
33
|
+
<%= yield %>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
<div id='copyright'>
|
37
|
+
Copyright © 2010, MyCompany. All rights reserved.
|
38
|
+
</div>
|
39
|
+
</body>
|
40
|
+
</html>
|
data/example/main.css
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#navigation {
|
2
|
+
float: left;
|
3
|
+
margin-bottom: 10px;
|
4
|
+
}
|
5
|
+
|
6
|
+
#userland {
|
7
|
+
float: right;
|
8
|
+
margin-bottom: 10px;
|
9
|
+
}
|
10
|
+
|
11
|
+
#flashes {
|
12
|
+
clear: both;
|
13
|
+
}
|
14
|
+
|
15
|
+
#content {
|
16
|
+
margin: 10px;
|
17
|
+
}
|
18
|
+
|
19
|
+
#copyright {
|
20
|
+
text-align: center;
|
21
|
+
}
|
22
|
+
|
23
|
+
.flash {
|
24
|
+
border: 1px solid #000000;
|
25
|
+
padding: 10px;
|
26
|
+
}
|
27
|
+
|
28
|
+
.flash.error {
|
29
|
+
}
|
30
|
+
|
31
|
+
.flash.notice {
|
32
|
+
}
|
33
|
+
|
34
|
+
label {
|
35
|
+
font-weight: bold;
|
36
|
+
}
|
37
|
+
|
38
|
+
input[type=text],input[type=password],select,textarea {
|
39
|
+
clear: both;
|
40
|
+
display: block;
|
41
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'authcan_easyroller'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
# AuthcanEasyroller
|
5
|
+
module AuthcanEasyroller
|
6
|
+
class Engine < Rails::Engine
|
7
|
+
engine_name :authcan_easyroller
|
8
|
+
|
9
|
+
initializer "authcan_easyroller.configure_rails_initilization" do |app|
|
10
|
+
# NOTE: The following has been commented out to bypass bug #3928 which will be fixed with the next release.
|
11
|
+
#
|
12
|
+
# Please comment these back in when Rails 3.0.0.beta2 or later version is used.
|
13
|
+
#
|
14
|
+
# Bug here: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/3928
|
15
|
+
#
|
16
|
+
app.config.filter_parameters << :password
|
17
|
+
app.config.filter_parameters << :password_confirmation
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: authcan_easyroller
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Topher Fangio
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-31 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: thoughtbot-shoulda
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
description: "This is a basic Rails engine utilizing Authlogic, CanCan and Easy Roles to create a starting point for simple Rails-based applications that need authentication and authorization. "
|
33
|
+
email: fangiotophia@gmail.com
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files:
|
39
|
+
- LICENSE
|
40
|
+
- README.rdoc
|
41
|
+
files:
|
42
|
+
- .gitignore
|
43
|
+
- LICENSE
|
44
|
+
- README.rdoc
|
45
|
+
- Rakefile
|
46
|
+
- VERSION
|
47
|
+
- app/controllers/authcan_easyroller_controller.rb
|
48
|
+
- app/controllers/user_sessions_controller.rb
|
49
|
+
- app/controllers/users_controller.rb
|
50
|
+
- app/helpers/authcan_easyroller_helper.rb
|
51
|
+
- app/models/ability.rb
|
52
|
+
- app/models/user.rb
|
53
|
+
- app/models/user_session.rb
|
54
|
+
- app/views/user_sessions/new.html.erb
|
55
|
+
- app/views/users/_form.html.erb
|
56
|
+
- app/views/users/edit.html.erb
|
57
|
+
- app/views/users/index.html.erb
|
58
|
+
- app/views/users/new.html.erb
|
59
|
+
- app/views/users/show.html.erb
|
60
|
+
- authcan_easyroller.gemspec
|
61
|
+
- config/routes.rb
|
62
|
+
- example/ability.rb
|
63
|
+
- example/application.html.erb
|
64
|
+
- example/main.css
|
65
|
+
- lib/authcan_easyroller.rb
|
66
|
+
- test/helper.rb
|
67
|
+
- test/test_authcan_easyroller.rb
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://github.com/topherfangio/authcan_easyroller
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options:
|
74
|
+
- --charset=UTF-8
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
requirements: []
|
92
|
+
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 1.3.6
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: Rails 3 engine for user authentication/authorization utilizing Authlogic, CanCan and EasyRoles
|
98
|
+
test_files:
|
99
|
+
- test/helper.rb
|
100
|
+
- test/test_authcan_easyroller.rb
|