heimdallr 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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in heimdallr.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/heimdallr.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "heimdallr/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "heimdallr"
7
+ s.version = Heimdallr::VERSION
8
+ s.authors = ["Peter Zotov"]
9
+ s.email = ["whitequark@whitequark.org"]
10
+ s.homepage = "http://github.com/roundlake/heimdallr"
11
+ s.summary = %q{Heimdallr is an ActiveModel extension which provides object- and field-level access control.}
12
+ s.description = %q{Heimdallr aims to provide an easy to configure and efficient object- and field-level access
13
+ control solution, reusing proven patterns from gems like CanCan and allowing one to control permissions in a very
14
+ fine-grained manner.}
15
+
16
+ s.rubyforge_project = "heimdallr"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_runtime_dependency "activesupport", '>= 3.0.0'
24
+ s.add_runtime_dependency "activemodel", '>= 3.0.0'
25
+
26
+ s.add_development_dependency "rspec"
27
+ s.add_development_dependency "activerecord"
28
+ end
data/lib/heimdallr.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "active_support"
2
+ require "active_model"
3
+
4
+ require "heimdallr/version"
5
+
6
+ require "heimdallr/evaluator"
7
+ require "heimdallr/proxy"
8
+ require "heimdallr/model"
9
+ require "heimdallr/resource"
@@ -0,0 +1,94 @@
1
+ module Heimdallr
2
+ class Evaluator
3
+ attr_reader :whitelist, :validations
4
+
5
+ def initialize(model_class, &block)
6
+ @model_class, @block = model_class, block
7
+
8
+ @whitelist = @validations = nil
9
+ @last_context = nil
10
+ end
11
+
12
+ def evaluate(context)
13
+ if context != @last_context
14
+ @whitelist = Hash.new { [] }
15
+ @validations = Hash.new { [] }
16
+
17
+ instance_exec context, &block
18
+
19
+ @whitelist.freeze
20
+ @validations.freeze
21
+
22
+ @last_context = context
23
+ end
24
+
25
+ self
26
+ end
27
+
28
+ def validate(action, record)
29
+ @validations[action].each do |validator|
30
+ validator.validate(record)
31
+ end
32
+ end
33
+
34
+ def can(actions, fields=@model_class.attribute_names)
35
+ actions = Array(actions)
36
+
37
+ case fields
38
+ when Hash # a list of validations
39
+ actions.each do |action|
40
+ @whitelist[action] += fields.keys
41
+ @validations[action] += make_validators(fields)
42
+ end
43
+
44
+ else # an array or a field name
45
+ actions.each do |action|
46
+ @whitelist[action] += Array(fields)
47
+ end
48
+ end
49
+ end
50
+
51
+ def cannot(actions, fields)
52
+ actions = Array(actions)
53
+
54
+ actions.each do |action|
55
+ @whitelist[action] -= fields
56
+ end
57
+ end
58
+
59
+ protected
60
+
61
+ def make_validators(fields)
62
+ validators = []
63
+
64
+ fields.each do |attribute, validations|
65
+ validations.each do |key, options|
66
+ key = "#{key.to_s.camelize}Validator"
67
+
68
+ begin
69
+ validator = key.include?('::') ? key.constantize : ActiveModel::Validations.const_get(key)
70
+ rescue NameError
71
+ raise ArgumentError, "Unknown validator: '#{key}'"
72
+ end
73
+
74
+ validators << validator.new(_parse_validates_options(options).merge(:attributes => [ attribute ]))
75
+ end
76
+ end
77
+
78
+ validators
79
+ end
80
+
81
+ def _parse_validates_options(options) #:nodoc:
82
+ case options
83
+ when TrueClass
84
+ {}
85
+ when Hash
86
+ options
87
+ when Range, Array
88
+ { :in => options }
89
+ else
90
+ { :with => options }
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,35 @@
1
+ module Heimdallr
2
+ module Model
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def restrict(&block)
7
+ @restrictions = Evaluator.new(self, &block)
8
+ end
9
+
10
+ def restricted?
11
+ !@restrictions.nil?
12
+ end
13
+
14
+ def restrictions(context)
15
+ @restrictions.evaluate(context)
16
+ end
17
+ end
18
+
19
+ module InstanceMethods
20
+ def to_proxy(context, action)
21
+ if self.class.restricted?
22
+ Proxy.new(context, action, self)
23
+ else
24
+ self
25
+ end
26
+ end
27
+
28
+ def validate_action(context, action)
29
+ if self.class.restricted?
30
+ self.class.restrictions(context).validate(action, self)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,61 @@
1
+ module Heimdallr
2
+ class Proxy
3
+ def initialize(context, action, object)
4
+ @context, @action, @object = context, action, object
5
+
6
+ @whitelist = @object.class.restrictions(context).whitelist[@action]
7
+ end
8
+
9
+ def self.filter_attributes(attributes, whitelist)
10
+ attributes.delete_if do |key, value|
11
+ !whitelist.include?(key)
12
+ end
13
+
14
+ attributes
15
+ end
16
+
17
+ def attributes
18
+ self.class.filter_attributes(@object.attributes, @whitelist)
19
+ end
20
+
21
+ def update_attributes(attributes)
22
+ @object.update_attributes(self.class.filter_attributes(attributes, @whitelist))
23
+ end
24
+
25
+ def update_attributes!(attributes)
26
+ @object.update_attributes!(self.class.filter_attributes(attributes, @whitelist))
27
+ end
28
+
29
+ def method_missing(method, *args)
30
+ if method.to_s.ends_with?("?") || method.to_s.ends_with?("=")
31
+ normalized_method = method[0..-2].to_sym
32
+ else
33
+ normalized_method = method
34
+ end
35
+
36
+ if defined?(ActiveRecord) && @object.is_a?(ActiveRecord::Base) &&
37
+ association = @object.class.reflect_on_association(method)
38
+ if association.collection?
39
+ raise "not implemented"
40
+ else
41
+ referenced = @object.send(method, *args)
42
+ if referenced.respond_to? :to_proxy
43
+ referenced.to_proxy(@context, @action)
44
+ else
45
+ referenced
46
+ end
47
+ end
48
+ elsif @whitelist.include? normalized_method
49
+ @object.send method, *args
50
+ elsif @object.respond_to? method
51
+ nil
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ def inspect
58
+ "#<Heimdallr::Proxy(#{@whitelist.join ", "}): #{@object.inspect}>"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,111 @@
1
+ module Heimdallr
2
+ module Resource
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ attr_reader :model
7
+
8
+ def resource_for(model, options={})
9
+ @model = model.to_s.capitalize.constantize
10
+
11
+ before_filter :load_all_resources, only: [ :index ].concat(options[:all] || [])
12
+ before_filter :load_one_resource, only: [ :show ].concat(options[:member] || [])
13
+ before_filter :load_referenced_resources, only: [ :update, :destroy ].concat(options[:collection] || [])
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def index
19
+ end
20
+
21
+ def show
22
+ end
23
+
24
+ def new
25
+ render :json => {
26
+ :fields => model.restrictions(security_context).whitelist[:create]
27
+ }
28
+ end
29
+
30
+ def create
31
+ model.transaction do
32
+ if params.has_key? model.name.underscore
33
+ scoped_model.new.to_proxy(security_context, :create).
34
+ update_attributes!(params[model.name.underscore])
35
+ else
36
+ @resources.each_with_index do |resource, index|
37
+ scoped_model.new.to_proxy(security_context, :create).
38
+ update_attributes!(params[model.name.underscore.pluralize][index])
39
+ end
40
+ end
41
+ end
42
+
43
+ if block_given?
44
+ yield
45
+ else
46
+ render_modified_resources
47
+ end
48
+ end
49
+
50
+ def edit
51
+ render :json => {
52
+ :fields => model.restrictions(security_context).whitelist[:update]
53
+ }
54
+ end
55
+
56
+ def update
57
+ model.transaction do
58
+ if params.has_key? model.name.underscore
59
+ @resources.first.to_proxy(security_context, :update).
60
+ update_attributes!(params[model.name.underscore])
61
+ else
62
+ @resources.each_with_index do |resource, index|
63
+ resource.to_proxy(security_context, :update).
64
+ update_attributes!(params[model.name.underscore.pluralize][index])
65
+ end
66
+ end
67
+ end
68
+
69
+ if block_given?
70
+ yield
71
+ else
72
+ render_modified_resources
73
+ end
74
+ end
75
+
76
+ def destroy
77
+ model.transaction do
78
+ @resources.each &:destroy
79
+ end
80
+
81
+ render :json => {}, :status => :ok
82
+ end
83
+
84
+ protected
85
+
86
+ def model
87
+ self.class.model
88
+ end
89
+
90
+ def scoped_model
91
+ self.model.scoped
92
+ end
93
+
94
+ def load_one_resource
95
+ @resource = scoped_model.find(params[:id])
96
+ end
97
+
98
+ def load_all_resources
99
+ @resources = scoped_model.scoped
100
+ end
101
+
102
+ def load_referenced_resources
103
+ @resources = scoped_model.find(params[:id].split(','))
104
+ end
105
+
106
+ def render_modified_resources
107
+ render :action => :index
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ module Heimdallr
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Heimdallr::Proxy do
4
+ pending "write it"
5
+ end
@@ -0,0 +1,5 @@
1
+ require "heimdallr"
2
+
3
+ RSpec.configure do |config|
4
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
5
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heimdallr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Peter Zotov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &80513130 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *80513130
25
+ - !ruby/object:Gem::Dependency
26
+ name: activemodel
27
+ requirement: &80522090 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *80522090
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &80518720 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *80518720
47
+ - !ruby/object:Gem::Dependency
48
+ name: activerecord
49
+ requirement: &79906240 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *79906240
58
+ description: ! "Heimdallr aims to provide an easy to configure and efficient object-
59
+ and field-level access\n control solution, reusing proven patterns from gems like
60
+ CanCan and allowing one to control permissions in a very\n fine-grained manner."
61
+ email:
62
+ - whitequark@whitequark.org
63
+ executables: []
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - .gitignore
68
+ - .rspec
69
+ - Gemfile
70
+ - Rakefile
71
+ - heimdallr.gemspec
72
+ - lib/heimdallr.rb
73
+ - lib/heimdallr/evaluator.rb
74
+ - lib/heimdallr/model.rb
75
+ - lib/heimdallr/proxy.rb
76
+ - lib/heimdallr/resource.rb
77
+ - lib/heimdallr/version.rb
78
+ - spec/proxy_spec.rb
79
+ - spec/spec_helper.rb
80
+ homepage: http://github.com/roundlake/heimdallr
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project: heimdallr
100
+ rubygems_version: 1.8.10
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Heimdallr is an ActiveModel extension which provides object- and field-level
104
+ access control.
105
+ test_files: []