protector-cancan 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 24a492131115dea9a185a89b83c01e3fd3dde523
4
+ data.tar.gz: 0d0ef48a9f0e1ed21f0a0bc6d46c3fcb3d513dc3
5
+ SHA512:
6
+ metadata.gz: e98380fa0ecc0a8238304ba9716dffabdf634a3e5e9dc12ca806b6ca749032e0cb2610c213bab330241b76ac28db69f263cd676f7036fe1990b3b572117a8122
7
+ data.tar.gz: 956455e9fed66a3700a14d4ec3dcee8763a1a92da5f750e0d535f32e646ef54718d879b2ae4aae355bece3d1dcb6146d572b004915e5e9f7903d164ecde0476c
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --tty
2
+ --color
3
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'pry'
7
+ gem 'rspec'
8
+
9
+ gem 'combustion', github: 'pat/combustion'
10
+ gem 'rails', '>= 4.0.0'
11
+ gem 'rspec-rails'
12
+
13
+ gem 'sqlite3', platform: :ruby
14
+ gem 'jdbc-sqlite3', platform: :jruby, require: 'jdbc/sqlite3'
15
+ gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby, github: 'jruby/activerecord-jdbc-adapter'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Boris Staal
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,73 @@
1
+ # Protector::Cancan
2
+
3
+ Integrates [Protector](https://github.com/inossidabile/protector) and [CanCan](https://github.com/ryanb/cancan).
4
+
5
+ Protector and CanCan are all about the same thing: access control. They however act on different fronts: Protector works on a model level and CanCan is all about controllers defense. With this gem you don't have to choose anymore: make them work together for the best result.
6
+
7
+ The integration makes CanCan aware of Protector restrictions. You still can have separate `Ability` instance and even extend (or override) what comes from Protector.
8
+
9
+ Additionally CanCan will automatically restrict instances with `current_user` during `load_resource` part.
10
+
11
+ ## Installation
12
+
13
+ You are expected to have generated CanCan ability by this moment. Proceed to [CanCan installation tutorial](https://github.com/ryanb/cancan#1-define-abilities) to make one if you don't.
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'protector-cancan'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Now modify your `Ability` definition in the following way:
24
+
25
+ ```ruby
26
+ class Ability
27
+ include CanCan::Ability
28
+
29
+ def intialize(user)
30
+ import_protector user # <- add this
31
+ end
32
+ end
33
+ ```
34
+
35
+ ## Example
36
+
37
+ For the case when you have the following model defined:
38
+
39
+ ```ruby
40
+ class Dummy < ActiveRecord::Base
41
+ protect do |user|
42
+ can :read if user
43
+ end
44
+ end
45
+ ```
46
+
47
+ If you call `can? :read, Dummy`, the gem will evaluate `Dummy` protection block against value passed to `import_protector` (by default it's `current_user`) and expand CanCan rules with resulting meta.
48
+
49
+ So in this particular case we will get `true` if `current_user` is set and `false` otherwise.
50
+
51
+ And that's how controller is going to work:
52
+
53
+ ```ruby
54
+ class DummiesController
55
+ load_and_authorize_resources
56
+
57
+ def index # Will be accessible if current_user isn't blank
58
+ @dummies # => Dummy.restrict!(current_user)
59
+ end
60
+
61
+ def show # Will be accessible if current_user isn't blank
62
+ @dummy # => Dummy.find(params[:id]).restrict!(current_user)
63
+ end
64
+ end
65
+ ```
66
+
67
+ ## Maintainers
68
+
69
+ * Boris Staal, [@inossidabile](http://staal.io)
70
+
71
+ ## License
72
+
73
+ It is free software, and may be redistributed under the terms of MIT license.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,54 @@
1
+ require 'set'
2
+
3
+ module Protector
4
+ module CanCan
5
+ module Ability
6
+
7
+ def import_protector(subject)
8
+ @protector_subject = subject
9
+ @protector_subject_defined = true
10
+ end
11
+
12
+ def protector_subject
13
+ @protector_subject
14
+ end
15
+
16
+ def protector_subject?
17
+ !!@protector_subject_defined
18
+ end
19
+
20
+ def self.included(mod)
21
+ mod.class_eval do
22
+
23
+ def can_with_protector?(action, entity, *extra_args)
24
+ if entity.respond_to?(:restrict!) && @protector_subject_defined
25
+ @protector_models ||= Set.new
26
+
27
+ model = entity
28
+ model = model.class unless model.is_a?(Class)
29
+
30
+ unless @protector_models.include?(model)
31
+ meta = entity.is_a?(Class) ? entity.protector_meta.evaluate(@protector_subject)
32
+ : entity.protector_meta(@protector_subject)
33
+
34
+ meta.access.each do |action, fields|
35
+ action = :read if action == :view # *doh*
36
+ can action, model unless fields.empty?
37
+ end
38
+
39
+ can :destroy, model if meta.destroyable?
40
+
41
+ @protector_models << model
42
+ end
43
+ end
44
+
45
+ can_without_protector? action, entity, *extra_args
46
+ end
47
+
48
+ alias_method_chain :can?, :protector
49
+
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ module Protector
2
+ module CanCan
3
+ module Resource extend ActiveSupport::Concern
4
+ included do
5
+ alias_method_chain :load_resource, :protector
6
+ alias_method_chain :load_collection, :protector
7
+ end
8
+
9
+ def load_resource_with_protector(*args)
10
+ resource = load_resource_without_protector(*args)
11
+
12
+ if resource.respond_to?(:restrict!) \
13
+ && !resource.protector_subject? \
14
+ && current_ability.protector_subject?
15
+
16
+ resource = resource.restrict!(current_ability.protector_subject)
17
+ end
18
+
19
+ resource
20
+ end
21
+
22
+ def load_collection_with_protector(*args)
23
+ collection = load_collection_without_protector(*args)
24
+
25
+ if collection.respond_to?(:restrict!) \
26
+ && !collection.protector_subject? \
27
+ && current_ability.protector_subject?
28
+
29
+ collection = collection.restrict!(current_ability.protector_subject)
30
+ end
31
+
32
+ collection
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,5 @@
1
+ module Protector
2
+ module Cancan
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'protector'
2
+ require 'cancan'
3
+ require 'active_support/all'
4
+ require 'protector/cancan/ability'
5
+ require 'protector/cancan/resource'
6
+ require 'protector/cancan/version'
7
+
8
+ CanCan::Ability.send :include, Protector::CanCan::Ability
9
+ CanCan::ControllerResource.send :include, Protector::CanCan::Resource
10
+ CanCan::InheritedResource.send :include, Protector::CanCan::Resource
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'protector/cancan/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "protector-cancan"
8
+ spec.version = Protector::Cancan::VERSION
9
+ spec.authors = ["Boris Staal"]
10
+ spec.email = ["boris@staal.io"]
11
+ spec.description = %q{Integration layer between Protector and CanCan}
12
+ spec.summary = spec.description
13
+ spec.homepage = "https://github.com/inossidabile/protector-cancan"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "protector", ">= 0.5.3"
22
+ spec.add_dependency "cancan"
23
+ spec.add_dependency "activesupport"
24
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helpers/boot'
2
+
3
+ describe DummiesController do
4
+ before(:all) do
5
+ Dummy.create! id: 1
6
+ end
7
+
8
+ after(:all) do
9
+ Dummy.find(1).destroy
10
+ end
11
+
12
+ describe "entities assignation" do
13
+ context "without integration" do
14
+ it "works for index" do
15
+ expect{ get :index }.to raise_error(CanCan::AccessDenied)
16
+ end
17
+
18
+ it "works for show" do
19
+ expect{ get :show, id: 1 }.to raise_error(CanCan::AccessDenied)
20
+ end
21
+
22
+ it "works for create" do
23
+ expect{ post :create }.to raise_error(CanCan::AccessDenied)
24
+ end
25
+
26
+ it "works for edit" do
27
+ expect{ get :edit, id: 1 }.to raise_error(CanCan::AccessDenied)
28
+ end
29
+
30
+ it "works for update" do
31
+ expect{ put :edit, id: 1 }.to raise_error(CanCan::AccessDenied)
32
+ end
33
+
34
+ it "works for destroy" do
35
+ expect{ delete :destroy, id: 1 }.to raise_error(CanCan::AccessDenied)
36
+ end
37
+ end
38
+
39
+ context "with integration" do
40
+ it "works for index" do
41
+ get :index, protector: true
42
+ expect(response).to be_success
43
+ assigns(:dummies).protector_subject?.should == true
44
+ assigns(:dummies).protector_subject.should == 'user'
45
+ end
46
+
47
+ it "works for show" do
48
+ get :show, id: 1, protector: true
49
+ expect(response).to be_success
50
+ assigns(:dummy).protector_subject?.should == true
51
+ assigns(:dummy).protector_subject.should == 'user'
52
+ end
53
+
54
+ it "works for create" do
55
+ post :create, protector: true
56
+ expect(response).to be_success
57
+ assigns(:dummy).protector_subject?.should == true
58
+ assigns(:dummy).protector_subject.should == 'user'
59
+ end
60
+
61
+ it "works for edit" do
62
+ get :edit, id: 1, protector: true
63
+ expect(response).to be_success
64
+ assigns(:dummy).protector_subject?.should == true
65
+ assigns(:dummy).protector_subject.should == 'user'
66
+ end
67
+
68
+ it "works for update" do
69
+ put :edit, id: 1, protector: true
70
+ expect(response).to be_success
71
+ assigns(:dummy).protector_subject?.should == true
72
+ assigns(:dummy).protector_subject.should == 'user'
73
+ end
74
+
75
+ it "works for destroy" do
76
+ delete :destroy, id: 1, protector: true
77
+ expect(response).to be_success
78
+ assigns(:dummy).protector_subject?.should == true
79
+ assigns(:dummy).protector_subject.should == 'user'
80
+ end
81
+
82
+ it "does not override set subject" do
83
+ get :test, protector: true
84
+ expect(response).to be_success
85
+ assigns(:dummies).protector_subject?.should == true
86
+ assigns(:dummies).protector_subject.should == 'test'
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,46 @@
1
+ class DummiesController < ActionController::Base
2
+ before_filter(only: :test) do
3
+ @dummies = Dummy.restrict!('test')
4
+ end
5
+
6
+ load_and_authorize_resource
7
+
8
+ def index
9
+ render nothing: true
10
+ end
11
+
12
+ def show
13
+ render nothing: true
14
+ end
15
+
16
+ def create
17
+ render nothing: true
18
+ end
19
+
20
+ def edit
21
+ render nothing: true
22
+ end
23
+
24
+ def update
25
+ render nothing: true
26
+ end
27
+
28
+ def destroy
29
+ render nothing: true
30
+ end
31
+
32
+ def test
33
+ render nothing: true
34
+ end
35
+
36
+ protected
37
+
38
+ def current_user
39
+ 'user'
40
+ end
41
+
42
+ def current_ability
43
+ ability = params[:protector] ? ProtectorAbility : DefaultAbility
44
+ @current_ability ||= ability.new(current_user)
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ class DefaultAbility
2
+ include CanCan::Ability
3
+
4
+ def initialize(user)
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ class Dummy < ActiveRecord::Base
2
+ protect do |user|
3
+ can :view
4
+ can :create
5
+ can :update
6
+ can :destroy
7
+ can :test
8
+ end
9
+
10
+ belongs_to :user
11
+ end
@@ -0,0 +1,7 @@
1
+ class ProtectorAbility
2
+ include CanCan::Ability
3
+
4
+ def initialize(user)
5
+ import_protector user
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ test:
2
+ adapter: <%= "jdbc" if defined? JRUBY_VERSION %>sqlite3
3
+ database: ":memory:"
4
+ verbosity: quiet
@@ -0,0 +1,7 @@
1
+ Rails.application.routes.draw do
2
+ resources :dummies do
3
+ collection do
4
+ get :test
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table(:dummies) do |t|
3
+ t.timestamps
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ *.log
@@ -0,0 +1,64 @@
1
+ require 'spec_helpers/boot'
2
+
3
+ describe Protector::CanCan::Ability do
4
+ let(:_Model) do
5
+ Class.new(ActiveRecord::Base) do
6
+ self.table_name = 'dummies'
7
+ end
8
+ end
9
+
10
+ let(:_Ability) do
11
+ Class.new do
12
+ include CanCan::Ability
13
+ end
14
+ end
15
+
16
+ it "defaults properly" do
17
+ _Ability.new.protector_subject?.should == false
18
+ end
19
+
20
+ it "initializes properly" do
21
+ _Ability.class_eval do
22
+ def initialize(user)
23
+ import_protector user
24
+ end
25
+ end
26
+
27
+ ability = _Ability.new('user')
28
+
29
+ ability.protector_subject?.should == true
30
+ ability.protector_subject.should == 'user'
31
+ ability.can?(:read, _Model).should == false
32
+ ability.can?(:create, _Model).should == false
33
+ ability.can?(:update, _Model).should == false
34
+ ability.can?(:destroy, _Model).should == false
35
+ end
36
+
37
+ it "proxies rules" do
38
+ _Ability.class_eval do
39
+ def initialize(user)
40
+ import_protector user
41
+ end
42
+ end
43
+
44
+ _Model.class_eval do
45
+ protect do |user|
46
+ can :view
47
+ can :create
48
+ can :update
49
+ can :destroy
50
+ can :test
51
+ end
52
+ end
53
+
54
+ ability = _Ability.new('user')
55
+
56
+ ability.protector_subject?.should == true
57
+ ability.protector_subject.should == 'user'
58
+ ability.can?(:read, _Model).should == true
59
+ ability.can?(:create, _Model).should == true
60
+ ability.can?(:update, _Model).should == true
61
+ ability.can?(:destroy, _Model).should == true
62
+ ability.can?(:test, _Model).should == true
63
+ end
64
+ end
@@ -0,0 +1,19 @@
1
+ require 'rspec'
2
+ require 'combustion'
3
+
4
+ RSpec.configure do |config|
5
+ config.treat_symbols_as_metadata_keys_with_true_values = true
6
+ config.run_all_when_everything_filtered = true
7
+ config.filter_run :focus
8
+
9
+ # Run specs in random order to surface order dependencies. If you find an
10
+ # order dependency and want to debug it, you can fix the order by providing
11
+ # the seed, which is printed after each run.
12
+ # --seed 1234
13
+ config.order = 'random'
14
+ end
15
+
16
+ Combustion.initialize! :active_record, :action_controller
17
+
18
+ require 'rspec/rails'
19
+ require 'rspec/autorun'
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: protector-cancan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Boris Staal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: protector
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.5.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.5.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: cancan
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Integration layer between Protector and CanCan
56
+ email:
57
+ - boris@staal.io
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .rspec
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lib/protector/cancan.rb
69
+ - lib/protector/cancan/ability.rb
70
+ - lib/protector/cancan/resource.rb
71
+ - lib/protector/cancan/version.rb
72
+ - protector-cancan.gemspec
73
+ - spec/controllers/dummies_spec.rb
74
+ - spec/internal/app/controllers/dummies_controller.rb
75
+ - spec/internal/app/models/default_ability.rb
76
+ - spec/internal/app/models/dummy.rb
77
+ - spec/internal/app/models/protector_ability.rb
78
+ - spec/internal/config/database.yml
79
+ - spec/internal/config/routes.rb
80
+ - spec/internal/db/schema.rb
81
+ - spec/internal/log/.gitignore
82
+ - spec/lib/protector/cancan/ability_spec.rb
83
+ - spec/spec_helpers/boot.rb
84
+ homepage: https://github.com/inossidabile/protector-cancan
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.0.2
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Integration layer between Protector and CanCan
108
+ test_files:
109
+ - spec/controllers/dummies_spec.rb
110
+ - spec/internal/app/controllers/dummies_controller.rb
111
+ - spec/internal/app/models/default_ability.rb
112
+ - spec/internal/app/models/dummy.rb
113
+ - spec/internal/app/models/protector_ability.rb
114
+ - spec/internal/config/database.yml
115
+ - spec/internal/config/routes.rb
116
+ - spec/internal/db/schema.rb
117
+ - spec/internal/log/.gitignore
118
+ - spec/lib/protector/cancan/ability_spec.rb
119
+ - spec/spec_helpers/boot.rb