heimdallr 1.0.0.RC2 → 1.0.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.
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.