consent 1.0.1 → 2.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.
- checksums.yaml +4 -4
- data/.gitignore +14 -6
- data/.rubocop.yml +9 -0
- data/.rubocop_todo.yml +9 -11
- data/Gemfile +5 -1
- data/Rakefile +9 -3
- data/app/models/concerns/consent/authorizable.rb +94 -0
- data/app/models/consent/application_record.rb +7 -0
- data/app/models/consent/history.rb +20 -0
- data/app/models/consent/permission.rb +71 -0
- data/config.ru +9 -0
- data/consent.gemspec +24 -20
- data/db/migrate/20211104225614_create_nitro_auth_authorization_permissions.rb +19 -0
- data/db/migrate/20220420135558_create_nitro_auth_authorization_histories.rb +15 -0
- data/doc/dependency_decisions.yml +3 -0
- data/docs/CHANGELOG.md +23 -0
- data/docs/README.md +355 -0
- data/lib/consent/ability.rb +113 -4
- data/lib/consent/dsl.rb +1 -0
- data/lib/consent/{railtie.rb → engine.rb} +11 -8
- data/lib/consent/model_additions.rb +64 -0
- data/lib/consent/permission_migration.rb +139 -0
- data/lib/consent/reloader.rb +6 -5
- data/lib/consent/rspec/consent_action.rb +7 -7
- data/lib/consent/rspec/consent_view.rb +8 -11
- data/lib/consent/rspec.rb +3 -3
- data/lib/consent/subject_coder.rb +29 -0
- data/lib/consent/symbol_adapter.rb +18 -0
- data/lib/consent/version.rb +1 -1
- data/lib/consent.rb +25 -13
- data/lib/generators/consent/permissions_generator.rb +5 -5
- data/mkdocs.yml +5 -0
- data/renovate.json +15 -2
- metadata +86 -34
- data/.rspec +0 -2
- data/.ruby-version +0 -1
- data/.travis.yml +0 -20
- data/LICENSE +0 -21
- data/README.md +0 -252
- data/TODO.md +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95c988d7005c2d40bc6c83a00421e15079737a8759ae635764baf1008e0bd588
|
4
|
+
data.tar.gz: bdd9f0cc35741ecdf89bbc9b68de9819bcedc87b3e98ddc05a00d086c66d0b6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '008f73e94d325ab83a1a63ffa696aea1e22c6db5b44ef49f9d11bc2c1d8773d5a9cf3eec7466870b4cfabb2d13ec23f4d6fa3742cfa5dd6c890322e83694fe0a'
|
7
|
+
data.tar.gz: aaa27addbb0c0372660f8680e981be5b48a5cc5bb30c42f22d0d75a347111f1733c5b410b67ca8f5e5259bdb76b9283f2af93ec5ce18350aeeed47ac48ccdc87
|
data/.gitignore
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
/.bundle/
|
2
2
|
/.yardoc
|
3
|
-
/Gemfile.lock
|
4
3
|
/_yardoc/
|
5
|
-
|
6
|
-
|
7
|
-
/pkg/
|
4
|
+
coverage
|
5
|
+
pkg
|
8
6
|
/spec/reports/
|
9
|
-
|
10
|
-
|
7
|
+
**/tmp/*
|
8
|
+
!**/tmp/.gitkeep
|
9
|
+
!tmp/.gitignore
|
10
|
+
vendor/bundle
|
11
|
+
*.log
|
12
|
+
*.sqlite
|
13
|
+
*.sqlite3
|
14
|
+
Gemfile.lock
|
15
|
+
|
16
|
+
# Ignore uploaded files in development
|
17
|
+
/storage/*
|
18
|
+
!/storage/.keep
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -1,21 +1,19 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on
|
3
|
+
# on 2022-08-23 19:11:09 UTC using RuboCop version 1.35.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
-
# Offense count:
|
10
|
-
# Configuration parameters:
|
11
|
-
|
12
|
-
Metrics/BlockLength:
|
9
|
+
# Offense count: 3
|
10
|
+
# Configuration parameters: AllowComments, AllowEmptyLambdas.
|
11
|
+
Lint/EmptyBlock:
|
13
12
|
Exclude:
|
14
|
-
- 'spec
|
15
|
-
- 'lib/consent/rspec.rb'
|
13
|
+
- 'spec/consent_spec.rb'
|
16
14
|
|
17
|
-
# Offense count:
|
18
|
-
|
15
|
+
# Offense count: 1
|
16
|
+
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
|
17
|
+
Metrics/MethodLength:
|
19
18
|
Exclude:
|
20
|
-
- '
|
21
|
-
- 'test/**/*'
|
19
|
+
- 'db/migrate/*.rb'
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
1
3
|
# frozen_string_literal: true
|
2
4
|
|
3
|
-
require
|
4
|
-
|
5
|
+
require "bundler/setup"
|
6
|
+
Bundler::GemHelper.install_tasks
|
5
7
|
|
8
|
+
require "rspec/core/rake_task"
|
6
9
|
RSpec::Core::RakeTask.new(:spec)
|
7
10
|
|
8
|
-
|
11
|
+
require "rubocop/rake_task"
|
12
|
+
RuboCop::RakeTask.new(:rubocop)
|
13
|
+
|
14
|
+
task default: %i[spec rubocop]
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Consent
|
4
|
+
module Authorizable
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
has_many :permissions, class_name: "Consent::Permission",
|
9
|
+
foreign_key: :role_id, inverse_of: false,
|
10
|
+
dependent: :delete_all, autosave: true
|
11
|
+
end
|
12
|
+
|
13
|
+
# Grants all permissions in o permissions hash formatted as:
|
14
|
+
#
|
15
|
+
# `{ <subject> => { <action> => <view> } }`
|
16
|
+
#
|
17
|
+
# @example role.grant_all({ "user" => { "read" => "all" }})
|
18
|
+
# @example role.grant_all({ User => { read: :all }})
|
19
|
+
#
|
20
|
+
# When `replace: true`, it mark all existing permisions for destruction
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# role.grant_all(User => { read: :territory })
|
24
|
+
# role.grant_all({ User => { write: :territory }, replace: true)
|
25
|
+
# role.permissions
|
26
|
+
# => [#<Consent::Permission subject: User(...), action: :write, view: :territory>]
|
27
|
+
#
|
28
|
+
# `grant_all` will only keep valid permissions, this excludes any permisison that grants nothing (:no_access)
|
29
|
+
#
|
30
|
+
# @param permissions [Hash] a hash formatted as documented above
|
31
|
+
# @param replace [Boolean] whether we should replace all existing granted permisions
|
32
|
+
#
|
33
|
+
def grant_all(permissions, replace: false)
|
34
|
+
changed = self.permissions
|
35
|
+
.from_hash(permissions)
|
36
|
+
.map { |permission| grant_permission(permission) }
|
37
|
+
(self.permissions - changed).each(&:mark_for_destruction) if replace
|
38
|
+
end
|
39
|
+
|
40
|
+
# Destructive form of {Authorizable#grant_all}. This methods grants all the given permissions and
|
41
|
+
# persists it to the database atomically
|
42
|
+
#
|
43
|
+
# @see #grant_all
|
44
|
+
# @yield after saving before commiting within the transaction
|
45
|
+
#
|
46
|
+
def grant_all!(*args, **kwargs)
|
47
|
+
transaction do
|
48
|
+
grant_all(*args, **kwargs)
|
49
|
+
tap(&:save!)
|
50
|
+
touch
|
51
|
+
yield if block_given?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Grants a permission to a role, replacing any existing permission for the same subject/action pair:
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# role.grant(subject: "user", action: "read", view: "all")
|
59
|
+
# role.grant(subject: "user", action: "read", view: "territory")
|
60
|
+
# role.permissions
|
61
|
+
# => [#<Consent::Permission subject: User(...), action: :read, view: :territory>]
|
62
|
+
#
|
63
|
+
# `grant` only grants valid permissions:
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# role.grant(subject: "user", action: "read", view: "no_access")
|
67
|
+
# role.permissions
|
68
|
+
# => []
|
69
|
+
#
|
70
|
+
# `grant` also does not persist the given permissions, so the caller must #save! the role
|
71
|
+
#
|
72
|
+
# @param subject [Symbol|String|Class] any valid subject
|
73
|
+
# @param action [String|Symbol] a valid action
|
74
|
+
# @param view [String|Symbol] a valid view
|
75
|
+
#
|
76
|
+
def grant(subject:, action:, view:)
|
77
|
+
grant_permission ::Consent::Permission.new(subject: subject, action: action, view: view)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def grant_permission(new_perm)
|
83
|
+
existing_perm = permissions.find { |p| p.subject.eql?(new_perm.subject) && p.action.eql?(new_perm.action) }
|
84
|
+
if existing_perm
|
85
|
+
existing_perm.view = new_perm.view
|
86
|
+
existing_perm.mark_for_destruction unless existing_perm.valid?
|
87
|
+
existing_perm
|
88
|
+
elsif new_perm.valid?
|
89
|
+
association(:permissions).add_to_target(new_perm)
|
90
|
+
new_perm
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Consent
|
4
|
+
class History < ::Consent::ApplicationRecord
|
5
|
+
enum command: { grant: "grant", revoke: "revoke" }
|
6
|
+
|
7
|
+
serialize :subject, ::Consent::SubjectCoder
|
8
|
+
validates :subject, presence: true
|
9
|
+
validates :action, presence: true
|
10
|
+
validates :view, presence: true
|
11
|
+
validates :command, presence: true
|
12
|
+
|
13
|
+
def self.record(command, permission)
|
14
|
+
create!(
|
15
|
+
**permission.slice(:role_id, :subject, :action, :view),
|
16
|
+
command: command.to_s
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Consent
|
4
|
+
class Permission < ::Consent::ApplicationRecord
|
5
|
+
serialize :subject, ::Consent::SubjectCoder
|
6
|
+
|
7
|
+
validates :subject, presence: true
|
8
|
+
validates :action, presence: true
|
9
|
+
validates :view, presence: true,
|
10
|
+
exclusion: {
|
11
|
+
in: [::Consent::NO_ACCESS],
|
12
|
+
message: "must grant access",
|
13
|
+
}
|
14
|
+
after_save { ::Consent::History.record(:grant, self) }
|
15
|
+
after_destroy { ::Consent::History.record(:revoke, self) }
|
16
|
+
|
17
|
+
scope :to, ->(subject:, action: nil, view: nil) do
|
18
|
+
where({ subject: subject, action: action, view: view }.compact)
|
19
|
+
end
|
20
|
+
|
21
|
+
# It is true when it is a replacement for another permission
|
22
|
+
# @private
|
23
|
+
#
|
24
|
+
def replaces?(permission)
|
25
|
+
subject == permission.subject && action == permission.action
|
26
|
+
end
|
27
|
+
|
28
|
+
# Symbol key of an action
|
29
|
+
#
|
30
|
+
def action
|
31
|
+
super&.to_sym
|
32
|
+
end
|
33
|
+
|
34
|
+
# Symbol key of a view or "1" for full access
|
35
|
+
#
|
36
|
+
def view
|
37
|
+
return "1" if Consent::FULL_ACCESS.include?(super)
|
38
|
+
|
39
|
+
super&.to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
# Transforms a hash of permissions and views to grant into a collection
|
43
|
+
# of Consent::Permission
|
44
|
+
#
|
45
|
+
# I.e.:
|
46
|
+
# Permission.from_hash User => { write: :territory }
|
47
|
+
# => [#<Consent::Permission view: :territory, action: :write, subject: User>]
|
48
|
+
#
|
49
|
+
# Permission.from_hash User => { write: :territory, read: :all }
|
50
|
+
# => [#<Consent::Permission view: :territory, action: :write, subject: User>,]
|
51
|
+
# #<Consent::Permission view: :all, action: :read, subject: User>]
|
52
|
+
#
|
53
|
+
# It also eliminates any invalid permission from the resulting set
|
54
|
+
#
|
55
|
+
# I.e.:
|
56
|
+
# Permission.from_hash User => { write: :territory, read: :no_access }, Department: { write: :all }
|
57
|
+
# => [#<Consent::Permission view: :territory, action: :write, subject: User>,]
|
58
|
+
# #<Consent::Permission view: :all, action: :write, subject: Department>]
|
59
|
+
#
|
60
|
+
# @param permissions [Hash] a set of permissions in the hash format
|
61
|
+
# @return [Array<Consent::Permission>]
|
62
|
+
#
|
63
|
+
def self.from_hash(permissions)
|
64
|
+
permissions.flat_map do |subject, actions|
|
65
|
+
actions.flat_map do |action, view|
|
66
|
+
new(subject: subject, action: action, view: view)
|
67
|
+
end
|
68
|
+
end.select(&:valid?)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/config.ru
ADDED
data/consent.gemspec
CHANGED
@@ -1,31 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require
|
5
|
+
require "consent/version"
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name =
|
8
|
+
spec.name = "consent"
|
9
9
|
spec.version = Consent::VERSION
|
10
|
-
spec.authors = [
|
11
|
-
spec.email = [
|
10
|
+
spec.authors = ["Carlos Palhares"]
|
11
|
+
spec.email = ["chjunior@gmail.com"]
|
12
12
|
|
13
|
-
spec.summary =
|
14
|
-
spec.description =
|
13
|
+
spec.summary = "Consent permission based authorization"
|
14
|
+
spec.description = "Consent permission based authorization"
|
15
|
+
spec.homepage = "https://github.com/powerhome/power-tools"
|
16
|
+
spec.license = "MIT"
|
17
|
+
spec.required_ruby_version = ">= 2.7"
|
15
18
|
|
16
|
-
spec.
|
19
|
+
spec.files = `git ls-files`.split.grep_v(/^(test|spec|features)/)
|
20
|
+
spec.require_paths = ["lib"]
|
17
21
|
|
18
|
-
spec.
|
19
|
-
file =~ /^(test|spec|features)/
|
20
|
-
end
|
21
|
-
spec.require_paths = ['lib']
|
22
|
+
spec.add_dependency "cancancan", "3.2.1"
|
22
23
|
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
24
|
+
spec.add_development_dependency "activerecord", ">= 5"
|
25
|
+
spec.add_development_dependency "bundler", "~> 2.1"
|
26
|
+
spec.add_development_dependency "combustion", "~> 1.3"
|
27
|
+
spec.add_development_dependency "license_finder", ">= 7.0"
|
28
|
+
spec.add_development_dependency "pry-byebug", "3.9.0"
|
29
|
+
spec.add_development_dependency "rake", "~> 13"
|
30
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
31
|
+
spec.add_development_dependency "rspec-rails", "~> 5.1.2"
|
32
|
+
spec.add_development_dependency "rubocop-powerhome", "0.5.0"
|
33
|
+
spec.add_development_dependency "sqlite3", "~> 1.4.2"
|
34
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
31
35
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateNitroAuthAuthorizationPermissions < ActiveRecord::Migration[5.2]
|
4
|
+
def change
|
5
|
+
create_table :"#{Consent.table_name_prefix}permissions" do |t|
|
6
|
+
t.string :subject, limit: 80
|
7
|
+
t.string :action, limit: 80
|
8
|
+
t.string :view, limit: 80
|
9
|
+
t.integer :role_id
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index :"#{Consent.table_name_prefix}permissions", :role_id
|
15
|
+
add_index :"#{Consent.table_name_prefix}permissions", :subject
|
16
|
+
add_index :"#{Consent.table_name_prefix}permissions", %i[subject action],
|
17
|
+
name: :"idx_#{Consent.table_name_prefix}permissions_on_subject_and_action"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateNitroAuthAuthorizationHistories < ActiveRecord::Migration[5.2]
|
4
|
+
def change
|
5
|
+
create_table :"#{Consent.table_name_prefix}histories" do |t|
|
6
|
+
t.string :command, limit: 6
|
7
|
+
t.string :subject, limit: 80
|
8
|
+
t.string :action, limit: 80
|
9
|
+
t.string :view, limit: 80
|
10
|
+
t.integer :role_id
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/docs/CHANGELOG.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
## [Unreleased]
|
2
|
+
|
3
|
+
## [2.0.0] - 2022-09-02
|
4
|
+
|
5
|
+
- Support multiple versions of rails (>= 5.2.8.1)
|
6
|
+
- Consent is now an engine, and provides the ability to store and manage permissions for a Consent::Authorizable class (i.e.: Role)
|
7
|
+
- Consent::Ability is improved to support the new Permission model
|
8
|
+
|
9
|
+
## [1.0.1] - 2021-05-10
|
10
|
+
|
11
|
+
- Improve Rspec matchers
|
12
|
+
|
13
|
+
## [1.0.0] - 2021-04-22
|
14
|
+
|
15
|
+
- Consent released with DSL to design permissions and convenient CanCan::Ability implementation.
|
16
|
+
|
17
|
+
## [0.6.0] - 2020-12-22
|
18
|
+
## [0.5.2] - 2019-11-28
|
19
|
+
## [0.5.0] - 2019-11-25
|
20
|
+
## [0.4.3] - 2019-04-02
|
21
|
+
## [0.4.2] - 2019-03-28
|
22
|
+
## [0.4] - 2019-03-27
|
23
|
+
## [0.3.1] - 2016-11-08
|