ioquatix-account_engine 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +0 -0
- data/lib/account_engine/configuration.rb +101 -0
- data/lib/account_engine/controller.rb +246 -0
- data/lib/account_engine/helper.rb +104 -0
- data/lib/account_engine/password.rb +432 -0
- data/lib/account_engine/support.rb +12 -0
- data/lib/account_engine/user_account/class_methods.rb +63 -0
- data/lib/account_engine/user_account.rb +184 -0
- data/lib/account_engine.rb +63 -0
- data/rails/app/controllers/account_controller.rb +162 -0
- data/rails/app/controllers/permissions_controller.rb +90 -0
- data/rails/app/controllers/roles_controller.rb +133 -0
- data/rails/app/controllers/users_controller.rb +144 -0
- data/rails/app/helpers/account_helper.rb +3 -0
- data/rails/app/helpers/permissions_helper.rb +3 -0
- data/rails/app/helpers/roles_helper.rb +3 -0
- data/rails/app/helpers/users_helper.rb +3 -0
- data/rails/app/models/permission.rb +129 -0
- data/rails/app/models/role.rb +60 -0
- data/rails/app/models/user.rb +5 -0
- data/rails/app/models/user_notify.rb +75 -0
- data/rails/app/views/account/_form.rhtml +8 -0
- data/rails/app/views/account/change_password.rhtml +17 -0
- data/rails/app/views/account/edit.rhtml +5 -0
- data/rails/app/views/account/forgot_password.rhtml +12 -0
- data/rails/app/views/account/home.rhtml +3 -0
- data/rails/app/views/account/login.rhtml +27 -0
- data/rails/app/views/account/logout.rhtml +8 -0
- data/rails/app/views/account/signup.rhtml +28 -0
- data/rails/app/views/permissions/_form.rhtml +14 -0
- data/rails/app/views/permissions/_list.rhtml +38 -0
- data/rails/app/views/permissions/edit.rhtml +5 -0
- data/rails/app/views/permissions/index.rhtml +3 -0
- data/rails/app/views/permissions/new.rhtml +5 -0
- data/rails/app/views/roles/_form.rhtml +8 -0
- data/rails/app/views/roles/_permissions.rhtml +25 -0
- data/rails/app/views/roles/edit.rhtml +5 -0
- data/rails/app/views/roles/index.rhtml +34 -0
- data/rails/app/views/roles/new.rhtml +5 -0
- data/rails/app/views/roles/show.rhtml +20 -0
- data/rails/app/views/user_notify/change_password.rhtml +10 -0
- data/rails/app/views/user_notify/delete.rhtml +5 -0
- data/rails/app/views/user_notify/forgot_password.rhtml +11 -0
- data/rails/app/views/user_notify/pending_delete.rhtml +9 -0
- data/rails/app/views/user_notify/signup.rhtml +12 -0
- data/rails/app/views/users/_form.rhtml +12 -0
- data/rails/app/views/users/edit.rhtml +5 -0
- data/rails/app/views/users/index.rhtml +38 -0
- data/rails/app/views/users/new.rhtml +5 -0
- data/rails/app/views/users/roles.rhtml +42 -0
- data/rails/app/views/users/show.rhtml +36 -0
- data/rails/assets/images/default/omnipotent.png +0 -0
- data/rails/assets/images/default/system.png +0 -0
- data/rails/assets/images/permissions/create.png +0 -0
- data/rails/assets/images/permissions/sync.png +0 -0
- data/rails/assets/images/roles/add_permission.png +0 -0
- data/rails/assets/images/roles/create.png +0 -0
- data/rails/assets/images/roles/edit.png +0 -0
- data/rails/assets/images/roles/remove_permission.png +0 -0
- data/rails/assets/images/roles/user.png +0 -0
- data/rails/assets/images/table_background.png +0 -0
- data/rails/assets/images/users/create.png +0 -0
- data/rails/assets/images/users/destroy.png +0 -0
- data/rails/assets/images/users/edit.png +0 -0
- data/rails/assets/images/users/show.png +0 -0
- data/rails/assets/javascripts/account_engine.js +166 -0
- data/rails/assets/stylesheets/account_engine.css +7 -0
- data/rails/assets/stylesheets/check_password.css +10 -0
- data/rails/assets/stylesheets/simple.css +168 -0
- data/rails/db/migrate/001_initial_schema.rb +49 -0
- data/rails/init.rb +21 -0
- data/rails/routes.rb +5 -0
- data/rails/tasks/account_engine.rake +123 -0
- metadata +165 -0
data/README
ADDED
File without changes
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# Configuration
|
2
|
+
module AccountEngine
|
3
|
+
# These are the default constants, used if nothing else is specified
|
4
|
+
mattr_accessor :email_charset, :site_email, :admin_email, :app_url, :app_name
|
5
|
+
mattr_accessor :generate_password, :validate_email, :salt, :security_token_life_hours
|
6
|
+
mattr_accessor :confirm_account, :use_email_notification
|
7
|
+
mattr_reader :registration
|
8
|
+
|
9
|
+
def self.registration=(r)
|
10
|
+
if [:open, :closed].include? r
|
11
|
+
@@registration = r
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
mattr_accessor :guest_role_name, :user_role_name
|
16
|
+
mattr_accessor :admin_role_name, :admin_login, :admin_password, :admin_email
|
17
|
+
|
18
|
+
mattr_accessor :login_page, :logout_page, :stealth
|
19
|
+
|
20
|
+
mattr_accessor :users_table, :roles_table, :permissions_table
|
21
|
+
|
22
|
+
# The names of the new Role and Permission tables
|
23
|
+
if ActiveRecord::Base.pluralize_table_names
|
24
|
+
@@users_table = "users"
|
25
|
+
@@roles_table = "roles"
|
26
|
+
@@permissions_table = "permissions"
|
27
|
+
else
|
28
|
+
@@users_table = "user"
|
29
|
+
@@roles_table = "role"
|
30
|
+
@@permissions_table = "permission"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Join tables for users <-> roles, and roles <-> permissions
|
34
|
+
def self.users_roles_table
|
35
|
+
"#{AccountEngine.users_table}_#{AccountEngine.roles_table}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.permissions_roles_table
|
39
|
+
"#{AccountEngine.permissions_table}_#{AccountEngine.roles_table}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.allow_registration?
|
43
|
+
return (@@registration == :open ? true : false)
|
44
|
+
end
|
45
|
+
|
46
|
+
#Generate random passwords
|
47
|
+
@@generate_password = true
|
48
|
+
|
49
|
+
# Source address for user emails
|
50
|
+
@@site_email = 'root@localhost'
|
51
|
+
|
52
|
+
# Destination email for system errors
|
53
|
+
@@admin_email = 'root@localhost'
|
54
|
+
|
55
|
+
# Sent in emails to users
|
56
|
+
@@app_url = 'http://localhost:3000/'
|
57
|
+
|
58
|
+
# Sent in emails to users
|
59
|
+
@@app_name = 'My Application'
|
60
|
+
|
61
|
+
# Whether the model will enforce email validation
|
62
|
+
@@validate_email = true
|
63
|
+
|
64
|
+
# Email charset
|
65
|
+
@@email_charset = 'utf-8'
|
66
|
+
|
67
|
+
# Security token lifetime in hours
|
68
|
+
@@security_token_life_hours = 48
|
69
|
+
|
70
|
+
# Registrations are open to the public
|
71
|
+
@@registration = :closed
|
72
|
+
|
73
|
+
# controls whether or not email is used
|
74
|
+
@@use_email_notification = true
|
75
|
+
|
76
|
+
# Controls whether accounts must be confirmed after signing up
|
77
|
+
# ONLY if this and use_email_notification are both true
|
78
|
+
@@confirm_account = false
|
79
|
+
|
80
|
+
# The names of the Guest and User roles
|
81
|
+
# The Guest role is automatically assigned to any visitor who is not logged in
|
82
|
+
@@guest_role_name = "Guest"
|
83
|
+
# The User role is given to every user
|
84
|
+
@@user_role_name = "User"
|
85
|
+
|
86
|
+
# The details for the Admin user and role
|
87
|
+
@@admin_role_name = "Administrator"
|
88
|
+
@@admin_login = "admin"
|
89
|
+
@@admin_password = "test123"
|
90
|
+
@@admin_email = "root@localhost"
|
91
|
+
|
92
|
+
# The controller/action
|
93
|
+
@@login_page = {:controller => 'account', :action => 'login'}
|
94
|
+
|
95
|
+
@@logout_page = {:controller => 'account', :action => 'logout'}
|
96
|
+
|
97
|
+
# If this is set to true, authorization failure messages won't volunteer
|
98
|
+
# any extra information, and missing actions will not be flagged as such.
|
99
|
+
@@stealth = false
|
100
|
+
end
|
101
|
+
|
@@ -0,0 +1,246 @@
|
|
1
|
+
|
2
|
+
require 'account_engine/support'
|
3
|
+
|
4
|
+
module AccountEngine
|
5
|
+
module Controller
|
6
|
+
# methods to be added to the ApplicationController
|
7
|
+
|
8
|
+
include AccountEngine::Support
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
# Returns an array containing all subclasses of ApplicationController
|
13
|
+
def all_controllers
|
14
|
+
#ObjectSpace.subclasses_of(ApplicationController)
|
15
|
+
subclasses_of(ApplicationController)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns an array of all action names for this controller
|
20
|
+
# (Actually returns the result of ApplicationController#action_methods, which is private)
|
21
|
+
def action_method_names
|
22
|
+
action_methods
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.included(base)
|
26
|
+
base.extend(ClassMethods)
|
27
|
+
|
28
|
+
base.class_eval do
|
29
|
+
# We don't want these actions to be exposed to the Permission
|
30
|
+
# system synchronisation, so we hide them for all controllers.
|
31
|
+
hide_action :require_without_load_path_reloading, :process_test
|
32
|
+
hide_action :action_method_names, :wsdl, :deepcopy
|
33
|
+
hide_action :readable?, :writable?, :r?, :w?, :authorize_action
|
34
|
+
hide_action :store_location, :redirect_back_or_default
|
35
|
+
|
36
|
+
# methods from the AccountEngine module itself
|
37
|
+
hide_action :link_if_authorized, :authorized?, :user_name_if_logged_in
|
38
|
+
hide_action :user?, :current_user, :logged_in?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
# Use this method to log a user in to the system
|
44
|
+
def authenticate_user
|
45
|
+
# If we are already logged in, return true
|
46
|
+
return true if current_user
|
47
|
+
|
48
|
+
login = params[:login]
|
49
|
+
password = params[:password]
|
50
|
+
id = params[:user_id]
|
51
|
+
key = params[:key]
|
52
|
+
user = nil
|
53
|
+
|
54
|
+
if login and password
|
55
|
+
user = User.authenticate(login, password)
|
56
|
+
end
|
57
|
+
|
58
|
+
if id and key and user.nil?
|
59
|
+
user = User.authenticate_by_token(id, key)
|
60
|
+
end
|
61
|
+
|
62
|
+
if user
|
63
|
+
user.update_attribute(:logged_in_at, Time.now)
|
64
|
+
session[:user] = user
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
|
68
|
+
return false
|
69
|
+
end
|
70
|
+
|
71
|
+
# overwrite this if you want to restrict access to only a few actions
|
72
|
+
# or if you want to check if the user has the correct rights
|
73
|
+
# example:
|
74
|
+
#
|
75
|
+
# # only allow nonbobs
|
76
|
+
# def authorized?(user)
|
77
|
+
# user.login != "bob"
|
78
|
+
# end
|
79
|
+
def authorize?(user)
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
# overwrite this method if you only want to protect certain actions of the controller
|
84
|
+
# example:
|
85
|
+
#
|
86
|
+
# # don't protect the login and the about method
|
87
|
+
# def protect?(action)
|
88
|
+
# if ['action', 'about'].include?(action)
|
89
|
+
# return false
|
90
|
+
# else
|
91
|
+
# return true
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
def protect?(action)
|
95
|
+
true
|
96
|
+
end
|
97
|
+
|
98
|
+
# login_required filter. add
|
99
|
+
#
|
100
|
+
# before_filter :login_required
|
101
|
+
#
|
102
|
+
# if the controller should be under any rights management.
|
103
|
+
# for finer access control you can overwrite
|
104
|
+
#
|
105
|
+
# def authorize?(user)
|
106
|
+
#
|
107
|
+
def login_required
|
108
|
+
if not protect?(action_name)
|
109
|
+
return true
|
110
|
+
end
|
111
|
+
|
112
|
+
if user? and authorize?(current_user)
|
113
|
+
return true
|
114
|
+
end
|
115
|
+
|
116
|
+
if authorize_action
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
|
120
|
+
if user?
|
121
|
+
# If we are logged in and were denied, we will need to actually back out of the operation
|
122
|
+
redirect_to request.env['HTTP_REFERER'] || session['prev_uri'] || "/"
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
126
|
+
# store current location so that we can
|
127
|
+
# come back after the user logged in
|
128
|
+
store_location
|
129
|
+
|
130
|
+
# call overwriteable reaction to unauthorized access
|
131
|
+
access_denied
|
132
|
+
end
|
133
|
+
|
134
|
+
# overwrite if you want to have special behavior in case the user is not authorized
|
135
|
+
# to access the current operation.
|
136
|
+
# the default action is to redirect to the login screen
|
137
|
+
# example use :
|
138
|
+
# a popup window might just close itself for instance
|
139
|
+
def access_denied
|
140
|
+
logger.info "Access denied! Action: #{action_name} User: #{current_user}"
|
141
|
+
redirect_to AccountEngine.login_page
|
142
|
+
return false
|
143
|
+
end
|
144
|
+
|
145
|
+
# store current uri in the session.
|
146
|
+
# we can return to this location by calling return_location
|
147
|
+
def store_location
|
148
|
+
session['return-to'] = request.request_uri
|
149
|
+
end
|
150
|
+
|
151
|
+
# move to the last store_location call or to the passed default one
|
152
|
+
def redirect_to_stored_or_default(default=nil)
|
153
|
+
if session['return-to'].nil?
|
154
|
+
redirect_to default
|
155
|
+
else
|
156
|
+
redirect_to session['return-to']
|
157
|
+
session['return-to'] = nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def redirect_back_or_default(default=nil)
|
162
|
+
if request.env["HTTP_REFERER"].nil?
|
163
|
+
redirect_to default
|
164
|
+
else
|
165
|
+
redirect_to(request.env["HTTP_REFERER"]) # same as redirect_to :back
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
# This method will return true if:
|
171
|
+
#
|
172
|
+
# * The Guest Role is authorized to perform the current action
|
173
|
+
# * The currently logged in user is omnipotent
|
174
|
+
# * The currently logged in user has permission to perform the current
|
175
|
+
# action.
|
176
|
+
#
|
177
|
+
# In all other cases, it will return false. This method is a replacement
|
178
|
+
# for the +login_required+ method provided by the AccountEngine. If the Guest
|
179
|
+
# role does not have permission for the current action, the user will be
|
180
|
+
# redirected to the login page (and redirected back to this action upon
|
181
|
+
# successful authentication). Users can also authenticate directly via
|
182
|
+
# a security token (see AccountEngine for details).
|
183
|
+
def authorize_action
|
184
|
+
required_permission = "%s/%s" % [ params["controller"], params["action"] ]
|
185
|
+
logger.debug "required_perm is #{required_permission}"
|
186
|
+
|
187
|
+
controller = params["controller"]
|
188
|
+
action = params["action"]
|
189
|
+
|
190
|
+
# EVERYONE should be able to get to the root. This might never come up, but
|
191
|
+
# better to be safe than sorry. This condition could just as easily be
|
192
|
+
# appended to the Guest check below, but it's clearer up here.
|
193
|
+
if (controller == nil && action == nil)
|
194
|
+
return true
|
195
|
+
end
|
196
|
+
|
197
|
+
# if the controller wasn't nil, but the action was, then we want to
|
198
|
+
# set the action to "index" so we can check authentication properly
|
199
|
+
action ||= "index"
|
200
|
+
|
201
|
+
# If someone is or can be logged in...
|
202
|
+
# calling 'user?' from the AccountEngine will ensure that a User is
|
203
|
+
# loaded into the session if possible. It could either be there already
|
204
|
+
# or via a user_id and security key
|
205
|
+
if user?
|
206
|
+
# ... then if that logged user is NOT authorised...
|
207
|
+
|
208
|
+
unless current_user.authorized?(controller, action)
|
209
|
+
# YOU... SHALL... NOT... PASS!
|
210
|
+
|
211
|
+
flash[:message] = "Permission warning: You are not authorized for the action '#{required_permission}'."
|
212
|
+
|
213
|
+
# Here we are distinguishing between unauthorized actions and actions which do
|
214
|
+
# not exist. It *might* be better to employ a 'steath' technique and simple
|
215
|
+
# claim that all nonsense actions are unauthorized too... but that can make it
|
216
|
+
# difficult to debug.
|
217
|
+
if !AccountEngine.stealth
|
218
|
+
if Permission.find_by_controller_and_action(controller, action)
|
219
|
+
|
220
|
+
# This is a real action, but the user is not allowed to perform it.
|
221
|
+
allowed_roles = Permission.find_by_controller_and_action(controller, action).roles.collect {|r| r.name}.join(', ')
|
222
|
+
your_roles = current_user.roles.collect {|r| r.name}.join(', ')
|
223
|
+
flash[:message] << " Allowed Roles: #{allowed_roles}. User '#{current_user.login}' has only the following: #{your_roles}."
|
224
|
+
|
225
|
+
else # This wasn't even a real action.
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
return false
|
230
|
+
end
|
231
|
+
else
|
232
|
+
|
233
|
+
# noone is or can be logged in...
|
234
|
+
unless User.guest_user_authorized?(controller, action)
|
235
|
+
flash[:message] = "You need to log in."
|
236
|
+
return false
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# If we get here, the user is either a guest and this action is permitted
|
241
|
+
# for guest users, or the user is logged in and the action is permitted by
|
242
|
+
# one or more of their associated roles. Let them pass..
|
243
|
+
return true
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
|
2
|
+
require 'account_engine/support'
|
3
|
+
|
4
|
+
module AccountEngine
|
5
|
+
module Helper
|
6
|
+
include AccountEngine::Support
|
7
|
+
|
8
|
+
def link_to_user(user = current_user)
|
9
|
+
link_to user.fullname, :controller => 'account', :action => 'show'
|
10
|
+
end
|
11
|
+
|
12
|
+
#--
|
13
|
+
# The methods to be included in both ApplicationController and ApplicationHelper
|
14
|
+
#++
|
15
|
+
|
16
|
+
# Returns an HTML link if the user has authorisation to perform the
|
17
|
+
# supplied action. All other options and parameters are identical to
|
18
|
+
# those for ActionView::link_to
|
19
|
+
# e.g.
|
20
|
+
# link_if_authorized("Home", {:controller => "home", :action => "index"})
|
21
|
+
#
|
22
|
+
# If either of the :controller or :action options are ommitted, the
|
23
|
+
# current controller or action will be used instead.
|
24
|
+
#
|
25
|
+
# This method can also take an additional block, which can override the actual
|
26
|
+
# user permissions (i.e. the user must have valid permissions AND this block
|
27
|
+
# must not return false or nil for the link to be generated).
|
28
|
+
#
|
29
|
+
# We also provide special elements with the html_options argument.
|
30
|
+
#
|
31
|
+
# === :wrap_in
|
32
|
+
# This can be used to wrap the link in a given tag. This is useful if some
|
33
|
+
# surrounding markup to the link should also be ommitted if the user is not
|
34
|
+
# authorised for that link. E.g.
|
35
|
+
# <ul>
|
36
|
+
# <%= link_if_authorised("Delete", {:action => "delete"}, :wrap_in => "li") %>
|
37
|
+
# ...
|
38
|
+
# </ul>
|
39
|
+
#
|
40
|
+
# In this case, if the user is not authorised for this link, the <li></li>
|
41
|
+
# element will not be generated. Please note that this is fairly simplistic
|
42
|
+
# and relies on Rails' own #content_tag method. For more sophisticated
|
43
|
+
# control of markup based on authorisation, use the #authorised?() method
|
44
|
+
# directly.
|
45
|
+
#
|
46
|
+
# === :show_text
|
47
|
+
# if this flag is set to true, the text given for the link will be shown
|
48
|
+
# (although not as a link) even if the use is NOT authorised for the given
|
49
|
+
# action.
|
50
|
+
def link_if_authorized(name, options = {}, html_options = {}, *params, &block)
|
51
|
+
result = html_options.delete(:show_text) ? name : ""
|
52
|
+
|
53
|
+
# we need to strip leading slashes when checking authorisation, but not when
|
54
|
+
# actually generating the link.
|
55
|
+
auth_options = options.dup
|
56
|
+
if auth_options[:controller]
|
57
|
+
auth_options[:controller] = auth_options[:controller].gsub(/^\//, '')
|
58
|
+
end
|
59
|
+
|
60
|
+
(block.nil? || (yield block)) && authorized?(auth_options) {
|
61
|
+
#result = link_to_with_current_styling(name, options, html_options, *params)
|
62
|
+
result = link_to(name, options, html_options, *params)
|
63
|
+
|
64
|
+
# TODO: won't this pass other things like html_options[:id], which is EVIL since two
|
65
|
+
# things shouldn't share the same ID.
|
66
|
+
wrap_tag = html_options.delete(:wrap_in)
|
67
|
+
result = content_tag(wrap_tag, result, html_options) if wrap_tag != nil
|
68
|
+
}
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns true, and also executes an optional code block if the current user
|
73
|
+
# is authorised for the supplied controller and action. If no action is
|
74
|
+
# supplied, "index" is used by default. Returns false if the user is not
|
75
|
+
# authorised.
|
76
|
+
# e.g.
|
77
|
+
# <% authorized?("person", "destroy") { %>
|
78
|
+
# <p>You have the power to destroy users! Well done.</p>
|
79
|
+
# <% } %>
|
80
|
+
def authorized?(options, &block) # default the action to "index"
|
81
|
+
controller = options[:controller]
|
82
|
+
action = options[:action]
|
83
|
+
|
84
|
+
# use the current controller/action if none is given in options
|
85
|
+
controller ||= @controller.controller_name
|
86
|
+
action ||= @controller.action_name
|
87
|
+
|
88
|
+
if current_user.nil?
|
89
|
+
RAILS_DEFAULT_LOGGER.debug "checking guest authorisation for #{controller}/#{action}"
|
90
|
+
if User.guest_user_authorized?(controller, action)
|
91
|
+
yield block if block != nil
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
else
|
95
|
+
RAILS_DEFAULT_LOGGER.debug "checking user:#{session[:user].id} authorisation for #{controller}/#{action}"
|
96
|
+
if current_user.authorized?(controller, action)
|
97
|
+
yield block if block != nil
|
98
|
+
return true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
return false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|