cbac 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Manifest +44 -0
  2. data/README.rdoc +48 -0
  3. data/Rakefile +36 -0
  4. data/cbac.gemspec +31 -0
  5. data/generators/cbac/USAGE +34 -0
  6. data/generators/cbac/cbac_generator.rb +45 -0
  7. data/generators/cbac/templates/config/context_roles.rb +10 -0
  8. data/generators/cbac/templates/config/privileges.rb +30 -0
  9. data/generators/cbac/templates/controllers/generic_roles_controller.rb +30 -0
  10. data/generators/cbac/templates/controllers/memberships_controller.rb +22 -0
  11. data/generators/cbac/templates/controllers/permissions_controller.rb +42 -0
  12. data/generators/cbac/templates/fixtures/cbac_generic_roles.yml +9 -0
  13. data/generators/cbac/templates/fixtures/cbac_memberships.yml +8 -0
  14. data/generators/cbac/templates/fixtures/cbac_permissions.yml +8 -0
  15. data/generators/cbac/templates/migrate/create_cbac.rb +40 -0
  16. data/generators/cbac/templates/stylesheets/cbac.css +65 -0
  17. data/generators/cbac/templates/views/generic_roles/index.html.erb +59 -0
  18. data/generators/cbac/templates/views/layouts/cbac.html.erb +17 -0
  19. data/generators/cbac/templates/views/memberships/_update.html.erb +12 -0
  20. data/generators/cbac/templates/views/memberships/index.html.erb +22 -0
  21. data/generators/cbac/templates/views/permissions/_update_context_role.html.erb +12 -0
  22. data/generators/cbac/templates/views/permissions/_update_generic_role.html.erb +12 -0
  23. data/generators/cbac/templates/views/permissions/index.html.erb +31 -0
  24. data/init.rb +11 -0
  25. data/lib/cbac.rb +104 -0
  26. data/lib/cbac/config.rb +10 -0
  27. data/lib/cbac/context_role.rb +27 -0
  28. data/lib/cbac/generic_role.rb +6 -0
  29. data/lib/cbac/membership.rb +4 -0
  30. data/lib/cbac/permission.rb +6 -0
  31. data/lib/cbac/privilege.rb +72 -0
  32. data/lib/cbac/privilege_set.rb +28 -0
  33. data/lib/cbac/privilege_set_record.rb +5 -0
  34. data/lib/cbac/setup.rb +31 -0
  35. data/tasks/cbac.rake +19 -0
  36. data/test/fixtures/cbac_generic_roles.yml +9 -0
  37. data/test/fixtures/cbac_memberships.yml +8 -0
  38. data/test/fixtures/cbac_permissions.yml +15 -0
  39. data/test/fixtures/cbac_privilege_set.yml +18 -0
  40. data/test/test_cbac_authorize_context_roles.rb +43 -0
  41. data/test/test_cbac_authorize_generic_roles.rb +37 -0
  42. data/test/test_cbac_context_role.rb +51 -0
  43. data/test/test_cbac_privilege.rb +99 -0
  44. data/test/test_cbac_privilege_set.rb +52 -0
  45. metadata +118 -0
@@ -0,0 +1,65 @@
1
+ /****
2
+ * General
3
+ */
4
+
5
+ div.cbac h1 {
6
+
7
+ }
8
+
9
+ div.cbac table {
10
+ padding: 0px;
11
+ margin: 0px;
12
+ border-collapse: collapse;
13
+ }
14
+
15
+ div.cbac div.linebreak {
16
+ height: 20px;
17
+ }
18
+
19
+ /****
20
+ * Table
21
+ */
22
+
23
+ div.cbac th {
24
+ background-color: #ABEB71;
25
+ }
26
+
27
+ div.cbac td, div.cbac th {
28
+ border: 1px solid black;
29
+ }
30
+
31
+ div.cbac td.small, div.cbac th.small {
32
+ width: 50px;
33
+ }
34
+
35
+ div.cbac td.medium, div.cbac th.medium {
36
+ width: 150px;
37
+ }
38
+
39
+ div.cbac td.large, div.cbac th.large {
40
+ width: 600px;
41
+ }
42
+
43
+ div.cbac td.checked {
44
+ text-align: center;
45
+ }
46
+
47
+ div.cbac td.submit {
48
+ text-align: right;
49
+ }
50
+
51
+ /****
52
+ * Controls
53
+ */
54
+
55
+ div.cbac input[type="text"] {
56
+ margin: 2px;
57
+ }
58
+
59
+ div.cbac td.medium input[type="text"] {
60
+ width: 138px;
61
+ }
62
+
63
+ div.cbac td.large input[type="text"] {
64
+ width: 588px;
65
+ }
@@ -0,0 +1,59 @@
1
+ <div class="cbac">
2
+ <h1>Generic roles</h1>
3
+ <table>
4
+ <tr>
5
+ <th class="medium">Name</th>
6
+ <th class="large">Remarks</th>
7
+ <th class="small">&nbsp;</th>
8
+ </tr>
9
+
10
+ <% Cbac::GenericRole.find(:all).each do |role| %>
11
+ <tr class="row">
12
+ <% form_for role do |r| %>
13
+ <td class="medium"><%= r.text_field :name %></td>
14
+ <td class="large"><%= r.text_field :remarks, :rows => 1 %></td>
15
+ <td class="small"><%= r.submit "OK" %></td>
16
+ <% end %>
17
+ </tr>
18
+ <% end%>
19
+ </table>
20
+
21
+ <div class="linebreak"></div>
22
+
23
+ <table>
24
+ <% form_for(Cbac::GenericRole.new) do |new_role| %>
25
+ <tr class="row">
26
+ <th colspan="2">New generic role</th>
27
+ </tr>
28
+ <tr class="row">
29
+ <td class="medium">Name</td>
30
+ <td class="medium"><%= new_role.text_field :name %></td>
31
+ </tr>
32
+ <tr class="row">
33
+ <td class="medium">Remarks</td>
34
+ <td class="large"><%= new_role.text_field :remarks %></td>
35
+ </tr>
36
+ <tr class="row">
37
+ <td colspan="2" class="submit"><%= new_role.submit "Create" %></td>
38
+ </tr>
39
+ <% end %>
40
+ </table>
41
+
42
+ <div class="linebreak"></div>
43
+
44
+ <table>
45
+ <% form_tag(:controller => "cbac/generic_roles", :action => "delete") do |f| %>
46
+ <tr>
47
+ <th colspan="2">Delete generic role</th>
48
+ </tr>
49
+ <tr>
50
+ <td class="medium">Select role</td>
51
+ <td class="medium"><%= select_tag "id", Cbac::GenericRole.find(:all).collect{|role|"<option value='#{role.id}'>#{role.name}</option>"} %>
52
+ </td>
53
+ </tr>
54
+ <tr>
55
+ <td colspan="2" class="submit"><%= submit_tag "Delete" %></td>
56
+ </tr>
57
+ <% end %>
58
+ </table>
59
+ </div>
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
7
+ <title>Context Based Access Control</title>
8
+ <%= javascript_include_tag :defaults %>
9
+ <%= stylesheet_link_tag "cbac" %>
10
+ </head>
11
+ <body>
12
+ <%= link_to "Permissions", cbac_permissions_path %>
13
+ <%= link_to "Generic roles", cbac_generic_roles_path %>
14
+ <%= link_to "Memberships", cbac_memberships_path %>
15
+ <%= yield %>
16
+ </body>
17
+ </html>
@@ -0,0 +1,12 @@
1
+ <% update_name = generic_role.id.to_s + "__" + user_id.to_s %>
2
+ <% unless update_partial %><div id="<%= update_name %>"><% end %>
3
+ <% remote_form_for "/cbac/memberships/update", :url => {:controller => "cbac/memberships", :action => "update"},
4
+ :update => update_name, :before => "$('#{update_name}').style.visibility = 'hidden';",
5
+ :complete => "$('#{update_name}').style.visibility = 'visible';" do %>
6
+ <%= hidden_field_tag "generic_role_id" + update_name, generic_role.id.to_s, :name => "generic_role_id" %>
7
+ <%= hidden_field_tag "user_id" + update_name, user_id.to_s, :name => "user_id" %>
8
+ <%= check_box_tag "member" + update_name, "1",
9
+ (Cbac::Membership.find(:all, :conditions => ["generic_role_id = ? AND user_id = ?", generic_role.id.to_s, user_id.to_s]).length > 0),
10
+ {:onclick => "this.form.onsubmit();", :name => "member"}%>
11
+ <% end %>
12
+ <% unless update_partial %></div><% end %>
@@ -0,0 +1,22 @@
1
+ <div class="cbac">
2
+ <h1>Memberships</h1>
3
+ <table>
4
+ <tr>
5
+ <th class="medium">Users</th>
6
+ <% @generic_roles.each do |role| %>
7
+ <th><%= role.name %></th>
8
+ <% end %>
9
+ </tr>
10
+ <% @users.each do |u| %>
11
+ <tr>
12
+ <td><%= u.name %></td>
13
+ <% @generic_roles.each do |generic_role| %>
14
+ <td class="checked">
15
+ <%= render :partial => "cbac/memberships/update.html", :locals => {:generic_role => generic_role,
16
+ :user_id => u.id.to_s,:update_partial => false} %>
17
+ </td>
18
+ <% end %>
19
+ </tr>
20
+ <% end %>
21
+ </table>
22
+ </div>
@@ -0,0 +1,12 @@
1
+ <% update_name = "cr__" + context_role.to_s + "__" + set_id.to_s %>
2
+ <% unless update_partial %><div id="<%= update_name %>"><% end %>
3
+ <% remote_form_for "/cbac/permissions/update", :url => cbac_permissions_update_path,
4
+ :update => update_name, :before => "$('#{update_name}').style.visibility = 'hidden';",
5
+ :complete => "$('#{update_name}').style.visibility = 'visible';" do %>
6
+ <%= hidden_field_tag "context_role" + update_name, context_role.to_s, :name => "context_role" %>
7
+ <%= hidden_field_tag "privilege_set_id" + update_name, set_id.to_s, :name => "privilege_set_id" %>
8
+ <%= check_box_tag "permission" + update_name, "1",
9
+ (Cbac::Permission.find(:all, :conditions => ["context_role = ? AND privilege_set_id = ?", context_role.to_s, set_id.to_s]).length > 0),
10
+ {:onclick => "this.form.onsubmit();", :name => "permission"}%>
11
+ <% end %>
12
+ <% unless update_partial %></div><% end %>
@@ -0,0 +1,12 @@
1
+ <% update_name = "gr__" + role.id.to_s + "__" + set_id.to_s %>
2
+ <% unless update_partial %><div id="<%= update_name %>"><% end %>
3
+ <% remote_form_for "/cbac/permissions/update", :url => cbac_permissions_update_path,
4
+ :update => update_name, :before => "$('#{update_name}').style.visibility = 'hidden';",
5
+ :complete => "$('#{update_name}').style.visibility = 'visible';" do %>
6
+ <%= hidden_field_tag "generic_role_id" + update_name, role.id.to_s, :name => "generic_role_id" %>
7
+ <%= hidden_field_tag "privilege_set_id" + update_name, set_id.to_s, :name => "privilege_set_id" %>
8
+ <%= check_box_tag "permission" + update_name, "1",
9
+ (Cbac::Permission.find(:all, :conditions => ["generic_role_id = ? AND privilege_set_id = ?", role.id.to_s, set_id.to_s]).length > 0),
10
+ {:onclick => "this.form.onsubmit();", :name => "permission"}%>
11
+ <% end %>
12
+ <% unless update_partial %></div><% end %>
@@ -0,0 +1,31 @@
1
+ <div class="cbac">
2
+ <h1>Permissions</h1>
3
+ <table>
4
+ <tr>
5
+ <th>Privilegeset</th>
6
+ <% @context_roles.each do |name, comment| %>
7
+ <th><%= name %></th>
8
+ <% end %>
9
+ <% @generic_roles.each do |role| %>
10
+ <th><%= role.name %></th>
11
+ <% end %>
12
+ </tr>
13
+ <% PrivilegeSet.sets.each do |token, set| %>
14
+ <tr>
15
+ <td><%= set.name %></td>
16
+ <% @context_roles.each do |context_role, comment| %>
17
+ <td class="checked">
18
+ <%= render :partial => "cbac/permissions/update_context_role.html", :locals => {:context_role => context_role.to_s,
19
+ :set_id => set.id.to_s, :update_partial => false} %>
20
+ </td>
21
+ <% end %>
22
+ <% @generic_roles.each do |role| %>
23
+ <td class="checked">
24
+ <%= render :partial => "cbac/permissions/update_generic_role.html", :locals => {:role => role,
25
+ :set_id => set.id.to_s, :update_partial => false} %>
26
+ </td>
27
+ <% end %>
28
+ </tr>
29
+ <% end %>
30
+ </table>
31
+ </div>
data/init.rb ADDED
@@ -0,0 +1,11 @@
1
+ # Configuration file
2
+ require File.dirname(__FILE__) + '/lib/cbac/config.rb'
3
+
4
+ # The following code contains configuration options. You can turn them on for
5
+ # gem development. For actual usage, it is advisable to set the configuration
6
+ # options in the environment files.
7
+ Cbac::Config.verbose = false
8
+
9
+ # Include CBAC core file
10
+ require File.dirname(__FILE__) + '/lib/cbac.rb'
11
+
@@ -0,0 +1,104 @@
1
+ # TODO: Check the permission table for double entries, ie: both an entry in the
2
+ # generic_role_id field and an entry in the context_role field. Solution: solve
3
+ # via model. Update model & add test
4
+
5
+
6
+ module Cbac
7
+ if Cbac::Setup.check
8
+ puts "CBAC properly installed"
9
+
10
+ require File.dirname(__FILE__) + '/cbac/privilege.rb'
11
+ require File.dirname(__FILE__) + '/cbac/privilege_set.rb'
12
+ require File.dirname(__FILE__) + '/cbac/context_role.rb'
13
+
14
+ # check performs a check to see if the user is allowed to access the given
15
+ # resource. Example: authorization_check("BlogController", "index", :get)
16
+ def authorization_check(controller, action, request, context = {})
17
+ # Determine the controller to look for
18
+ controller_method = [controller, action].join("/")
19
+ # Get the privilegesets
20
+ privilege_sets = Privilege.select(controller_method, request)
21
+ # Check the privilege sets
22
+ check_privilege_sets(privilege_sets, context)
23
+ end
24
+
25
+ # Check the given privilege_set symbol
26
+ # TODO following code is not yet tested
27
+ def check_privilege_set(privilege_set, context = {})
28
+ check_privilege_sets([PrivilegeSet.sets[privilege_set.to_sym]], context)
29
+ end
30
+
31
+ # Check the given privilege_sets
32
+ def check_privilege_sets(privilege_sets, context = {})
33
+ # Check the generic roles
34
+ return true if privilege_sets.any? { |set| Cbac::GenericRole.find(:all, :conditions => ["user_id= ? AND privilege_set_id = ?", current_user, set.id],:joins => [:generic_role_members, :permissions]).length > 0 }
35
+ # Check the context roles Get the permissions
36
+ privilege_sets.collect{|privilege_set|Cbac::Permission.find(:all, :conditions => ["privilege_set_id = ? AND generic_role_id = 0", privilege_set.id.to_s])}.flatten.each do |permission|
37
+ puts "Checking for context_role:#{permission.context_role} on privilege_set:#{permission.privilegeset.name}" if Cbac::Config.verbose
38
+ eval_string = ContextRole.roles[permission.context_role.to_sym]
39
+ # Not sure if this will work everywhere
40
+ return true if eval_string.call(context)
41
+ end
42
+ # not authorized
43
+ puts "Not authorized for: #{controller_method}" if Cbac::Config.verbose
44
+ false
45
+ end
46
+
47
+ # Code that performs authorization
48
+ def authorize
49
+ authorization_check(params[:controller], params[:action], request.request_method) || unauthorized
50
+ end
51
+
52
+ # Default unauthorized method Override this method to supply your own code
53
+ # for incorrect authorization
54
+ def unauthorized
55
+ render :text => "You are not authorized to perform this action", :status => 401
56
+ end
57
+
58
+ # Default implementation of the current_user method
59
+ def current_user
60
+ session[:currentuser].to_i
61
+ end
62
+
63
+ # Load controller classes and methods
64
+ def load_controller_methods
65
+ begin
66
+ Dir.glob("app/controllers/**/*.rb").each{|file| require file}
67
+ rescue LoadError
68
+ raise "Could not load controller classes"
69
+ end
70
+ # Make this iterative TODO
71
+ @classes = ApplicationController.subclasses
72
+ end
73
+
74
+ # Extracts the class name from the filename
75
+ def extract_class_name(filename)
76
+ File.basename(filename).chomp(".rb").camelize
77
+ end
78
+
79
+ # ### Initializer Include privileges file - contains the privilege and
80
+ # privilege definitions
81
+ begin
82
+ require File.join(RAILS_ROOT, "config", "privileges.rb")
83
+ rescue MissingSourceFile
84
+ puts "CBAC warning: Could not load config/privileges.rb (Did you run ./script/generate cbac)"
85
+ end
86
+ # Include context roles file - contains the context role definitions
87
+ begin
88
+ require File.join(RAILS_ROOT, "config", "context_roles.rb")
89
+ rescue MissingSourceFile
90
+ puts "CBAC warning: Could not load config/context_roles.rb (Did you run ./script/generate cbac)"
91
+ end
92
+
93
+ # ### Database autoload code
94
+ else
95
+ # This is the code that is executed if CBAc is not properly installed/
96
+ # configured. It includes a different authorize method, aimes at refusing
97
+ # all authorizations
98
+ def authorize
99
+ render :text => "Authorization error", :status => 401
100
+ false
101
+ end
102
+ end
103
+ end
104
+
@@ -0,0 +1,10 @@
1
+ module Cbac
2
+ # Class containing configuration options for the Cbac system. The following
3
+ # configuration options are supported: verbose. Determines whether or not to
4
+ # output results to the console. All outputs are processed as puts commands.
5
+ class Config
6
+ class << self
7
+ attr_accessor :verbose
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # ContextRole is the class containing the context role definitions
2
+ #
3
+ # Usage: ContextRole.add :logged_in_user, "!session[:currentuser].nil?"
4
+ class ContextRole
5
+ class << self
6
+ # Hash containing all the context roles. Keys are the role names Values are
7
+ # the Ruby eval strings Eval strings must result in true or false
8
+ attr_reader :roles
9
+
10
+ # Adds a context role to the list of context roles. @symbol defines the name
11
+ # of the context role @context_rule defines the ruby code to be evaluated
12
+ # when determining role membership
13
+ #
14
+ # If the context role already exists, an exception is thrown.
15
+ def add(symbol, context_rule = "", &block)
16
+ symbol = symbol.to_sym
17
+ @roles = Hash.new if @roles.nil?
18
+ raise ArgumentError, "CBAC: ContextRole was already defined" if @roles.keys.include?(symbol)
19
+ # TODO following code
20
+ #raise ArgumentError, "CBAC: cannot specify both string rule and block rule" unless context_rule.nil? and block.nil?
21
+ # TODO context parameter in block statement is not explicitly tested
22
+ block = eval("Proc.new {|context| " + context_rule + "}") if block.nil?
23
+ @roles[symbol] = block
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,6 @@
1
+ class Cbac::GenericRole < ActiveRecord::Base
2
+ set_table_name "cbac_generic_roles"
3
+
4
+ has_many :generic_role_members, :class_name => "Cbac::Membership", :foreign_key => "generic_role_id"
5
+ has_many :permissions, :class_name => "Cbac::Permission", :foreign_key => "generic_role_id"
6
+ end
@@ -0,0 +1,4 @@
1
+ class Cbac::Membership < ActiveRecord::Base
2
+ set_table_name "cbac_memberships"
3
+ belongs_to :generic_role, :class_name => "Cbac::GenericRole", :foreign_key => "generic_role_id"
4
+ end
@@ -0,0 +1,6 @@
1
+ class Cbac::Permission < ActiveRecord::Base
2
+ set_table_name "cbac_permissions"
3
+
4
+ belongs_to :generic_role, :class_name => "Cbac::GenericRole", :foreign_key => "generic_role_id"
5
+ belongs_to :privilegeset, :class_name => "Cbac::PrivilegeSetRecord", :foreign_key => "privilege_set_id"
6
+ end
@@ -0,0 +1,72 @@
1
+ # Class containing all the privileges
2
+ #
3
+ # To define a new controller method resource: Privilege.resource :privilegeset,
4
+ # "controller/method"
5
+ #
6
+ class Privilege
7
+ class << self
8
+ attr_reader :get_resources, :post_resources, :model_attributes, :models
9
+
10
+ # Links a resource with a PrivilegeSet
11
+ #
12
+ # An ArgumentError exception is thrown if the PrivilegeSet does not exist.
13
+ # To create PrivilegeSets, use the PrivilegeSet.add method
14
+ def resource(privilege_set, method, action="GET")
15
+ privilege_set = privilege_set.to_sym
16
+ @get_resources = Hash.new if @get_resources.nil?
17
+ @post_resources = Hash.new if @post_resources.nil?
18
+ action_aliases = {"GET" => ["GET", "get", "g","idempotent"], "POST" => ["POST", "post", "p"]}
19
+ raise ArgumentError, "CBAC: PrivilegeSet does not exist: #{privilege_set}" unless PrivilegeSet.sets.include?(privilege_set)
20
+ action_option = action_aliases.find { |name, aliases| aliases.include?(action.to_s) }
21
+ raise ArgumentError, "CBAC: Wrong value for argument 'action' in Privilege.resource: #{action}" if action_option.nil?
22
+ case action_option[0]
23
+ when "GET"
24
+ (@get_resources[method] ||= Array.new) << PrivilegeSet.sets[privilege_set]
25
+ when "POST"
26
+ (@post_resources[method] ||= Array.new) << PrivilegeSet.sets[privilege_set]
27
+ else
28
+ end
29
+ end
30
+
31
+ def model_attribute
32
+
33
+ end
34
+ def model
35
+
36
+ end
37
+
38
+ # Finds the privilege sets associated with the given controller_method and
39
+ # action_type Valid values for action_type are "get", "post" and "put".
40
+ # "put" is converted into "post".
41
+ #
42
+ # If incorrect values are given for action_type the method will raise an
43
+ # ArgumentError. If the controller and action name are not found, an
44
+ # exception is being raised.
45
+ def select(controller_method, action_type)
46
+ action_type = action_type.to_s
47
+ post_methods = ["post", "put", "delete"]
48
+ if action_type == "get"
49
+ privilege_sets = Privilege.get_resources[controller_method]
50
+ else if post_methods.include?(action_type)
51
+ privilege_sets = Privilege.post_resources[controller_method]
52
+ else
53
+ raise ArgumentError, "CBAC: Incorrect action_type: #{action_type}"
54
+ end
55
+ end
56
+ # Error handling if no privilege_sets were found
57
+ if privilege_sets.nil?
58
+ if action_type == "get"
59
+ if !Privilege.post_resources[controller_method].nil?
60
+ raise "CBAC: PrivilegeSets only exist for other action: post on method: #{controller_method}"
61
+ end
62
+ else
63
+ if !Privilege.get_resources[controller_method].nil?
64
+ raise "CBAC: PrivilegeSets only exist for other action: get on method: #{controller_method}"
65
+ end
66
+ end
67
+ raise "CBAC: Could not find any privilege sets associated with: #{controller_method} and action: #{action_type}"
68
+ end
69
+ privilege_sets
70
+ end
71
+ end
72
+ end