turnstile 0.1.0
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/Manifest +7 -0
- data/README.rdoc +57 -0
- data/Rakefile +14 -0
- data/lib/turnstile/authorization.rb +155 -0
- data/lib/turnstile/role.rb +133 -0
- data/lib/turnstile/rule.rb +34 -0
- data/lib/turnstile.rb +3 -0
- data/turnstile.gemspec +30 -0
- metadata +84 -0
data/Manifest
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
Turnstile is a simple authorization module.
|
2
|
+
With turnstile you'll be able to define rules for each role to access your controllers and views.
|
3
|
+
|
4
|
+
= Roles, Rules and Privileges
|
5
|
+
|
6
|
+
You can define all roles, all rules and all privileges in the config file, placed in config/initializers/turnstile.rb
|
7
|
+
|
8
|
+
= Privileges
|
9
|
+
|
10
|
+
|
11
|
+
privilege :read do
|
12
|
+
allows_to :show, :index
|
13
|
+
denies_to :destroy, :create
|
14
|
+
end
|
15
|
+
|
16
|
+
privilege :manage do
|
17
|
+
allows_to :create, :new
|
18
|
+
allows_to :destroy
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
= Rules to Roles
|
23
|
+
|
24
|
+
role :reader do
|
25
|
+
can :read => :posts
|
26
|
+
can :read => :comments
|
27
|
+
end
|
28
|
+
|
29
|
+
role :admin do
|
30
|
+
inherits :reader
|
31
|
+
can :manage => :posts
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
= The Default Role
|
36
|
+
|
37
|
+
|
38
|
+
You need to set a role to be used when the current user has no role
|
39
|
+
|
40
|
+
default :reader
|
41
|
+
|
42
|
+
|
43
|
+
An example of config file can be found in config/initializers/turnstile.rb in this repo.
|
44
|
+
|
45
|
+
= The User Model
|
46
|
+
|
47
|
+
To set the model, so far it is hardcoded, so you need a string column called
|
48
|
+
user_role
|
49
|
+
in your user model, and be sure to have a method that returns the current user, called
|
50
|
+
current_user
|
51
|
+
|
52
|
+
= Controllers
|
53
|
+
For each controller that you want to monirate just call:
|
54
|
+
|
55
|
+
before_filter :verify_role_permissions!
|
56
|
+
|
57
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('turnstile', '0.1.0') do |p|
|
6
|
+
p.description = "Simple authorization for rails"
|
7
|
+
p.url = "http://github.com/milare/turnstile"
|
8
|
+
p.author = "Bruno Milare"
|
9
|
+
p.email = "milare@gmail.com"
|
10
|
+
p.ignore_pattern = ["spec/*", "config/initializers/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# Turnstile::Authorization
|
2
|
+
# Module that is used as interface to the application
|
3
|
+
module Turnstile
|
4
|
+
module Authorization
|
5
|
+
|
6
|
+
# Keep track of all permissions defined
|
7
|
+
# A permission is defined in the setup with such as:
|
8
|
+
#
|
9
|
+
# privilege :manage do
|
10
|
+
# allows_to :create, :new
|
11
|
+
# end
|
12
|
+
@@permissions = {}
|
13
|
+
|
14
|
+
# used in tests so far, to perform the setup
|
15
|
+
def self.read_config_file
|
16
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../config/initializers/turnstile')
|
17
|
+
end
|
18
|
+
|
19
|
+
# Run the config file with all definitions
|
20
|
+
def self.setup(&block)
|
21
|
+
yield if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
# Used by tests to reset all configurations done
|
25
|
+
def self.reset
|
26
|
+
@@permissions = {}
|
27
|
+
Role.clear
|
28
|
+
end
|
29
|
+
|
30
|
+
# Finds a permission
|
31
|
+
# Turnstile::Authorization.find_permission(:manage)
|
32
|
+
def self.find_permission(permission_name)
|
33
|
+
@@permissions[permission_name]
|
34
|
+
end
|
35
|
+
|
36
|
+
# From setup defines the default role
|
37
|
+
# If the role is not found tries to define
|
38
|
+
# common names for guests
|
39
|
+
# The default role is used when there is no current role
|
40
|
+
def default(role_str)
|
41
|
+
role = Role.find(role_str.to_sym)
|
42
|
+
if !role
|
43
|
+
role ||= Role.find(:guest)
|
44
|
+
role ||= Role.find(:visitor)
|
45
|
+
if !role
|
46
|
+
Role.set_default_role(role)
|
47
|
+
else
|
48
|
+
Role.set_default_role(Role.first)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
Role.set_default_role(role)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Method used in each controller that requires authorization
|
57
|
+
# Actually it handles the requests performed by the controller
|
58
|
+
# and then check if the current_role id allowed to perfom the action
|
59
|
+
def verify_role_permissions!
|
60
|
+
if request || request.params
|
61
|
+
action = request.params[:action] ? request.params[:action] : nil
|
62
|
+
controller = request.params[:controller] ? request.params[:controller] : nil
|
63
|
+
if action and controller
|
64
|
+
if !current_role.is_allowed_to? action, controller
|
65
|
+
flash[:alert] = "Unauthorized action"
|
66
|
+
redirect_to root_path
|
67
|
+
end
|
68
|
+
else
|
69
|
+
redirect_to root_path
|
70
|
+
end
|
71
|
+
else
|
72
|
+
redirect_to root_path
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the default role in this scope
|
77
|
+
def default_role
|
78
|
+
Role.default_role
|
79
|
+
end
|
80
|
+
|
81
|
+
# Helper to get current_role
|
82
|
+
# TODO: Find a way to get the current_user role in a dynamic way
|
83
|
+
# This way like current_user.user_role is too hardcoded
|
84
|
+
def current_role
|
85
|
+
current_role = current_user ? Role.find(current_user.user_role.to_sym) : nil
|
86
|
+
current_role ||= Role.default_role
|
87
|
+
end
|
88
|
+
|
89
|
+
# Set the current_role
|
90
|
+
def current_role=(role)
|
91
|
+
current_role = role
|
92
|
+
end
|
93
|
+
|
94
|
+
# Methods to define a permission
|
95
|
+
# privilege :manage do
|
96
|
+
# allows_to :create, :new
|
97
|
+
# denies_to :destroy
|
98
|
+
# end
|
99
|
+
def privilege(name, &block)
|
100
|
+
@current_permission = name
|
101
|
+
@@permissions[@current_permission] = {}
|
102
|
+
yield if block_given?
|
103
|
+
end
|
104
|
+
|
105
|
+
def allows_to(*actions)
|
106
|
+
@@permissions[@current_permission][:allow] ||= []
|
107
|
+
actions.each do |action|
|
108
|
+
@@permissions[@current_permission][:allow] << action
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def denies_to(*actions)
|
113
|
+
@@permissions[@current_permission][:deny] ||= []
|
114
|
+
actions.each do |action|
|
115
|
+
@@permissions[@current_permission][:deny] << action
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Methods to set a privilege(permission) to a role
|
120
|
+
# role :admin do
|
121
|
+
# can :manage => :posts
|
122
|
+
# inherits :reader
|
123
|
+
# end
|
124
|
+
def role(role, &block)
|
125
|
+
@current_role = Role.find(role)
|
126
|
+
@current_role ||= Role.new(:name => role, :rules => [])
|
127
|
+
yield if block_given?
|
128
|
+
end
|
129
|
+
|
130
|
+
def inherits(role)
|
131
|
+
parent = Role.find(role)
|
132
|
+
parent ? @current_role.merge_rules(parent.rules) : @current_role.rules
|
133
|
+
end
|
134
|
+
|
135
|
+
def can(rules_set)
|
136
|
+
rules = []
|
137
|
+
rules_set.keys.each do |permission|
|
138
|
+
actions = Authorization.find_permission(permission)
|
139
|
+
controller = rules_set[permission]
|
140
|
+
if actions[:allow]
|
141
|
+
actions[:allow].each do |action|
|
142
|
+
rules << Rule.new(:action => action.to_s, :controller => controller.to_s, :allow => true, :active => true)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
if actions[:deny]
|
146
|
+
actions[:deny].each do |action|
|
147
|
+
rules << Rule.new(:action => action.to_s, :controller => controller.to_s, :allow => false, :active => true)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
@current_role.merge_rules(rules)
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# Role class
|
2
|
+
# Provides all needed methods to handle a role and its rules
|
3
|
+
class Role
|
4
|
+
|
5
|
+
attr_accessor :name, :rules
|
6
|
+
|
7
|
+
# Class variable to keep control of all roles
|
8
|
+
@@roles = {}
|
9
|
+
|
10
|
+
# Class variable to keep the default role, it means that if
|
11
|
+
# there is no current_role than the default is used
|
12
|
+
@@default = nil
|
13
|
+
|
14
|
+
# Class methods
|
15
|
+
# Adds a role to the roles hash
|
16
|
+
def self.add_role(role)
|
17
|
+
@@roles[role.name.to_sym] = role
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.all_roles
|
21
|
+
@@roles
|
22
|
+
end
|
23
|
+
|
24
|
+
# Find a role by its name
|
25
|
+
# Role.find(:admin)
|
26
|
+
# returns Role or nil
|
27
|
+
def self.find(role_sym)
|
28
|
+
@@roles[role_sym]
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.first
|
32
|
+
@@roles.first
|
33
|
+
end
|
34
|
+
|
35
|
+
# Remove all role from memory, used for tests so far
|
36
|
+
def self.clear
|
37
|
+
@@roles = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.set_default_role(role)
|
41
|
+
@@default = role
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.default_role
|
45
|
+
@@default
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(role)
|
49
|
+
@name = role[:name]
|
50
|
+
@rules = role[:rules]
|
51
|
+
|
52
|
+
# Helper for each initialized role
|
53
|
+
# is_role? for a role with name 'role'
|
54
|
+
# is_admin? when admin role is instantiated
|
55
|
+
Role.class_eval <<-METHOD
|
56
|
+
def is_#{@name}?
|
57
|
+
@name == '#{@name}'
|
58
|
+
end
|
59
|
+
METHOD
|
60
|
+
|
61
|
+
Role.add_role self
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# Return all(array) controllers that an user can access
|
66
|
+
def accessible_controllers
|
67
|
+
controllers = []
|
68
|
+
@rules.each do |rule|
|
69
|
+
if rule.allows?
|
70
|
+
controllers << rule.controller
|
71
|
+
end
|
72
|
+
end
|
73
|
+
controllers.uniq
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return the allowed actions in a controller for the current_role
|
77
|
+
def allowed_actions_in(controller)
|
78
|
+
actions = []
|
79
|
+
@rules.each do |rule|
|
80
|
+
if rule.controller == controller.to_s and rule.allows?
|
81
|
+
actions << rule.action
|
82
|
+
end
|
83
|
+
end
|
84
|
+
actions.uniq
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return the denied actions in a controller for the current_role
|
88
|
+
def denied_actions_in(controller)
|
89
|
+
actions = []
|
90
|
+
@rules.each do |rule|
|
91
|
+
if rule.controller == controller.to_s and rule.denies?
|
92
|
+
actions << rule.action
|
93
|
+
end
|
94
|
+
end
|
95
|
+
actions.uniq
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return if a role is allowed to perform an action in a controller
|
99
|
+
# current_role.is_allowed_to? :create, :posts
|
100
|
+
def is_allowed_to?(action, controller)
|
101
|
+
@rules.each do |rule|
|
102
|
+
if rule.action == action.to_s and rule.controller == controller.to_s
|
103
|
+
return rule.allows?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
false
|
107
|
+
end
|
108
|
+
|
109
|
+
# Merges a set of rules with the current_role rules
|
110
|
+
# current_role.merge_rules(set_of_rules[])
|
111
|
+
# Used to apply rules to an user and for inheritance
|
112
|
+
def merge_rules(new_rules)
|
113
|
+
self.rules ||= []
|
114
|
+
new_set = new_rules
|
115
|
+
overwritten_set = remove_set = []
|
116
|
+
|
117
|
+
self.rules.each do |rule|
|
118
|
+
new_rules.each do |included_rule|
|
119
|
+
if included_rule.action == rule.action and included_rule.controller == rule.controller
|
120
|
+
overwritten_set << included_rule
|
121
|
+
remove_set << rule
|
122
|
+
new_set.delete(included_rule)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
self.rules = self.rules - remove_set + overwritten_set + new_set
|
127
|
+
self.rules
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Rule class
|
2
|
+
# A simple to class to manipulate the array of rules
|
3
|
+
class Rule
|
4
|
+
|
5
|
+
attr_accessor :action, :controller, :allow, :active
|
6
|
+
|
7
|
+
def initialize(rules)
|
8
|
+
|
9
|
+
@action = rules[:action]
|
10
|
+
@controller = rules[:controller]
|
11
|
+
@allow = rules[:allow]
|
12
|
+
@active = rules[:active]
|
13
|
+
end
|
14
|
+
|
15
|
+
# The current rule allows or denies some action?
|
16
|
+
def allow_or_deny?
|
17
|
+
@allow == true ? :allow : :deny
|
18
|
+
end
|
19
|
+
|
20
|
+
def is_active?
|
21
|
+
@active
|
22
|
+
end
|
23
|
+
|
24
|
+
def allows?
|
25
|
+
@allow ? true : false
|
26
|
+
end
|
27
|
+
|
28
|
+
def denies?
|
29
|
+
@allow ? false : true
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
end
|
data/lib/turnstile.rb
ADDED
data/turnstile.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{turnstile}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Bruno Milare"]
|
9
|
+
s.date = %q{2010-08-25}
|
10
|
+
s.description = %q{Simple authorization for rails}
|
11
|
+
s.email = %q{milare@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/turnstile.rb", "lib/turnstile/authorization.rb", "lib/turnstile/role.rb", "lib/turnstile/rule.rb"]
|
13
|
+
s.files = ["README.rdoc", "Rakefile", "lib/turnstile.rb", "lib/turnstile/authorization.rb", "lib/turnstile/role.rb", "lib/turnstile/rule.rb", "Manifest", "turnstile.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/milare/turnstile}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Turnstile", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{turnstile}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Simple authorization for rails}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: turnstile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Bruno Milare
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-25 00:00:00 -03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Simple authorization for rails
|
23
|
+
email: milare@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README.rdoc
|
30
|
+
- lib/turnstile.rb
|
31
|
+
- lib/turnstile/authorization.rb
|
32
|
+
- lib/turnstile/role.rb
|
33
|
+
- lib/turnstile/rule.rb
|
34
|
+
files:
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- lib/turnstile.rb
|
38
|
+
- lib/turnstile/authorization.rb
|
39
|
+
- lib/turnstile/role.rb
|
40
|
+
- lib/turnstile/rule.rb
|
41
|
+
- Manifest
|
42
|
+
- turnstile.gemspec
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/milare/turnstile
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --line-numbers
|
50
|
+
- --inline-source
|
51
|
+
- --title
|
52
|
+
- Turnstile
|
53
|
+
- --main
|
54
|
+
- README.rdoc
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 11
|
72
|
+
segments:
|
73
|
+
- 1
|
74
|
+
- 2
|
75
|
+
version: "1.2"
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project: turnstile
|
79
|
+
rubygems_version: 1.3.7
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Simple authorization for rails
|
83
|
+
test_files: []
|
84
|
+
|