requirement_authorization 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ pkg/*.gem
data/README.markdown ADDED
@@ -0,0 +1,64 @@
1
+ # Requirement Authorization
2
+
3
+ Requirement authorization is a lightweight DSL designed to separate the concerns of resource access from gathering information required to access the resource.
4
+
5
+ WARNING: This puppy isn't tested yet, but its the very next thing I plan on doing. I wanted to get this up on github first so that we could gemify this into our own project and build out tests.
6
+
7
+ ## Installation
8
+
9
+ In your rails config/environment.rb file, just add
10
+
11
+ config.gem 'requirement_authorization', :source => 'http://gemcutter.org'
12
+
13
+ ## Examples
14
+
15
+ A more interesting example may be to protect a paid feature from being accessed by users who did not pay for that feature:
16
+
17
+ requirement :feature do |r|
18
+ r.guard_unless { |feature| current_user.account.send("#{feature}_enabled?") }
19
+ r.resolution { |feature| redirect_to upgrade_path(feature) }
20
+ end
21
+
22
+ In the controller just add
23
+
24
+ class AwesomeSauceController < ActionController::Base
25
+ feature_required :awesome_sauce
26
+ end
27
+
28
+ A more trivial example for SSL
29
+
30
+ requirement :ssl do |r|
31
+ r.guard_unless { request.ssl? }
32
+ r.resolution { redirect_to "https://" + request.host + request.request_uri }
33
+ end
34
+
35
+ Then in the controller:
36
+
37
+ class PaymentMethodsController << ActionController::Base
38
+ ssl_required
39
+ end
40
+
41
+ ## License
42
+
43
+ Copyright (c) 2009 Brad Gessler
44
+
45
+ Permission is hereby granted, free of charge, to any person
46
+ obtaining a copy of this software and associated documentation
47
+ files (the "Software"), to deal in the Software without
48
+ restriction, including without limitation the rights to use,
49
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
50
+ copies of the Software, and to permit persons to whom the
51
+ Software is furnished to do so, subject to the following
52
+ conditions:
53
+
54
+ The above copyright notice and this permission notice shall be
55
+ included in all copies or substantial portions of the Software.
56
+
57
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
58
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
59
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
60
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
61
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
62
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
63
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
64
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run specs.'
6
+ task :default => :spec
7
+
8
+ require 'rake'
9
+ require 'spec/rake/spectask'
10
+
11
+ desc "Run all examples"
12
+ Spec::Rake::SpecTask.new('spec') do |t|
13
+ t.spec_files = FileList['spec/*.rb']
14
+ t.fail_on_error = false
15
+ end
16
+
17
+ desc 'Generate documentation for the has_default plugin.'
18
+ Rake::RDocTask.new(:rdoc) do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'HasDefault'
21
+ rdoc.options << '--line-numbers' << '--inline-source'
22
+ rdoc.rdoc_files.include('README')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
25
+
26
+ begin
27
+ require 'jeweler'
28
+ Jeweler::Tasks.new do |gemspec|
29
+ gemspec.name = "requirement_authorization"
30
+ gemspec.summary = "A lightweight authorization framework that separates the concern of resource access from gathering information necessary to fulfill a request, like singing in with a username and password."
31
+ gemspec.description = ""
32
+ gemspec.email = "brad@conden.se"
33
+ gemspec.homepage = "http://github.com/bradgessler/requirement_authorization"
34
+ gemspec.authors = ["Brad Gessler"]
35
+ end
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
38
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,102 @@
1
+ # TODO release this as a plugin as it matures
2
+ module RequirementAuthorization
3
+ METHOD_SUFIX = '_required'
4
+
5
+ def self.included(base)
6
+ base.send :extend, ClassMethods
7
+ end
8
+
9
+ class Requirement
10
+ CONTROLLER_OPTIONS = [:only, :except, :if, :unless]
11
+
12
+ def initialize(opts={},&block)
13
+ yield self if block_given?
14
+ end
15
+
16
+ # A proc or method that must return a true for the requirement to be satisified
17
+ def guard(proc=nil, &block)
18
+ @guard = controller_filter_proc(proc, &block)
19
+ end
20
+ alias :guard_if :guard
21
+
22
+ def guard_unless(proc=nil, &block)
23
+ # Lazily invert the return of the controller proc.
24
+ @guard = Proc.new{|c, args| not controller_filter_proc(proc, &block).call(c, *args)}
25
+ end
26
+
27
+ # This is the method that we'll call if the guard fails. The resolution should take the user
28
+ # through a flow where they can satisify the requirements for the requirement and pass on through.
29
+ def resolution(proc=nil, &block)
30
+ @resolution = controller_filter_proc(proc, &block)
31
+ end
32
+
33
+ # Sets up the filter for the controller by wrapping this requirement up in a proc.
34
+ def filter(controller, *args)
35
+ args, controller_options = extract_filter_args!(args)
36
+ controller.before_filter Proc.new{|c| self.call(c, *args)}, controller_options
37
+ end
38
+
39
+ # The gaurd, resolution process. This is where the magic happens.
40
+ def call(controller_instance, *args)
41
+ @resolution.call(controller_instance, *args) if @guard.call(controller_instance, *args)
42
+ end
43
+
44
+ protected
45
+ # Packages up a proc or a block into something that a controller can deal with
46
+ def controller_filter_proc(proc=nil, &block)
47
+ if block_given? # Instance eval the block in the context of the controller
48
+ Proc.new{|c, args| c.instance_eval(&block)}
49
+ elsif proc.respond_to?(:call) # Just run the proc, easy!
50
+ Proc.new{|c, args| proc.call(*args)}
51
+ else # This could be a symbol or string, which we'll send to the controller to see if it exists
52
+ # TODO make this arity dyanmic...
53
+ # The arity check lets us call a method in a controller with or without arguments
54
+ Proc.new{|c, args| c.method(proc).arity == 0 ? c.send(proc) : c.send(proc, *args)}
55
+ end
56
+ end
57
+
58
+ def extract_filter_args!(args)
59
+ # Gives us the last hash in an array of args (e.g. before_filter :act1, :act2, :only => [:fun])
60
+ # would return {:only => [:fun]}
61
+ options = args.last.is_a?(Hash) ? args.pop : {}
62
+ # Collect all of the controller options and delete them out of the options hash
63
+ controller_options = options.inject({}) do |memo, (option, value)|
64
+ memo[option] = options.delete(option) if CONTROLLER_OPTIONS.include?(option)
65
+ memo
66
+ end
67
+ # The remaining pairs in the options hash should be put back into the args array
68
+ # unless the hash is empty. If we pushed an empty hash theres a chance we'd screw
69
+ # up the arity of calling functions within the controllers.
70
+ args.push options unless options.empty?
71
+ [ args, controller_options ]
72
+ end
73
+ end
74
+
75
+ module ClassMethods
76
+ # Setups a hash table of requirements that may be used by class methods from
77
+ # other sub-classed controllers
78
+ def requirement(requirement, opts={}, &block)
79
+ self.requirements.merge! requirement.to_s => Requirement.new(opts, &block)
80
+
81
+ # Build out the class method for this requirement. This is primarly used towards the
82
+ # top of a controller.
83
+ self.class.class_eval %{
84
+ def #{requirement}#{METHOD_SUFIX}(*args)
85
+ requirements['#{requirement}'].filter(self, *args)
86
+ end}
87
+
88
+ # Build out the instance method so that this requirement can be called from other
89
+ # instance methods. This proves to be insanely useful for composing requirements
90
+ # together or reusing them from other methods.
91
+ self.class_eval %{
92
+ def #{requirement}#{METHOD_SUFIX}(*args)
93
+ self.class.send(:requirements)['#{requirement}'].call(self, *args)
94
+ end}
95
+ end
96
+
97
+ protected
98
+ def requirements
99
+ @@requirements ||= {}
100
+ end
101
+ end
102
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ require File.dirname(__FILE__) + '/../lib/requirement_authorization.rb'
2
+
3
+ ActionController::Base.send(:include, RequirementAuthorization)
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/../lib/requirement_authorization'
2
+
3
+ describe RequirementAuthorization do
4
+ it "should have tests"
5
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: requirement_authorization
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brad Gessler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-20 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: ""
17
+ email: brad@conden.se
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ files:
25
+ - .gitignore
26
+ - README.markdown
27
+ - Rakefile
28
+ - VERSION
29
+ - lib/requirement_authorization.rb
30
+ - rails/init.rb
31
+ - spec/requirement_authorization_spec.rb
32
+ has_rdoc: true
33
+ homepage: http://github.com/bradgessler/requirement_authorization
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --charset=UTF-8
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.3.5
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: A lightweight authorization framework that separates the concern of resource access from gathering information necessary to fulfill a request, like singing in with a username and password.
60
+ test_files:
61
+ - spec/requirement_authorization_spec.rb