omniperm 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +21 -0
- data/.gitignore +32 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +146 -0
- data/MIT-LICENSE +20 -0
- data/README.md +47 -0
- data/Rakefile +19 -0
- data/lib/generators/omniperm/install_generator.rb +19 -0
- data/lib/generators/omniperm/templates/omniperm.rb +18 -0
- data/lib/generators/omniperm/templates/omniperm.yml +18 -0
- data/lib/omniperm/config.rb +52 -0
- data/lib/omniperm/core.rb +30 -0
- data/lib/omniperm/decorators.rb +112 -0
- data/lib/omniperm/helpers.rb +24 -0
- data/lib/omniperm/version.rb +3 -0
- data/omniperm.gemspec +22 -0
- data/test/decorators_test.rb +46 -0
- data/test/gemloader.rb +12 -0
- data/test/helper.rb +25 -0
- data/test/models/user.rb +7 -0
- data/test/omniperm_test.rb +50 -0
- data/test/services/class_service.rb +8 -0
- data/test/services/external_service.rb +28 -0
- data/test/services/external_service_class_decorated.rb +24 -0
- data/test/services/internal_service.rb +34 -0
- data/test/services/module_service.rb +8 -0
- data/test/test_config.yml +27 -0
- metadata +28 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a95c6e48a800f14e564fc1739f3de6c7523f6da0df3a74a7551cf1bfb1112ae
|
4
|
+
data.tar.gz: d0cfb2fa102d4364af689f12aa261f03400c340d0f03579463da58625221c252
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc51b8f2f0f9e59d60d74dfa8955f5a26017eff84ded89abd0e39498a731bea21a4c1eb692baec1e89c6fc9e215dc81e33d3a3dfd9adc7b60d84efe8dead6acd
|
7
|
+
data.tar.gz: 4a9b02818fd4a898918c67ff1c423290bcbbf60bbbbdf8fedc5eed741a53760314fc815aa36ce27fd27ecb1cc0555040b031b14f527a13e907edbbcff9e5244f
|
@@ -0,0 +1,21 @@
|
|
1
|
+
version: 2.1
|
2
|
+
orbs:
|
3
|
+
ruby: circleci/ruby@0.1.2
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
docker:
|
8
|
+
- image: circleci/ruby:2.6.3-stretch-node
|
9
|
+
executor: ruby/default
|
10
|
+
steps:
|
11
|
+
- checkout
|
12
|
+
- run:
|
13
|
+
name: Install bundler
|
14
|
+
command: gem install bundler:2.1.4
|
15
|
+
- run:
|
16
|
+
name: Which bundler?
|
17
|
+
command: bundle -v
|
18
|
+
- ruby/bundle-install
|
19
|
+
- run:
|
20
|
+
name: Run tests
|
21
|
+
command: rake
|
data/.gitignore
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# General
|
2
|
+
.DS_Store
|
3
|
+
.AppleDouble
|
4
|
+
.LSOverride
|
5
|
+
|
6
|
+
# Icon must end with two \r
|
7
|
+
Icon
|
8
|
+
|
9
|
+
# Thumbnails
|
10
|
+
._*
|
11
|
+
|
12
|
+
# Files that might appear in the root of a volume
|
13
|
+
.DocumentRevisions-V100
|
14
|
+
.fseventsd
|
15
|
+
.Spotlight-V100
|
16
|
+
.TemporaryItems
|
17
|
+
.Trashes
|
18
|
+
.VolumeIcon.icns
|
19
|
+
.com.apple.timemachine.donotpresent
|
20
|
+
|
21
|
+
# Directories potentially created on remote AFP share
|
22
|
+
.AppleDB
|
23
|
+
.AppleDesktop
|
24
|
+
Network Trash Folder
|
25
|
+
Temporary Items
|
26
|
+
.apdisk
|
27
|
+
|
28
|
+
.ruby-gemset
|
29
|
+
.ruby-version
|
30
|
+
|
31
|
+
tmp/
|
32
|
+
*.gem
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
omniperm (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
actioncable (6.0.3.2)
|
10
|
+
actionpack (= 6.0.3.2)
|
11
|
+
nio4r (~> 2.0)
|
12
|
+
websocket-driver (>= 0.6.1)
|
13
|
+
actionmailbox (6.0.3.2)
|
14
|
+
actionpack (= 6.0.3.2)
|
15
|
+
activejob (= 6.0.3.2)
|
16
|
+
activerecord (= 6.0.3.2)
|
17
|
+
activestorage (= 6.0.3.2)
|
18
|
+
activesupport (= 6.0.3.2)
|
19
|
+
mail (>= 2.7.1)
|
20
|
+
actionmailer (6.0.3.2)
|
21
|
+
actionpack (= 6.0.3.2)
|
22
|
+
actionview (= 6.0.3.2)
|
23
|
+
activejob (= 6.0.3.2)
|
24
|
+
mail (~> 2.5, >= 2.5.4)
|
25
|
+
rails-dom-testing (~> 2.0)
|
26
|
+
actionpack (6.0.3.2)
|
27
|
+
actionview (= 6.0.3.2)
|
28
|
+
activesupport (= 6.0.3.2)
|
29
|
+
rack (~> 2.0, >= 2.0.8)
|
30
|
+
rack-test (>= 0.6.3)
|
31
|
+
rails-dom-testing (~> 2.0)
|
32
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
33
|
+
actiontext (6.0.3.2)
|
34
|
+
actionpack (= 6.0.3.2)
|
35
|
+
activerecord (= 6.0.3.2)
|
36
|
+
activestorage (= 6.0.3.2)
|
37
|
+
activesupport (= 6.0.3.2)
|
38
|
+
nokogiri (>= 1.8.5)
|
39
|
+
actionview (6.0.3.2)
|
40
|
+
activesupport (= 6.0.3.2)
|
41
|
+
builder (~> 3.1)
|
42
|
+
erubi (~> 1.4)
|
43
|
+
rails-dom-testing (~> 2.0)
|
44
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
45
|
+
activejob (6.0.3.2)
|
46
|
+
activesupport (= 6.0.3.2)
|
47
|
+
globalid (>= 0.3.6)
|
48
|
+
activemodel (6.0.3.2)
|
49
|
+
activesupport (= 6.0.3.2)
|
50
|
+
activerecord (6.0.3.2)
|
51
|
+
activemodel (= 6.0.3.2)
|
52
|
+
activesupport (= 6.0.3.2)
|
53
|
+
activestorage (6.0.3.2)
|
54
|
+
actionpack (= 6.0.3.2)
|
55
|
+
activejob (= 6.0.3.2)
|
56
|
+
activerecord (= 6.0.3.2)
|
57
|
+
marcel (~> 0.3.1)
|
58
|
+
activesupport (6.0.3.2)
|
59
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
60
|
+
i18n (>= 0.7, < 2)
|
61
|
+
minitest (~> 5.1)
|
62
|
+
tzinfo (~> 1.1)
|
63
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
64
|
+
builder (3.2.4)
|
65
|
+
concurrent-ruby (1.1.6)
|
66
|
+
crass (1.0.6)
|
67
|
+
erubi (1.9.0)
|
68
|
+
globalid (0.4.2)
|
69
|
+
activesupport (>= 4.2.0)
|
70
|
+
i18n (1.8.5)
|
71
|
+
concurrent-ruby (~> 1.0)
|
72
|
+
loofah (2.6.0)
|
73
|
+
crass (~> 1.0.2)
|
74
|
+
nokogiri (>= 1.5.9)
|
75
|
+
mail (2.7.1)
|
76
|
+
mini_mime (>= 0.1.1)
|
77
|
+
marcel (0.3.3)
|
78
|
+
mimemagic (~> 0.3.2)
|
79
|
+
method_source (1.0.0)
|
80
|
+
mimemagic (0.3.5)
|
81
|
+
mini_mime (1.0.2)
|
82
|
+
mini_portile2 (2.4.0)
|
83
|
+
minitest (5.11.3)
|
84
|
+
minitest-sprint (1.2.1)
|
85
|
+
path_expander (~> 1.1)
|
86
|
+
nio4r (2.5.2)
|
87
|
+
nokogiri (1.10.10)
|
88
|
+
mini_portile2 (~> 2.4.0)
|
89
|
+
path_expander (1.1.0)
|
90
|
+
rack (2.2.3)
|
91
|
+
rack-test (1.1.0)
|
92
|
+
rack (>= 1.0, < 3)
|
93
|
+
rails (6.0.3.2)
|
94
|
+
actioncable (= 6.0.3.2)
|
95
|
+
actionmailbox (= 6.0.3.2)
|
96
|
+
actionmailer (= 6.0.3.2)
|
97
|
+
actionpack (= 6.0.3.2)
|
98
|
+
actiontext (= 6.0.3.2)
|
99
|
+
actionview (= 6.0.3.2)
|
100
|
+
activejob (= 6.0.3.2)
|
101
|
+
activemodel (= 6.0.3.2)
|
102
|
+
activerecord (= 6.0.3.2)
|
103
|
+
activestorage (= 6.0.3.2)
|
104
|
+
activesupport (= 6.0.3.2)
|
105
|
+
bundler (>= 1.3.0)
|
106
|
+
railties (= 6.0.3.2)
|
107
|
+
sprockets-rails (>= 2.0.0)
|
108
|
+
rails-dom-testing (2.0.3)
|
109
|
+
activesupport (>= 4.2.0)
|
110
|
+
nokogiri (>= 1.6)
|
111
|
+
rails-html-sanitizer (1.3.0)
|
112
|
+
loofah (~> 2.3)
|
113
|
+
railties (6.0.3.2)
|
114
|
+
actionpack (= 6.0.3.2)
|
115
|
+
activesupport (= 6.0.3.2)
|
116
|
+
method_source
|
117
|
+
rake (>= 0.8.7)
|
118
|
+
thor (>= 0.20.3, < 2.0)
|
119
|
+
rake (13.0.1)
|
120
|
+
sprockets (4.0.2)
|
121
|
+
concurrent-ruby (~> 1.0)
|
122
|
+
rack (> 1, < 3)
|
123
|
+
sprockets-rails (3.2.1)
|
124
|
+
actionpack (>= 4.0)
|
125
|
+
activesupport (>= 4.0)
|
126
|
+
sprockets (>= 3.0.0)
|
127
|
+
thor (1.0.1)
|
128
|
+
thread_safe (0.3.6)
|
129
|
+
tzinfo (1.2.7)
|
130
|
+
thread_safe (~> 0.1)
|
131
|
+
websocket-driver (0.7.3)
|
132
|
+
websocket-extensions (>= 0.1.0)
|
133
|
+
websocket-extensions (0.1.5)
|
134
|
+
zeitwerk (2.4.0)
|
135
|
+
|
136
|
+
PLATFORMS
|
137
|
+
ruby
|
138
|
+
|
139
|
+
DEPENDENCIES
|
140
|
+
minitest (~> 5.0)
|
141
|
+
minitest-sprint
|
142
|
+
omniperm!
|
143
|
+
rails
|
144
|
+
|
145
|
+
BUNDLED WITH
|
146
|
+
2.1.4
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2020 Samuel BOHN
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
[![Circle CI](https://circleci.com/gh/3pns/omniperm.svg?style=svg)](https://circleci.com/gh/3pns/omniperm)
|
2
|
+
|
3
|
+
# OmniPerm
|
4
|
+
|
5
|
+
## About
|
6
|
+
|
7
|
+
With Omniperm you can centralize your authorization strategies in a YAML file against a configurable context
|
8
|
+
|
9
|
+
## Install
|
10
|
+
|
11
|
+
Install for Ruby on Rails applications
|
12
|
+
|
13
|
+
```
|
14
|
+
rails g omniperm:install
|
15
|
+
```
|
16
|
+
|
17
|
+
## How to use
|
18
|
+
|
19
|
+
Manually authorize a method
|
20
|
+
|
21
|
+
```
|
22
|
+
def my_method
|
23
|
+
service_authorized?
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
Authorize all methods of a class and its children
|
28
|
+
|
29
|
+
```
|
30
|
+
class MyService < Omniperm::AuthorizationRequired
|
31
|
+
|
32
|
+
def do_something
|
33
|
+
end
|
34
|
+
|
35
|
+
def do_another_thing
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class MySubService < MyService
|
40
|
+
|
41
|
+
def do_something
|
42
|
+
end
|
43
|
+
|
44
|
+
def do_another_thing
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
require "rake/testtask"
|
6
|
+
|
7
|
+
desc "Run all the tests"
|
8
|
+
task default: :test
|
9
|
+
|
10
|
+
desc 'Run unit tests.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
puts "running tests"
|
13
|
+
#t.libs << 'lib'
|
14
|
+
t.libs << 'test'
|
15
|
+
# t.pattern = 'test/**/*_test.rb'
|
16
|
+
t.test_files = FileList["test/**/*_test.rb", "test/**/spec_*.rb", "test/gemloader.rb"]
|
17
|
+
t.verbose = true
|
18
|
+
t.warning = false
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Omniperm
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
|
8
|
+
desc "Creates Omniperm initializer and copy a template authorizations.yml to your application."
|
9
|
+
|
10
|
+
def copy_initializer
|
11
|
+
copy_file "omniperm.rb", "config/initializers/omniperm.rb"
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy_authorizations
|
15
|
+
copy_file "omniperm.yml", "config/omniperm.yml"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Configure Omniperm to tailor fit your application
|
2
|
+
Omniperm.configure do |config|
|
3
|
+
config.config_file = 'config/omniperm.yml'
|
4
|
+
|
5
|
+
# Strategies not allowed will be unauthorized
|
6
|
+
# Example of common types of strategies :
|
7
|
+
# Use roles : admin/staff/user/superuser
|
8
|
+
# Third party services : nanosoft/pears/amazonia
|
9
|
+
config.whitelisted_strategies = ["nanosoft", "pears", "amazonia"]
|
10
|
+
|
11
|
+
# determine_strategy shall return the strategy of a given object
|
12
|
+
config.determine_strategy = -> (obj, instance_variables){
|
13
|
+
return obj if obj.class == String
|
14
|
+
return @role unless @role.nil?
|
15
|
+
return obj.role if obj.respond_to?("role")
|
16
|
+
return obj.owner.role if obj.respond_to?("item")
|
17
|
+
}
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#############################
|
2
|
+
######### Omniperm ##########
|
3
|
+
#############################
|
4
|
+
# Describe your authorization strategy in this file
|
5
|
+
|
6
|
+
# ExampleClass:
|
7
|
+
# default: false
|
8
|
+
# pear: true
|
9
|
+
# amazonia: false
|
10
|
+
# ExampleModule:
|
11
|
+
# pear: true
|
12
|
+
# nanosoft: false
|
13
|
+
# NestedClass: true
|
14
|
+
# TestClass: true
|
15
|
+
# default: true
|
16
|
+
# Nested:
|
17
|
+
# nested_method: false
|
18
|
+
# another_nested_method: true
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'yaml' if not Object.const_defined?("YAML")
|
2
|
+
|
3
|
+
module Omniperm
|
4
|
+
# Configures global settings for Omniperm
|
5
|
+
# Omniperm.configure do |config|
|
6
|
+
# config.config_file = 'config/service_authorizations.yml'
|
7
|
+
# end
|
8
|
+
class << self
|
9
|
+
def configure
|
10
|
+
yield config
|
11
|
+
end
|
12
|
+
|
13
|
+
def config
|
14
|
+
@_config ||= Config.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Config
|
19
|
+
attr_accessor :config_file, :whitelisted_strategies, :rules, :determine_strategy
|
20
|
+
attr_reader :rules
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@authorizable_name = 'service'
|
24
|
+
# @config_file = "config/#{@authorizable_name}_authorizations.yml" # for multi contexts
|
25
|
+
@config_file = 'config/omniperm.yml'
|
26
|
+
@whitelisted_strategies = []
|
27
|
+
begin
|
28
|
+
@rules = YAML.load_file(@config_file)
|
29
|
+
rescue
|
30
|
+
@rules = {}
|
31
|
+
end
|
32
|
+
@determine_strategy = -> (obj){
|
33
|
+
return "default"
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def config_file=(config_file)
|
38
|
+
@config_file = config_file
|
39
|
+
@rules = YAML.load_file(@config_file)
|
40
|
+
end
|
41
|
+
|
42
|
+
def determine_strategy=(new_strategy)
|
43
|
+
@determine_strategy = new_strategy
|
44
|
+
Object.class_eval do
|
45
|
+
def __omniperm_determine_strategy(obj)
|
46
|
+
self.instance_exec(obj, &Omniperm.config.determine_strategy)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Omniperm
|
2
|
+
module Core
|
3
|
+
def self.authorize(returnable, authorization)
|
4
|
+
return authorization if returnable.to_s == "boolean"
|
5
|
+
raise Exception.new "Omniperm: Unauthorized" if returnable.to_s == "raise" and authorization == false
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.authorize_service(context, returnable: "boolean", hierarchy: "", strategy: "default")
|
9
|
+
method_name = caller_locations(2,1)[0].label
|
10
|
+
whitelisted_strategies = Omniperm.config.whitelisted_strategies
|
11
|
+
rules = Omniperm.config.rules
|
12
|
+
|
13
|
+
return self.authorize(returnable, false) unless whitelisted_strategies.include?(strategy)
|
14
|
+
stack = [] + hierarchy.split("::") + [method_name] + [""]
|
15
|
+
while stack.slice!(-1) != nil do
|
16
|
+
rule = rules
|
17
|
+
stack.each do |key|
|
18
|
+
next if key.nil?
|
19
|
+
rule = rule[key] unless rule.nil?
|
20
|
+
return self.authorize(returnable, rule) if [true, false].include?(rule)
|
21
|
+
end
|
22
|
+
next if rule.nil?
|
23
|
+
return self.authorize(returnable, rule[strategy]) if rule.is_a? Hash and rule.keys.include?(strategy)
|
24
|
+
return self.authorize(returnable, rule["default"]) if rule.is_a? Hash and rule.keys.include?("default")
|
25
|
+
return self.authorize(returnable, rule) if [true, false].include?(rule)
|
26
|
+
end
|
27
|
+
return self.authorize(returnable, false)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Omniperm
|
2
|
+
module CallbackDecorators
|
3
|
+
|
4
|
+
module MethodsDecorator
|
5
|
+
|
6
|
+
def __before_method
|
7
|
+
end
|
8
|
+
|
9
|
+
def __after_method
|
10
|
+
end
|
11
|
+
|
12
|
+
def decorate_methods_with_before_and_after
|
13
|
+
target_methods = singleton_methods(false) + instance_methods(false)
|
14
|
+
target_methods.reject{|value| [:inject_before_to_methods, :__before_method, :__after_method].include?(value) }.each { |m|
|
15
|
+
# Rename original method
|
16
|
+
target = nil
|
17
|
+
target = self if instance_methods(false).include?(m.to_sym)
|
18
|
+
target = self.singleton_class if singleton_methods(false).include?(m.to_sym)
|
19
|
+
if target
|
20
|
+
target.send(:alias_method, "__#{m}_original", m)
|
21
|
+
target.define_method m do |*args, **kwargs|
|
22
|
+
before_method = Proc.new {
|
23
|
+
if respond_to?("__before_method")
|
24
|
+
return_value = __before_method
|
25
|
+
else
|
26
|
+
return_value = target.__before_method
|
27
|
+
end
|
28
|
+
|
29
|
+
return return_value unless return_value.nil?
|
30
|
+
};
|
31
|
+
before_method.call
|
32
|
+
|
33
|
+
if kwargs.empty?
|
34
|
+
return self.send "__#{m}_original", *args
|
35
|
+
else
|
36
|
+
return self.send "__#{m}_original", *args, **kwargs
|
37
|
+
end
|
38
|
+
# after_method = Proc.new {
|
39
|
+
# return_value = __after_method
|
40
|
+
# return return_value unless return_value.nil?
|
41
|
+
# };
|
42
|
+
# after_method.call
|
43
|
+
end
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class ClassDecorator
|
50
|
+
extend CallbackDecorators::MethodsDecorator
|
51
|
+
|
52
|
+
def self.inherited(subclass)
|
53
|
+
TracePoint.trace(:end) do |t|
|
54
|
+
if subclass == t.self
|
55
|
+
subclass.decorate_methods_with_before_and_after
|
56
|
+
t.disable
|
57
|
+
end
|
58
|
+
end
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
module MixinDecorator
|
64
|
+
def self.included base
|
65
|
+
base.extend CallbackDecorators::MethodsDecorator
|
66
|
+
TracePoint.trace(:end) do |t|
|
67
|
+
if base == t.self
|
68
|
+
base.decorate_methods_with_before_and_after
|
69
|
+
t.disable
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# is working
|
77
|
+
class AuthorizationRequired < CallbackDecorators::ClassDecorator
|
78
|
+
|
79
|
+
def self.__before_method
|
80
|
+
# Warning: method name will be dected as __before_method
|
81
|
+
unless service_authorized?
|
82
|
+
return false # stop execution
|
83
|
+
end
|
84
|
+
return # allow execution to continue
|
85
|
+
end
|
86
|
+
|
87
|
+
def __before_method
|
88
|
+
# Warning: method name will be dected as __before_method
|
89
|
+
unless service_authorized?
|
90
|
+
return false # stop execution
|
91
|
+
end
|
92
|
+
return # allow execution to continue
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
# is broken / dont work anymore
|
98
|
+
module Authorizable
|
99
|
+
include CallbackDecorators::MixinDecorator
|
100
|
+
|
101
|
+
def __before_method
|
102
|
+
unless service_authorized?
|
103
|
+
return false # stop execution
|
104
|
+
end
|
105
|
+
return
|
106
|
+
end
|
107
|
+
|
108
|
+
def __after_method
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'core.rb'
|
2
|
+
|
3
|
+
module Omniperm
|
4
|
+
module Helpers
|
5
|
+
private
|
6
|
+
def hierarchy
|
7
|
+
if [Class, Module].include?(self.class)
|
8
|
+
name
|
9
|
+
else
|
10
|
+
self.class.to_s
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def service_authorized?(obj = nil)
|
15
|
+
strategy = __omniperm_determine_strategy(obj)
|
16
|
+
Core.authorize_service(obj, returnable: "boolean", hierarchy: hierarchy, strategy: strategy)
|
17
|
+
end
|
18
|
+
|
19
|
+
def service_authorized!(obj = nil)
|
20
|
+
strategy = __omniperm_determine_strategy(obj)
|
21
|
+
Core.authorize_service(obj, returnable: "raise", hierarchy: hierarchy, strategy: strategy)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/omniperm.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'omniperm/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{omniperm}
|
8
|
+
s.version = Omniperm::VERSION
|
9
|
+
s.authors = ['Samuel Bohn']
|
10
|
+
s.email = %w(samuel@hydrodigit.com)
|
11
|
+
s.date = %q{2020-07-21}
|
12
|
+
s.description = %q{With Omniperm you can centralize your authorization strategies in a YAML file against a configurable context }
|
13
|
+
s.summary = 'Flexible Authorization for Ruby or Rails'
|
14
|
+
s.homepage = 'https://github.com/3pns/omniperm'
|
15
|
+
s.license = 'MIT'
|
16
|
+
s.files = `git ls-files`.split($/)
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_development_dependency 'minitest', "~> 5.0"
|
20
|
+
s.add_development_dependency 'minitest-sprint'
|
21
|
+
s.add_development_dependency 'rails'
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
describe Omniperm::Core do
|
6
|
+
before do
|
7
|
+
@user = User.new(:pear)
|
8
|
+
@user_googol = User.new(:googol)
|
9
|
+
@service_class_decorated_allowed = Services::ExternalServiceClassDecorated.new(@user)
|
10
|
+
@service_class_decorated_nanosoft_allowed = Services::ExternalServiceClassDecorated.new(@user_googol)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should perform authorization' do
|
14
|
+
skip("todo: fix method name not properly detected using inheritance")
|
15
|
+
assert_equal 42, @service_class_decorated_allowed.load_data
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should perform authorization' do
|
19
|
+
skip("todo: fix method name not properly detected using inheritance")
|
20
|
+
assert_equal 42, @service_class_decorated_allowed.buy
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should perform authorization' do
|
24
|
+
assert_equal false, @service_class_decorated_allowed.bucket
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should perform authorization' do
|
28
|
+
assert_equal false, @service_class_decorated_allowed.n42
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should perform authorization' do
|
32
|
+
assert_equal 42, @service_class_decorated_nanosoft_allowed.load_data
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should perform authorization' do
|
36
|
+
assert_equal 42, @service_class_decorated_nanosoft_allowed.buy
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should perform authorization' do
|
40
|
+
assert_equal 42, @service_class_decorated_nanosoft_allowed.bucket
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should perform authorization' do
|
44
|
+
assert_equal 42, @service_class_decorated_nanosoft_allowed.n42
|
45
|
+
end
|
46
|
+
end
|
data/test/gemloader.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
project = 'omniperm'
|
5
|
+
gemspec = File.expand_path("#{project}.gemspec", Dir.pwd)
|
6
|
+
Gem::Specification.load(gemspec).dependencies.each do |dep|
|
7
|
+
begin
|
8
|
+
gem dep.name, *dep.requirement.as_list
|
9
|
+
rescue Gem::LoadError
|
10
|
+
warn "Cannot load #{dep.name} #{dep.requirement.to_s}"
|
11
|
+
end
|
12
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ENV["RAILS_ENV"] = "test"
|
4
|
+
require_relative '../lib/omniperm'
|
5
|
+
require_relative 'models/user'
|
6
|
+
require_relative 'services/class_service'
|
7
|
+
require_relative 'services/module_service'
|
8
|
+
require_relative 'services/external_service'
|
9
|
+
require_relative 'services/internal_service'
|
10
|
+
require_relative 'services/external_service_class_decorated'
|
11
|
+
require 'minitest/spec'
|
12
|
+
require 'minitest/autorun'
|
13
|
+
Dir["models/services/*.rb"].each {|file| require file }
|
14
|
+
|
15
|
+
# configure Omniperm
|
16
|
+
Omniperm.configure do |config|
|
17
|
+
config.config_file = 'test/test_config.yml'
|
18
|
+
config.whitelisted_strategies = ["nanosoft", "pear", "googol"]
|
19
|
+
config.determine_strategy = -> (obj){
|
20
|
+
return obj if obj.class == String
|
21
|
+
return @user.adaptor_type if @user
|
22
|
+
return obj.adaptor_type if obj.class == User
|
23
|
+
return "default"
|
24
|
+
}
|
25
|
+
end
|
data/test/models/user.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
describe Omniperm::Core do
|
6
|
+
before do
|
7
|
+
@user = User.new(:pear)
|
8
|
+
@service = Services::ExternalService.new(@user)
|
9
|
+
@internal_service = Services::InternalService.new(@user)
|
10
|
+
@secret_service = Services::InternalService::SecretService.new(@user)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should properly detect hierarchy and method_name from a module method' do
|
14
|
+
assert_equal 42, Services::ModuleService.compute(@user)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should properly detect hierarchy and method_name from a class method' do
|
18
|
+
assert_equal 42, Services::ClassService.compute(@user)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should deny if element has default to true' do
|
22
|
+
assert_equal false, @internal_service.save_data
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should priorize elements higher in the hierarchy' do
|
26
|
+
assert_equal 42, @secret_service.save_data
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should find element without passing any arg' do
|
30
|
+
assert_equal false, @internal_service.authorize_with_no_args
|
31
|
+
assert_equal 42, @secret_service.authorize_with_no_args
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should authorize if element in hierarchy is true' do
|
35
|
+
assert_equal 42, @service.load_data
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should authorize if method is true' do
|
39
|
+
assert_equal 42, @service.buy
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should deny if method exist but has no rule for strategy' do
|
43
|
+
assert_equal false, @service.bucket
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should not authorize if no rule in the hierarchy' do
|
47
|
+
assert_equal false, @service.n42
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Services
|
2
|
+
class ExternalService
|
3
|
+
|
4
|
+
def initialize(user)
|
5
|
+
@user = user
|
6
|
+
end
|
7
|
+
|
8
|
+
def load_data
|
9
|
+
return false unless service_authorized?(@user)
|
10
|
+
return 42
|
11
|
+
end
|
12
|
+
|
13
|
+
def buy
|
14
|
+
return false unless service_authorized?(@user)
|
15
|
+
return 42
|
16
|
+
end
|
17
|
+
|
18
|
+
def bucket
|
19
|
+
return false unless service_authorized?(@user)
|
20
|
+
return 42
|
21
|
+
end
|
22
|
+
|
23
|
+
def n42
|
24
|
+
return false unless service_authorized?(@user)
|
25
|
+
return 42
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Services
|
2
|
+
class ExternalServiceClassDecorated < Omniperm::AuthorizationRequired
|
3
|
+
|
4
|
+
def initialize(user)
|
5
|
+
@user = user
|
6
|
+
end
|
7
|
+
|
8
|
+
def load_data
|
9
|
+
return 42
|
10
|
+
end
|
11
|
+
|
12
|
+
def buy
|
13
|
+
return 42
|
14
|
+
end
|
15
|
+
|
16
|
+
def bucket
|
17
|
+
return 42
|
18
|
+
end
|
19
|
+
|
20
|
+
def n42
|
21
|
+
return 42
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Services
|
2
|
+
class InternalService
|
3
|
+
|
4
|
+
def initialize(user)
|
5
|
+
@user = user
|
6
|
+
end
|
7
|
+
|
8
|
+
def authorize_with_no_args
|
9
|
+
return false unless service_authorized?
|
10
|
+
return 42
|
11
|
+
end
|
12
|
+
|
13
|
+
def save_data
|
14
|
+
return false unless service_authorized?(@user)
|
15
|
+
return 42
|
16
|
+
end
|
17
|
+
|
18
|
+
class SecretService
|
19
|
+
def initialize(user)
|
20
|
+
@user = user
|
21
|
+
end
|
22
|
+
|
23
|
+
def authorize_with_no_args
|
24
|
+
return false unless service_authorized?
|
25
|
+
return 42
|
26
|
+
end
|
27
|
+
|
28
|
+
def save_data
|
29
|
+
return false unless service_authorized?(@user)
|
30
|
+
return 42
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Services:
|
2
|
+
InternalService:
|
3
|
+
default: false
|
4
|
+
SecretService: true
|
5
|
+
ExternalService:
|
6
|
+
load_data: true
|
7
|
+
buy:
|
8
|
+
nanosoft: true
|
9
|
+
pear: true
|
10
|
+
bucket:
|
11
|
+
googol: true
|
12
|
+
nanosoft: true
|
13
|
+
ModuleService:
|
14
|
+
compute:
|
15
|
+
pear: true
|
16
|
+
ClassService:
|
17
|
+
compute:
|
18
|
+
pear: true
|
19
|
+
ExternalServiceClassDecorated:
|
20
|
+
googol: true # all true for googol
|
21
|
+
load_data: true
|
22
|
+
buy:
|
23
|
+
nanosoft: true
|
24
|
+
pear: true
|
25
|
+
bucket:
|
26
|
+
googol: true
|
27
|
+
nanosoft: true
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniperm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Bohn
|
@@ -60,7 +60,34 @@ executables: []
|
|
60
60
|
extensions: []
|
61
61
|
extra_rdoc_files: []
|
62
62
|
files:
|
63
|
+
- ".circleci/config.yml"
|
64
|
+
- ".gitignore"
|
65
|
+
- Gemfile
|
66
|
+
- Gemfile.lock
|
67
|
+
- MIT-LICENSE
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- lib/generators/omniperm/install_generator.rb
|
71
|
+
- lib/generators/omniperm/templates/omniperm.rb
|
72
|
+
- lib/generators/omniperm/templates/omniperm.yml
|
63
73
|
- lib/omniperm.rb
|
74
|
+
- lib/omniperm/config.rb
|
75
|
+
- lib/omniperm/core.rb
|
76
|
+
- lib/omniperm/decorators.rb
|
77
|
+
- lib/omniperm/helpers.rb
|
78
|
+
- lib/omniperm/version.rb
|
79
|
+
- omniperm.gemspec
|
80
|
+
- test/decorators_test.rb
|
81
|
+
- test/gemloader.rb
|
82
|
+
- test/helper.rb
|
83
|
+
- test/models/user.rb
|
84
|
+
- test/omniperm_test.rb
|
85
|
+
- test/services/class_service.rb
|
86
|
+
- test/services/external_service.rb
|
87
|
+
- test/services/external_service_class_decorated.rb
|
88
|
+
- test/services/internal_service.rb
|
89
|
+
- test/services/module_service.rb
|
90
|
+
- test/test_config.yml
|
64
91
|
homepage: https://github.com/3pns/omniperm
|
65
92
|
licenses:
|
66
93
|
- MIT
|