permission_policy 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c8ea6d2e2a7840711423115ac405faa69f1f4e1f
4
- data.tar.gz: 1537caf4e3d92a1a32710e9e6bc895f68a7becce
3
+ metadata.gz: e282b311b9a97875e9918d7fe57f6c1c19150d81
4
+ data.tar.gz: 24241e62b1a2355be883b2d3edec11f141c2d250
5
5
  SHA512:
6
- metadata.gz: dc15add153228915f7260aaf366db7363a8aeb033b1e4df5ed6bf6717b074e533f127988e5f08098502364ae3846dcf79c433c68a5e29489b0a772058dbd5e7c
7
- data.tar.gz: d00d2c74aa4effd92e9b9851af5f616ea6996b216f975693f01d0a8673317f248cd65cbae7398a140e78662f5419dab7b5146d19e54a564605dbdefa23ab5766
6
+ metadata.gz: 7049c534d01ebaa7e4472c57e6a12dd56973d9eb4d22d3e2c18da977adde0472ce1a09c9d0516b353b0d7b50ef6e9ba9e6cd1c1ab97485880609cc5cb358b4aa
7
+ data.tar.gz: b7b4be9758970112f4c462aee6328ddecc667e7d7d99430976c5f4bdedf9fe1a510bddb415e25620b6ac636a293d17899c5fdb43cf9f3f00e9464a04fefb0b80
data/.rubocop.yml ADDED
@@ -0,0 +1,56 @@
1
+ AllCops:
2
+ Exclude:
3
+ - '**/Rakefile'
4
+ - '**/Gemfile'
5
+ - '**/Guardfile'
6
+ - 'spec/**/*'
7
+
8
+ Metrics/LineLength:
9
+ Max: 120 # this is already a lot
10
+
11
+ Lint/AssignmentInCondition:
12
+ Enabled: false # it can be a nice shortcut
13
+
14
+ Style/Documentation:
15
+ Enabled: false # atm we don't need this
16
+
17
+ Style/DoubleNegation:
18
+ Enabled: false # the double !! are great for explicitly return true or false
19
+
20
+ Style/SignalException:
21
+ Enabled: false # prefer raise over fail
22
+
23
+ Style/AndOr:
24
+ Enabled: false # for controlflow 'and' & 'or' are great
25
+ # but yes they can be tricky because of their operational precendence:
26
+ # http://ruby-doc.org/core-2.0/doc/syntax/precedence_rdoc.html
27
+
28
+ Metrics/CyclomaticComplexity:
29
+ # count of the number of linearly independent paths
30
+ Max: 4 # don't go ever higher than 5, because then it makes no sense at all.
31
+ # for a tradeoff you can disable it locally in a section of a file with:
32
+ # rubocop:disable Metrics/CyclomaticComplexity
33
+
34
+ Metrics/PerceivedComplexity:
35
+ # the perceived complexity is often higher than the cyclomatic complexity
36
+ Max: 5 # the optimal value can be different for each developer depending on the experience
37
+ # for a tradeoff you can disable it locally in a section of a file with:
38
+ # rubocop:disable Metrics/PerceivedComplexity
39
+
40
+ Metrics/MethodLength:
41
+ Max: 10 # this is already a lot, but for some controller actions a big if/else is more readable
42
+
43
+ Style/GuardClause:
44
+ Enabled: false # usually this blows up the line over the maximum line length
45
+
46
+ Style/WordArray:
47
+ Enabled: false # sometimes not as readable as plain string array
48
+
49
+ Style/TrailingComma:
50
+ Enabled: false # we think it's good practice, because of cleaner git diffs
51
+
52
+ Style/RegexpLiteral:
53
+ Enabled: false # we like %r{} a lot
54
+
55
+ Style/RaiseArgs:
56
+ Enabled: false # don't agree
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :rspec, cmd: 'bundle exec rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { 'spec' }
4
+ watch('spec/spec_helper.rb') { 'spec' }
5
+ end
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec)
6
+ RuboCop::RakeTask.new
5
7
 
6
- task default: :spec
8
+ task default: [:spec, :rubocop]
@@ -0,0 +1,50 @@
1
+ module PermissionPolicy
2
+ class Authorization
3
+ attr_reader :preconditions
4
+
5
+ def initialize(context)
6
+ @preconditions = []
7
+
8
+ PermissionPolicy.preconditions.each do |precondition|
9
+ set! precondition, context.public_send(precondition)
10
+ @preconditions << precondition
11
+ end
12
+ end
13
+
14
+ # Decides if the action is allowed based on the matching strategy.
15
+ # You may want to use this method for controlflow inside views.
16
+ #
17
+ # Example:
18
+ #
19
+ # do_something if allowed?(:manage, subject: my_subject)
20
+ #
21
+ def allowed?(action, options = {})
22
+ strategy_for(action, options).allowed?
23
+ end
24
+
25
+ # Delegates to #allowed? but raises a NotAllowed exception when false.
26
+ # You may want to use this method for halting the execution of a controller method.
27
+ #
28
+ # Example:
29
+ #
30
+ # def edit
31
+ # allow!(:manage, subject: my_subject)
32
+ # end
33
+ #
34
+ def authorize!(action, options = {})
35
+ !!allowed?(action, options) or raise PermissionPolicy::NotAllowed
36
+ end
37
+
38
+ private
39
+
40
+ # Finds the matching strategy which can decide if the action is allowed by lazy checking
41
+ def strategy_for(*args)
42
+ PermissionPolicy.strategies.lazy.map { |klass| Strategies.const_get(klass).new(self, *args) }.find(&:match?)
43
+ end
44
+
45
+ def set!(var, value)
46
+ self.class.send(:attr_reader, var)
47
+ instance_variable_set(:"@#{var}", value) or raise PermissionPolicy::MissingPrecondition.new(var)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ module PermissionPolicy
2
+ class Configuration < OpenStruct
3
+ def preconditions
4
+ precondition_attributes || [:current_user]
5
+ end
6
+
7
+ def strategies
8
+ strategy_order || [:UnknownStrategy]
9
+ end
10
+ end
11
+
12
+ class << self
13
+ attr_accessor :configuration
14
+
15
+ extend Forwardable
16
+ delegate [:preconditions, :strategies] => :config
17
+
18
+ def configure
19
+ yield(config)
20
+ end
21
+
22
+ def config
23
+ self.configuration ||= Configuration.new
24
+ end
25
+
26
+ def authorize_with(*args)
27
+ configure { |c| c.precondition_attributes = *args }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,25 @@
1
+ require 'active_support/concern'
2
+
3
+ module PermissionPolicy
4
+ module ControllerAdditions
5
+ module ClassMethods
6
+ def authorize_with(*args)
7
+ PermissionPolicy.authorize_with(*args)
8
+ end
9
+ end
10
+
11
+ module InstanceMethods
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ helper_method :allowed?
16
+ delegate :allowed?, to: :permission_policy
17
+ delegate :authorize!, to: :permission_policy
18
+ end
19
+
20
+ def permission_policy
21
+ @permission_policy ||= PermissionPolicy::Authorization.new(self)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module PermissionPolicy
2
+ class MissingPrecondition < StandardError
3
+ attr_reader :precondition
4
+
5
+ def initialize(precondition)
6
+ @precondition = precondition
7
+ end
8
+
9
+ def message
10
+ "missing precondition: #{precondition}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module PermissionPolicy
2
+ class NotAllowed < StandardError
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module PermissionPolicy
2
+ class Railtie < ::Rails::Railtie
3
+ initializer 'permission_policy.configure_controller' do
4
+ ActiveSupport.on_load :action_controller do
5
+ include PermissionPolicy::ControllerAdditions::InstanceMethods
6
+ extend PermissionPolicy::ControllerAdditions::ClassMethods
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ module PermissionPolicy
2
+ module Strategies
3
+ #
4
+ # The base strategy defines the object API for all strategies which can be
5
+ # used for permission checks. Each strategy should inherit from it and
6
+ # implement #match? and #allowed?
7
+ #
8
+ class BaseStrategy
9
+ # attributes which are available for #match? and #allowed?
10
+ # are passed from the authorization class.
11
+ #
12
+ # precondition_attributes:: for example [:current_user]
13
+ # action:: This will be :view or :manage
14
+ # options:: A hash having :subject or :feature as keys
15
+ attr_accessor :action, :options
16
+
17
+ def initialize(authorization, action = nil, options = {})
18
+ authorization.preconditions.each do |attribute|
19
+ self.class.send(:attr_accessor, attribute)
20
+ instance_variable_set(:"@#{attribute}", authorization.public_send(attribute))
21
+ end
22
+
23
+ self.action = action
24
+ self.options = options
25
+ end
26
+
27
+ # Check if the strategy is responsible for handling the permission check
28
+ # Has to return true or false
29
+ def match?
30
+ raise NotImplementedError, 'please implement #match? '\
31
+ "for #{self.class.name} which should return true or false, "\
32
+ 'depending on if it can decide #allowed?'
33
+ end
34
+
35
+ # Check if user has necessary permission
36
+ # Has to return true or false
37
+ def allowed?
38
+ raise NotImplementedError, 'please implement #allowed? '\
39
+ "for #{self.class.name} which should decide if the action is allowed, "\
40
+ 'based on the given attributes'
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ module PermissionPolicy
2
+ module Strategies
3
+ # Fallback strategy if no other matches. It allways matches and always
4
+ # denies access no matter what.
5
+ class UnknownStrategy < BaseStrategy
6
+ def match?
7
+ true
8
+ end
9
+
10
+ def allowed?
11
+ false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module PermissionPolicy
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -1,4 +1,16 @@
1
- require "permission_policy/version"
1
+ require 'ostruct'
2
+ require 'permission_policy/version'
3
+ require 'permission_policy/configuration'
4
+ require 'permission_policy/railtie' if defined?(Rails)
5
+ require 'permission_policy/controller_additions'
2
6
 
3
7
  module PermissionPolicy
8
+ autoload :Authorization, 'permission_policy/authorization'
9
+ autoload :MissingPrecondition, 'permission_policy/errors/missing_precondition'
10
+ autoload :NotAllowed, 'permission_policy/errors/not_allowed'
11
+
12
+ module Strategies
13
+ autoload :BaseStrategy, 'permission_policy/strategies/base_strategy'
14
+ autoload :UnknownStrategy, 'permission_policy/strategies/unknown_strategy'
15
+ end
4
16
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = PermissionPolicy::VERSION
9
9
  spec.authors = ['Marco Schaden', 'Maximilian Schulz']
10
10
  spec.email = ['marco@railslove.com', 'max@railslove.com']
11
- spec.summary = %q{Without order, there is chaos}
12
- spec.description = %q{Expandable object oriented authorization solution for Ruby/Rails applications}
11
+ spec.summary = 'Without order, there is chaos'
12
+ spec.description = 'Expandable object oriented authorization solution for Ruby/Rails applications'
13
13
  spec.homepage = ''
14
14
  spec.license = 'MIT'
15
15
 
@@ -18,10 +18,13 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'activesupport', '>= 3.0.0'
21
+ spec.add_dependency 'activesupport', '>= 4.0.0'
22
22
 
23
23
  spec.add_development_dependency 'bundler', '~> 1.6'
24
24
  spec.add_development_dependency 'rspec', '~> 3.0.0'
25
+ spec.add_development_dependency 'guard-rspec'
25
26
  spec.add_development_dependency 'rake'
26
27
  spec.add_development_dependency 'pry'
28
+ spec.add_development_dependency 'rubocop'
29
+ spec.add_development_dependency 'actionpack', '>= 4.0.0'
27
30
  end
@@ -0,0 +1,30 @@
1
+ module PermissionPolicy
2
+ RSpec.describe 'Authorization' do
3
+ before do
4
+ PermissionPolicy.authorize_with :my_user, :my_account
5
+ end
6
+
7
+ subject { PermissionPolicy::Authorization.new(context) }
8
+
9
+ context 'valid' do
10
+ let(:context) { double('context', my_user: 'foo', my_account: 'bar') }
11
+
12
+ it 'sets attribute readers for each precondition' do
13
+ expect(subject.my_user).to eq('foo')
14
+ expect(subject.my_account).to eq('bar')
15
+ end
16
+
17
+ it 'remembers precondition attributes' do
18
+ expect(subject.preconditions).to eq([:my_user, :my_account])
19
+ end
20
+ end
21
+
22
+ context 'invalid' do
23
+ let(:context) { double('context', my_user: nil, my_account: nil) }
24
+
25
+ it 'raises missing precondition error' do
26
+ expect { subject }.to raise_error('missing precondition: my_user')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module PermissionPolicy
2
+ RSpec.describe Configuration do
3
+ before { PermissionPolicy.configuration = nil }
4
+
5
+ it 'has a default precondition' do
6
+ expect(subject.preconditions).to eq([:current_user])
7
+ end
8
+
9
+ it 'has a default strategy' do
10
+ expect(subject.strategies).to eq([:UnknownStrategy])
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ require 'action_controller'
2
+
3
+ class MetalTestController < ActionController::Metal
4
+ include AbstractController::Helpers
5
+ include PermissionPolicy::ControllerAdditions::InstanceMethods
6
+ extend PermissionPolicy::ControllerAdditions::ClassMethods
7
+
8
+ def foo
9
+ end
10
+
11
+ def bar
12
+ end
13
+ end
14
+
15
+
16
+ module PermissionPolicy
17
+ RSpec.describe 'ControllerAdditions' do
18
+ subject { MetalTestController.new }
19
+
20
+ context 'authorization' do
21
+ before do
22
+ MetalTestController.authorize_with :foo, :bar
23
+ is_expected.to receive(:foo) { 'foo' }
24
+ is_expected.to receive(:bar) { 'bar' }
25
+ end
26
+
27
+ it 'is available throuth #permission_policy' do
28
+ expect(subject.permission_policy).to be_kind_of(PermissionPolicy::Authorization)
29
+ end
30
+
31
+ it 'is initialized with preconditions' do
32
+ expect(subject.permission_policy.foo).to eq('foo')
33
+ expect(subject.permission_policy.bar).to eq('bar')
34
+ end
35
+ end
36
+
37
+ context 'helpers' do
38
+ it { is_expected.to respond_to :authorize! }
39
+ it { is_expected.to respond_to :allowed? }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ module PermissionPolicy
2
+ module Strategies
3
+ RSpec.describe BaseStrategy do
4
+
5
+ context 'valid' do
6
+ let(:authorization) { double('authorization', preconditions: [:current_user], current_user: 'me') }
7
+ let(:action) { :foo }
8
+ let(:options) { { something: 'nice' } }
9
+
10
+ subject { described_class.new(authorization, action, options) }
11
+
12
+ it 'sets dynamic attributes' do
13
+ expect(subject.current_user).to eq('me')
14
+ end
15
+
16
+ it 'sets action' do
17
+ expect(subject.action).to eq(:foo)
18
+ end
19
+
20
+ it 'sets options' do
21
+ expect(subject.options[:something]).to eq('nice')
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,8 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'permission_policy'
4
+
5
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
1
6
 
2
7
  RSpec.configure do |config|
3
8
  # The settings below are suggested to provide a good initial experience
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: permission_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Schaden
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-08 00:00:00.000000000 Z
12
+ date: 2015-01-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: 3.0.0
20
+ version: 4.0.0
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: 3.0.0
27
+ version: 4.0.0
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: bundler
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: 3.0.0
56
+ - !ruby/object:Gem::Dependency
57
+ name: guard-rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: rake
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -81,6 +95,34 @@ dependencies:
81
95
  - - ">="
82
96
  - !ruby/object:Gem::Version
83
97
  version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rubocop
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: actionpack
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 4.0.0
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: 4.0.0
84
126
  description: Expandable object oriented authorization solution for Ruby/Rails applications
85
127
  email:
86
128
  - marco@railslove.com
@@ -91,13 +133,27 @@ extra_rdoc_files: []
91
133
  files:
92
134
  - ".gitignore"
93
135
  - ".rspec"
136
+ - ".rubocop.yml"
94
137
  - Gemfile
138
+ - Guardfile
95
139
  - LICENSE.txt
96
140
  - README.md
97
141
  - Rakefile
98
142
  - lib/permission_policy.rb
143
+ - lib/permission_policy/authorization.rb
144
+ - lib/permission_policy/configuration.rb
145
+ - lib/permission_policy/controller_additions.rb
146
+ - lib/permission_policy/errors/missing_precondition.rb
147
+ - lib/permission_policy/errors/not_allowed.rb
148
+ - lib/permission_policy/railtie.rb
149
+ - lib/permission_policy/strategies/base_strategy.rb
150
+ - lib/permission_policy/strategies/unknown_strategy.rb
99
151
  - lib/permission_policy/version.rb
100
152
  - permission_policy.gemspec
153
+ - spec/permission_policy/authorization_spec.rb
154
+ - spec/permission_policy/configuration_spec.rb
155
+ - spec/permission_policy/controller_additions_spec.rb
156
+ - spec/permission_policy/strategies/base_strategy_spec.rb
101
157
  - spec/spec_helper.rb
102
158
  homepage: ''
103
159
  licenses:
@@ -124,4 +180,8 @@ signing_key:
124
180
  specification_version: 4
125
181
  summary: Without order, there is chaos
126
182
  test_files:
183
+ - spec/permission_policy/authorization_spec.rb
184
+ - spec/permission_policy/configuration_spec.rb
185
+ - spec/permission_policy/controller_additions_spec.rb
186
+ - spec/permission_policy/strategies/base_strategy_spec.rb
127
187
  - spec/spec_helper.rb