protector 0.0.1

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: ffb03e438ddf160d6def55eedad02d8666ed2c4a
4
+ data.tar.gz: 8c1cf283df6c5ddd6e0cb96dd8439331406911b4
5
+ SHA512:
6
+ metadata.gz: 83ac0169c982cfa2a839eefb4dda73c10c8432516cb2eea9919e1a337233f1c86be2fbbd53164a3da01b65e19bc929afcef50e48e3918c11348f095fe2376d1c
7
+ data.tar.gz: 5eaa624122f8c6272b0e7b2f139f83e7f12961f487a3019119f8e05bdd82b68fa2eee5d0d4ebb1888cc122aac58b080c27abf9f4a0a05eec7f34818e72e5063b
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,10 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'rake'
5
+ gem 'pry'
6
+ gem 'rspec'
7
+ gem 'guard'
8
+ gem 'guard-rspec'
9
+
10
+ gem 'activerecord', '>= 3.0'
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,33 @@
1
+ # Protector
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'protector'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install protector
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
30
+
31
+ ## LICENSE
32
+
33
+ It is free software, and may be redistributed under the terms of MIT license.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ desc "Default: run the unit tests."
8
+ task :default => :spec
@@ -0,0 +1,105 @@
1
+ module Protector
2
+ module Adapters
3
+ module ActiveRecord
4
+ def self.activate!
5
+ ::ActiveRecord::Base.send :include, Protector::Adapters::ActiveRecord::Base
6
+ ::ActiveRecord::Relation.send :include, Protector::Adapters::ActiveRecord::Relation
7
+ end
8
+
9
+ module Base
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include Protector::DSL::Base
14
+ include Protector::DSL::Entry
15
+
16
+ validate(on: :create) do
17
+ return unless @protector_subject
18
+ errors[:base] << I18n.t('protector.invalid') unless creatable?
19
+ end
20
+
21
+ validate(on: :update) do
22
+ return unless @protector_subject
23
+ errors[:base] << I18n.t('protector.invalid') unless updatable?
24
+ end
25
+
26
+ before_destroy do
27
+ return true unless @protector_subject
28
+ destroyable?
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ def restrict(subject)
34
+ all.restrict(subject)
35
+ end
36
+ end
37
+
38
+ def protector_meta
39
+ unless @protector_subject
40
+ raise "Unprotected entity detected: use `restrict` method to protect it."
41
+ end
42
+
43
+ self.class.protector_meta.evaluate(
44
+ self.class,
45
+ self.class.column_names,
46
+ @protector_subject,
47
+ self
48
+ )
49
+ end
50
+
51
+ def visible?
52
+ protector_meta.relation.where(
53
+ self.class.primary_key => send(self.class.primary_key)
54
+ ).any?
55
+ end
56
+
57
+ def creatable?
58
+ fields = HashWithIndifferentAccess[changed.map{|x| [x, __send__(x)]}]
59
+ protector_meta.creatable?(fields)
60
+ end
61
+
62
+ def updatable?
63
+ fields = HashWithIndifferentAccess[changed.map{|x| [x, __send__(x)]}]
64
+ protector_meta.updatable?(fields)
65
+ end
66
+
67
+ def destroyable?
68
+ protector_meta.destroyable?
69
+ end
70
+ end
71
+
72
+ module Relation
73
+ extend ActiveSupport::Concern
74
+
75
+ included do
76
+ include Protector::DSL::Base
77
+
78
+ alias_method_chain :exec_queries, :protector
79
+ end
80
+
81
+ def protector_meta
82
+ @klass.protector_meta.evaluate(@klass, @klass.column_names, @protector_subject)
83
+ end
84
+
85
+ def count
86
+ super || 0
87
+ end
88
+
89
+ def sum
90
+ super || 0
91
+ end
92
+
93
+ def calculate(*args)
94
+ return super unless @protector_subject
95
+ merge(protector_meta.relation).unrestrict.calculate *args
96
+ end
97
+
98
+ def exec_queries_with_protector(*args)
99
+ return exec_queries_without_protector unless @protector_subject
100
+ @records = merge(protector_meta.relation).unrestrict.send :exec_queries
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,143 @@
1
+ module Protector
2
+ module DSL
3
+ # DSL meta storage and evaluator
4
+ class Meta
5
+ class Box
6
+ attr_accessor :access, :relation, :destroyable
7
+
8
+ def initialize(model, fields, subject, entry, blocks)
9
+ @model = model
10
+ @fields = fields
11
+ @access = {update: {}, view: {}, create: {}}.with_indifferent_access
12
+ @relation = false
13
+ @destroyable = false
14
+
15
+ blocks.each do |b|
16
+ if b.arity == 2
17
+ instance_exec subject, entry, &b
18
+ elsif b.arity == 1
19
+ instance_exec subject, &b
20
+ else
21
+ instance_exec &b
22
+ end
23
+ end
24
+ end
25
+
26
+ def scope(&block)
27
+ @relation = @model.instance_eval(&block)
28
+ end
29
+
30
+ def can(action, *fields)
31
+ return @destroyable = true if action == :destroy
32
+ return unless @access[action]
33
+
34
+ if fields.size == 0
35
+ @fields.each{|f| @access[action][f] = nil}
36
+ else
37
+ fields.each do |a|
38
+ if a.is_a?(Array)
39
+ a.each{|f| @access[action][f] = nil}
40
+ elsif a.is_a?(Hash)
41
+ @access[action].merge!(a)
42
+ else
43
+ @access[action][a] = nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def cannot(action, *fields)
50
+ return @destroyable = false if action == :destroy
51
+ return unless @access[action]
52
+
53
+ if fields.size == 0
54
+ @access[action].clear
55
+ else
56
+ fields.each do |a|
57
+ if a.is_a?(Array)
58
+ a.each{|f| @access[action].delete(f)}
59
+ else
60
+ @access[action].delete(a)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def creatable?(fields=false)
67
+ modifiable? :create, fields
68
+ end
69
+
70
+ def updatable?(fields=false)
71
+ modifiable? :update, fields
72
+ end
73
+
74
+ def destroyable?
75
+ @destroyable
76
+ end
77
+
78
+ private
79
+
80
+ def modifiable?(part, fields)
81
+ return false unless @access[part].length > 0
82
+
83
+ if fields
84
+ return false if (fields.keys - @access[part].keys).length > 0
85
+
86
+ fields.each do |k,v|
87
+ case x = @access[part][k]
88
+ when Range
89
+ return false unless x.include?(v)
90
+ when Proc
91
+ return false unless x.call(v)
92
+ end
93
+ end
94
+ end
95
+
96
+ true
97
+ end
98
+ end
99
+
100
+ def <<(block)
101
+ (@blocks ||= []) << block
102
+ end
103
+
104
+ def evaluate(model, fields, subject, entry=nil)
105
+ Box.new(model, fields, subject, entry, @blocks)
106
+ end
107
+ end
108
+
109
+ module Base
110
+ extend ActiveSupport::Concern
111
+
112
+ included do
113
+ attr_reader :protector_subject
114
+ end
115
+
116
+ def restrict(subject)
117
+ @protector_subject = subject
118
+ self
119
+ end
120
+
121
+ def unrestrict
122
+ @protector_subject = nil
123
+ self
124
+ end
125
+ end
126
+
127
+ module Entry
128
+ extend ActiveSupport::Concern
129
+
130
+ included do
131
+ class <<self
132
+ attr_reader :protector_meta
133
+ end
134
+ end
135
+
136
+ module ClassMethods
137
+ def protect(&block)
138
+ (@protector_meta ||= Meta.new) << block
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,3 @@
1
+ module Protector
2
+ VERSION = "0.0.1"
3
+ end
data/lib/protector.rb ADDED
@@ -0,0 +1,13 @@
1
+ require "active_support/all"
2
+
3
+ require "protector/version"
4
+ require "protector/dsl"
5
+ require "protector/adapters/active_record"
6
+
7
+ I18n.load_path << Dir[File.join File.expand_path(File.dirname(__FILE__)), '..', 'locales', '*.yml']
8
+
9
+ Protector::Adapters::ActiveRecord.activate! if defined?(ActiveRecord)
10
+
11
+ module Protector
12
+ # Your code goes here...
13
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,3 @@
1
+ en:
2
+ protector:
3
+ invalid: "Access denied"
data/protector.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'protector/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "protector"
8
+ spec.version = Protector::VERSION
9
+ spec.authors = ["Boris Staal"]
10
+ spec.email = ["boris@staal.io"]
11
+ spec.description = %q{Comfortable (seriously) white-list security restrictions for models on a field level}
12
+ spec.summary = %q{Protector is a successor to the Heimdallr gem: it hits the same goals keeping the Ruby way}
13
+ spec.homepage = "https://github.com/inossidabile/protector"
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 "activesupport"
22
+ spec.add_dependency "i18n"
23
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helpers/boot'
2
+
3
+ describe Protector::Adapters::ActiveRecord do
4
+ before(:all) do
5
+ ActiveRecord::Schema.verbose = false
6
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
7
+ ActiveRecord::Migration.create_table :dummies do |t|
8
+ t.string :string
9
+ t.integer :number
10
+ t.text :text
11
+ t.timestamps
12
+ end
13
+
14
+ class Dummy < ActiveRecord::Base; end
15
+ Dummy.create! string: 'zomgstring', number: 999, text: 'zomgtext'
16
+ Dummy.create! string: 'zomgstring', number: 777, text: 'zomgtext'
17
+
18
+ Protector::Adapters::ActiveRecord.activate!
19
+ end
20
+
21
+ describe Protector::Adapters::ActiveRecord::Base do
22
+ before(:each) do
23
+ @dummy = Class.new(ActiveRecord::Base) do
24
+ self.table_name = "dummies"
25
+ end
26
+ end
27
+
28
+ it "includes" do
29
+ @dummy.ancestors.should include(Protector::Adapters::ActiveRecord::Base)
30
+ end
31
+
32
+ it "scopes" do
33
+ scope = @dummy.restrict('!')
34
+ scope.should be_an_instance_of ActiveRecord::Relation
35
+ scope.protector_subject.should == '!'
36
+ end
37
+
38
+ it_behaves_like "a model"
39
+ end
40
+
41
+ describe Protector::Adapters::ActiveRecord::Relation do
42
+ before(:all) do
43
+ @dummy = Class.new(ActiveRecord::Base) do
44
+ self.table_name = "dummies"
45
+ end
46
+ end
47
+
48
+ it "includes" do
49
+ @dummy.all.ancestors.should include(Protector::Adapters::ActiveRecord::Base)
50
+ end
51
+
52
+ it "saves subject" do
53
+ @dummy.all.restrict('!').where(number: 999).protector_subject.should == '!'
54
+ end
55
+
56
+ context "with null relation" do
57
+ before(:each) do
58
+ @dummy.instance_eval do
59
+ protect{ scope{ none } }
60
+ end
61
+ end
62
+
63
+ it "counts" do
64
+ @dummy.all.count.should == 2
65
+ @dummy.all.restrict('!').count.should == 0
66
+ end
67
+
68
+ it "fetches" do
69
+ fetched = @dummy.all.restrict('!').to_a
70
+
71
+ @dummy.all.to_a.length.should == 2
72
+ fetched.length.should == 0
73
+ end
74
+ end
75
+
76
+ context "with active relation" do
77
+ before(:each) do
78
+ @dummy.instance_eval do
79
+ protect{ scope{ where(number: 999) } }
80
+ end
81
+ end
82
+
83
+ it "counts" do
84
+ @dummy.all.count.should == 2
85
+ @dummy.all.restrict('!').count.should == 1
86
+ end
87
+
88
+ it "fetches" do
89
+ fetched = @dummy.all.restrict('!').to_a
90
+
91
+ @dummy.all.to_a.length.should == 2
92
+ fetched.length.should == 1
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,116 @@
1
+ require 'spec_helpers/boot'
2
+
3
+ describe Protector::DSL do
4
+ describe Protector::DSL::Base do
5
+ before :each do
6
+ @base = Class.new{ include Protector::DSL::Base }
7
+ end
8
+
9
+ it "defines proper methods" do
10
+ @base.instance_methods.should include(:restrict)
11
+ @base.instance_methods.should include(:protector_subject)
12
+ end
13
+
14
+ it "remembers protection subject" do
15
+ base = @base.new
16
+ base.restrict("universe")
17
+ base.protector_subject.should == "universe"
18
+ end
19
+
20
+ it "forgets protection subject" do
21
+ base = @base.new
22
+ base.restrict("universe")
23
+ base.protector_subject.should == "universe"
24
+ base.unrestrict
25
+ base.protector_subject.should == nil
26
+ end
27
+ end
28
+
29
+ describe Protector::DSL::Entry do
30
+ before :each do
31
+ @entry = Class.new{ include Protector::DSL::Entry }
32
+ end
33
+
34
+ it "instantiates meta entity" do
35
+ @entry.instance_eval do
36
+ protect do; end
37
+ end
38
+
39
+ @entry.protector_meta.should be_an_instance_of(Protector::DSL::Meta)
40
+ end
41
+ end
42
+
43
+ describe Protector::DSL::Meta do
44
+ l = -> (x) { x > 4 }
45
+
46
+ before :each do
47
+ @meta = Protector::DSL::Meta.new
48
+
49
+ # << -> FTW!
50
+ @meta << -> {
51
+ scope { 'relation' }
52
+ }
53
+
54
+ @meta << -> (user) {
55
+ user.should == 'user'
56
+
57
+ can :view
58
+ cannot :view, %w(field5), :field4
59
+ }
60
+
61
+ @meta << -> (user, entry) {
62
+ user.should == 'user'
63
+ entry.should == 'entry'
64
+
65
+ can :update, %w(field1 field2 field3),
66
+ field4: 0..5,
67
+ field5: l
68
+
69
+ can :destroy
70
+ }
71
+ end
72
+
73
+ it "evaluates" do
74
+ @meta.evaluate(nil, [], 'user', 'entry')
75
+ end
76
+
77
+ it "sets relation" do
78
+ data = @meta.evaluate(nil, [], 'user', 'entry')
79
+ data.relation.should == 'relation'
80
+ end
81
+
82
+ it "sets access" do
83
+ data = @meta.evaluate(nil, %w(field1 field2 field3 field4 field5), 'user', 'entry')
84
+ data.access.should == {
85
+ "update" => {
86
+ "field1" => nil,
87
+ "field2" => nil,
88
+ "field3" => nil,
89
+ "field4" => 0..5,
90
+ "field5" => l
91
+ },
92
+ "view" => {
93
+ "field1" => nil,
94
+ "field2" => nil,
95
+ "field3" => nil
96
+ },
97
+ "create" => {}
98
+ }
99
+ end
100
+
101
+ it "marks destroyable" do
102
+ data = @meta.evaluate(nil, [], 'user', 'entry')
103
+ data.destroyable?.should == true
104
+ end
105
+
106
+ it "marks updatable" do
107
+ data = @meta.evaluate(nil, [], 'user', 'entry')
108
+ data.updatable?.should == true
109
+ end
110
+
111
+ it "marks creatable" do
112
+ data = @meta.evaluate(nil, [], 'user', 'entry')
113
+ data.creatable?.should == false
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,18 @@
1
+ require 'pry'
2
+ require 'protector'
3
+
4
+ require 'active_record'
5
+
6
+ require_relative 'model'
7
+
8
+ RSpec.configure do |config|
9
+ config.treat_symbols_as_metadata_keys_with_true_values = true
10
+ config.run_all_when_everything_filtered = true
11
+ config.filter_run :focus
12
+
13
+ # Run specs in random order to surface order dependencies. If you find an
14
+ # order dependency and want to debug it, you can fix the order by providing
15
+ # the seed, which is printed after each run.
16
+ # --seed 1234
17
+ config.order = 'random'
18
+ end
@@ -0,0 +1,333 @@
1
+ RSpec::Matchers.define :invalidate do
2
+ match do |actual|
3
+ actual.save.should == false
4
+ actual.errors[:base].should == ["Access denied"]
5
+ end
6
+ end
7
+
8
+ RSpec::Matchers.define :validate do
9
+ match do |actual|
10
+ actual.class.transaction do
11
+ actual.save.should == true
12
+ raise ActiveRecord::Rollback
13
+ end
14
+
15
+ true
16
+ end
17
+ end
18
+
19
+ shared_examples_for "a model" do
20
+ it "evaluates meta properly" do
21
+ @dummy.instance_eval do
22
+ protect do |subject, dummy|
23
+ subject.should == '!'
24
+
25
+ scope { limit(5) }
26
+
27
+ can :view
28
+ can :create
29
+ can :update
30
+ end
31
+ end
32
+
33
+ fields = Hash[*%w(id string number text created_at updated_at).map{|x| [x, nil]}.flatten]
34
+ dummy = @dummy.new.restrict('!')
35
+ meta = dummy.protector_meta
36
+
37
+ meta.access[:view].should == fields
38
+ meta.access[:create].should == fields
39
+ meta.access[:update].should == fields
40
+ end
41
+
42
+ describe "visibility" do
43
+ it "marks blocked" do
44
+ @dummy.instance_eval do
45
+ protect do
46
+ scope { none }
47
+ end
48
+ end
49
+
50
+ @dummy.first.restrict('!').visible?.should == false
51
+ end
52
+
53
+ it "marks allowed" do
54
+ @dummy.instance_eval do
55
+ protect do
56
+ scope { limit(5) }
57
+ end
58
+ end
59
+
60
+ @dummy.first.restrict('!').visible?.should == true
61
+ end
62
+ end
63
+
64
+ describe "creatability" do
65
+ context "with empty meta" do
66
+ before(:each) do
67
+ @dummy.instance_eval do
68
+ protect do; end
69
+ end
70
+ end
71
+
72
+ it "marks blocked" do
73
+ dummy = @dummy.new(string: 'bam', number: 1)
74
+ dummy.restrict('!').creatable?.should == false
75
+ end
76
+
77
+ it "invalidates" do
78
+ dummy = @dummy.new(string: 'bam', number: 1).restrict('!')
79
+ dummy.should invalidate
80
+ end
81
+ end
82
+
83
+ context "by list of fields" do
84
+ before(:each) do
85
+ @dummy.instance_eval do
86
+ protect do
87
+ can :create, :string
88
+ end
89
+ end
90
+ end
91
+
92
+ it "marks blocked" do
93
+ dummy = @dummy.new(string: 'bam', number: 1)
94
+ dummy.restrict('!').creatable?.should == false
95
+ end
96
+
97
+ it "marks allowed" do
98
+ dummy = @dummy.new(string: 'bam')
99
+ dummy.restrict('!').creatable?.should == true
100
+ end
101
+
102
+ it "invalidates" do
103
+ dummy = @dummy.new(string: 'bam', number: 1).restrict('!')
104
+ dummy.should invalidate
105
+ end
106
+
107
+ it "validates" do
108
+ dummy = @dummy.new(string: 'bam').restrict('!')
109
+ dummy.should validate
110
+ end
111
+ end
112
+
113
+ context "by lambdas" do
114
+ before(:each) do
115
+ @dummy.instance_eval do
116
+ protect do
117
+ can :create, string: -> (x) { x.length == 5 }
118
+ end
119
+ end
120
+ end
121
+
122
+ it "marks blocked" do
123
+ dummy = @dummy.new(string: 'bam')
124
+ dummy.restrict('!').creatable?.should == false
125
+ end
126
+
127
+ it "marks allowed" do
128
+ dummy = @dummy.new(string: '12345')
129
+ dummy.restrict('!').creatable?.should == true
130
+ end
131
+
132
+ it "invalidates" do
133
+ dummy = @dummy.new(string: 'bam').restrict('!')
134
+ dummy.should invalidate
135
+ end
136
+
137
+ it "validates" do
138
+ dummy = @dummy.new(string: '12345').restrict('!')
139
+ dummy.should validate
140
+ end
141
+ end
142
+
143
+ context "by ranges" do
144
+ before(:each) do
145
+ @dummy.instance_eval do
146
+ protect do
147
+ can :create, number: 0..2
148
+ end
149
+ end
150
+ end
151
+
152
+ it "marks blocked" do
153
+ dummy = @dummy.new(number: 500)
154
+ dummy.restrict('!').creatable?.should == false
155
+ end
156
+
157
+ it "marks allowed" do
158
+ dummy = @dummy.new(number: 2)
159
+ dummy.restrict('!').creatable?.should == true
160
+ end
161
+
162
+ it "invalidates" do
163
+ dummy = @dummy.new(number: 500).restrict('!')
164
+ dummy.should invalidate
165
+ end
166
+
167
+ it "validates" do
168
+ dummy = @dummy.new(number: 2).restrict('!')
169
+ dummy.should validate
170
+ end
171
+ end
172
+ end
173
+
174
+ describe "updatability" do
175
+ context "with empty meta" do
176
+ before(:each) do
177
+ @dummy.instance_eval do
178
+ protect do; end
179
+ end
180
+ end
181
+
182
+ it "marks blocked" do
183
+ dummy = @dummy.first
184
+ dummy.assign_attributes(string: 'bam', number: 1)
185
+ dummy.restrict('!').updatable?.should == false
186
+ end
187
+
188
+ it "invalidates" do
189
+ dummy = @dummy.first.restrict('!')
190
+ dummy.assign_attributes(string: 'bam', number: 1)
191
+ dummy.should invalidate
192
+ end
193
+ end
194
+
195
+ context "by list of fields" do
196
+ before(:each) do
197
+ @dummy.instance_eval do
198
+ protect do
199
+ can :update, :string
200
+ end
201
+ end
202
+ end
203
+
204
+ it "marks blocked" do
205
+ dummy = @dummy.first
206
+ dummy.assign_attributes(string: 'bam', number: 1)
207
+ dummy.restrict('!').updatable?.should == false
208
+ end
209
+
210
+ it "marks allowed" do
211
+ dummy = @dummy.first
212
+ dummy.assign_attributes(string: 'bam')
213
+ dummy.restrict('!').updatable?.should == true
214
+ end
215
+
216
+ it "invalidates" do
217
+ dummy = @dummy.first.restrict('!')
218
+ dummy.assign_attributes(string: 'bam', number: 1)
219
+ dummy.should invalidate
220
+ end
221
+
222
+ it "validates" do
223
+ dummy = @dummy.first.restrict('!')
224
+ dummy.assign_attributes(string: 'bam')
225
+ dummy.should validate
226
+ end
227
+ end
228
+
229
+ context "by lambdas" do
230
+ before(:each) do
231
+ @dummy.instance_eval do
232
+ protect do
233
+ can :update, string: -> (x) { x.length == 5 }
234
+ end
235
+ end
236
+ end
237
+
238
+ it "marks blocked" do
239
+ dummy = @dummy.first
240
+ dummy.assign_attributes(string: 'bam')
241
+ dummy.restrict('!').updatable?.should == false
242
+ end
243
+
244
+ it "marks allowed" do
245
+ dummy = @dummy.first
246
+ dummy.assign_attributes(string: '12345')
247
+ dummy.restrict('!').updatable?.should == true
248
+ end
249
+
250
+ it "invalidates" do
251
+ dummy = @dummy.first.restrict('!')
252
+ dummy.assign_attributes(string: 'bam')
253
+ dummy.should invalidate
254
+ end
255
+
256
+ it "validates" do
257
+ dummy = @dummy.first.restrict('!')
258
+ dummy.assign_attributes(string: '12345')
259
+ dummy.should validate
260
+ end
261
+ end
262
+
263
+ context "by ranges" do
264
+ before(:each) do
265
+ @dummy.instance_eval do
266
+ protect do
267
+ can :update, number: 0..2
268
+ end
269
+ end
270
+ end
271
+
272
+ it "marks blocked" do
273
+ dummy = @dummy.first
274
+ dummy.assign_attributes(number: 500)
275
+ dummy.restrict('!').updatable?.should == false
276
+ end
277
+
278
+ it "marks allowed" do
279
+ dummy = @dummy.first
280
+ dummy.assign_attributes(number: 2)
281
+ dummy.restrict('!').updatable?.should == true
282
+ end
283
+
284
+ it "invalidates" do
285
+ dummy = @dummy.first.restrict('!')
286
+ dummy.assign_attributes(number: 500)
287
+ dummy.should invalidate
288
+ end
289
+
290
+ it "validates" do
291
+ dummy = @dummy.first.restrict('!')
292
+ dummy.assign_attributes(number: 2)
293
+ dummy.should validate
294
+ end
295
+ end
296
+ end
297
+
298
+ describe "destroyability" do
299
+ it "marks blocked" do
300
+ @dummy.instance_eval do
301
+ protect do; end
302
+ end
303
+
304
+ @dummy.first.restrict('!').destroyable?.should == false
305
+ end
306
+
307
+ it "marks allowed" do
308
+ @dummy.instance_eval do
309
+ protect do; can :destroy; end
310
+ end
311
+
312
+ @dummy.first.restrict('!').destroyable?.should == true
313
+ end
314
+
315
+ it "invalidates" do
316
+ @dummy.instance_eval do
317
+ protect do; end
318
+ end
319
+
320
+ @dummy.first.restrict('!').destroy.should == false
321
+ end
322
+
323
+ it "validates" do
324
+ @dummy.instance_eval do
325
+ protect do; can :destroy; end
326
+ end
327
+
328
+ dummy = @dummy.create!.restrict('!')
329
+ dummy.destroy.should == dummy
330
+ dummy.destroyed?.should == true
331
+ end
332
+ end
333
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: protector
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Boris Staal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: i18n
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
+ description: Comfortable (seriously) white-list security restrictions for models on
42
+ a field level
43
+ email:
44
+ - boris@staal.io
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - .rspec
51
+ - Gemfile
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - lib/protector.rb
56
+ - lib/protector/adapters/active_record.rb
57
+ - lib/protector/dsl.rb
58
+ - lib/protector/version.rb
59
+ - locales/en.yml
60
+ - protector.gemspec
61
+ - spec/lib/adapters/active_record_spec.rb
62
+ - spec/lib/dsl_spec.rb
63
+ - spec/spec_helpers/boot.rb
64
+ - spec/spec_helpers/model.rb
65
+ homepage: https://github.com/inossidabile/protector
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.0.3
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: 'Protector is a successor to the Heimdallr gem: it hits the same goals keeping
89
+ the Ruby way'
90
+ test_files:
91
+ - spec/lib/adapters/active_record_spec.rb
92
+ - spec/lib/dsl_spec.rb
93
+ - spec/spec_helpers/boot.rb
94
+ - spec/spec_helpers/model.rb