permits 0.1.4 → 1.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/README.md +17 -0
- data/Rakefile +7 -7
- data/lib/generators/permits/install/install_generator.rb +1 -0
- data/lib/generators/permits/install/templates/create_permits_invites.rb +14 -0
- data/lib/permits/concerns/has_permissions.rb +11 -0
- data/lib/permits/forms/invite_form.rb +65 -0
- data/lib/permits/forms/new_invite_form.rb +58 -0
- data/lib/permits/invite.rb +54 -0
- data/lib/permits/permission.rb +1 -1
- data/lib/permits/policy/base.rb +2 -2
- data/lib/permits/version.rb +1 -1
- data/lib/permits.rb +10 -4
- metadata +22 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17d6289fc58da4116cf85b5cf7853ebfe67d6366a4e9c96b64b0d0d42533d1a4
|
4
|
+
data.tar.gz: '08f713827ae201b471163040ce273e2b7f5beb5e59f31c9af0d808e3d1248f7e'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45c24dc5c7c076e30644f57a9779d50d252551921fa3e7eb7de6d21fe588f5e5217c5f90638cf98bc570ec9daf75526f910f0f015bf4e72ec923e860f0ecb420
|
7
|
+
data.tar.gz: 4bec82908bb725ea9a36fac20d0d1ee091923509b2f3a52b2ce59649fb011a586e67e70c7d93e3ad1ce480f496432af84354787ea285753a75bc8eda67d3b8a3
|
data/README.md
CHANGED
@@ -76,6 +76,23 @@ CustomPolicy.authorize!(owner, resource, :some_action)
|
|
76
76
|
CustomPolicy.authorized?(owner, resource, :some_action)
|
77
77
|
```
|
78
78
|
|
79
|
+
### Invites
|
80
|
+
`Permits` provides a simple pre-permissioned Invite system, with a `Permits::Invite` model, a `Permits::Form::NewInviteForm` form object for creating new invites, and a `Permits::Form::InviteForm` form object for accepting, declining and revoking invites. Invites can be pre-assigned permissions, so that when the invite is accepted, the invitee is automatically granted the permissions.
|
81
|
+
|
82
|
+
```irb
|
83
|
+
group = Group.create(name: "Group 1")
|
84
|
+
group_user = User.create(name: "User 1")
|
85
|
+
user_permission = Permits::Permission.create(owner: group_user, resource: group, action: :super_user)
|
86
|
+
|
87
|
+
NewInviteForm.new(
|
88
|
+
invited_by: group_user,
|
89
|
+
email: "invitee@email-address.com",
|
90
|
+
permission_attributes: {
|
91
|
+
group => [:read, :edit]
|
92
|
+
}
|
93
|
+
).save!
|
94
|
+
```
|
95
|
+
|
79
96
|
## Installation
|
80
97
|
Add this line to your application's Gemfile:
|
81
98
|
|
data/Rakefile
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
require
|
1
|
+
require "rake"
|
2
2
|
|
3
3
|
begin
|
4
|
-
require
|
4
|
+
require "bundler/setup"
|
5
5
|
Bundler::GemHelper.install_tasks
|
6
6
|
rescue LoadError
|
7
|
-
puts
|
7
|
+
puts "although not required, bundler is recommended for running the tests"
|
8
8
|
end
|
9
9
|
|
10
10
|
task default: :spec
|
11
11
|
|
12
|
-
require
|
12
|
+
require "rspec/core/rake_task"
|
13
13
|
RSpec::Core::RakeTask.new(:spec)
|
14
14
|
|
15
|
-
require
|
15
|
+
require "rubocop/rake_task"
|
16
16
|
RuboCop::RakeTask.new do |task|
|
17
|
-
task.requires <<
|
18
|
-
task.requires <<
|
17
|
+
task.requires << "rubocop-performance"
|
18
|
+
task.requires << "rubocop-rspec"
|
19
19
|
end
|
@@ -5,6 +5,7 @@ module Permits
|
|
5
5
|
|
6
6
|
def copy_application_policy
|
7
7
|
template "create_permits_permissions.rb", "db/migrate/#{Time.current.strftime("%Y%m%d%H%M%S")}_create_permits_permissions.rb"
|
8
|
+
template "create_permits_invites.rb", "db/migrate/#{Time.current.strftime("%Y%m%d%H%M%S")}_create_permits_invites.rb"
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreatePermitsInvites < ActiveRecord::Migration[7.1]
|
2
|
+
def change
|
3
|
+
create_table :permits_invites, id: :uuid do |t|
|
4
|
+
t.references :invited_by, polymorphic: true, index: true, type: :uuid, null: false
|
5
|
+
t.references :invitee, polymorphic: true, index: true, type: :uuid, null: true
|
6
|
+
t.string :email, null: false
|
7
|
+
t.string :aasm_state, null: false
|
8
|
+
t.string :slug, null: false
|
9
|
+
t.datetime :started_at
|
10
|
+
t.datetime :ended_at
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Permits
|
2
|
+
module Forms
|
3
|
+
class InviteForm
|
4
|
+
include ActiveModel::Model
|
5
|
+
include ActiveModel::Attributes
|
6
|
+
include ActiveModel::Validations
|
7
|
+
|
8
|
+
attribute :invite_id
|
9
|
+
attribute :invited
|
10
|
+
attribute :token
|
11
|
+
|
12
|
+
validates :invite_id, presence: true
|
13
|
+
validates :invited, presence: true
|
14
|
+
validates :token, presence: true
|
15
|
+
|
16
|
+
def accept
|
17
|
+
return false unless valid_invite?
|
18
|
+
|
19
|
+
ActiveRecord::Base.transaction do
|
20
|
+
invite.accept!
|
21
|
+
end
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def decline
|
26
|
+
return false unless valid_invite?
|
27
|
+
|
28
|
+
ActiveRecord::Base.transaction do
|
29
|
+
invite.decline!
|
30
|
+
end
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def destroy
|
35
|
+
ActiveRecord::Base.transaction do
|
36
|
+
invite.permissions.destroy_all
|
37
|
+
invite.destroy
|
38
|
+
end
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def valid_invite?
|
45
|
+
return false unless valid?
|
46
|
+
|
47
|
+
valid_email? && valid_token?
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_email?
|
51
|
+
raise I18n.t("errors.invited_does_not_respond_to_email") unless invited.respond_to?(:email)
|
52
|
+
|
53
|
+
invite.email == invited.email
|
54
|
+
end
|
55
|
+
|
56
|
+
def valid_token?
|
57
|
+
invite.slug == token
|
58
|
+
end
|
59
|
+
|
60
|
+
def invite
|
61
|
+
@invite ||= ::Permits::Invite.find(invite_id)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Permits
|
2
|
+
module Forms
|
3
|
+
class NewInviteForm
|
4
|
+
include ActiveModel::Model
|
5
|
+
include ActiveModel::Attributes
|
6
|
+
include ActiveModel::Validations
|
7
|
+
|
8
|
+
attribute :invited_by
|
9
|
+
attribute :email
|
10
|
+
attribute :permission_attributes
|
11
|
+
|
12
|
+
validates :invited_by, presence: true
|
13
|
+
validates :email, presence: true
|
14
|
+
|
15
|
+
def save
|
16
|
+
return false unless valid?
|
17
|
+
|
18
|
+
ActiveRecord::Base.transaction do
|
19
|
+
invite.save!
|
20
|
+
process_permissions if permission_attributes.present?
|
21
|
+
end
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def invite
|
26
|
+
@invite ||= Invite.new(
|
27
|
+
invited_by: invited_by,
|
28
|
+
email: email,
|
29
|
+
started_at: Time.current
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def process_permissions
|
36
|
+
permission_attributes.each do |resource, permissions|
|
37
|
+
policy_class = if resource.respond_to?(:policy_class)
|
38
|
+
resource.policy_class
|
39
|
+
else
|
40
|
+
::Permits::Policy::Base
|
41
|
+
end
|
42
|
+
next unless policy_class.authorized?(invited_by, resource, :invite)
|
43
|
+
|
44
|
+
create_permissions(resource, permissions)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_permissions(resource, *permissions)
|
49
|
+
permissions.flatten.each do |permits|
|
50
|
+
invite.permissions.find_or_create_by!(
|
51
|
+
resource: resource,
|
52
|
+
permits: permits
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "timelines"
|
2
|
+
|
3
|
+
module Permits
|
4
|
+
class Invite < ActiveRecord::Base
|
5
|
+
self.table_name = "permits_invites"
|
6
|
+
|
7
|
+
include ::AASM
|
8
|
+
include ::Timelines::Ephemeral
|
9
|
+
include ::Permits::HasPermissions
|
10
|
+
|
11
|
+
belongs_to :invited_by, polymorphic: true, required: true
|
12
|
+
belongs_to :invitee, polymorphic: true, optional: true
|
13
|
+
|
14
|
+
validates :email, presence: true
|
15
|
+
validates :aasm_state, presence: true
|
16
|
+
validates :slug, presence: true
|
17
|
+
|
18
|
+
scope :active, -> { where(ended_at: nil) }
|
19
|
+
|
20
|
+
attribute :slug, default: -> { SecureRandom.hex(3).upcase }
|
21
|
+
|
22
|
+
aasm do
|
23
|
+
state :pending, initial: true
|
24
|
+
state :accepted
|
25
|
+
state :declined
|
26
|
+
|
27
|
+
event :accept do
|
28
|
+
transitions from: :pending, to: :accepted, after: :add_invitee_permissions
|
29
|
+
end
|
30
|
+
|
31
|
+
event :decline do
|
32
|
+
transitions from: :pending, to: :declined, after: :end_invite_permissions
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_invitee_permissions
|
37
|
+
ActiveRecord::Base.transaction do
|
38
|
+
permissions.each do |permission|
|
39
|
+
invitee_permission = permission.dup
|
40
|
+
invitee_permission.update!(owner: invitee, started_at: Time.current)
|
41
|
+
permission.destroy
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def end_invite_permissions
|
47
|
+
ActiveRecord::Base.transaction do
|
48
|
+
permissions.each do |permission|
|
49
|
+
permission.update!(ended_at: Time.current)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/permits/permission.rb
CHANGED
@@ -17,7 +17,7 @@ module Permits
|
|
17
17
|
def permits_is_permissable
|
18
18
|
return if Permits.config.permits&.include?(permits&.to_sym)
|
19
19
|
|
20
|
-
errors.add(:permits, "
|
20
|
+
errors.add(:permits, I18n.t("errors.invalid_permits_param", class: resource.class.name))
|
21
21
|
end
|
22
22
|
|
23
23
|
scope :permits_any, -> { all }
|
data/lib/permits/policy/base.rb
CHANGED
@@ -29,7 +29,7 @@ module Permits
|
|
29
29
|
return false unless valid?
|
30
30
|
|
31
31
|
if respond_to?("#{action}?")
|
32
|
-
|
32
|
+
send("#{action}?")
|
33
33
|
else
|
34
34
|
has_action_permissions?(action)
|
35
35
|
end
|
@@ -40,7 +40,7 @@ module Permits
|
|
40
40
|
def has_action_permissions?(action, for_resource: resource)
|
41
41
|
return false unless owner_permissions.respond_to?("permits_#{action}")
|
42
42
|
|
43
|
-
|
43
|
+
owner_permissions.send("permits_#{action}").where(resource: for_resource).exists?
|
44
44
|
end
|
45
45
|
|
46
46
|
def owner_permissions
|
data/lib/permits/version.rb
CHANGED
data/lib/permits.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
-
require "
|
2
|
-
require "
|
1
|
+
require "active_support/configurable"
|
2
|
+
require "aasm"
|
3
|
+
require "permits/concerns/has_permissions"
|
4
|
+
require "permits/forms/new_invite_form"
|
5
|
+
require "permits/forms/invite_form"
|
6
|
+
require "permits/invite"
|
3
7
|
require "permits/permission"
|
4
|
-
require "permits/policy/base"
|
5
8
|
require "permits/policy/unauthorized_error"
|
6
|
-
require "
|
9
|
+
require "permits/policy/base"
|
10
|
+
require "permits/railtie"
|
11
|
+
require "permits/version"
|
12
|
+
require "timelines"
|
7
13
|
|
8
14
|
module Permits
|
9
15
|
include ActiveSupport::Configurable
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: permits
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Craig Gilchrist
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aasm
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.5.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.5.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rails
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,8 +148,13 @@ files:
|
|
134
148
|
- Rakefile
|
135
149
|
- lib/generators/permits/install/USAGE
|
136
150
|
- lib/generators/permits/install/install_generator.rb
|
151
|
+
- lib/generators/permits/install/templates/create_permits_invites.rb
|
137
152
|
- lib/generators/permits/install/templates/create_permits_permissions.rb
|
138
153
|
- lib/permits.rb
|
154
|
+
- lib/permits/concerns/has_permissions.rb
|
155
|
+
- lib/permits/forms/invite_form.rb
|
156
|
+
- lib/permits/forms/new_invite_form.rb
|
157
|
+
- lib/permits/invite.rb
|
139
158
|
- lib/permits/permission.rb
|
140
159
|
- lib/permits/policy/base.rb
|
141
160
|
- lib/permits/policy/unauthorized_error.rb
|
@@ -164,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
183
|
- !ruby/object:Gem::Version
|
165
184
|
version: '0'
|
166
185
|
requirements: []
|
167
|
-
rubygems_version: 3.4
|
186
|
+
rubygems_version: 3.5.4
|
168
187
|
signing_key:
|
169
188
|
specification_version: 4
|
170
189
|
summary: A User and Role management gem for Rails 7
|