access_context 1.0.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35f221094e6d947704c8e17e0c4644318d636fc077a0c4d62262f8b5378c076d
4
- data.tar.gz: 867c0f9a28e8c95c3a031b248ab4531856b82c7933408faee8ec54c6b787719c
3
+ metadata.gz: cf8fd9c40fd8ead10a5b0339819ebbe28df810da205a62ad2cfe89941719b378
4
+ data.tar.gz: 3c470c7062ef14ef0bf3b4f84de9300debfa56d6c7660a507f87ba8aacad3ec5
5
5
  SHA512:
6
- metadata.gz: 79f979e9c208881bb092bd43e72882fc978f9f75fc98e83d2a7e346b47c8b3f048fa67f6d6221083f21249d3291b175030a4d938d5b433f76ad01db107403e2a
7
- data.tar.gz: 0d2e2bd9bb250b6b38da3383a5fac485dc8c93a4a644a351e1ac5579a6d0ec91a551cc6e2b00fdf9782eb6936ac744c30456f1fce8f796733872e3a5de3e84d4
6
+ metadata.gz: 10bc8fbdb23048b79ed04cff2331798b146697e077646795b3b84a5cd43b541a4fcbcfc7619c73c391379b4944fdc2ca2a2a2cae6710f76f6b0bef9c3b6e88d7
7
+ data.tar.gz: 2c21b244b465bf0f4e9a0b39af640dd27e57f849b3861a49022c944b77e1806a2359218e8747ed094a88f1d1f0d833f770b600d4098876f3addf7ab4ebbf9da0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.0.0] - 2026-10-10
4
+
5
+ - Add `AccessContext#granted_by_permission?` to check a context against a single permission
6
+ - `AccessContext#permit?` also checks `user.permissions` when the user responds to it
7
+ - `AccessContext#permit?` also checks `user.roles` when the user responds to it
8
+ - `AccessContext#permit?` no longer requires `user.role`; a missing or `nil` role counts as no role
9
+ - Fix loading with activesupport 7.1+
10
+
11
+ ### Upgrading from 1.x
12
+
13
+ `permit?(user)` used to check only `user.role`. It now also checks `user.roles` and `user.permissions` whenever the user responds to them. Before upgrading, check whether your `User` already has `roles` or `permissions` methods for other reasons (e.g. a `has_many :roles` association from rolify):
14
+
15
+ - `roles` must return role names (or `AccessContext::Role` objects) defined in `AccessContext.config`. Unknown names, or records that are not names, will raise.
16
+ - `permissions` must return permission names (or `AccessContext::Permission` objects) defined in `AccessContext.config`. Any matching name now grants access, and an unknown name raises `AccessContext::Error`.
17
+
18
+ If those methods mean something else in your app, rename them or pass `permit?` an object that only exposes what AccessContext should see, for example `permit?(OpenStruct.new(role: user.role))`.
19
+
20
+ ## [1.1.0] - 2025-03-24
21
+
22
+ - Able to setup context without actions
23
+
3
24
  ## [1.0.0] - 2025-03-23
4
25
 
5
26
  - Initial release
data/README.md CHANGED
@@ -29,6 +29,7 @@ AccessContext.config do
29
29
  job :welcome, :perform
30
30
  end
31
31
  grant 'admin_default' do
32
+ controller "admin/products"
32
33
  controller 'admin/dashboard', [:show]
33
34
  context :controller, "admin/orders", :show
34
35
  end
@@ -62,6 +63,24 @@ AccessContext.service(:visit, :call).permit?(support) #=> true
62
63
  AccessContext.view("admin/orders", :order_edit_button).permit?(support) #=> false
63
64
  ```
64
65
 
66
+ `permit?` also supports users with several roles via `roles`, and users with their own `permissions`. Each of `role`, `roles` and `permissions` is only checked when `User` responds to it, and access is granted when any of them grants the context.
67
+
68
+ ```ruby
69
+ user = User.new(roles: [:support, :admin])
70
+ AccessContext.controller('admin/dashboard', :show).permit?(user) #=> true
71
+
72
+ support = User.new(role: :support, permissions: ['admin_default'])
73
+ AccessContext.controller('admin/dashboard', :show).permit?(support) #=> true
74
+ ```
75
+
76
+ Or check whether a specific permission grants the context, regardless of role. An unknown permission name raises `AccessContext::Error`.
77
+
78
+ ```ruby
79
+ AccessContext.controller(:home, :show).granted_by_permission?(:default) #=> true
80
+ AccessContext.controller("admin/products", :new).granted_by_permission?(:admin_default) #=> true
81
+ AccessContext.view("admin/orders", :order_edit_button).granted_by_permission?(:default) #=> false
82
+ ```
83
+
65
84
  3. Integrate it with Rails
66
85
 
67
86
  ```ruby
@@ -6,8 +6,12 @@ class AccessContext
6
6
  permissions[name.to_sym] = new(name, &block)
7
7
  end
8
8
 
9
- def [](name)
10
- permissions[name.to_sym]
9
+ def [](permission)
10
+ if permission.is_a?(Permission)
11
+ permission
12
+ else
13
+ permissions[permission.to_sym]
14
+ end
11
15
  end
12
16
 
13
17
  def all
@@ -28,13 +32,17 @@ class AccessContext
28
32
 
29
33
  def permit?(context)
30
34
  contexts.any? do |c|
31
- c == context
35
+ c.contains?(context)
32
36
  end
33
37
  end
34
38
 
35
39
  def context(type, name, targets=[])
36
- ([targets] || []).flatten.each do |target|
37
- contexts << AccessContext.new(type, name, target)
40
+ if targets.present?
41
+ ([targets] || []).flatten.each do |target|
42
+ contexts << AccessContext.new(type, name, target)
43
+ end
44
+ else
45
+ contexts << AccessContext.new(type, name)
38
46
  end
39
47
  end
40
48
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class AccessContext
4
- VERSION = "1.0.0"
4
+ VERSION = "2.0.0"
5
5
  end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "access_context/version"
4
+ require 'active_support'
5
+ require 'active_support/dependencies/autoload'
6
+ require 'active_support/core_ext'
4
7
 
5
8
  class AccessContext
6
9
 
@@ -36,14 +39,14 @@ class AccessContext
36
39
 
37
40
  attr_reader :type, :name, :target
38
41
 
39
- def initialize(type, name, target)
42
+ def initialize(type, name, target=nil)
40
43
  @type = type.to_sym
41
44
  @name = name.to_sym
42
- @target = target.to_sym
45
+ @target = target&.to_sym
43
46
  end
44
47
 
45
48
  def permit?(user)
46
- permit_role?(user.role)
49
+ user_roles(user).any? { |role| permit_role?(role) } || permit_permissions?(user)
47
50
  end
48
51
 
49
52
  def permit_role?(role)
@@ -52,10 +55,37 @@ class AccessContext
52
55
  end
53
56
  end
54
57
 
55
- def ==(context)
58
+ def granted_by_permission?(permission)
59
+ granted_permission = AccessContext::Permission[permission]
60
+ raise Error, "Unknown permission: #{permission.inspect}" unless granted_permission
61
+
62
+ granted_permission.permit?(self)
63
+ end
64
+
65
+ def contains?(context)
56
66
  type == context.type &&
57
67
  name == context.name &&
58
- target == context.target
68
+ (
69
+ target == context.target ||
70
+ target.blank?
71
+ )
72
+ end
73
+
74
+ private
75
+
76
+ def user_roles(user)
77
+ roles = []
78
+ roles.concat(Array(user.role)) if user.respond_to?(:role)
79
+ roles.concat(Array(user.roles)) if user.respond_to?(:roles)
80
+ roles
81
+ end
82
+
83
+ def permit_permissions?(user)
84
+ return false unless user.respond_to?(:permissions)
85
+
86
+ Array(user.permissions).any? do |permission|
87
+ granted_by_permission?(permission)
88
+ end
59
89
  end
60
90
 
61
91
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: access_context
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hopper Gee
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-03-23 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2026-10-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: Setup role with lightweight context-based permission
14
28
  email:
15
29
  - hopper.gee@hey.com