permits 0.1.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 +7 -0
- data/README.md +105 -0
- data/Rakefile +19 -0
- data/lib/generators/permits/install/USAGE +2 -0
- data/lib/generators/permits/install/install_generator.rb +11 -0
- data/lib/generators/permits/install/templates/create_permits_permissions.rb +18 -0
- data/lib/permits/permission.rb +25 -0
- data/lib/permits/policy/base.rb +51 -0
- data/lib/permits/policy/unauthorized_error.rb +6 -0
- data/lib/permits/railtie.rb +4 -0
- data/lib/permits/version.rb +3 -0
- data/lib/permits.rb +23 -0
- data/lib/tasks/permits_tasks.rake +4 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '081bbbbc6f1e3880c891c01fb9f5a046abb33001d2435ee81075ec7b8144c578'
|
4
|
+
data.tar.gz: 48388184153b9ca8f289dd601f4fcef2f1bbc0de669c8a8a28e7f5a47bfca432
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9bf6cf28caa90ede7b4ccd5fcfbd55f05a466bdb1ebdda4a38d0ce19a00ce046791ff6f81ae9889277abedff29575afc97552175c0e55a513acb8e31bdf20707
|
7
|
+
data.tar.gz: 6381568c986046227947542c0ad02ea305a325198164c861aae46f5f24ad1735de11b38c2bdf7347e095feeea27d4717d6d67e27658528ce2a8c364c5229b73c
|
data/README.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Permits
|
2
|
+
`Permits` offers an ActiveRecord-based permissions system for Rails applications, with historic records (via `Timelines::Ephemeral`) and a simple DSL for defining permissions and policies.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
### Initialization
|
6
|
+
Set up the valid `permits` values (roles/actions/levels):
|
7
|
+
```ruby
|
8
|
+
# config/initializers/permits.rb
|
9
|
+
|
10
|
+
# user levels example
|
11
|
+
::Permits.configure do |config|
|
12
|
+
config.permits = %i[admin user super_user]
|
13
|
+
end
|
14
|
+
|
15
|
+
# user actions example
|
16
|
+
::Permits.configure do |config|
|
17
|
+
config.actions = %i[read write destroy]
|
18
|
+
end
|
19
|
+
|
20
|
+
# mixed example
|
21
|
+
::Permits.configure do |config|
|
22
|
+
config.roles = %i[admin super_user read write destroy]
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
### Creating Permission Record
|
27
|
+
Create a `Permission` record to track an `Owner's` access to a selected `Resource`, and define the action/level that the `Owner` is permitted to work with the `Resource`:
|
28
|
+
|
29
|
+
Give a `User` permission to do `write` to a certain `Resource`:
|
30
|
+
```ruby
|
31
|
+
Permits::Permission.create(owner: owner, resource: resource, action: :write)
|
32
|
+
```
|
33
|
+
|
34
|
+
### Checking Permissions with the default Policy
|
35
|
+
The `Permits::Policy::Base` class provides two different class methods for authorizing access:
|
36
|
+
- `Permits::Policy::Base.authorize!` will raise an `Permits::Policy::UnauthorizedError` if the `Owner` does not have the required permission
|
37
|
+
- `Permits::Policy::Base.authorized?` will return `true` if the `Owner` has the required permission, and `false` if not
|
38
|
+
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
Permits::Permission.create(owner: owner, resource: resource, action: :write)
|
42
|
+
|
43
|
+
Permits::Policy::Base.authorize!(owner, resource, :write) # => true
|
44
|
+
Permits::Policy::Base.authorize!(owner, resource, :read) # => Permits::Policy::UnauthorizedError
|
45
|
+
Permits::Policy::Base.authorize!(owner, other_resource, :write) # => Permits::Policy::UnauthorizedError
|
46
|
+
|
47
|
+
Permits::Policy::Base.authorized?(owner, resource, :write) # => true
|
48
|
+
Permits::Policy::Base.authorized?(owner, resource, :read) # => false
|
49
|
+
Permits::Policy::Base.authorized?(owner, resource, :write) # => false
|
50
|
+
```
|
51
|
+
|
52
|
+
### Custom Policies
|
53
|
+
You can define custom policies by subclassing `Permits::Policy::Base` and defining an `#{action_name}?` method. Furthermore you can still leverage the `has_action_permissions?` method from the `Base` class to perform the check that they have a suitable `Permission` record while also making additional checks (such as checking they are an Admin, or checking that a resource is active/actionable)
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
# config/initializers/permits.rb
|
57
|
+
::Permits.configure do |config|
|
58
|
+
config.roles = %i[some_action]
|
59
|
+
end
|
60
|
+
|
61
|
+
# app/models/custom_policy.rb
|
62
|
+
class CustomPolicy < Permits::Policy::Base
|
63
|
+
def some_action?
|
64
|
+
owner == resource.owner || owner.admin?
|
65
|
+
end
|
66
|
+
|
67
|
+
def some_action_with_level?
|
68
|
+
owner.admin? && has_action_permissions?(:some_action)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# irb
|
73
|
+
Permits::Permission.create(owner: owner, resource: resource, action: :some_action)
|
74
|
+
|
75
|
+
CustomPolicy.authorize!(owner, resource, :some_action)
|
76
|
+
CustomPolicy.authorized?(owner, resource, :some_action)
|
77
|
+
```
|
78
|
+
|
79
|
+
## Installation
|
80
|
+
Add this line to your application's Gemfile:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
gem "permits"
|
84
|
+
```
|
85
|
+
|
86
|
+
And then execute:
|
87
|
+
```bash
|
88
|
+
$ bundle
|
89
|
+
```
|
90
|
+
|
91
|
+
Or install it yourself as:
|
92
|
+
```bash
|
93
|
+
$ gem install permits
|
94
|
+
```
|
95
|
+
|
96
|
+
Install the required files to your project:
|
97
|
+
```bash
|
98
|
+
$ rails generate permits:install
|
99
|
+
```
|
100
|
+
|
101
|
+
## Contributing
|
102
|
+
Contribution directions go here.
|
103
|
+
|
104
|
+
## License
|
105
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
rescue LoadError
|
7
|
+
puts 'although not required, bundler is recommended for running the tests'
|
8
|
+
end
|
9
|
+
|
10
|
+
task default: :spec
|
11
|
+
|
12
|
+
require 'rspec/core/rake_task'
|
13
|
+
RSpec::Core::RakeTask.new(:spec)
|
14
|
+
|
15
|
+
require 'rubocop/rake_task'
|
16
|
+
RuboCop::RakeTask.new do |task|
|
17
|
+
task.requires << 'rubocop-performance'
|
18
|
+
task.requires << 'rubocop-rspec'
|
19
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Permits
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < ::Rails::Generators::Base
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
5
|
+
|
6
|
+
def copy_application_policy
|
7
|
+
template "create_permits_permissions.rb", "db/migrate/#{Time.current.strftime("%Y%m%d%H%M%S")}_create_permits_permissions.rb"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreatePermitPermissions < ActiveRecord::Migration[7.1]
|
2
|
+
def change
|
3
|
+
create_table :permits_permissions, id: :uuid do |t|
|
4
|
+
t.string :owner_id, null: false
|
5
|
+
t.string :owner_type, null: false
|
6
|
+
t.string :resource_id, null: false
|
7
|
+
t.string :resource_type, null: false
|
8
|
+
t.string :permits, null: false
|
9
|
+
|
10
|
+
t.datetime :started_at
|
11
|
+
t.datetime :ended_at
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
add_index :permits_permissions, [:owner_id, :owner_type]
|
16
|
+
add_index :permits_permissions, [:resource_id, :resource_type]
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "timelines"
|
2
|
+
|
3
|
+
module Permits
|
4
|
+
class Permission < ActiveRecord::Base
|
5
|
+
self.table_name = "permits_permissions"
|
6
|
+
|
7
|
+
include ::Timelines::Ephemeral
|
8
|
+
attribute :started_at, default: -> { Time.current }
|
9
|
+
|
10
|
+
belongs_to :owner, polymorphic: true, required: true
|
11
|
+
belongs_to :resource, polymorphic: true, required: true
|
12
|
+
|
13
|
+
validates :owner, presence: true
|
14
|
+
validates :resource, presence: true
|
15
|
+
validate :permits_is_permissable
|
16
|
+
|
17
|
+
def permits_is_permissable
|
18
|
+
return if Permits.config.permits&.include?(permits&.to_sym)
|
19
|
+
|
20
|
+
errors.add(:permits, "is not a valid permits for #{resource.class}")
|
21
|
+
end
|
22
|
+
|
23
|
+
scope :permits_any, -> { all }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Permits
|
2
|
+
module Policy
|
3
|
+
class Base
|
4
|
+
include ActiveModel::Model
|
5
|
+
include ActiveModel::Attributes
|
6
|
+
include ActiveModel::Validations
|
7
|
+
|
8
|
+
attribute :owner
|
9
|
+
attribute :resource
|
10
|
+
|
11
|
+
validates :owner, presence: true
|
12
|
+
validates :resource, presence: true
|
13
|
+
|
14
|
+
def self.authorize!(owner, resource, action)
|
15
|
+
policy = new(owner: owner, resource: resource)
|
16
|
+
if policy.authorized?(action)
|
17
|
+
true
|
18
|
+
else
|
19
|
+
raise ::Permits::Policy::UnauthorizedError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.authorized?(owner, resource, action)
|
24
|
+
policy = new(owner: owner, resource: resource)
|
25
|
+
policy.authorized?(action)
|
26
|
+
end
|
27
|
+
|
28
|
+
def authorized?(action)
|
29
|
+
return false unless valid?
|
30
|
+
|
31
|
+
if respond_to?("#{action}?")
|
32
|
+
return send("#{action}?")
|
33
|
+
else
|
34
|
+
has_action_permissions?(action)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def has_action_permissions?(action)
|
41
|
+
return false unless owner_permissions.respond_to?("permits_#{action}")
|
42
|
+
|
43
|
+
return owner_permissions.send("permits_#{action}").where(resource: resource).exists?
|
44
|
+
end
|
45
|
+
|
46
|
+
def owner_permissions
|
47
|
+
@owner_permissions ||= ::Permits::Permission.active.where(owner: owner).includes(:resource)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/permits.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "permits/version"
|
2
|
+
require "permits/railtie"
|
3
|
+
require "permits/permission"
|
4
|
+
require "permits/policy/base"
|
5
|
+
require "permits/policy/unauthorized_error"
|
6
|
+
require "active_support/configurable"
|
7
|
+
|
8
|
+
module Permits
|
9
|
+
include ActiveSupport::Configurable
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def configure
|
13
|
+
yield config
|
14
|
+
|
15
|
+
if config.permits
|
16
|
+
config.permits = config.permits.map!(&:to_sym)
|
17
|
+
config.permits.each do |role|
|
18
|
+
::Permits::Permission.scope "permits_#{role}", -> { where(permits: role) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: permits
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Craig Gilchrist
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-10-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: timelines
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dotenv
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '7.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '7.1'
|
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.4'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.4'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec-rails
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.21'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.21'
|
125
|
+
description: Provides access authorization for owners and resources, via a simple
|
126
|
+
Permission ActiveRecord model and a Policy class.
|
127
|
+
email:
|
128
|
+
- craig.a.gilchrist@gmail.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- README.md
|
134
|
+
- Rakefile
|
135
|
+
- lib/generators/permits/install/USAGE
|
136
|
+
- lib/generators/permits/install/install_generator.rb
|
137
|
+
- lib/generators/permits/install/templates/create_permits_permissions.rb
|
138
|
+
- lib/permits.rb
|
139
|
+
- lib/permits/permission.rb
|
140
|
+
- lib/permits/policy/base.rb
|
141
|
+
- lib/permits/policy/unauthorized_error.rb
|
142
|
+
- lib/permits/railtie.rb
|
143
|
+
- lib/permits/version.rb
|
144
|
+
- lib/tasks/permits_tasks.rake
|
145
|
+
homepage: https://github.com/Craggar/permits
|
146
|
+
licenses:
|
147
|
+
- MIT
|
148
|
+
metadata:
|
149
|
+
homepage_uri: https://github.com/Craggar/permits
|
150
|
+
source_code_uri: https://github.com/Craggar/permits
|
151
|
+
changelog_uri: https://github.com/Craggar/permits/CHANGELOG.md
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubygems_version: 3.4.10
|
168
|
+
signing_key:
|
169
|
+
specification_version: 4
|
170
|
+
summary: A User and Role management gem for Rails 7
|
171
|
+
test_files: []
|