action_policy-graphiti 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7db91afa6d3c7fbc23e5d0551cb462666a488930e6e3faebe4652da3ffc3d31b
4
+ data.tar.gz: e23d9222e846dd0c649093e296617ed0114a8377f97d28ee13ab36726d65f22c
5
+ SHA512:
6
+ metadata.gz: 5120f1911ee32eca98396110b06c4173114f1bff437f8e51af7f856e35352166bce58f38d129ab7be4f95618e5a15e6b216a7fc4d16cd7fedc4f9dffcbc7b5a4
7
+ data.tar.gz: f28b0083a0fe65b96983fa7b53d292852a009cb1a56bf34f83cd1e9bf862e33ec4e66027afbe625a81df633b1bf74a1863a083f5c85ed308010268845e9b42f9
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Change Log
2
+
3
+ ## [0.0.1] - 2023-05-02
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 shrimple.tech
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,92 @@
1
+ # Action Policy Graphiti
2
+
3
+ This gem allows you to use [Action Policy](https://github.com/palkan/action_policy) as an authorization framework for [Graphiti](https://www.graphiti.dev) applications.
4
+
5
+ The following features are currently enabled:
6
+
7
+ - Authorization of `create`, `update` and `destroy` actions
8
+ - Resource scoping
9
+
10
+ **This gem is under heavy development, was not yet released (since it is not production ready) so use it at your own risk!**
11
+
12
+ ## Installation
13
+
14
+ **This gem was not yet released and can be installed only via a github link**
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem "action_policy-graphiti"
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ The integration is done via including a behaviour module into your Graphiti resources:
25
+
26
+ ```ruby
27
+ class TestResource < ApplicationResource
28
+ include ActionPolicy::Graphiti::Behaviour
29
+ end
30
+ ```
31
+
32
+ Authorization of actions is done via using corresponding class methods:
33
+
34
+ ```ruby
35
+ class TestResource < ApplicationResource
36
+ include ActionPolicy::Graphiti::Behaviour
37
+
38
+ authorize_action :create
39
+ authorize_action :update
40
+ authorize_action :destroy
41
+ end
42
+ ```
43
+ **Note:** current implementation requires you to place `authorize_` directives **after** `before_save` and `before_destroy` hooks (since it is adding authorization checks as hooks and we want them to be called **after** all the regular hooks were completed).
44
+
45
+ Scoping is done via adding the following class method call:
46
+ ```ruby
47
+ class TestResource < ApplicationResource
48
+ include ActionPolicy::Graphiti::Behaviour
49
+
50
+ authorize_scope
51
+ end
52
+ ```
53
+ **Note:** current implementation requires you to place `authorize_scope` call **after** the explicit `base_scope` method (scoping is performed by base scope results modification).
54
+
55
+ You can also use authorization context building inside Graphiti resources (just like with Action Policy in controllers):
56
+ ```ruby
57
+ class TestResource < ApplicationResource
58
+ include ActionPolicy::Graphiti::Behaviour
59
+
60
+ authorize :parameter, through: :acquire_parameter
61
+
62
+ def acquire_parameter
63
+ # Your code goes here
64
+ end
65
+ end
66
+ ```
67
+ Or in a base class:
68
+ ```ruby
69
+ class ApplicationResource < Graphiti::Resource
70
+ include ActionPolicy::Graphiti::Behaviour
71
+
72
+ authorize :parameter, through: :acquire_parameter
73
+
74
+ def acquire_parameter
75
+ # Your code goes here
76
+ end
77
+ end
78
+ ```
79
+ And then in a corresponding policy:
80
+ ```ruby
81
+ class ApplicationPolicy < ActionPolicy::Base
82
+ authorize :parameter
83
+ end
84
+ ```
85
+
86
+ ## Contributing
87
+
88
+ Bug reports and pull requests are welcome on GitHub at https://github.com/shrimple-tech/action_policy-graphiti.
89
+
90
+ ## License
91
+
92
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/graphiti/lookup_chain"
4
+
5
+ module ActionPolicy
6
+ module Graphiti
7
+ # Automatically authorize actions
8
+ module Behaviour
9
+ # Authorization configuration class methods
10
+ # Meant to be used in Graphiti resources
11
+ module ClassMethods
12
+ AUTHORIZABLE_ACTIONS = %i[create update destroy].freeze
13
+ IMPLICITLY_AUTHORIZABLE_ACTIONS = %i[index show].freeze
14
+
15
+ def authorize_action(action, **arguments)
16
+ if AUTHORIZABLE_ACTIONS.include?(action)
17
+ rule = "#{action}?".to_sym
18
+
19
+ callback_and_arguments = callback_and_arguments_for_action(action)
20
+
21
+ callback = callback_and_arguments[:callback]
22
+ callback_arguments = callback_and_arguments[:arguments]
23
+
24
+ send(callback, **callback_arguments) do |model|
25
+ authorize! model, with: ActionPolicy.lookup(self), to: rule, **arguments
26
+ end
27
+ elsif IMPLICITLY_AUTHORIZABLE_ACTIONS.include?(action)
28
+ raise ArgumentError, "Index and show authorization is done implicitly by scoping"
29
+ else
30
+ raise ArgumentError, "Unknown action cannot be authorized"
31
+ end
32
+ end
33
+
34
+ def callback_and_arguments_for_action(action)
35
+ if action == :destroy
36
+ callback = :before_destroy
37
+ arguments = {}
38
+ else
39
+ callback = :before_save
40
+ arguments = { only: [action] }
41
+ end
42
+
43
+ {
44
+ callback: callback,
45
+ arguments: arguments
46
+ }
47
+ end
48
+
49
+ def authorize_create
50
+ authorize_action(:create)
51
+ end
52
+
53
+ def authorize_update
54
+ authorize_action(:update)
55
+ end
56
+
57
+ def authorize_destroy
58
+ authorize_action(:destroy)
59
+ end
60
+
61
+ def authorize_scope(_scope_name = nil)
62
+ original_base_scope = instance_method(:base_scope)
63
+
64
+ define_method(:base_scope) do |*args, &block|
65
+ authorized_scope(
66
+ original_base_scope.bind(self).call(*args, &block),
67
+ with: ActionPolicy.lookup(self)
68
+ )
69
+ end
70
+ end
71
+ end
72
+
73
+ def self.included(base)
74
+ base.include(ActionPolicy::Behaviour)
75
+
76
+ base.extend(ClassMethods)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/graphiti/policy_name_inferrer"
4
+
5
+ module ActionPolicy
6
+ # Allow policies to be found not only for ActiveRecords
7
+ # but for Graphiti Resources (<EntityName>Resource) too
8
+ module LookupChain
9
+ INFER_FROM_RESOURCE_CLASS = lambda do |resource, namespace: nil, strict_namespace: false, **_options|
10
+ policy_name = Graphiti::PolicyNameInferrer.infer(resource)
11
+ lookup_within_namespace(policy_name, namespace, strict: strict_namespace)
12
+ end
13
+
14
+ INFER_FOR_BASE_FROM_POLYMORPHIC_RESOURCE_CLASS = lambda do |resource, namespace: nil, strict_namespace: false, **_options|
15
+ policy_name = Graphiti::PolicyNameInferrer.infer_polymorphic(resource)
16
+ lookup_within_namespace(policy_name, namespace, strict: strict_namespace)
17
+ end
18
+
19
+ chain << ActionPolicy::LookupChain::INFER_FROM_RESOURCE_CLASS
20
+ chain << ActionPolicy::LookupChain::INFER_FOR_BASE_FROM_POLYMORPHIC_RESOURCE_CLASS
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/graphiti/resource_analyzer"
4
+
5
+ module ActionPolicy
6
+ module Graphiti
7
+ # Infer policy names for Graphiti resources
8
+ module PolicyNameInferrer
9
+ def self.infer(resource)
10
+ model_name = ResourceAnalyzer.model_name(resource)
11
+
12
+ "#{model_name}Policy" if model_name
13
+ end
14
+
15
+ def self.infer_polymorphic(resource)
16
+ base_model_name = ResourceAnalyzer.base_model_name(resource)
17
+
18
+ "#{base_model_name}Policy" if base_model_name
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/graphiti/resource_validator"
4
+
5
+ module ActionPolicy
6
+ module Graphiti
7
+ # A helper module used to get resource details
8
+ module ResourceAnalyzer
9
+ def self.resource_class(resource)
10
+ return unless ResourceValidator.graphiti_resource?(resource)
11
+
12
+ resource.is_a?(Class) ? resource : resource.class
13
+ end
14
+
15
+ def self.base_resource_class(polymorphic_resource)
16
+ return unless ResourceValidator.polymorphic_graphiti_resource?(polymorphic_resource)
17
+
18
+ resource_class = resource.is_a?(Class) ? resource : resource.class
19
+
20
+ polymorphic_marker_index = resource_class.ancestors.find_index(::Graphiti::Resource::Polymorphism)
21
+
22
+ resource_class.ancestors[polymorphic_marker_index + 1]
23
+ end
24
+
25
+ def self.model_name(resource)
26
+ resource_class(resource)&.name&.delete_suffix("Resource")
27
+ end
28
+
29
+ def self.base_model_name(polymorphic_resource)
30
+ base_resource_class(polymorphic_resource)&.name&.delete_suffix("Resource")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ module Graphiti
5
+ # Validate Graphiti resources
6
+ # Provides a few helpers mostly used in policy class lookups
7
+ module ResourceValidator
8
+ def self.graphiti_resource?(resource)
9
+ resource_class = resource.is_a?(Class) ? resource : resource.class
10
+
11
+ return unless resource_class.name.end_with?("Resource") &&
12
+ resource_class.ancestors.include?(::Graphiti::Resource)
13
+
14
+ true
15
+ end
16
+
17
+ def self.polymorphic_graphiti_resource?(resource)
18
+ resource_class = resource.is_a?(Class) ? resource : resource.class
19
+
20
+ return unless resource_class.name.end_with?("Resource") &&
21
+ resource_class.ancestors.include?(::Graphiti::Resource::Polymorphism) &&
22
+ resource_class.ancestors.include?(::Graphiti::Resource)
23
+
24
+ true
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ module Graphiti
5
+ VERSION = "0.0.1"
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphiti"
4
+ require "action_policy"
5
+ require "active_support"
6
+
7
+ require "action_policy/graphiti/behaviour"
8
+
9
+ module ActionPolicy
10
+ # Top-level module for ActionPolicy - Graphiti integration
11
+ module Graphiti
12
+ class << self
13
+ # Which rule to use when no specified (e.g. `authorize: true`)
14
+ # Defaults to `:show?`
15
+ attr_accessor :default_authorize_rule
16
+ end
17
+
18
+ self.default_authorize_rule = :show?
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/graphiti"
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: action_policy-graphiti
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrei Mochalov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-06-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: action_policy
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: graphiti
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: debug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '13.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '13.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '3.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '3.8'
97
+ - !ruby/object:Gem::Dependency
98
+ name: solargraph
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.41'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.41'
111
+ description: This gem allows you to use a JSON:API implementation (Graphiti) with
112
+ ActionPolicy
113
+ email:
114
+ - factyy@gmail.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - CHANGELOG.md
120
+ - LICENSE.txt
121
+ - README.md
122
+ - lib/action_policy-graphiti.rb
123
+ - lib/action_policy/graphiti.rb
124
+ - lib/action_policy/graphiti/behaviour.rb
125
+ - lib/action_policy/graphiti/lookup_chain.rb
126
+ - lib/action_policy/graphiti/policy_name_inferrer.rb
127
+ - lib/action_policy/graphiti/resource_analyzer.rb
128
+ - lib/action_policy/graphiti/resource_validator.rb
129
+ - lib/action_policy/graphiti/version.rb
130
+ homepage: https://github.com/shrimple-tech/action_policy-graphiti
131
+ licenses:
132
+ - MIT
133
+ metadata:
134
+ homepage_uri: https://github.com/shrimple-tech/action_policy-graphiti
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 2.7.0
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubygems_version: 3.4.10
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: This gem allows you to use a JSON:API implementation (Graphiti) with ActionPolicy.
154
+ test_files: []