has_permission 0.2.8

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/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 8
3
+ :major: 0
4
+ :minor: 2
@@ -0,0 +1,39 @@
1
+ module ActionController
2
+ module Has
3
+ module Permission
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def requires_permission(*args)
11
+ options = args.extract_options!
12
+ args.each do |target|
13
+ self.class_eval <<-_RUBY
14
+ def #{target}_with_permission
15
+ klass = #{options[:class] || "controller_name.classify.constantize"}
16
+ klass.with_permission(current_user).can_#{target}? || #{options[:access_denied] || "access_denied"}
17
+ #{target}_without_permission
18
+ end
19
+ _RUBY
20
+ alias_method_chain target, "permission"
21
+ end
22
+ end
23
+
24
+ def permission_method_chain(*args)
25
+ args.each do |target|
26
+ self.class_eval <<-_RUBY
27
+ def #{target}_with_permission
28
+ #{target}_without_permission.with_permission(current_user)
29
+ end
30
+ _RUBY
31
+ alias_method_chain target, "permission"
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,61 @@
1
+ module ActiveRecord
2
+ module Has
3
+ module Permission
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def has_permission(options = {})
11
+ class_eval <<-_DEF
12
+ def self.permission_namespace
13
+ "#{options[:namespace] || 'Permission'}"
14
+ end
15
+ _DEF
16
+ extend ActiveRecord::Has::Permission::SingletonMethods
17
+ include ActiveRecord::Has::Permission::InstanceMethods
18
+ end
19
+
20
+ end
21
+
22
+ module SingletonMethods
23
+
24
+ def with_permission(user)
25
+ permission_class.new :user => user, :object => self
26
+ end
27
+
28
+ def permission_class
29
+ if respond_to?(:base_class)
30
+ begin
31
+ [permission_namespace, "#{self.to_s}Permission"].join('::').constantize
32
+ rescue
33
+ [permission_namespace ,"#{self.base_class.to_s}Permission"].join('::').constantize
34
+ end
35
+ elsif respond_to?(:proxy_reflection)
36
+ begin
37
+ [permission_namespace, "#{self.proxy_reflection.class_name}Permsission"].join('::').constantize
38
+ rescue
39
+ [permission_namespace, "#{self.proxy_reflection.class_name.constantize.base_class.to_s}Permission"].join('::').constantize
40
+ end
41
+ else
42
+ begin
43
+ [permission_namespace, "#{self.to_s}Permission"].join('::').constantize
44
+ rescue
45
+ [permission_namespace, "#{self.superclass.to_s}Permission"].join('::').constantize
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ module InstanceMethods
52
+
53
+ def with_permission(user)
54
+ self.class.permission_class.new :user => user, :object => self
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_record'
2
+ require 'active_record/has/permission'
3
+ require 'action_controller'
4
+ require 'action_controller/has/permission'
5
+ require 'permission/base'
6
+ require 'permission/array'
7
+ require 'permission_exception'
8
+
9
+ ActiveRecord::Base.send :include, ActiveRecord::Has::Permission
10
+ ActionController::Base.send :include, ActionController::Has::Permission
11
+ Array.send :include, Permission::Array
@@ -0,0 +1,15 @@
1
+ module Permission
2
+ module Array
3
+
4
+ # helper method for adding to an array based on permission i.e. [].push_if("some feature", Feature.with_permission(current_user).can_write?)
5
+ def push_if(*args)
6
+ raise ArgumentError.new "wrong number of arguments (#{args.length} for 2)" if args.length < 2
7
+ if args.pop
8
+ push(*args)
9
+ else
10
+ self
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,85 @@
1
+ module Permission
2
+ class Base
3
+ require 'forwardable'
4
+ extend Forwardable
5
+
6
+ # delegate important object methods so they don't return the values for the proxy - am I missing any??
7
+ def_delegators :@object, :==, :=~, :class, :enum_for, :eql?, :new, :freeze, :frozen?,
8
+ :hash, :id, :instance_of?, :is_a?, :kind_of?,
9
+ :method, :methods, :object_id, :respond_to?, :send,
10
+ :taint, :tainted?, :to_a, :to_enum, :to_s, :to_yaml, :to_yaml_properties, :to_yaml_style,
11
+ :type, :untaint, :to_param
12
+
13
+ attr_accessor :user, :object
14
+
15
+ DEFAULT_FINDERS = /^((find|with).*|first|last|all|children|paginate|scoped|count)$/
16
+ DEFAULT_SEARCHES = /^search_.*/
17
+
18
+ def initialize(params)
19
+ @user = params[:user]
20
+ @object = params[:object]
21
+ end
22
+
23
+ def method_missing(method, *args)
24
+ object.send(method, *args)
25
+ end
26
+
27
+ def update_attribute(name, value)
28
+ if can_write?(name)
29
+ object.update_attribute(name, value)
30
+ else
31
+ raise PermissionException.new("#{user} does not have permission to access #{name} on #{object}")
32
+ end
33
+ end
34
+
35
+ def update_attributes(attributes)
36
+ object.update_attributes(attributes.reject{|key,value| !can_write?(key) })
37
+ end
38
+
39
+ def attribute_names
40
+ object.attribute_names.reject{|key| !can_read?(key) }
41
+ end
42
+
43
+ def column_names
44
+ object.column_names.reject{|key| !can_read?(key) }
45
+ end
46
+
47
+ def attributes
48
+ object.attributes.reject{|key,value| !can_read?(key) }
49
+ end
50
+
51
+ def read_attribute(attr_name)
52
+ if can_read?(attr_name)
53
+ object.read_attribute(attr_name)
54
+ else
55
+ raise PermissionException.new("#{user} does not have permission to access #{attr_name} on #{object}")
56
+ end
57
+ end
58
+
59
+ def write_attribute(attr_name, value)
60
+ if can_write?(attr_name)
61
+ object.write_attribute(attr_name, value)
62
+ else
63
+ raise PermissionException.new("#{user} does not have permission to access #{attr_name} on #{object}")
64
+ end
65
+ end
66
+
67
+ def can_read?(attr_name)
68
+ true
69
+ end
70
+
71
+ def can_write?(attr_name)
72
+ true
73
+ end
74
+
75
+ protected
76
+
77
+ def check_roles(user, roles, object)
78
+ roles.each do |role|
79
+ return true if user != nil && user.has_role?(user, object)
80
+ end
81
+ false
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,2 @@
1
+ class PermissionException < Exception
2
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ArrayTest < Test::Unit::TestCase
4
+
5
+ should "have method push_if" do
6
+ assert_equal true, [].methods.include?("push_if")
7
+ end
8
+
9
+ should "not push if condition is false" do
10
+ assert_equal [], [].push_if("test", false)
11
+ end
12
+
13
+ should "push if condition is true" do
14
+ assert_equal ["test"], [].push_if("test", true)
15
+ end
16
+ end
@@ -0,0 +1,133 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class HasPermissionTest < Test::Unit::TestCase
4
+ context "model instance" do
5
+ setup do
6
+ @model = Model.new
7
+ end
8
+
9
+ should "not intercept method" do
10
+ assert_equal "no permission", @model.some_method
11
+ end
12
+
13
+ should "intercept method" do
14
+ assert_equal "with permission", @model.with_permission(nil).some_method
15
+ end
16
+
17
+ should "use model id" do
18
+ assert_equal @model.id, @model.with_permission(nil).id
19
+ end
20
+
21
+ should "use model to_param" do
22
+ assert_equal @model.to_param, @model.with_permission(nil).to_param
23
+ end
24
+
25
+ should "use model class" do
26
+ assert_equal @model.class, @model.with_permission(nil).class
27
+ end
28
+
29
+ should "use model ==" do
30
+ assert @model.with_permission(nil) == @model
31
+ end
32
+
33
+ should "use model eql?" do
34
+ assert @model.with_permission(nil).eql?(@model)
35
+ end
36
+
37
+ should "throw PermissionException for attribute that does not allow reading" do
38
+ assert_raise PermissionException do
39
+ @model.with_permission(nil).read_attribute(:no_access)
40
+ end
41
+ end
42
+
43
+ should "allow access for readable attribute" do
44
+ @model.with_permission(nil).read_attribute(:read_access)
45
+ end
46
+
47
+ should "throw PermissionException for attribute that does not allow writing" do
48
+ assert_raise PermissionException do
49
+ @model.with_permission(nil).write_attribute(:no_access, "test")
50
+ end
51
+ end
52
+
53
+ should "allow access for writeable attribute" do
54
+ @model.with_permission(nil).write_attribute(:write_access, "test")
55
+ end
56
+
57
+ should "only allow writeable attribute for update attributes" do
58
+ @model.expects(:update_attributes).with(:write_access => "test")
59
+ @model.with_permission(nil).update_attributes(:no_access => "test", :write_access => "test")
60
+ end
61
+
62
+ should "only allow readable attributes for attributes" do
63
+ assert_equal [:read_access], @model.with_permission(nil).attributes.keys
64
+ end
65
+
66
+ should "only allow readable attributes for attribute_names" do
67
+ assert_equal [:read_access], @model.with_permission(nil).attribute_names
68
+ end
69
+
70
+ should "only allow readable attributes for column_names" do
71
+ assert_equal [:read_access], @model.with_permission(nil).column_names
72
+ end
73
+
74
+ should "only allow writeable attribute for update attribute" do
75
+ assert_raise PermissionException do
76
+ @model.with_permission(nil).update_attribute(:no_access, "test")
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ context "model class" do
83
+ should "not intercept method" do
84
+ assert_equal ["no permission"], Model.all
85
+ end
86
+
87
+ should "intercept method" do
88
+ assert_equal ["with permission"], Model.with_permission(nil).all
89
+ end
90
+ end
91
+
92
+ context "controller permission" do
93
+ setup do
94
+ @controller = ModelController.new
95
+ end
96
+
97
+ should "call access denied for index" do
98
+ @controller.expects(:access_denied).once
99
+ @controller.index
100
+ end
101
+
102
+ context "with options" do
103
+ setup do
104
+ @controller2 = Model2Controller.new
105
+ end
106
+
107
+ should "call no access for index" do
108
+ @controller2.expects(:no_access).once
109
+ @controller2.index
110
+ end
111
+
112
+ end
113
+ end
114
+
115
+ should "use default namespace setting" do
116
+ assert_equal Permission::ModelPermission, Model.permission_class
117
+ end
118
+
119
+ should "override default namespace setting" do
120
+ assert_equal ModelBPermission, ModelB.permission_class
121
+ end
122
+
123
+ should "use class to_s method" do
124
+ assert_equal Model.to_s, Model.with_permission(nil).to_s
125
+ end
126
+
127
+ should "use class class method" do
128
+ assert_equal Model.class, Model.with_permission(nil).class
129
+ end
130
+
131
+ # TODO need to test proxy associations somehow
132
+
133
+ end
@@ -0,0 +1,149 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'has_permission'
8
+
9
+ # this doesn't seem like the best way to do this - maybe someone will suggest a better method
10
+
11
+ class Model
12
+
13
+ include ActiveRecord::Has::Permission
14
+
15
+ has_permission
16
+
17
+ def read_attribute(attr_name)
18
+ "test"
19
+ end
20
+
21
+ def write_attribute(attr_name, value)
22
+ value
23
+ end
24
+
25
+ def column_names
26
+ [:read_access, :no_access]
27
+ end
28
+
29
+ def attribute_names
30
+ [:read_access, :no_access]
31
+ end
32
+
33
+ def attributes
34
+ {:read_access => "test", :no_access => "test"}
35
+ end
36
+
37
+ def some_method
38
+ "no permission"
39
+ end
40
+
41
+ def self.all
42
+ ["no permission"]
43
+ end
44
+
45
+ end
46
+
47
+ class ModelB
48
+ include ActiveRecord::Has::Permission
49
+
50
+ has_permission :namespace => ''
51
+ end
52
+
53
+ class ModelBPermission < Permission::Base
54
+ def can_index?
55
+ false
56
+ end
57
+
58
+ def can_show?
59
+ true
60
+ end
61
+ end
62
+
63
+ module Permission
64
+ class ModelPermission < Permission::Base
65
+
66
+ def can_index?
67
+ false
68
+ end
69
+
70
+ def can_show?
71
+ true
72
+ end
73
+
74
+ def can_read?(attr_name)
75
+ case attr_name
76
+ when :read_access : true
77
+ when :no_access : false
78
+ else true
79
+ end
80
+ end
81
+
82
+ def can_write?(attr_name)
83
+ case attr_name.to_s
84
+ when "write_access" : true
85
+ when "no_access" : false
86
+ else true
87
+ end
88
+ end
89
+
90
+ def some_method
91
+ "with permission"
92
+ end
93
+
94
+ def method_missing(method, *args)
95
+ if method.to_s.match(DEFAULT_FINDERS)
96
+ ["with permission"]
97
+ else
98
+ object.send(method, *args)
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ class ModelController
105
+ include ActionController::Has::Permission
106
+
107
+ def controller_name
108
+ "model"
109
+ end
110
+
111
+ def index
112
+ "index"
113
+ end
114
+
115
+ def show
116
+ "show"
117
+ end
118
+
119
+ def current_user
120
+ nil
121
+ end
122
+
123
+ requires_permission :index, :show
124
+ end
125
+
126
+ class Model2Controller
127
+ include ActionController::Has::Permission
128
+
129
+ def controller_name
130
+ "model"
131
+ end
132
+
133
+ def index
134
+ "index"
135
+ end
136
+
137
+ def show
138
+ "show"
139
+ end
140
+
141
+ def current_user
142
+ nil
143
+ end
144
+
145
+ requires_permission :index, :show, :access_denied => 'no_access', :class => ModelB
146
+ end
147
+
148
+ class Test::Unit::TestCase
149
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_permission
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.8
5
+ platform: ruby
6
+ authors:
7
+ - Brian Johnson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-26 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Fine grained access control based on method interception. This is a proxy that allows you to define permissions on your models without breaking MVC by putting user code in your models.
17
+ email: github@brianjohnson.cc
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - VERSION.yml
26
+ - lib/action_controller/has/permission.rb
27
+ - lib/active_record/has/permission.rb
28
+ - lib/has_permission.rb
29
+ - lib/permission/array.rb
30
+ - lib/permission/base.rb
31
+ - lib/permission_exception.rb
32
+ - test/array_test.rb
33
+ - test/has_permission_test.rb
34
+ - test/test_helper.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/johnsbrn/has_permission
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --inline-source
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: Access control library
64
+ test_files: []
65
+