jakewendt-authorized 0.1.4
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/README.rdoc +52 -0
- data/app/controllers/roles_controller.rb +38 -0
- data/app/models/role.rb +34 -0
- data/config/routes.rb +9 -0
- data/generators/authorized/USAGE +0 -0
- data/generators/authorized/authorized_generator.rb +66 -0
- data/generators/authorized/templates/functional/roles_controller_test.rb +142 -0
- data/generators/authorized/templates/migrations/create_roles.rb +14 -0
- data/generators/authorized/templates/migrations/create_roles_users.rb +14 -0
- data/generators/authorized/templates/stylesheets/authorized.css +0 -0
- data/generators/authorized/templates/unit/role_test.rb +29 -0
- data/lib/authorized.rb +45 -0
- data/lib/authorized/authorization.rb +69 -0
- data/lib/authorized/controller.rb +87 -0
- data/lib/authorized/core_extension.rb +16 -0
- data/lib/authorized/factories.rb +15 -0
- data/lib/authorized/factory_test_helper.rb +47 -0
- data/lib/authorized/helper.rb +28 -0
- data/lib/authorized/pending.rb +72 -0
- data/lib/authorized/permissive_controller.rb +25 -0
- data/lib/authorized/resourceful_controller.rb +81 -0
- data/lib/authorized/tasks.rb +1 -0
- data/lib/authorized/user_model.rb +144 -0
- data/lib/tasks/application.rake +40 -0
- data/lib/tasks/database.rake +52 -0
- data/lib/tasks/documentation.rake +68 -0
- data/lib/tasks/rcov.rake +41 -0
- metadata +276 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
module Authorized
|
2
|
+
module Authorization
|
3
|
+
|
4
|
+
module Controller
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
base.alias_method_chain :method_missing, :authorization
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
|
12
|
+
def auth_redirections(permission_name)
|
13
|
+
if respond_to?(:redirections) &&
|
14
|
+
redirections.is_a?(Hash) &&
|
15
|
+
!redirections[permission_name].blank?
|
16
|
+
redirections[permission_name]
|
17
|
+
else
|
18
|
+
HashWithIndifferentAccess.new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing_with_authorization(symb,*args, &block)
|
23
|
+
method_name = symb.to_s
|
24
|
+
|
25
|
+
if method_name =~ /^may_(not_)?(.+)_required$/
|
26
|
+
full_permission_name = "#{$1}#{$2}"
|
27
|
+
negate = !!$1 # double bang converts to boolean
|
28
|
+
permission_name = $2
|
29
|
+
verb,target = permission_name.split(/_/,2)
|
30
|
+
|
31
|
+
# using target words where singular == plural won't work here
|
32
|
+
if !target.blank? && target == target.singularize
|
33
|
+
unless permission = current_user.try(
|
34
|
+
"may_#{permission_name}?",
|
35
|
+
instance_variable_get("@#{target}")
|
36
|
+
)
|
37
|
+
message = "You don't have permission to " <<
|
38
|
+
"#{verb} this #{target}."
|
39
|
+
end
|
40
|
+
else
|
41
|
+
# current_user may be nil so must use try and NOT send
|
42
|
+
unless permission = current_user.try("may_#{permission_name}?")
|
43
|
+
message = "You don't have permission to " <<
|
44
|
+
"#{permission_name.gsub(/_/,' ')}."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# exclusive or
|
49
|
+
unless negate ^ permission
|
50
|
+
# if message is nil, negate will be true
|
51
|
+
message ||= "Access denied. May #{(negate)?'not ':''}" <<
|
52
|
+
"#{permission_name.gsub(/_/,' ')}."
|
53
|
+
ar = auth_redirections(full_permission_name)
|
54
|
+
access_denied(
|
55
|
+
(ar[:message]||message),
|
56
|
+
(ar[:redirect_to]||"/")
|
57
|
+
)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
method_missing_without_authorization(symb, *args, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end # CclsEngine
|
69
|
+
ActionController::Base.send(:include,Authorized::Authorization::Controller)
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'ssl_requirement'
|
2
|
+
module Authorized
|
3
|
+
module Controller
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
base.send(:include, SslRequirement)
|
8
|
+
# My ssl_required? overrides SslRequirement so MUST come AFTER!
|
9
|
+
base.send(:include, InstanceMethods)
|
10
|
+
base.class_eval do
|
11
|
+
class << self
|
12
|
+
alias_method_chain :inherited, :ccls_before_filters
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def inherited_with_ccls_before_filters(base)
|
22
|
+
identifier = 'ccls_ensure_proper_protocol'
|
23
|
+
unless filter_chain.select(&:before?).map(&:identifier
|
24
|
+
).include?(identifier)
|
25
|
+
before_filter :ensure_proper_protocol,
|
26
|
+
:identifier => identifier
|
27
|
+
end
|
28
|
+
# identifier = 'ccls_build_menu_js'
|
29
|
+
# unless filter_chain.select(&:before?).map(&:identifier
|
30
|
+
# ).include?(identifier)
|
31
|
+
# before_filter :build_menu_js,
|
32
|
+
# :identifier => identifier
|
33
|
+
# end
|
34
|
+
inherited_without_ccls_before_filters(base)
|
35
|
+
end
|
36
|
+
|
37
|
+
end # ClassMethods
|
38
|
+
|
39
|
+
module InstanceMethods
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def ssl_required?
|
44
|
+
# Force https everywhere (that doesn't have ssl_allowed set)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def redirect_to_referer_or_default(default)
|
49
|
+
redirect_to( session[:refer_to] ||
|
50
|
+
request.env["HTTP_REFERER"] || default )
|
51
|
+
session[:refer_to] = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Flash error message and redirect
|
55
|
+
def access_denied(
|
56
|
+
message="You don't have permission to complete that action.",
|
57
|
+
default=root_path )
|
58
|
+
session[:return_to] = request.request_uri
|
59
|
+
flash[:error] = message
|
60
|
+
redirect_to default
|
61
|
+
end
|
62
|
+
|
63
|
+
# # The menu is on every page and this seems as the
|
64
|
+
# # only way for me to force it into the application
|
65
|
+
# # layout.
|
66
|
+
# def build_menu_js
|
67
|
+
# js = "" <<
|
68
|
+
# "if ( typeof(translatables) == 'undefined' ){\n" <<
|
69
|
+
# " var translatables = [];\n" <<
|
70
|
+
# "}\n"
|
71
|
+
# Page.roots.each do |page|
|
72
|
+
# js << "" <<
|
73
|
+
# "tmp={tag:'#menu_#{dom_id(page)}',locales:{}};\n"
|
74
|
+
# %w( en es ).each do |locale|
|
75
|
+
# js << "tmp.locales['#{locale}']='#{page.menu(locale)}'\n"
|
76
|
+
# end
|
77
|
+
# js << "translatables.push(tmp);\n"
|
78
|
+
# end
|
79
|
+
# @template.content_for :head do
|
80
|
+
# @template.javascript_tag js
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
|
84
|
+
end # InstanceMethods
|
85
|
+
end # Controller
|
86
|
+
end # CclsEngine
|
87
|
+
ActionController::Base.send(:include,Authorized::Controller)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Authorized
|
2
|
+
module CoreExtension
|
3
|
+
|
4
|
+
def class_exists?(full_class_name)
|
5
|
+
name_spaces = full_class_name.to_s.split('::')
|
6
|
+
class_name = name_spaces.pop
|
7
|
+
name_space = name_spaces.join('::')
|
8
|
+
klass = ((name_space.blank?) ? Module : name_space.constantize).const_get(class_name.to_s)
|
9
|
+
return klass.is_a?(Class)
|
10
|
+
rescue NameError
|
11
|
+
return false
|
12
|
+
end
|
13
|
+
|
14
|
+
end # CoreExtension
|
15
|
+
end # Authorized
|
16
|
+
include Authorized::CoreExtension
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Factory.define :role do |f|
|
2
|
+
f.sequence(:name) { |n| "name#{n}" }
|
3
|
+
end
|
4
|
+
|
5
|
+
Factory.define :user do |f|
|
6
|
+
f.sequence(:uid) { |n| "UID#{n}" }
|
7
|
+
# f.sequence(:username) { |n| "username#{n}" }
|
8
|
+
# f.sequence(:email) { |n| "username#{n}@example.com" }
|
9
|
+
# f.password 'V@1!dP@55w0rd'
|
10
|
+
# f.password_confirmation 'V@1!dP@55w0rd'
|
11
|
+
# f.role_name 'user'
|
12
|
+
end
|
13
|
+
Factory.define :admin_user, :parent => :user do |f|
|
14
|
+
f.administrator true
|
15
|
+
end # parent must be defined first
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Authorized::FactoryTestHelper
|
2
|
+
|
3
|
+
def active_user(options={})
|
4
|
+
u = Factory(:user, options)
|
5
|
+
# leave this special save here just in case I change things.
|
6
|
+
# although this would need changed for UCB CAS.
|
7
|
+
# u.save_without_session_maintenance
|
8
|
+
# u
|
9
|
+
end
|
10
|
+
alias_method :user, :active_user
|
11
|
+
|
12
|
+
def superuser(options={})
|
13
|
+
u = active_user(options)
|
14
|
+
u.roles << Role.find_or_create_by_name('superuser')
|
15
|
+
u
|
16
|
+
end
|
17
|
+
alias_method :super_user, :superuser
|
18
|
+
|
19
|
+
def admin_user(options={})
|
20
|
+
u = active_user(options)
|
21
|
+
u.roles << Role.find_or_create_by_name('administrator')
|
22
|
+
u
|
23
|
+
end
|
24
|
+
alias_method :admin, :admin_user
|
25
|
+
alias_method :administrator, :admin_user
|
26
|
+
|
27
|
+
def interviewer(options={})
|
28
|
+
u = active_user(options)
|
29
|
+
u.roles << Role.find_or_create_by_name('interviewer')
|
30
|
+
u
|
31
|
+
end
|
32
|
+
|
33
|
+
def reader(options={})
|
34
|
+
u = active_user(options)
|
35
|
+
u.roles << Role.find_or_create_by_name('reader')
|
36
|
+
u
|
37
|
+
end
|
38
|
+
# alias_method :employee, :reader
|
39
|
+
|
40
|
+
def editor(options={})
|
41
|
+
u = active_user(options)
|
42
|
+
u.roles << Role.find_or_create_by_name('editor')
|
43
|
+
u
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
ActiveSupport::TestCase.send(:include,Authorized::FactoryTestHelper)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Authorized
|
2
|
+
module Helper
|
3
|
+
|
4
|
+
def user_roles
|
5
|
+
s = ''
|
6
|
+
if current_user.may_administrate?
|
7
|
+
s << "<ul>"
|
8
|
+
@roles.each do |role|
|
9
|
+
s << "<li>"
|
10
|
+
if @user.role_names.include?(role.name)
|
11
|
+
s << link_to( "Remove user role of '#{role.name}'",
|
12
|
+
user_role_path(@user,role.name),
|
13
|
+
:method => :delete )
|
14
|
+
else
|
15
|
+
s << link_to( "Assign user role of '#{role.name}'",
|
16
|
+
user_role_path(@user,role.name),
|
17
|
+
:method => :put )
|
18
|
+
end
|
19
|
+
s << "</li>\n"
|
20
|
+
end
|
21
|
+
s << "</ul>\n"
|
22
|
+
end
|
23
|
+
s
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
ActionView::Base.send(:include, Authorized::Helper)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Some code from jeremymcanally's "pending"
|
2
|
+
# http://github.com/jeremymcanally/pending/tree/master
|
3
|
+
|
4
|
+
module ActiveSupport
|
5
|
+
module Testing
|
6
|
+
module Pending
|
7
|
+
|
8
|
+
unless defined?(Spec)
|
9
|
+
|
10
|
+
@@pending_cases = []
|
11
|
+
@@at_exit = false
|
12
|
+
|
13
|
+
def pending(description = "", &block)
|
14
|
+
if description.is_a?(Symbol)
|
15
|
+
is_pending = $tags[description]
|
16
|
+
return block.call unless is_pending
|
17
|
+
end
|
18
|
+
|
19
|
+
if block_given?
|
20
|
+
failed = false
|
21
|
+
|
22
|
+
begin
|
23
|
+
block.call
|
24
|
+
rescue Exception
|
25
|
+
failed = true
|
26
|
+
end
|
27
|
+
|
28
|
+
flunk("<#{description}> did not fail.") unless failed
|
29
|
+
end
|
30
|
+
|
31
|
+
caller[0] =~ (/(.*):(.*):in `(.*)'/)
|
32
|
+
#puts caller.inspect
|
33
|
+
|
34
|
+
# looks like we lose the name of the 'method' in 1.9.1
|
35
|
+
#"/Users/jakewendt/github_repo/jakewendt/ucb_ccls_homex/test/unit/subject_test.rb:145:in `block in <class:SubjectTest>'",
|
36
|
+
|
37
|
+
# @@pending_cases << "#{$3} at #{$1}, line #{$2}"
|
38
|
+
# Gotta remember these as the next Regex will overwrite them.
|
39
|
+
filename = $1
|
40
|
+
linenumber = $2
|
41
|
+
# ruby 1.8.7
|
42
|
+
# Hx/Addresses Controller should NOT create new address with employee login and invalid address:
|
43
|
+
# ruby 1.9.1
|
44
|
+
#Hx/Addresses Controller block (2 levels) in <class:AddressesControllerTest>:
|
45
|
+
testmethod = $3
|
46
|
+
|
47
|
+
model = self.class.to_s.gsub(/Test$/,'').titleize
|
48
|
+
method = testmethod.gsub(/_/,' ').gsub(/^test /,'')
|
49
|
+
@@pending_cases << "#{model} #{method}:\n.\t#{filename} line #{linenumber}"
|
50
|
+
# @@pending_cases << "#{testmethod} at #{filename}, line #{linenumber}"
|
51
|
+
print "P"
|
52
|
+
|
53
|
+
@@at_exit ||= begin
|
54
|
+
at_exit do
|
55
|
+
# For some reason, special characters don't always
|
56
|
+
# print the way you would expect. Leading white space (tabs)
|
57
|
+
# and some carriage returns just weren't happening?
|
58
|
+
# Is this at_exit doing some parsing??
|
59
|
+
puts "\nPending Cases:"
|
60
|
+
@@pending_cases.each do |test_case|
|
61
|
+
puts test_case
|
62
|
+
end
|
63
|
+
puts " \n"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
ActiveSupport::TestCase.send(:include, ActiveSupport::Testing::Pending)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PermissiveController
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
module ClassMethods
|
6
|
+
def permissive(*args)
|
7
|
+
options = args.extract_options!
|
8
|
+
resource = options[:resource] || ActiveSupport::ModelName.new(
|
9
|
+
self.model_name.split('::').last.gsub(/Controller$/,'').singularize)
|
10
|
+
|
11
|
+
# remove NameSpace or create the may*required permission
|
12
|
+
|
13
|
+
before_filter "may_create_#{resource.plural}_required",
|
14
|
+
:only => [:new,:create]
|
15
|
+
before_filter "may_read_#{resource.plural}_required",
|
16
|
+
:only => [:show,:index]
|
17
|
+
before_filter "may_update_#{resource.plural}_required",
|
18
|
+
:only => [:edit,:update]
|
19
|
+
before_filter "may_destroy_#{resource.plural}_required",
|
20
|
+
:only => :destroy
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
ActionController::Base.send(:include,PermissiveController)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ResourcefulController
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
module ClassMethods
|
6
|
+
def resourceful(*args)
|
7
|
+
options = args.extract_options!
|
8
|
+
resource = options[:resource] || ActiveSupport::ModelName.new(
|
9
|
+
self.model_name.split('::').last.gsub(/Controller$/,'').singularize)
|
10
|
+
# self.model_name.gsub(/Controller$/,'').singularize)
|
11
|
+
|
12
|
+
permissive
|
13
|
+
|
14
|
+
before_filter :valid_id_required,
|
15
|
+
:only => [:show,:edit,:update,:destroy]
|
16
|
+
|
17
|
+
# by using before filters, the user
|
18
|
+
# can still add stuff to the action.
|
19
|
+
before_filter :get_all, :only => :index
|
20
|
+
before_filter :get_new, :only => :new
|
21
|
+
|
22
|
+
define_method :destroy do
|
23
|
+
instance_variable_get("@#{resource.singular}").send(:destroy)
|
24
|
+
redirect_to send("#{resource.plural}_path")
|
25
|
+
end
|
26
|
+
|
27
|
+
define_method :create do
|
28
|
+
begin
|
29
|
+
instance_variable_set("@#{resource.singular}",
|
30
|
+
resource.constantize.send(:new,params[resource.singular]))
|
31
|
+
instance_variable_get("@#{resource.singular}").send(:save!)
|
32
|
+
flash[:notice] = 'Success!'
|
33
|
+
redirect_to instance_variable_get("@#{resource.singular}")
|
34
|
+
rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid
|
35
|
+
flash.now[:error] = "There was a problem creating " <<
|
36
|
+
"the #{resource.singular}"
|
37
|
+
render :action => "new"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method :update do
|
42
|
+
begin
|
43
|
+
instance_variable_get("@#{resource.singular}").send(
|
44
|
+
:update_attributes!,params[resource.singular])
|
45
|
+
flash[:notice] = 'Success!'
|
46
|
+
redirect_to send(options[:update_redirect]||
|
47
|
+
"#{resource.plural}_path")
|
48
|
+
rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid
|
49
|
+
flash.now[:error] = "There was a problem updating " <<
|
50
|
+
"the #{resource.singular}"
|
51
|
+
render :action => "edit"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
define_method :get_all do
|
58
|
+
instance_variable_set("@#{resource.plural}",
|
59
|
+
resource.constantize.send(:all) )
|
60
|
+
end
|
61
|
+
|
62
|
+
define_method :get_new do
|
63
|
+
instance_variable_set("@#{resource.singular}",
|
64
|
+
resource.constantize.send(:new) )
|
65
|
+
end
|
66
|
+
|
67
|
+
define_method :valid_id_required do
|
68
|
+
if( !params[:id].blank? &&
|
69
|
+
resource.constantize.send(:exists?,params[:id]) )
|
70
|
+
instance_variable_set("@#{resource.singular}",
|
71
|
+
resource.constantize.send(:find,params[:id]) )
|
72
|
+
else
|
73
|
+
access_denied("Valid id required!",
|
74
|
+
send("#{resource.plural}_path") )
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
ActionController::Base.send(:include,ResourcefulController)
|