arrthorizer 0.1.0.pre
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/.gitignore +21 -0
- data/.travis.yml +6 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +8 -0
- data/arrthorizer.gemspec +24 -0
- data/config.ru +7 -0
- data/lib/arrthorizer/arrthorizer_exception.rb +11 -0
- data/lib/arrthorizer/context.rb +65 -0
- data/lib/arrthorizer/context_builder.rb +11 -0
- data/lib/arrthorizer/context_role.rb +31 -0
- data/lib/arrthorizer/permission.rb +14 -0
- data/lib/arrthorizer/privilege.rb +44 -0
- data/lib/arrthorizer/rails/configuration.rb +67 -0
- data/lib/arrthorizer/rails/controller_action.rb +45 -0
- data/lib/arrthorizer/rails/controller_concern.rb +70 -0
- data/lib/arrthorizer/rails/controller_configuration.rb +36 -0
- data/lib/arrthorizer/rails/controller_context_builder.rb +39 -0
- data/lib/arrthorizer/rails.rb +24 -0
- data/lib/arrthorizer/registry.rb +30 -0
- data/lib/arrthorizer/role.rb +31 -0
- data/lib/arrthorizer/roles.rb +19 -0
- data/lib/arrthorizer/version.rb +3 -0
- data/lib/arrthorizer.rb +28 -0
- data/lib/generators/arrthorizer/install/USAGE +9 -0
- data/lib/generators/arrthorizer/install/install_generator.rb +62 -0
- data/lib/generators/arrthorizer/install/templates/config.yml +49 -0
- data/spec/arrthorizer_exception/inner_spec.rb +21 -0
- data/spec/context/equals_spec.rb +44 -0
- data/spec/context/merge_spec.rb +37 -0
- data/spec/context_builder/build_spec.rb +12 -0
- data/spec/context_role/to_key_spec.rb +21 -0
- data/spec/context_spec.rb +49 -0
- data/spec/controllers/some_controller_spec.rb +79 -0
- data/spec/integration/registry/missing_handler_spec.rb +25 -0
- data/spec/integration/role_spec.rb +17 -0
- data/spec/internal/app/assets/images/rails.png +0 -0
- data/spec/internal/app/assets/javascripts/application.js +15 -0
- data/spec/internal/app/assets/javascripts/test.js.coffee +3 -0
- data/spec/internal/app/assets/stylesheets/application.css +13 -0
- data/spec/internal/app/assets/stylesheets/test.css.scss +3 -0
- data/spec/internal/app/controllers/application_controller.rb +3 -0
- data/spec/internal/app/controllers/some_controller.rb +17 -0
- data/spec/internal/app/helpers/application_helper.rb +2 -0
- data/spec/internal/app/helpers/test_helper.rb +2 -0
- data/spec/internal/app/mailers/.gitkeep +0 -0
- data/spec/internal/app/models/.gitkeep +0 -0
- data/spec/internal/app/roles/namespaced/context_role.rb +9 -0
- data/spec/internal/app/roles/unnamespaced_context_role.rb +6 -0
- data/spec/internal/app/views/layouts/application.html.erb +11 -0
- data/spec/internal/app/views/some/some_action.html.erb +2 -0
- data/spec/internal/config/application.rb +65 -0
- data/spec/internal/config/arrthorizer.yml +9 -0
- data/spec/internal/config/boot.rb +6 -0
- data/spec/internal/config/database.yml +25 -0
- data/spec/internal/config/environment.rb +5 -0
- data/spec/internal/config/routes.rb +3 -0
- data/spec/internal/db/schema.rb +3 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/permission/grant_spec.rb +14 -0
- data/spec/privilege/accessible_to_spec.rb +32 -0
- data/spec/privilege/get_spec.rb +35 -0
- data/spec/privilege/initialize_spec.rb +15 -0
- data/spec/privilege/make_accessible_to_spec.rb +22 -0
- data/spec/rails/.gitkeep +0 -0
- data/spec/rails/controller_action/initialize_spec.rb +42 -0
- data/spec/rails/controller_action/key_for_spec.rb +17 -0
- data/spec/rails/controller_action/to_key_spec.rb +14 -0
- data/spec/rails/controller_concern/arrthorizer_context_spec.rb +22 -0
- data/spec/rails/controller_concern/authorize_spec.rb +113 -0
- data/spec/rails/controller_concern/integration_spec.rb +75 -0
- data/spec/rails/controller_concern/to_prepare_context_spec.rb +38 -0
- data/spec/rails/controller_configuration/initialize_spec.rb +19 -0
- data/spec/role/get_spec.rb +29 -0
- data/spec/role/shared_examples/finding_the_right_role.rb +6 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/reset.rb +26 -0
- metadata +244 -0
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
spec/internal/db/*.sqlite3
|
19
|
+
|
20
|
+
.ruby-version
|
21
|
+
.ruby-gemset
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 René van den Berg
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
[](https://travis-ci.org/ReneB/arrthorizer)
|
2
|
+
(The build is currently supposed to fail, since the gem is not in a releasable state yet)
|
3
|
+
|
4
|
+
# Arrthorizer
|
5
|
+
|
6
|
+
TODO: Write a gem description
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'arrthorizer'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install arrthorizer
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
TODO: Write usage instructions here
|
25
|
+
|
26
|
+
## Contributing
|
27
|
+
|
28
|
+
1. Fork it
|
29
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
30
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
31
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
32
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/arrthorizer.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'arrthorizer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "arrthorizer"
|
8
|
+
gem.version = Arrthorizer::VERSION
|
9
|
+
gem.authors = ["René van den Berg", "Lennaert Meijvogel"]
|
10
|
+
gem.email = ["rene.vandenberg@ogd.nl", "lennaert.meijvogel@ogd.nl"]
|
11
|
+
gem.description = %q{Contextual authorization for your Rails (3+) application}
|
12
|
+
gem.summary = %q{Contextual authorization for your Rails (3+) application}
|
13
|
+
gem.homepage = "https://github.com/BUS-ogd/arrthorizer"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'rails'
|
21
|
+
gem.add_development_dependency 'combustion', '~> 0.5.1'
|
22
|
+
gem.add_development_dependency 'sqlite3'
|
23
|
+
gem.add_development_dependency 'rspec-rails'
|
24
|
+
end
|
data/config.ru
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Arrthorizer
|
4
|
+
# Make it possible to specify the context that Arrthorizer can evaluate from inside the controller:
|
5
|
+
#
|
6
|
+
# class MyController
|
7
|
+
# to_prepare_context do |c|
|
8
|
+
# c.defaults do
|
9
|
+
# { bookcase: Cms::Bookcase.find(params[:id]) }
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# c.for_action(:some_action) do
|
13
|
+
# arrthorizer_defaults.merge({ bookcase: Cms::Book.find(params[:id]).bookcase })
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# class MyController
|
19
|
+
# to_prepare_context do |c|
|
20
|
+
# c.defaults do
|
21
|
+
# params
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# In non-Rails environments:
|
27
|
+
#
|
28
|
+
# class MySomething
|
29
|
+
# # probably include something like Arrthorizer::Config
|
30
|
+
# to_prepare_context do |c|
|
31
|
+
# c.defaults do
|
32
|
+
# { bookcase: Cms::Bookcase.find(params[:id]) }
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# if params[:action] == :edit
|
36
|
+
# arrthorizer_defaults.merge { author: User.find(params[:author_id] }
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
|
41
|
+
class Context < OpenStruct
|
42
|
+
ConversionError = Class.new(Arrthorizer::ArrthorizerException)
|
43
|
+
|
44
|
+
def merge(hash)
|
45
|
+
self.class.new(to_hash.merge(hash))
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_hash
|
49
|
+
marshal_dump
|
50
|
+
end
|
51
|
+
|
52
|
+
def ==(other)
|
53
|
+
to_hash == other.to_hash
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module_function
|
58
|
+
def Context(contents)
|
59
|
+
return contents if contents.is_a? Context
|
60
|
+
|
61
|
+
return Context.new(contents.to_hash)
|
62
|
+
rescue NoMethodError
|
63
|
+
raise Arrthorizer::Context::ConversionError, "Can't convert #{contents} to an Arrthorizer::Context"
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
##
|
4
|
+
# This is the superclass for all ContextRoles that the application may contain.
|
5
|
+
# A ContextRole is a role that a User may or may not have, depending on the
|
6
|
+
# current context. This can be things like 'the author of a certain post' or 'a
|
7
|
+
# member of the current group'
|
8
|
+
module Arrthorizer
|
9
|
+
class ContextRole < Role
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
def to_key
|
13
|
+
self.class.to_key
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.to_key
|
17
|
+
name
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.applies_to_user?(*args)
|
21
|
+
instance.applies_to_user?(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.inherited(klass)
|
25
|
+
super
|
26
|
+
|
27
|
+
Role.register(klass.instance)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
module Permission
|
3
|
+
InvalidPermission = Class.new(ArrthorizerException)
|
4
|
+
|
5
|
+
def self.grant(privilege, config = {})
|
6
|
+
privilege = Privilege.get(privilege)
|
7
|
+
role = Role.get(config[:to])
|
8
|
+
|
9
|
+
privilege.make_accessible_to(role)
|
10
|
+
rescue Registry::NotFound => e
|
11
|
+
raise InvalidPermission, e.message
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
class Privilege
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(attrs)
|
6
|
+
@name = attrs[:name].to_s
|
7
|
+
|
8
|
+
(attrs[:roles] || []).each do |role|
|
9
|
+
self.make_accessible_to(role)
|
10
|
+
end
|
11
|
+
|
12
|
+
self.class.register(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.get(name_or_privilege)
|
16
|
+
registry.fetch(name_or_privilege)
|
17
|
+
end
|
18
|
+
|
19
|
+
def make_accessible_to(role)
|
20
|
+
permitted_roles.add(role)
|
21
|
+
end
|
22
|
+
|
23
|
+
def accessible_to?(role)
|
24
|
+
!!permitted_roles.fetch(role) { false }
|
25
|
+
end
|
26
|
+
|
27
|
+
def permitted_roles
|
28
|
+
@permitted_roles ||= Registry.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_key
|
32
|
+
name
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
def self.register(privilege)
|
37
|
+
registry.add(privilege)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.registry
|
41
|
+
@registry ||= Registry.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
module Rails
|
3
|
+
class Configuration
|
4
|
+
FileNotFound = Class.new(Arrthorizer::ArrthorizerException)
|
5
|
+
|
6
|
+
mattr_accessor :config_file
|
7
|
+
self.config_file = "config/arrthorizer.yml"
|
8
|
+
|
9
|
+
def self.load
|
10
|
+
Parser.new.process(config)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def self.config
|
15
|
+
@config ||= YAML.load(config_file_contents) || {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.config_file_contents
|
19
|
+
File.read(config_file_location)
|
20
|
+
rescue Errno::ENOENT
|
21
|
+
raise FileNotFound, "Arrthorizer requires a config file at #{config_file}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.config_file_location
|
25
|
+
::Rails.root.join config_file
|
26
|
+
end
|
27
|
+
|
28
|
+
class Parser
|
29
|
+
def process(config)
|
30
|
+
config.each_pair do |privilege_name, configuration|
|
31
|
+
parse_privilege_node(privilege_name, OpenStruct.new(configuration))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def parse_privilege_node(name, description)
|
37
|
+
privilege = Arrthorizer::Privilege.new(name: name, roles: roles_from(description))
|
38
|
+
|
39
|
+
description.actions.each do |node|
|
40
|
+
controller_name = controller_from(node)
|
41
|
+
|
42
|
+
actions_from(node, controller_name).each do |action|
|
43
|
+
action.privilege = privilege
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# node looks like this: { "actions" => { "controller_name" => ["namespaced/action_name"] }, roles: [ "Namespaced::Role" ] }
|
49
|
+
def roles_from(node)
|
50
|
+
(node.roles || []).map(&:constantize)
|
51
|
+
end
|
52
|
+
|
53
|
+
# node looks like this: { "controller_name" => ["namespaced/action_name", "another_action_name"] }
|
54
|
+
def controller_from(node)
|
55
|
+
node.keys.first
|
56
|
+
end
|
57
|
+
|
58
|
+
# node looks like this: { "controller_name" => ["namespaced/action_name", "another_action_name"] }
|
59
|
+
def actions_from(node, controller_name)
|
60
|
+
node[controller_name].map do |action_name|
|
61
|
+
Arrthorizer::Rails::ControllerAction.new(controller: controller_name, action: action_name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
module Rails
|
3
|
+
class ControllerAction
|
4
|
+
ControllerNotDefined = Class.new(Arrthorizer::ArrthorizerException)
|
5
|
+
ActionNotDefined = Class.new(Arrthorizer::ArrthorizerException)
|
6
|
+
|
7
|
+
attr_accessor :privilege
|
8
|
+
attr_reader :controller_path, :action_name
|
9
|
+
|
10
|
+
def self.get_current(controller)
|
11
|
+
fetch(key_for(controller))
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(attrs)
|
15
|
+
self.controller_path = attrs.fetch(:controller) { raise ControllerNotDefined }
|
16
|
+
self.action_name = attrs.fetch(:action) { raise ActionNotDefined }
|
17
|
+
|
18
|
+
self.class.register(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_key
|
22
|
+
"#{controller_path}##{action_name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
attr_writer :controller_path, :action_name
|
27
|
+
|
28
|
+
def self.key_for(controller)
|
29
|
+
"#{controller.controller_path}##{controller.action_name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.fetch(key)
|
33
|
+
registry.fetch(key)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.register(controller_action)
|
37
|
+
registry.add(controller_action)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.registry
|
41
|
+
@registry ||= Registry.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
module Rails
|
3
|
+
module ControllerConcern
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
protected
|
8
|
+
class_attribute :arrthorizer_configuration, instance_writer: false
|
9
|
+
|
10
|
+
##
|
11
|
+
# This is a hook method that provides access to the context for a
|
12
|
+
# given HTTP request. For each request, an Arrthorizer::Context object is
|
13
|
+
# built and provided to all ContextRoles that are configured as having
|
14
|
+
# access to the given controller action.
|
15
|
+
def arrthorizer_context
|
16
|
+
arrthorizer_context_builder.build_for_action
|
17
|
+
end
|
18
|
+
|
19
|
+
def arrthorizer_defaults
|
20
|
+
arrthorizer_context_builder.build_default
|
21
|
+
end
|
22
|
+
|
23
|
+
def authorize
|
24
|
+
action = Arrthorizer::Rails::ControllerAction.get_current(self)
|
25
|
+
roles = action.privilege.permitted_roles
|
26
|
+
|
27
|
+
roles.any? do |role|
|
28
|
+
role.applies_to_user?(current_user, arrthorizer_context)
|
29
|
+
end || forbidden
|
30
|
+
end
|
31
|
+
|
32
|
+
def forbidden
|
33
|
+
render text: 'Access Denied', status: :forbidden
|
34
|
+
end
|
35
|
+
|
36
|
+
def arrthorizer_context_builder
|
37
|
+
@context_builder ||= Arrthorizer::Rails::ControllerContextBuilder.new(self, arrthorizer_configuration)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module ClassMethods
|
42
|
+
##
|
43
|
+
# This method sets up Arrthorizer to verify that a user has the proper
|
44
|
+
# rights to access a # given controller action. Options can be provided
|
45
|
+
# and are passed to before_filter.
|
46
|
+
def requires_authorization(options = {})
|
47
|
+
before_filter :authorize, options
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# This method sets up Arrthorizer to not verify requests to all (when
|
52
|
+
# no options are provided) or selected (when :only or :except are
|
53
|
+
# provided) actions in this controller and its subclasses.
|
54
|
+
# Options can be provided and are passed to skip_filter.
|
55
|
+
def does_not_require_authorization(options = {})
|
56
|
+
skip_filter :authorize, options
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# This is the configuration method for building Arrthorizer contexts from HTTP requests.
|
61
|
+
# The developer specifies how contexts for authorization checks should be built
|
62
|
+
# by providing a block to this method. For more information, check the documentation
|
63
|
+
# on Arrthorizer::Rails::ControllerConfiguration
|
64
|
+
def to_prepare_context(&block)
|
65
|
+
self.arrthorizer_configuration = Arrthorizer::Rails::ControllerConfiguration.new(&block)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
module Rails
|
3
|
+
class ControllerConfiguration
|
4
|
+
Error = Class.new(Arrthorizer::ArrthorizerException)
|
5
|
+
|
6
|
+
def initialize(&block)
|
7
|
+
yield self
|
8
|
+
rescue LocalJumpError
|
9
|
+
raise Error, "No builder block provided to ContextBuilder.new"
|
10
|
+
end
|
11
|
+
|
12
|
+
def defaults(&block)
|
13
|
+
self.defaults_block = block
|
14
|
+
end
|
15
|
+
|
16
|
+
def for_action(action, &block)
|
17
|
+
add_action_block(action, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def block_for(action)
|
21
|
+
action_blocks.fetch(action) { defaults_block }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
attr_accessor :defaults_block
|
26
|
+
|
27
|
+
def add_action_block(action, &block)
|
28
|
+
action_blocks[action] = block
|
29
|
+
end
|
30
|
+
|
31
|
+
def action_blocks
|
32
|
+
@action_blocks ||= HashWithIndifferentAccess.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
module Rails
|
3
|
+
class ControllerContextBuilder < Arrthorizer::ContextBuilder
|
4
|
+
attr_accessor :controller, :configuration
|
5
|
+
|
6
|
+
def initialize(controller, configuration)
|
7
|
+
self.controller = controller
|
8
|
+
self.configuration = configuration
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_default
|
12
|
+
config = config_for_action(nil)
|
13
|
+
|
14
|
+
build_from_block(&config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_for_action
|
18
|
+
config = config_for_action(controller.action_name)
|
19
|
+
|
20
|
+
build_from_block(&config)
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def build_from_block(&config)
|
25
|
+
if block_given?
|
26
|
+
context_hash = controller.instance_eval(&config)
|
27
|
+
|
28
|
+
build_from_hash(context_hash)
|
29
|
+
else
|
30
|
+
build_from_hash({})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def config_for_action(action)
|
35
|
+
configuration.try(:block_for, action)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
module Rails
|
3
|
+
autoload :ControllerAction, "arrthorizer/rails/controller_action"
|
4
|
+
autoload :ControllerConfiguration, "arrthorizer/rails/controller_configuration"
|
5
|
+
autoload :ControllerConcern, "arrthorizer/rails/controller_concern"
|
6
|
+
autoload :ControllerContextBuilder, "arrthorizer/rails/controller_context_builder"
|
7
|
+
autoload :Configuration, "arrthorizer/rails/configuration"
|
8
|
+
|
9
|
+
class Engine < ::Rails::Engine
|
10
|
+
config.to_prepare do
|
11
|
+
Arrthorizer::Rails::Configuration.load
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module_function
|
16
|
+
def initialize!
|
17
|
+
ActionController::Base.send(:include, ControllerConcern)
|
18
|
+
|
19
|
+
if defined?(ActionController::API)
|
20
|
+
ActionController::API.send(:include, ControllerConcern)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
class Registry
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
NotFound = Class.new(ArrthorizerException)
|
6
|
+
|
7
|
+
def each(&block)
|
8
|
+
storage.values.each(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
self.storage = Hash.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(object)
|
16
|
+
storage[object.to_key] = object
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch(key, &block)
|
20
|
+
block ||= proc { raise NotFound, "Could not find value for #{key.inspect}" }
|
21
|
+
|
22
|
+
formatted_key = key.respond_to?(:to_key) ? key.to_key : key
|
23
|
+
|
24
|
+
storage.fetch(formatted_key, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
attr_accessor :storage
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# This is the abstract superclass for all roles in the hierarchy.
|
2
|
+
module Arrthorizer
|
3
|
+
class Role
|
4
|
+
# Template method: This method is implemented in the
|
5
|
+
# ContextRole subclasses.
|
6
|
+
def applies_to_user?(user, context = {})
|
7
|
+
raise NotImplementedError, "#{self.name} does not implement #applies_to_user?(user, context)"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.get(name_or_role)
|
11
|
+
if name_or_role.respond_to?(:instance)
|
12
|
+
get(name_or_role.instance)
|
13
|
+
else
|
14
|
+
registry.fetch(name_or_role)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.register(role)
|
19
|
+
registry.add(role)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.registry
|
23
|
+
@registry ||= Registry.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_key
|
27
|
+
name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Arrthorizer
|
2
|
+
class Everybody < Arrthorizer::ContextRole
|
3
|
+
def applies_to_user?(*args)
|
4
|
+
true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Nobody < Arrthorizer::ContextRole
|
9
|
+
def applies_to_user?(*args)
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class LoggedInUser < Arrthorizer::ContextRole
|
15
|
+
def applies_to_user?(user, context)
|
16
|
+
!user.nil?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|