arsecurity 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +31 -0
- data/examples/initializers/security.rb +37 -0
- data/lib/arsecurity.rb +58 -0
- data/lib/arsecurity_default.rb +53 -0
- data/lib/arsecurity_handler.rb +24 -0
- data/lib/arsecurity_permission.rb +9 -0
- data/lib/arsecurity_util.rb +90 -0
- metadata +71 -0
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 [name of plugin creator]
|
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/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
PKG_NAME = "arsecurity"
|
4
|
+
PKG_VERSION = "0.1.0"
|
5
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
6
|
+
PKG_FILES = FileList[
|
7
|
+
'[A-Z]*',
|
8
|
+
'lib/**/*',
|
9
|
+
'examples/**/*'
|
10
|
+
]
|
11
|
+
spec = Gem::Specification.new do |s|
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
s.summary = "A security component for Activerecord"
|
14
|
+
s.name = PKG_NAME
|
15
|
+
s.version = PKG_VERSION
|
16
|
+
s.require_path = 'lib'
|
17
|
+
s.homepage = %q{http://arsecurity.rubyforge.org/}
|
18
|
+
s.rubyforge_project = 'Activerecord Security'
|
19
|
+
s.has_rdoc = false
|
20
|
+
s.authors = ["Leon Li"]
|
21
|
+
s.email = "scorpio_leon@hotmail.com"
|
22
|
+
s.files = PKG_FILES
|
23
|
+
s.description = <<-EOF
|
24
|
+
A security component for Activerecord, it can manage CRUD permissions with attribute level by configuration, you can implement RBAC easily with it. It depend on the AOP framework Rinter(Rinterceptor) and the OO query tool Rquerypad(Optinal)
|
25
|
+
EOF
|
26
|
+
s.add_dependency('rinterceptor', '>= 0.1.0')
|
27
|
+
end
|
28
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
29
|
+
pkg.need_zip = true
|
30
|
+
pkg.need_tar = true
|
31
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
ActiveRecord::Base.class_eval do
|
2
|
+
include ArsecurityDefault
|
3
|
+
def self.rinter_skip?
|
4
|
+
!YourContext.security_check
|
5
|
+
end
|
6
|
+
def rinter_skip?
|
7
|
+
!YourContext.security_check
|
8
|
+
end
|
9
|
+
end
|
10
|
+
def free_access(&block)
|
11
|
+
old_security_check = YourContext.security_check
|
12
|
+
YourContext.security_check = false
|
13
|
+
result = yield
|
14
|
+
YourContext.security_check = old_security_check
|
15
|
+
result
|
16
|
+
end
|
17
|
+
class YourSecurityHandler < DefaultArsecurityHandler
|
18
|
+
class << self
|
19
|
+
#override
|
20
|
+
def accept?
|
21
|
+
current_user && current_user.is_admin?
|
22
|
+
end
|
23
|
+
#override
|
24
|
+
def reject?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
#override
|
28
|
+
def permissions
|
29
|
+
current_user && current_user.permissions
|
30
|
+
end
|
31
|
+
|
32
|
+
def current_user
|
33
|
+
YourContext.your_user
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
ArsecurityUtil.handler = YourSecurityHandler
|
data/lib/arsecurity.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rinterceptor'
|
2
|
+
module Arsecurity
|
3
|
+
CREATE = 'create'
|
4
|
+
READ = 'read'
|
5
|
+
UPDATE = 'update'
|
6
|
+
DELETE = 'delete'
|
7
|
+
def rinter_update_around(invocation)
|
8
|
+
if ArsecurityUtil.authorized?(UPDATE, invocation.object.class.name, invocation.object, invocation)
|
9
|
+
return invocation.invoke
|
10
|
+
else
|
11
|
+
raise ArsecurityNotAuthorizedException
|
12
|
+
end
|
13
|
+
end
|
14
|
+
def rinter_create_around(invocation)
|
15
|
+
if ArsecurityUtil.authorized?(CREATE, invocation.object.class.name, invocation.object, invocation)
|
16
|
+
return invocation.invoke
|
17
|
+
else
|
18
|
+
raise ArsecurityNotAuthorizedException
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def rinter_delete_around(invocation)
|
22
|
+
if ArsecurityUtil.authorized?(DELETE, invocation.object.class.name, invocation.object, invocation)
|
23
|
+
return invocation.invoke
|
24
|
+
else
|
25
|
+
raise ArsecurityNotAuthorizedException
|
26
|
+
end
|
27
|
+
end
|
28
|
+
module ClassMethods
|
29
|
+
def rinter_update_around(invocation)
|
30
|
+
if ArsecurityUtil.authorized?(UPDATE, invocation.object.name, nil, invocation)
|
31
|
+
return invocation.invoke
|
32
|
+
else
|
33
|
+
raise ArsecurityNotAuthorizedException
|
34
|
+
end
|
35
|
+
end
|
36
|
+
def rinter_read_around(invocation)
|
37
|
+
if ArsecurityUtil.authorized?(READ, invocation.object.name, nil, invocation)
|
38
|
+
return invocation.invoke
|
39
|
+
else
|
40
|
+
raise ArsecurityNotAuthorizedException
|
41
|
+
end
|
42
|
+
end
|
43
|
+
def rinter_delete_around(invocation)
|
44
|
+
if ArsecurityUtil.authorized?(DELETE, invocation.object.name, nil, invocation)
|
45
|
+
return invocation.invoke
|
46
|
+
else
|
47
|
+
raise ArsecurityNotAuthorizedException
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
include Rinterceptor
|
52
|
+
end
|
53
|
+
|
54
|
+
class ArsecurityNotAuthorizedException < RuntimeError
|
55
|
+
end
|
56
|
+
|
57
|
+
class ArsecurityIllegalException < RuntimeError
|
58
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module ArsecurityDefault
|
4
|
+
#value can be regexp, symbol or string
|
5
|
+
CLASS_READ_METHOD = ["find_every", "count"]
|
6
|
+
CLASS_DELETE_METHOD = "delete_all"
|
7
|
+
CLASS_UPDATE_METHOD = "update_all"
|
8
|
+
INSTANCE_CREATE = {:create => :create}
|
9
|
+
INSTANCE_DELETE = {:delete => /^destroy$/}
|
10
|
+
INSTANCE_UPDATE = {:update => :update}
|
11
|
+
CLASS_READ = {:read => CLASS_READ_METHOD}
|
12
|
+
CLASS_DELETE = {:delete => CLASS_DELETE_METHOD}
|
13
|
+
CLASS_UPDATE = {:update => CLASS_UPDATE_METHOD}
|
14
|
+
INCLUDE_I_METHODS = INSTANCE_CREATE.merge(INSTANCE_UPDATE).merge(INSTANCE_DELETE)
|
15
|
+
INCLUDE_S_METHODS = CLASS_READ.merge(CLASS_UPDATE).merge(CLASS_DELETE)
|
16
|
+
def self.rinter_before_include_class(base)
|
17
|
+
base.instance_variable_set(:@include_i_methods, INCLUDE_I_METHODS)
|
18
|
+
base.instance_variable_set(:@include_s_methods, INCLUDE_S_METHODS)
|
19
|
+
end
|
20
|
+
include Arsecurity
|
21
|
+
end
|
22
|
+
|
23
|
+
class DefaultArsecurityHandler < ArsecurityHandler
|
24
|
+
class << self
|
25
|
+
def get_conditions(invocation)
|
26
|
+
case invocation.method
|
27
|
+
when *ArsecurityDefault::CLASS_READ_METHOD
|
28
|
+
args = invocation.args || []
|
29
|
+
options = args.extract_options!
|
30
|
+
invocation.args = args
|
31
|
+
invocation.options = options
|
32
|
+
conditions = options[:conditions]
|
33
|
+
when ArsecurityDefault::CLASS_DELETE_METHOD
|
34
|
+
conditions = invocation.args[0]
|
35
|
+
when ArsecurityDefault::CLASS_UPDATE_METHOD
|
36
|
+
conditions = invocation.args[1]
|
37
|
+
end
|
38
|
+
conditions
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_conditions(invocation, conditions)
|
42
|
+
case invocation.method
|
43
|
+
when *ArsecurityDefault::CLASS_READ_METHOD
|
44
|
+
options = invocation.options
|
45
|
+
options[:conditions] = conditions
|
46
|
+
when ArsecurityDefault::CLASS_DELETE_METHOD
|
47
|
+
invocation.args[0] = conditions
|
48
|
+
when ArsecurityDefault::CLASS_UPDATE_METHOD
|
49
|
+
invocation.args[1] = conditions
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class ArsecurityHandler
|
2
|
+
class << self
|
3
|
+
#for customize logic, such as for administrator
|
4
|
+
def accept?
|
5
|
+
raise ArsecurityIllegalException.new("ArsecurityHandler.accept? should be implemented")
|
6
|
+
end
|
7
|
+
#for customize logic, such as for time restriction
|
8
|
+
def reject?
|
9
|
+
raise ArsecurityIllegalException.new("ArsecurityHandler.reject? should be implemented")
|
10
|
+
end
|
11
|
+
def permissions
|
12
|
+
raise ArsecurityIllegalException.new("ArsecurityHandler.permissions should be implemented")
|
13
|
+
end
|
14
|
+
|
15
|
+
#for changing conditions
|
16
|
+
def get_conditions(invocation)
|
17
|
+
raise ArsecurityIllegalException.new("ArsecurityHandler.get_conditions should be implemented")
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_conditions(invocation, conditions)
|
21
|
+
raise ArsecurityIllegalException.new("ArsecurityHandler.set_conditions should be implemented")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class ArsecurityPermission
|
2
|
+
attr_accessor :type, :method, :instance_condition, :sql_condition
|
3
|
+
def initialize(permission)
|
4
|
+
@type = permission[:type]
|
5
|
+
@method = permission[:method]
|
6
|
+
@instance_condition = permission[:instance_condition]
|
7
|
+
@sql_condition = permission[:sql_condition]
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class ArsecurityUtil
|
2
|
+
class << self
|
3
|
+
attr_accessor :handler
|
4
|
+
def authorized?(method, type, instance, invocation)
|
5
|
+
return true if handler.accept?
|
6
|
+
return false if handler.reject?
|
7
|
+
result = false
|
8
|
+
permissions = handler.permissions
|
9
|
+
|
10
|
+
unless permissions.nil? || permissions.empty?
|
11
|
+
result = check_permissions(permissions, method, type, instance, invocation)
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
|
16
|
+
def check_permissions(permissions, method, type, instance, invocation)
|
17
|
+
|
18
|
+
permissions.each do |permission|
|
19
|
+
permission = ArsecurityPermission.new(permission) if permission.is_a?(Hash)
|
20
|
+
next if permission.type != type
|
21
|
+
unless permission.method.nil?
|
22
|
+
next if permission.method != method
|
23
|
+
end
|
24
|
+
#instance not nil mean persist
|
25
|
+
|
26
|
+
unless instance.nil?
|
27
|
+
if permission.instance_condition.nil? || permission.instance_condition.empty?
|
28
|
+
return true
|
29
|
+
else
|
30
|
+
result = ERB.new("<% result = (#{permission.instance_condition}) ? true : false %><%= result %>").result(permission.send(:binding))
|
31
|
+
return true if result == 'true'
|
32
|
+
end
|
33
|
+
else
|
34
|
+
#singleton methods, mean has permission to do this action, but check if there is any restriction need be attached
|
35
|
+
unless permission.sql_condition.nil? || permission.sql_condition.empty?
|
36
|
+
conditions = handler.get_conditions(invocation)
|
37
|
+
if conditions.nil? || conditions.empty?
|
38
|
+
conditions = permission.sql_condition
|
39
|
+
elsif conditions.is_a?(String)
|
40
|
+
conditions = "(" << conditions << ") and (" << permission.sql_condition << ")"
|
41
|
+
elsif conditions.is_a?(Array)
|
42
|
+
conditions[0] = "(" << conditions[0] << ") and (" << permission.sql_condition << ")"
|
43
|
+
elsif conditions.is_a?(Hash)
|
44
|
+
new_conditions = []
|
45
|
+
new_conditions[0] = ""
|
46
|
+
conditions.each do |k, v|
|
47
|
+
new_conditions[0] << " #{k} = #{attribute_condition(v)}"
|
48
|
+
if v.is_a?(Range)
|
49
|
+
new_conditions << v.first
|
50
|
+
new_conditions << v.last
|
51
|
+
else
|
52
|
+
new_conditions << v
|
53
|
+
end
|
54
|
+
end
|
55
|
+
conditions = new_conditions
|
56
|
+
conditions[0] = "(" << conditions[0] << ") and (" << permission.sql_condition << ")"
|
57
|
+
end
|
58
|
+
handler.set_conditions(invocation, conditions)
|
59
|
+
end
|
60
|
+
return true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_permission(permission, method, type, instance)
|
67
|
+
return false if permission.type != type
|
68
|
+
|
69
|
+
unless permission.method.nil?
|
70
|
+
return false if permission.method != method
|
71
|
+
end
|
72
|
+
|
73
|
+
if permission.instance_condition.blank?
|
74
|
+
return true
|
75
|
+
else
|
76
|
+
result = ERB.new("<% result = (#{permission.instance_condition}) ? true : false %><%= result %>").result(permission.send(:binding))
|
77
|
+
return result == 'true'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def attribute_condition(argument)
|
82
|
+
case argument
|
83
|
+
when nil then "IS ?"
|
84
|
+
when Array, ActiveRecord::Associations::AssociationCollection then "IN (?)"
|
85
|
+
when Range then "BETWEEN ? AND ?"
|
86
|
+
else "= ?"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: arsecurity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Leon Li
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-17 00:00:00 +08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rinterceptor
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.1.0
|
24
|
+
version:
|
25
|
+
description: A security component for Activerecord, it can manage CRUD permissions with attribute level by configuration, you can implement RBAC easily with it. It depend on the AOP framework Rinter(Rinterceptor) and the OO query tool Rquerypad(Optinal)
|
26
|
+
email: scorpio_leon@hotmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- MIT-LICENSE
|
35
|
+
- Rakefile
|
36
|
+
- CHANGELOG
|
37
|
+
- lib/arsecurity.rb
|
38
|
+
- lib/arsecurity_permission.rb
|
39
|
+
- lib/arsecurity_handler.rb
|
40
|
+
- lib/arsecurity_default.rb
|
41
|
+
- lib/arsecurity_util.rb
|
42
|
+
- examples/initializers
|
43
|
+
- examples/initializers/security.rb
|
44
|
+
has_rdoc: false
|
45
|
+
homepage: http://arsecurity.rubyforge.org/
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project: Activerecord Security
|
66
|
+
rubygems_version: 1.3.1
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: A security component for Activerecord
|
70
|
+
test_files: []
|
71
|
+
|