heimdallr 1.0.0.RC2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,4 +3,5 @@
3
3
  .yardoc
4
4
  Gemfile.lock
5
5
  pkg/*
6
- doc/*
6
+ doc/*
7
+ tmp/debug.log
data/README.md CHANGED
@@ -16,24 +16,33 @@ class Article < ActiveRecord::Base
16
16
 
17
17
  belongs_to :owner, :class_name => 'User'
18
18
 
19
- restrict do |user|
20
- if user.admin? || user == self.owner
19
+ restrict do |user, record|
20
+ if user.admin?
21
21
  # Administrator or owner can do everything
22
22
  scope :fetch
23
- scope :destroy
23
+ scope :delete
24
24
  can [:view, :create, :update]
25
25
  else
26
- # Other users can view only non-classified articles...
27
- scope :fetch, -> { where('secrecy_level < ?', 5) }
28
-
29
- # ... and see all fields except the actual security level...
30
- can :view
31
- cannot :view, [:secrecy_level]
26
+ # Other users can view only their own or non-classified articles...
27
+ scope :fetch, -> { where('owner_id = ? or secrecy_level < ?', user.id, 5) }
28
+ scope :delete, -> { where('owner_id = ?', user.id) }
29
+
30
+ # ... and see all fields except the actual security level
31
+ # (through owners can see everything)...
32
+ if record.try(:owner) == user
33
+ can :view
34
+ can :update, {
35
+ secrecy_level: { inclusion: { in: 0..4 } }
36
+ }
37
+ else
38
+ can :view
39
+ cannot :view, [:secrecy_level]
40
+ end
32
41
 
33
42
  # ... and can create them with certain restrictions.
34
43
  can :create, %w(content)
35
- can [:create, :update], {
36
- owner: user,
44
+ can :create, {
45
+ owner_id: user.id,
37
46
  secrecy_level: { inclusion: { in: 0..4 } }
38
47
  }
39
48
  end
@@ -85,7 +94,7 @@ secure.find 2
85
94
  # -- No, it is not.
86
95
  ```
87
96
 
88
- The DSL is described in documentation for [Heimdallr::Model](http://rubydoc.info/gems/heimdallr/1.0.0.RC2/Heimdallr/Model).
97
+ The DSL is described in documentation for [Heimdallr::Model](http://rubydoc.info/gems/heimdallr/master/Heimdallr/Model).
89
98
 
90
99
  Ideology
91
100
  --------
@@ -94,8 +103,18 @@ Heimdallr aims to make security explicit, but nevertheless convenient. It does n
94
103
  implicit operations which may be used maliciously; instead, it forces you to explicitly call `#insecure`
95
104
  method which returns the underlying object. This single point of entry is easily recognizable with code.
96
105
 
97
- Heimdallr would raise exceptions in all cases of forbidden or potentially unsecure access except for attribute
98
- reading to allow for writing uncrufted code in templates (particularly [JBuilder](http://github.com/rails/jbuilder) ones).
106
+ Heimdallr has two restrictions strategies: explicit and implicit. By default it will use explicit strategy
107
+ that means it will raise an exception for every insecure request. Calling `.implicit` will give you a copy
108
+ of proxy object switched to another strategy. With that it will silently return nil for every attribute
109
+ that is inaccessible.
110
+
111
+ Typical cases
112
+ -------------
113
+
114
+ While working with MVC you'll mostly use Heimdallr-wrapped models inside your controllers and views. To
115
+ protect your controllers using DSL from the model you can use [Heimdallr::Resource](http://github.com/roundlake/heimdallr-resource) extension gem.
116
+
117
+ To facilitate views you can use `implicit` strategy which is described above.
99
118
 
100
119
  Compatibility
101
120
  -------------
@@ -107,6 +126,8 @@ Licensing
107
126
 
108
127
  Copyright (C) 2012 Peter Zotov <whitequark@whitequark.org>
109
128
 
129
+ Funded by Round Lake.
130
+
110
131
  Permission is hereby granted, free of charge, to any person obtaining a copy of
111
132
  this software and associated documentation files (the "Software"), to deal in
112
133
  the Software without restriction, including without limitation the rights to
data/Rakefile CHANGED
@@ -1,21 +1,7 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
2
3
 
3
- Dir.chdir File.dirname(__FILE__)
4
+ RSpec::Core::RakeTask.new(:spec)
4
5
 
5
- gemspec = Bundler.load_gemspec Dir["{,*}.gemspec"].first
6
-
7
- task :release => :prepare_for_release
8
-
9
- task :prepare_for_release do
10
- readme = File.read('README.yard.md')
11
- readme.gsub! /{([[:alnum:]:]+)}/i do |match|
12
- %Q|[#{$1}](http://rubydoc.info/gems/#{gemspec.name}/#{gemspec.version}/#{$1.gsub '::', '/'})|
13
- end
14
-
15
- File.open('README.md', 'w') do |f|
16
- f.write readme
17
- end
18
-
19
- %x|git add README.md|
20
- #%x|git commit -m "Bump version to #{gemspec.version}."|
21
- end
6
+ desc "Default: run the unit tests."
7
+ task :default => [:spec]
data/heimdallr.gemspec CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "heimdallr"
6
- s.version = "1.0.0.RC2"
6
+ s.version = "1.0.0"
7
7
  s.authors = ["Peter Zotov", "Boris Staal"]
8
8
  s.email = ["whitequark@whitequark.org", "boris@roundlake.ru"]
9
9
  s.homepage = "http://github.com/roundlake/heimdallr"
@@ -28,8 +28,9 @@ module Heimdallr
28
28
  # Define a scope. A special +:fetch+ scope is applied to any other scope
29
29
  # automatically.
30
30
  #
31
- # @overload scope(name, block)
32
- # This form accepts an explicit lambda.
31
+ # @overload scope(name, block=nil)
32
+ # This form accepts an explicit lambda. If omitted, a scope will be
33
+ # defined to include all possible records.
33
34
  #
34
35
  # @example
35
36
  # scope :fetch, -> { where(:protected => false) }
@@ -45,8 +46,12 @@ module Heimdallr
45
46
  # where(:invisible => false)
46
47
  # end
47
48
  # end
48
- def scope(name, explicit_block, &implicit_block)
49
- @scopes[name] = explicit_block || implicit_block
49
+ def scope(name, explicit_block=nil, &implicit_block)
50
+ unless [:fetch, :delete].include?(name)
51
+ raise "There is no such scope as #{name}"
52
+ end
53
+
54
+ @scopes[name] = explicit_block || implicit_block || -> { scoped }
50
55
  end
51
56
 
52
57
  # Define allowed operations for action(s).
@@ -98,7 +103,7 @@ module Heimdallr
98
103
  def cannot(actions, fields)
99
104
  Array(actions).map(&:to_sym).each do |action|
100
105
  @allowed_fields[action] -= fields.map(&:to_sym)
101
- @fixtures.delete_at *fields
106
+ fields.each { |field| @fixtures.delete field }
102
107
  end
103
108
  end
104
109
 
@@ -140,12 +145,12 @@ module Heimdallr
140
145
  }
141
146
  end
142
147
 
143
- # Compute the restrictions for a given +context+. Invokes a +block+ passed to the
144
- # +initialize+ once.
148
+ # Compute the restrictions for a given +context+ and possibly a specific +record+.
149
+ # Invokes a +block+ passed to the +initialize+ once.
145
150
  #
146
151
  # @raise [RuntimeError] if the evaluated block did not define a set of valid restrictions
147
- def evaluate(context)
148
- if context != @last_context
152
+ def evaluate(context, record=nil)
153
+ if [context, record] != @last_context
149
154
  @scopes = {}
150
155
  @allowed_fields = Hash.new { [] }
151
156
  @validators = Hash.new { [] }
@@ -153,7 +158,7 @@ module Heimdallr
153
158
 
154
159
  @allowed_fields[:view] += [ :id ]
155
160
 
156
- instance_exec context, &@block
161
+ instance_exec context, record, &@block
157
162
 
158
163
  unless @scopes[:fetch]
159
164
  raise RuntimeError, "A :fetch scope must be defined"
@@ -166,7 +171,7 @@ module Heimdallr
166
171
  [@scopes, @allowed_fields, @validators, @fixtures].
167
172
  map(&:freeze)
168
173
 
169
- @last_context = context
174
+ @last_context = [context, record]
170
175
  end
171
176
 
172
177
  self
@@ -39,11 +39,11 @@ module Heimdallr
39
39
  end
40
40
  end
41
41
 
42
- # Evaluate the restrictions for a given +context+.
42
+ # Evaluate the restrictions for a given +context+ and +record+.
43
43
  #
44
44
  # @return [Evaluator]
45
- def restrictions(context)
46
- @restrictions.evaluate(context)
45
+ def restrictions(context, record=nil)
46
+ @restrictions.evaluate(context, record)
47
47
  end
48
48
 
49
49
  # @api private
@@ -18,7 +18,7 @@ module Heimdallr
18
18
  def initialize(context, record, options={})
19
19
  @context, @record, @options = context, record, options
20
20
 
21
- @restrictions = @record.class.restrictions(context)
21
+ @restrictions = @record.class.restrictions(context, record)
22
22
  end
23
23
 
24
24
  # @method decrement(field, by=1)
@@ -105,8 +105,10 @@ module Heimdallr
105
105
  class_eval(<<-EOM, __FILE__, __LINE__)
106
106
  def #{method}
107
107
  scope = @restrictions.request_scope(:delete)
108
- if scope.where({ @record.class.primary_key => @record.to_key }).count != 0
108
+ if scope.where({ @record.class.primary_key => @record.to_key }).any?
109
109
  @record.#{method}
110
+ else
111
+ raise PermissionError, "Deletion is forbidden"
110
112
  end
111
113
  end
112
114
  EOM
@@ -128,6 +130,10 @@ module Heimdallr
128
130
  # @macro delegate
129
131
  delegate :assign_attributes, :to => :@record
130
132
 
133
+ # @method attributes=
134
+ # @macro delegate
135
+ delegate :attributes=, :to => :@record
136
+
131
137
  # Class name of the underlying model.
132
138
  # @return [String]
133
139
  def class_name
@@ -246,6 +252,15 @@ module Heimdallr
246
252
  }.merge(@restrictions.reflection)
247
253
  end
248
254
 
255
+ def modifiable?
256
+ @restrictions.can? :update
257
+ end
258
+
259
+ def destroyable?
260
+ scope = @restrictions.request_scope(:delete)
261
+ scope.where({ @record.class.primary_key => @record.to_key }).any?
262
+ end
263
+
249
264
  protected
250
265
 
251
266
  # Raises an exception if any of the changed attributes are not valid
data/spec/proxy_spec.rb CHANGED
@@ -1,5 +1,125 @@
1
1
  require 'spec_helper'
2
2
 
3
+ class User < ActiveRecord::Base; end
4
+
5
+ class Article < ActiveRecord::Base
6
+ include Heimdallr::Model
7
+
8
+ belongs_to :owner, :class_name => 'User'
9
+
10
+ restrict do |user, record|
11
+ if user.admin?
12
+ # Administrator or owner can do everything
13
+ scope :fetch
14
+ scope :delete
15
+ can [:view, :create, :update]
16
+ else
17
+ # Other users can view only their own or non-classified articles...
18
+ scope :fetch, -> { where('owner_id = ? or secrecy_level < ?', user.id, 5) }
19
+ scope :delete, -> { where('owner_id = ?', user.id) }
20
+
21
+ # ... and see all fields except the actual security level
22
+ # (through owners can see everything)...
23
+ if record.try(:owner) == user
24
+ can :view
25
+ can :update, {
26
+ secrecy_level: { inclusion: { in: 0..4 } }
27
+ }
28
+ else
29
+ can :view
30
+ cannot :view, [:secrecy_level]
31
+ end
32
+
33
+ # ... and can create them with certain restrictions.
34
+ can :create, %w(content)
35
+ can :create, {
36
+ owner_id: user.id,
37
+ secrecy_level: { inclusion: { in: 0..4 } }
38
+ }
39
+ end
40
+ end
41
+ end
42
+
3
43
  describe Heimdallr::Proxy do
4
- pending "write it"
44
+ before(:all) do
45
+ @john = User.create! :admin => false
46
+ Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 10
47
+ Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 3
48
+ end
49
+
50
+ before(:each) do
51
+ @admin = User.new :admin => true
52
+ @looser = User.new :admin => false
53
+ end
54
+
55
+ it "should apply restrictions" do
56
+ proxy = Article.restrict(@admin)
57
+ proxy.should be_a_kind_of Heimdallr::Proxy::Collection
58
+
59
+ proxy = Article.restrict(@looser)
60
+ proxy.should be_a_kind_of Heimdallr::Proxy::Collection
61
+ end
62
+
63
+ it "should handle fetch scope" do
64
+ Article.restrict(@admin).all.count.should == 2
65
+ Article.restrict(@looser).all.count.should == 1
66
+ Article.restrict(@john).all.count.should == 2
67
+ end
68
+
69
+ it "should handle destroy scope" do
70
+ article = Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 0
71
+ expect { article.restrict(@looser).destroy }.should raise_error
72
+ expect { article.restrict(@john).destroy }.should_not raise_error
73
+
74
+ article = Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 0
75
+ expect { article.restrict(@admin).destroy }.should_not raise_error
76
+ end
77
+
78
+ it "should handle list of fields to view" do
79
+ article = Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 0
80
+ expect { article.restrict(@looser).secrecy_level }.should raise_error
81
+ expect { article.restrict(@admin).secrecy_level }.should_not raise_error
82
+ expect { article.restrict(@john).secrecy_level }.should_not raise_error
83
+ article.restrict(@looser).content.should == 'test'
84
+ end
85
+
86
+ it "should handle entities creation" do
87
+ expect { Article.restrict(@looser).create! :content => 'test', :secrecy_level => 10 }.should raise_error
88
+
89
+ article = Article.restrict(@john).create! :content => 'test', :secrecy_level => 3
90
+ article.owner_id.should == @john.id
91
+ end
92
+
93
+ it "should handle entities update" do
94
+ article = Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 10
95
+ expect {
96
+ article.restrict(@john).update_attributes! :secrecy_level => 8
97
+ }.should raise_error
98
+ expect {
99
+ article.restrict(@looser).update_attributes! :secrecy_level => 3
100
+ }.should raise_error
101
+ expect {
102
+ article.restrict(@admin).update_attributes! :secrecy_level => 10
103
+ }.should_not raise_error
104
+ end
105
+
106
+ it "should handle implicit strategy" do
107
+ article = Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 4
108
+ expect { article.restrict(@looser).secrecy_level }.should raise_error
109
+ article.restrict(@looser).implicit.secrecy_level.should == nil
110
+ end
111
+
112
+ it "should answer if object is modifiable" do
113
+ article = Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 4
114
+ article.restrict(@john).modifiable?.should == true
115
+ article.restrict(@admin).modifiable?.should == true
116
+ article.restrict(@looser).modifiable?.should == false
117
+ end
118
+
119
+ it "should answer if object is destroyable" do
120
+ article = Article.create! :owner_id => @john.id, :content => 'test', :secrecy_level => 4
121
+ article.restrict(@john).destroyable?.should == true
122
+ article.restrict(@admin).destroyable?.should == true
123
+ article.restrict(@looser).destroyable?.should == false
124
+ end
5
125
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,22 @@
1
1
  require "heimdallr"
2
+ require "active_record"
3
+ require "sqlite3"
4
+ require "logger"
5
+ require "uri"
2
6
 
3
- RSpec.configure do |config|
4
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ ROOT = File.join(File.dirname(__FILE__), '..')
8
+
9
+ ActiveRecord::Base.logger = Logger.new('tmp/debug.log')
10
+ ActiveRecord::Base.configurations = YAML::load(IO.read('tmp/database.yml'))
11
+ ActiveRecord::Base.establish_connection('test')
12
+
13
+ ActiveRecord::Base.connection.create_table(:users) do |t|
14
+ t.boolean :admin
5
15
  end
16
+
17
+ ActiveRecord::Base.connection.create_table(:articles) do |t|
18
+ t.belongs_to :owner
19
+ t.text :content
20
+ t.integer :secrecy_level
21
+ t.timestamps
22
+ end
data/tmp/.gitkeep ADDED
File without changes
data/tmp/database.yml ADDED
@@ -0,0 +1,5 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+ pool: 5
5
+ timeout: 5000
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heimdallr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.RC2
5
- prerelease: 6
4
+ version: 1.0.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Peter Zotov
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-04-02 00:00:00.000000000 Z
13
+ date: 2012-04-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
- requirement: &76679020 !ruby/object:Gem::Requirement
17
+ requirement: &70194887048460 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 3.0.0
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *76679020
25
+ version_requirements: *70194887048460
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: activemodel
28
- requirement: &76678260 !ruby/object:Gem::Requirement
28
+ requirement: &70194887047820 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 3.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *76678260
36
+ version_requirements: *70194887047820
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rspec
39
- requirement: &76677800 !ruby/object:Gem::Requirement
39
+ requirement: &70194887047340 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *76677800
47
+ version_requirements: *70194887047340
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: activerecord
50
- requirement: &79365140 !ruby/object:Gem::Requirement
50
+ requirement: &70194887039320 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,7 +55,7 @@ dependencies:
55
55
  version: '0'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *79365140
58
+ version_requirements: *70194887039320
59
59
  description: ! "Heimdallr aims to provide an easy to configure and efficient object-
60
60
  and field-level access\n control solution, reusing proven patterns from gems like
61
61
  CanCan and allowing one to manage permissions in a very\n fine-grained manner."
@@ -72,7 +72,6 @@ files:
72
72
  - Gemfile
73
73
  - LICENSE
74
74
  - README.md
75
- - README.yard.md
76
75
  - Rakefile
77
76
  - heimdallr.gemspec
78
77
  - lib/heimdallr.rb
@@ -84,6 +83,8 @@ files:
84
83
  - lib/heimdallr/validator.rb
85
84
  - spec/proxy_spec.rb
86
85
  - spec/spec_helper.rb
86
+ - tmp/.gitkeep
87
+ - tmp/database.yml
87
88
  homepage: http://github.com/roundlake/heimdallr
88
89
  licenses: []
89
90
  post_install_message:
@@ -99,14 +100,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
100
  required_rubygems_version: !ruby/object:Gem::Requirement
100
101
  none: false
101
102
  requirements:
102
- - - ! '>'
103
+ - - ! '>='
103
104
  - !ruby/object:Gem::Version
104
- version: 1.3.1
105
+ version: '0'
105
106
  requirements: []
106
107
  rubyforge_project:
107
- rubygems_version: 1.8.17
108
+ rubygems_version: 1.8.15
108
109
  signing_key:
109
110
  specification_version: 3
110
111
  summary: Heimdallr is an ActiveModel extension which provides object- and field-level
111
112
  access control.
112
- test_files: []
113
+ test_files:
114
+ - spec/proxy_spec.rb
115
+ - spec/spec_helper.rb
data/README.yard.md DELETED
@@ -1,126 +0,0 @@
1
- Heimdallr
2
- =========
3
-
4
- Heimdallr is a gem for managing security restrictions for ActiveRecord objects on field level; think
5
- of it as a supercharged [CanCan](https://github.com/ryanb/cancan). Heimdallr favors whitelisting over blacklisting,
6
- convention over configuration and is duck-type compatible with most of existing code.
7
-
8
- ``` ruby
9
- # Define a typical set of models.
10
- class User < ActiveRecord::Base
11
- has_many :articles
12
- end
13
-
14
- class Article < ActiveRecord::Base
15
- include Heimdallr::Model
16
-
17
- belongs_to :owner, :class_name => 'User'
18
-
19
- restrict do |user|
20
- if user.admin? || user == self.owner
21
- # Administrator or owner can do everything
22
- scope :fetch
23
- scope :destroy
24
- can [:view, :create, :update]
25
- else
26
- # Other users can view only non-classified articles...
27
- scope :fetch, -> { where('secrecy_level < ?', 5) }
28
-
29
- # ... and see all fields except the actual security level...
30
- can :view
31
- cannot :view, [:secrecy_level]
32
-
33
- # ... and can create them with certain restrictions.
34
- can :create, %w(content)
35
- can [:create, :update], {
36
- owner: user,
37
- secrecy_level: { inclusion: { in: 0..4 } }
38
- }
39
- end
40
- end
41
- end
42
-
43
- # Create some fictional data.
44
- admin = User.create admin: true
45
- johndoe = User.create admin: false
46
-
47
- Article.create id: 1, owner: admin, content: "Nothing happens", secrecy_level: 0
48
- Article.create id: 2, owner: admin, content: "This is a secret", secrecy_level: 10
49
- Article.create id: 3, owner: johndoe, content: "Hello World"
50
-
51
- # Get a restricted scope for the user.
52
- secure = Article.restrict(johndoe)
53
-
54
- # Use any ARel methods:
55
- secure.pluck(:content)
56
- # => ["Nothing happens", "Hello World"]
57
-
58
- # Everything should be permitted explicitly:
59
- secure.first.delete
60
- # ! Heimdallr::PermissionError is raised
61
- secure.find(1).secrecy_level
62
- # ! Heimdallr::PermissionError is raised
63
-
64
- # There is a helper for views to be easily written:
65
- view_passed = secure.first.implicit
66
- view_passed.secrecy_level
67
- # => nil
68
-
69
- # If only a single value is possible, it is inferred automatically:
70
- secure.create! content: "My second article"
71
- # => Article(id: 4, owner: johndoe, content: "My second article", security_level: 0)
72
-
73
- # ... and cannot be changed:
74
- secure.create! owner: admin, content: "I'm a haxx0r"
75
- # ! Heimdallr::PermissionError is raised
76
-
77
- # You can use any valid ActiveRecord validators, too:
78
- secure.create! content: "Top Secret", secrecy_level: 10
79
- # ! ActiveRecord::RecordInvalid is raised
80
-
81
- # John Doe would not see what he is not permitted to, ever:
82
- # -- I know that you have this classified material! It's in folder #2.
83
- secure.find 2
84
- # ! ActiveRecord::RecordNotFound is raised
85
- # -- No, it is not.
86
- ```
87
-
88
- The DSL is described in documentation for {Heimdallr::Model}.
89
-
90
- Ideology
91
- --------
92
-
93
- Heimdallr aims to make security explicit, but nevertheless convenient. It does not allow one to call any
94
- implicit operations which may be used maliciously; instead, it forces you to explicitly call `#insecure`
95
- method which returns the underlying object. This single point of entry is easily recognizable with code.
96
-
97
- Heimdallr would raise exceptions in all cases of forbidden or potentially unsecure access except for attribute
98
- reading to allow for writing uncrufted code in templates (particularly [JBuilder](http://github.com/rails/jbuilder) ones).
99
-
100
- Compatibility
101
- -------------
102
-
103
- Ruby 1.8 and ActiveRecord versions prior to 3.0 are not supported.
104
-
105
- Licensing
106
- ---------
107
-
108
- Copyright (C) 2012 Peter Zotov <whitequark@whitequark.org>
109
-
110
- Permission is hereby granted, free of charge, to any person obtaining a copy of
111
- this software and associated documentation files (the "Software"), to deal in
112
- the Software without restriction, including without limitation the rights to
113
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
114
- of the Software, and to permit persons to whom the Software is furnished to do
115
- so, subject to the following conditions:
116
-
117
- The above copyright notice and this permission notice shall be included in all
118
- copies or substantial portions of the Software.
119
-
120
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
121
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
122
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
123
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
124
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
125
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
126
- SOFTWARE.