authoreyes 0.1.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: c692f2b44a5992e6ed48d9edb77cda2accfa2bc1
4
+ data.tar.gz: 63e2a6282929830fa130217e901c2a55e1c9897e
5
+ SHA512:
6
+ metadata.gz: 7086240162b0a8734af9922554d20b9867706bbe9619917642d72f2531596c13229a97d16f59831fac56820bd94547bc367c6c3862f525ac248cbca8a58ec1f9
7
+ data.tar.gz: 77bb72a8bf572b7c06595993985f48e896c68327681b563cb6cd2fd1429da4af2fe2b4c77a710e94364d626a6d1d34f1430aade2b56eda9ef6bebd50caf45313
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.gem
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 5.0'
6
+
7
+ group :test do
8
+ gem "codeclimate-test-reporter", require: nil
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Xavier Bick
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Authoreyes
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/authoreyes.svg)](https://badge.fury.io/rb/authoreyes) [![Build Status](https://travis-ci.org/tektite-software/authoreyes.svg?branch=master)](https://travis-ci.org/tektite-software/authoreyes) [![Code Climate](https://codeclimate.com/github/tektite-software/authoreyes/badges/gpa.svg)](https://codeclimate.com/github/tektite-software/authoreyes) [![Test Coverage](https://codeclimate.com/github/tektite-software/authoreyes/badges/coverage.svg)](https://codeclimate.com/github/tektite-software/authoreyes/coverage) [![Inline docs](http://inch-ci.org/github/tektite-software/authoreyes.svg?branch=master)](http://inch-ci.org/github/tektite-software/authoreyes)
4
+
5
+ #### Warning! This gem is an alpha!
6
+
7
+ _Authoreyes_ (pronounced "authorize") is intended to be a modern, Rails 5 compatible replacement for [Declarative Authorization](https://github.com/stffn/declarative_authorization/).
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'authoreyes'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install authoreyes
24
+
25
+ ## Usage
26
+
27
+ For Rails authorization in Rails versions 4 and below, please use [Declarative Authorization](https://github.com/stffn/declarative_authorization) or one of its forks.
28
+
29
+ __Warning! This gem is not finished!__ Although authorization functionality _does_ work, you will need to do a few things to actually use it in your application...
30
+
31
+ At this point, to use Authoreyes, you must do the following:
32
+ 1. Add an `authorization_rules.rb` file.
33
+ 2. Create an Authoreyes DSL Parser object.
34
+ 3. Use the DSL Parser object to parse your authorization rules.
35
+ 4. Create an Authoreyes Authorization Engine object passing in the Parser object.
36
+ 5. Use the Engine's `permit!` and `permit?` methods in your application.
37
+
38
+ ## Contributing
39
+
40
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tektite-software/authoreyes.
41
+
42
+ __Please check out the wiki for guides on contributing to this project.__
43
+
44
+ ## Acknowledgements
45
+
46
+ This gem was originally based on [stffn](https://github.com/stffn)'s gem [Declarative_Authorization](https://github.com/stffn/declarative_authorization). Many thanks to stffn and all who contributed to Declarative Authorization for a great gem!
47
+
48
+
49
+ ## License
50
+
51
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
52
+
53
+ :copyright: 2016 Tektite Software
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'authoreyes/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'authoreyes'
8
+ spec.version = Authoreyes::VERSION
9
+ spec.authors = ['Tektite Software', 'Xavier Bick']
10
+ spec.email = ['fxb9500@gmail.com']
11
+
12
+ spec.summary = 'A modern authorization plugin for Rails.'
13
+ spec.description = 'A powerful, modern authorization plugin for Ruby on
14
+ Rails featuring a declarative DSL for centralized
15
+ authorization roles.
16
+ Based on Declarative Authorization.'
17
+ spec.homepage = 'https://www.github.com/tektite-software/authoreyes'
18
+ spec.license = 'MIT'
19
+
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ spec.bindir = 'exe'
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ['lib']
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.12'
27
+ spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'minitest', '~> 5.0'
29
+ end
@@ -0,0 +1,20 @@
1
+ authorization do
2
+ role :guest do
3
+ # add permissions for guests here, e.g.
4
+ #has_permission_on :conferences, :to => :read
5
+ end
6
+
7
+ # permissions on other roles, such as
8
+ #role :admin do
9
+ # has_permission_on :conferences, :to => :manage
10
+ #end
11
+ end
12
+
13
+ privileges do
14
+ # default privilege hierarchies to facilitate RESTful Rails apps
15
+ privilege :manage, :includes => [:create, :read, :update, :delete]
16
+ privilege :read, :includes => [:index, :show]
17
+ privilege :create, :includes => :new
18
+ privilege :update, :includes => :edit
19
+ privilege :delete, :includes => :destroy
20
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "authoreyes"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/authoreyes.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'authoreyes/version'
2
+ require 'authoreyes/authorization'
3
+ require 'authoreyes/parser'
4
+
5
+ module Authoreyes
6
+ # Your code goes here...
7
+ end
@@ -0,0 +1,80 @@
1
+ # Authorization
2
+ require 'rails'
3
+ require 'authoreyes/authorization/engine'
4
+ require 'authoreyes/authorization/authorization_rule_set'
5
+ require 'authoreyes/authorization/authorization_rule'
6
+ require 'authoreyes/authorization/attribute'
7
+ require 'authoreyes/authorization/attribute_with_permission'
8
+ require 'authoreyes/authorization/anonymous_user'
9
+
10
+ require "set"
11
+ require "forwardable"
12
+
13
+ module Authoreyes
14
+ module Authorization
15
+ # An exception raised if anything goes wrong in the Authorization realm
16
+ class AuthorizationError < StandardError ; end
17
+ # NotAuthorized is raised if the current user is not allowed to perform
18
+ # the given operation possibly on a specific object.
19
+ class NotAuthorized < AuthorizationError ; end
20
+ # AttributeAuthorizationError is more specific than NotAuthorized, signaling
21
+ # that the access was denied on the grounds of attribute conditions.
22
+ class AttributeAuthorizationError < NotAuthorized ; end
23
+ # AuthorizationUsageError is used whenever a situation is encountered
24
+ # in which the application misused the plugin. That is, if, e.g.,
25
+ # authorization rules may not be evaluated.
26
+ class AuthorizationUsageError < AuthorizationError ; end
27
+ # NilAttributeValueError is raised by Attribute#validate? when it hits a nil attribute value.
28
+ # The exception is raised to ensure that the entire rule is invalidated.
29
+ class NilAttributeValueError < AuthorizationError ; end
30
+
31
+ AUTH_DSL_FILES = [Pathname.new(Rails.root || '').join("config", "authorization_rules.rb").to_s] unless defined? AUTH_DSL_FILES
32
+
33
+ # Controller-independent method for retrieving the current user.
34
+ # Needed for model security where the current controller is not available.
35
+ def self.current_user
36
+ Thread.current["current_user"] || AnonymousUser.new
37
+ end
38
+
39
+ # Controller-independent method for setting the current user.
40
+ def self.current_user=(user)
41
+ Thread.current["current_user"] = user
42
+ end
43
+
44
+ # For use in test cases only
45
+ def self.ignore_access_control(state = nil) # :nodoc:
46
+ Thread.current["ignore_access_control"] = state unless state.nil?
47
+ Thread.current["ignore_access_control"] || false
48
+ end
49
+
50
+ def self.activate_authorization_rules_browser? # :nodoc:
51
+ ::Rails.env.development?
52
+ end
53
+
54
+ @@dot_path = "dot"
55
+ def self.dot_path
56
+ @@dot_path
57
+ end
58
+
59
+ def self.dot_path= (path)
60
+ @@dot_path = path
61
+ end
62
+
63
+ @@default_role = :guest
64
+ def self.default_role
65
+ @@default_role
66
+ end
67
+
68
+ def self.default_role= (role)
69
+ @@default_role = role.to_sym
70
+ end
71
+
72
+ def self.is_a_association_proxy? (object)
73
+ if Rails.version < "3.2"
74
+ object.respond_to?(:proxy_reflection)
75
+ else
76
+ object.respond_to?(:proxy_association)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,11 @@
1
+ module Authoreyes
2
+ module Authorization
3
+ # Represents a pseudo-user to facilitate anonymous users in applications
4
+ class AnonymousUser
5
+ attr_reader :role_symbols
6
+ def initialize (roles = [Authorization.default_role])
7
+ @role_symbols = roles
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,164 @@
1
+ module Authoreyes
2
+ module Authorization
3
+ class Attribute
4
+ # attr_conditions_hash of form
5
+ # { :object_attribute => [operator, value_block], ... }
6
+ # { :object_attribute => { :attr => ... } }
7
+ def initialize (conditions_hash)
8
+ @conditions_hash = conditions_hash
9
+ end
10
+
11
+ def initialize_copy (from)
12
+ @conditions_hash = deep_hash_clone(@conditions_hash)
13
+ end
14
+
15
+ def validate? (attr_validator, object = nil, hash = nil)
16
+ object ||= attr_validator.object
17
+ return false unless object
18
+
19
+ if ( Authorization.is_a_association_proxy?(object) &&
20
+ object.respond_to?(:empty?) )
21
+ return false if object.empty?
22
+ object.each do |member|
23
+ return true if validate?(attr_validator, member, hash)
24
+ end
25
+ return false
26
+ end
27
+
28
+ (hash || @conditions_hash).all? do |attr, value|
29
+ attr_value = object_attribute_value(object, attr)
30
+ if value.is_a?(Hash)
31
+ if attr_value.is_a?(Enumerable)
32
+ attr_value.any? do |inner_value|
33
+ validate?(attr_validator, inner_value, value)
34
+ end
35
+ elsif attr_value == nil
36
+ raise NilAttributeValueError, "Attribute #{attr.inspect} is nil in #{object.inspect}."
37
+ else
38
+ validate?(attr_validator, attr_value, value)
39
+ end
40
+ elsif value.is_a?(Array) and value.length == 2 and value.first.is_a?(Symbol)
41
+ evaluated = if value[1].is_a?(Proc)
42
+ attr_validator.evaluate(value[1])
43
+ else
44
+ value[1]
45
+ end
46
+ case value[0]
47
+ when :is
48
+ attr_value == evaluated
49
+ when :is_not
50
+ attr_value != evaluated
51
+ when :contains
52
+ begin
53
+ attr_value.include?(evaluated)
54
+ rescue NoMethodError => e
55
+ raise AuthorizationUsageError, "Operator contains requires a " +
56
+ "subclass of Enumerable as attribute value, got: #{attr_value.inspect} " +
57
+ "contains #{evaluated.inspect}: #{e}"
58
+ end
59
+ when :does_not_contain
60
+ begin
61
+ !attr_value.include?(evaluated)
62
+ rescue NoMethodError => e
63
+ raise AuthorizationUsageError, "Operator does_not_contain requires a " +
64
+ "subclass of Enumerable as attribute value, got: #{attr_value.inspect} " +
65
+ "does_not_contain #{evaluated.inspect}: #{e}"
66
+ end
67
+ when :intersects_with
68
+ begin
69
+ !(evaluated.to_set & attr_value.to_set).empty?
70
+ rescue NoMethodError => e
71
+ raise AuthorizationUsageError, "Operator intersects_with requires " +
72
+ "subclasses of Enumerable, got: #{attr_value.inspect} " +
73
+ "intersects_with #{evaluated.inspect}: #{e}"
74
+ end
75
+ when :is_in
76
+ begin
77
+ evaluated.include?(attr_value)
78
+ rescue NoMethodError => e
79
+ raise AuthorizationUsageError, "Operator is_in requires a " +
80
+ "subclass of Enumerable as value, got: #{attr_value.inspect} " +
81
+ "is_in #{evaluated.inspect}: #{e}"
82
+ end
83
+ when :is_not_in
84
+ begin
85
+ !evaluated.include?(attr_value)
86
+ rescue NoMethodError => e
87
+ raise AuthorizationUsageError, "Operator is_not_in requires a " +
88
+ "subclass of Enumerable as value, got: #{attr_value.inspect} " +
89
+ "is_not_in #{evaluated.inspect}: #{e}"
90
+ end
91
+ when :lt
92
+ attr_value && attr_value < evaluated
93
+ when :lte
94
+ attr_value && attr_value <= evaluated
95
+ when :gt
96
+ attr_value && attr_value > evaluated
97
+ when :gte
98
+ attr_value && attr_value >= evaluated
99
+ else
100
+ raise AuthorizationError, "Unknown operator #{value[0]}"
101
+ end
102
+ else
103
+ raise AuthorizationError, "Wrong conditions hash format"
104
+ end
105
+ end
106
+ end
107
+
108
+ # resolves all the values in condition_hash
109
+ def obligation (attr_validator, hash = nil)
110
+ hash = (hash || @conditions_hash).clone
111
+ hash.each do |attr, value|
112
+ if value.is_a?(Hash)
113
+ hash[attr] = obligation(attr_validator, value)
114
+ elsif value.is_a?(Array) and value.length == 2
115
+ hash[attr] = [value[0], attr_validator.evaluate(value[1])]
116
+ else
117
+ raise AuthorizationError, "Wrong conditions hash format"
118
+ end
119
+ end
120
+ hash
121
+ end
122
+
123
+ def to_long_s (hash = nil)
124
+ if hash
125
+ hash.inject({}) do |memo, key_val|
126
+ key, val = key_val
127
+ memo[key] = case val
128
+ when Array then "#{val[0]} { #{val[1].respond_to?(:to_ruby) ? val[1].to_ruby.gsub(/^proc \{\n?(.*)\n?\}$/m, '\1') : "..."} }"
129
+ when Hash then to_long_s(val)
130
+ end
131
+ memo
132
+ end
133
+ else
134
+ "if_attribute #{to_long_s(@conditions_hash).inspect}"
135
+ end
136
+ end
137
+
138
+ protected
139
+ def object_attribute_value (object, attr)
140
+ begin
141
+ object.send(attr)
142
+ rescue ArgumentError, NoMethodError => e
143
+ raise AuthorizationUsageError, "Error occurred while validating attribute ##{attr} on #{object.inspect}: #{e}.\n" +
144
+ "Please check your authorization rules and ensure the attribute is correctly spelled and \n" +
145
+ "corresponds to a method on the model you are authorizing for."
146
+ end
147
+ end
148
+
149
+ def deep_hash_clone (hash)
150
+ hash.inject({}) do |memo, (key, val)|
151
+ memo[key] = case val
152
+ when Hash
153
+ deep_hash_clone(val)
154
+ when NilClass, Symbol
155
+ val
156
+ else
157
+ val.clone
158
+ end
159
+ memo
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end