rbacan 0.3.1 → 0.4.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: 4c7c83e7614f1a076cad01e548331f8955bccd3c766ccec2ef6d25efdb0513fa
4
- data.tar.gz: 6508cbb415d83cb6f46b03fdd978e19e0e95b41aa7aa63d363e42d79af88fd25
3
+ metadata.gz: 066e77f9b2f426249a569e679c9c1348e28b2f3af5f19f480a9059ce237b5258
4
+ data.tar.gz: 83cb20bd2b2e97c558f0f97f2c0452417f01668f73cc3bccbf48eddde3e7cd81
5
5
  SHA512:
6
- metadata.gz: 1ad7859a4ec5a84c5c03e15e7c87192ce13f98dbef56ba79235fd9fbf30e4d077bfae8929e85722ca276d0233a2cdd9f229bd0026b4c0a0a189343f3bf6acede
7
- data.tar.gz: 20475109b9d14d6ecce15b23bbf153fb5482f8a241ec3d7284e96a7798e0068565cb9e9a72405242a4d289806f58f3f501bc2ba911ae5692a36e1d2fd0cb5356
6
+ metadata.gz: 5b2c8e3ac69a2fc9ea125022eb1d8ac2fd63ee702c2b6e8b1300e6c5dc317ae9db8b5dc1fd96167ea418b1a1cded3f2b7282edd6f09a2ec4fd19783556b4eb4d
7
+ data.tar.gz: f1a0312c57018c184383266dfeab2b9c2fe216431e63558996d14162e1c56291c85edee0ab00bab716498da4bd34f7d3dd56232ded31e48947b1c932078ac72a
data/.gitignore CHANGED
@@ -9,3 +9,6 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ # Built gem files
14
+ *.gem
data/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.4.0] - 2026-04-13
6
+
7
+ ### Added
8
+
9
+ - `primary_key_type` config option — auto-detects host app's primary key type (supports `:uuid` and `:bigint`)
10
+ - `tenant_scoped` config option — when `true`, adds `tenant_id` to `roles` and `user_roles` tables
11
+ - `tenant_class` config option — configurable tenant model class name (default: `"Tenant"`)
12
+ - `Rbacan.resolve_primary_key_type` — helper to resolve the effective PK type at runtime
13
+ - Tenant-aware methods on `Permittable`:
14
+ - `roles_for_tenant(tenant_id)` — returns roles assigned in a specific tenant (+ global)
15
+ - `has_role_in_tenant?(role_name, tenant_id)` — checks role in tenant context
16
+ - `can_in_tenant?(permission_name, tenant_id)` — checks permission in tenant context
17
+ - `assign_role` now accepts optional `tenant_id:` keyword argument for tenant-scoped assignments
18
+ - `remove_role` now accepts optional `tenant_id:` keyword argument
19
+ - `Role` scopes: `.global` (where tenant_id IS NULL), `.for_tenant(tenant_id)` (tenant + global roles)
20
+ - `Role` model: conditional `belongs_to :tenant` when `tenant_scoped` is enabled
21
+ - `UserRole` model: conditional `belongs_to :tenant` when `tenant_scoped` is enabled
22
+ - Initializer template now documents all new configuration options
23
+ - Migration templates now use explicit `type:` on `t.references` for FK columns
24
+
25
+ ### Fixed
26
+
27
+ - `t.bigint :user_id` was hardcoded in `create_user_roles` migration — now uses `t.references :user` with proper type detection matching the host app's PK type
28
+ - Migration templates now conditionally pass `id: :uuid` to `create_table` based on host app configuration
29
+ - `create_role_permissions` migration now specifies explicit `type:` on foreign key references
30
+
31
+ ---
32
+
5
33
  ## [0.3.0] - 2026-03-31
6
34
 
7
35
  ### Added
data/CLAUDE.md ADDED
@@ -0,0 +1,51 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ bin/setup # install dependencies
9
+ rake spec # run all tests
10
+ bundle exec rspec spec/rbacan_spec.rb # run a single spec file
11
+ bundle exec rake install # install gem locally
12
+ bundle exec rake release # tag, push commits/tags, push .gem to RubyGems
13
+ gem build rbacan.gemspec # build .gem file manually
14
+ gem push rbacan-<version>.gem # push to RubyGems manually
15
+ ```
16
+
17
+ ## Architecture
18
+
19
+ This is a Rails engine gem that provides Role-Based Access Control (RBAC).
20
+
21
+ **Data model** — four tables managed by the gem's migrations:
22
+ - `roles` / `permissions` — named entities
23
+ - `role_permissions` — join table linking roles to permissions
24
+ - `user_roles` — join table linking the host app's user model to roles
25
+
26
+ **Entry points:**
27
+ - `lib/rbacan.rb` — main module; holds `mattr_accessor` config accessors and a `configure` block. All class names and table names are configurable here (defaults to `User`, `Rbacan::Role`, etc.).
28
+ - `lib/rbacan/permittable.rb` — `ActiveSupport::Concern` included in the host app's user model via `include Rbacan::Permittable`. Adds `assign_role`, `remove_role`, and `can?` instance methods.
29
+ - `lib/rbacan/roles_and_permissions.rb` — utility module (`RolesAndPermissions`) used in seeds to bulk-create roles/permissions and assign permissions to roles.
30
+ - `lib/rbacan/engine.rb` — Rails engine that auto-loads `app/models`.
31
+
32
+ **Generator** (`rails generate rbacan:install`) copies four migration files, `db/copy_to_seeds.rb`, and `config/initializers/rbacan.rb` into the host app.
33
+
34
+ **Configuration** (host app's `config/initializers/rbacan.rb`):
35
+ ```ruby
36
+ Rbacan.configure do |config|
37
+ config.permittable_class = "User" # change if your user model has a different name
38
+ end
39
+ ```
40
+
41
+ ## Releasing a new version
42
+
43
+ 1. Bump `lib/rbacan/version.rb`
44
+ 2. `gem build rbacan.gemspec`
45
+ 3. `gem push rbacan-<version>.gem`
46
+
47
+ RubyGems push host is `https://rubygems.org`. GitHub repo: `https://github.com/hamdi777/RBACan`.
48
+
49
+ ## Known state
50
+
51
+ The spec suite has a placeholder test (`expect(false).to eq(true)`) in `spec/rbacan_spec.rb` that intentionally fails — it was never replaced with real tests.
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbacan (0.3.1)
5
- rails (>= 5.2)
4
+ rbacan (0.4.0)
5
+ rails (>= 5.2, < 9)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -82,7 +82,7 @@ GEM
82
82
  tzinfo (~> 2.0, >= 2.0.5)
83
83
  uri (>= 0.13.1)
84
84
  base64 (0.3.0)
85
- bigdecimal (4.1.0)
85
+ bigdecimal (4.1.1)
86
86
  builder (3.3.0)
87
87
  concurrent-ruby (1.3.6)
88
88
  connection_pool (3.0.2)
@@ -118,8 +118,7 @@ GEM
118
118
  net-smtp
119
119
  marcel (1.1.0)
120
120
  mini_mime (1.1.5)
121
- mini_portile2 (2.8.9)
122
- minitest (6.0.2)
121
+ minitest (6.0.3)
123
122
  drb (~> 2.0)
124
123
  prism (~> 1.5)
125
124
  net-imap (0.6.3)
@@ -132,11 +131,22 @@ GEM
132
131
  net-smtp (0.5.1)
133
132
  net-protocol
134
133
  nio4r (2.7.5)
135
- nokogiri (1.19.2)
136
- mini_portile2 (~> 2.8.2)
134
+ nokogiri (1.19.2-aarch64-linux-gnu)
135
+ racc (~> 1.4)
136
+ nokogiri (1.19.2-aarch64-linux-musl)
137
+ racc (~> 1.4)
138
+ nokogiri (1.19.2-arm-linux-gnu)
139
+ racc (~> 1.4)
140
+ nokogiri (1.19.2-arm-linux-musl)
141
+ racc (~> 1.4)
142
+ nokogiri (1.19.2-arm64-darwin)
143
+ racc (~> 1.4)
144
+ nokogiri (1.19.2-x86_64-darwin)
137
145
  racc (~> 1.4)
138
146
  nokogiri (1.19.2-x86_64-linux-gnu)
139
147
  racc (~> 1.4)
148
+ nokogiri (1.19.2-x86_64-linux-musl)
149
+ racc (~> 1.4)
140
150
  pp (0.6.3)
141
151
  prettyprint
142
152
  prettyprint (0.2.0)
@@ -146,7 +156,7 @@ GEM
146
156
  stringio
147
157
  racc (1.8.1)
148
158
  rack (3.2.6)
149
- rack-session (2.1.1)
159
+ rack-session (2.1.2)
150
160
  base64 (>= 0.1.0)
151
161
  rack (>= 3.0.0)
152
162
  rack-test (2.2.0)
@@ -204,9 +214,14 @@ GEM
204
214
  rspec-support (~> 3.13.0)
205
215
  rspec-support (3.13.7)
206
216
  securerandom (0.4.1)
207
- sqlite3 (2.9.2)
208
- mini_portile2 (~> 2.8.0)
217
+ sqlite3 (2.9.2-aarch64-linux-gnu)
218
+ sqlite3 (2.9.2-aarch64-linux-musl)
219
+ sqlite3 (2.9.2-arm-linux-gnu)
220
+ sqlite3 (2.9.2-arm-linux-musl)
221
+ sqlite3 (2.9.2-arm64-darwin)
222
+ sqlite3 (2.9.2-x86_64-darwin)
209
223
  sqlite3 (2.9.2-x86_64-linux-gnu)
224
+ sqlite3 (2.9.2-x86_64-linux-musl)
210
225
  stringio (3.2.0)
211
226
  thor (1.5.0)
212
227
  timeout (0.6.1)
@@ -222,18 +237,24 @@ GEM
222
237
  zeitwerk (2.7.5)
223
238
 
224
239
  PLATFORMS
225
- ruby
226
- x86_64-linux
240
+ aarch64-linux-gnu
241
+ aarch64-linux-musl
242
+ arm-linux-gnu
243
+ arm-linux-musl
244
+ arm64-darwin
245
+ x86_64-darwin
246
+ x86_64-linux-gnu
247
+ x86_64-linux-musl
227
248
 
228
249
  DEPENDENCIES
229
- activerecord (>= 5.2)
250
+ activerecord (>= 5.2, < 9)
230
251
  bundler (>= 2.0)
231
252
  generator_spec (~> 0.9.4)
232
- railties (>= 5.2)
253
+ railties (>= 5.2, < 9)
233
254
  rake (~> 13.0)
234
255
  rbacan!
235
256
  rspec (~> 3.0)
236
- sqlite3 (>= 1.4)
257
+ sqlite3
237
258
 
238
259
  CHECKSUMS
239
260
  action_text-trix (2.1.18) sha256=3fdb83f8bff4145d098be283cdd47ac41caf5110bfa6df4695ed7127d7fb3642
@@ -249,7 +270,7 @@ CHECKSUMS
249
270
  activestorage (8.1.3) sha256=0564ce9309143951a67615e1bb4e090ee54b8befed417133cae614479b46384d
250
271
  activesupport (8.1.3) sha256=21a5e0dfbd4c3ddd9e1317ec6a4d782fa226e7867dc70b0743acda81a1dca20e
251
272
  base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
252
- bigdecimal (4.1.0) sha256=6dc07767aa3dc456ccd48e7ae70a07b474e9afd7c5bc576f80bd6da5c8dd6cae
273
+ bigdecimal (4.1.1) sha256=1c09efab961da45203c8316b0cdaec0ff391dfadb952dd459584b63ebf8054ca
253
274
  builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
254
275
  concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
255
276
  connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
@@ -270,22 +291,27 @@ CHECKSUMS
270
291
  mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941
271
292
  marcel (1.1.0) sha256=fdcfcfa33cc52e93c4308d40e4090a5d4ea279e160a7f6af988260fa970e0bee
272
293
  mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
273
- mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289
274
- minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d
294
+ minitest (6.0.3) sha256=88ac8a1de36c00692420e7cb3cc11a0773bbcb126aee1c249f320160a7d11411
275
295
  net-imap (0.6.3) sha256=9bab75f876596d09ee7bf911a291da478e0cd6badc54dfb82874855ccc82f2ad
276
296
  net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
277
297
  net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
278
298
  net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
279
299
  nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
280
- nokogiri (1.19.2) sha256=38fdd8b59db3d5ea9e7dfb14702e882b9bf819198d5bf976f17ebce12c481756
300
+ nokogiri (1.19.2-aarch64-linux-gnu) sha256=c34d5c8208025587554608e98fd88ab125b29c80f9352b821964e9a5d5cfbd19
301
+ nokogiri (1.19.2-aarch64-linux-musl) sha256=7f6b4b0202d507326841a4f790294bf75098aef50c7173443812e3ac5cb06515
302
+ nokogiri (1.19.2-arm-linux-gnu) sha256=b7fa1139016f3dc850bda1260988f0d749934a939d04ef2da13bec060d7d5081
303
+ nokogiri (1.19.2-arm-linux-musl) sha256=61114d44f6742ff72194a1b3020967201e2eb982814778d130f6471c11f9828c
304
+ nokogiri (1.19.2-arm64-darwin) sha256=58d8ea2e31a967b843b70487a44c14c8ba1866daa1b9da9be9dbdf1b43dee205
305
+ nokogiri (1.19.2-x86_64-darwin) sha256=7d9af11fda72dfaa2961d8c4d5380ca0b51bc389dc5f8d4b859b9644f195e7a4
281
306
  nokogiri (1.19.2-x86_64-linux-gnu) sha256=fa8feca882b73e871a9845f3817a72e9734c8e974bdc4fbad6e4bc6e8076b94f
307
+ nokogiri (1.19.2-x86_64-linux-musl) sha256=93128448e61a9383a30baef041bf1f5817e22f297a1d400521e90294445069a8
282
308
  pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
283
309
  prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
284
310
  prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
285
311
  psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
286
312
  racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
287
313
  rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2
288
- rack-session (2.1.1) sha256=0b6dc07dea7e4b583f58a48e8b806d4c9f1c6c9214ebc202ec94562cbea2e4e9
314
+ rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8
289
315
  rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463
290
316
  rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
291
317
  rails (8.1.3) sha256=6d017ba5348c98fc909753a8169b21d44de14d2a0b92d140d1a966834c3c9cd3
@@ -293,7 +319,7 @@ CHECKSUMS
293
319
  rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89
294
320
  railties (8.1.3) sha256=913eb0e0cb520aac687ffd74916bd726d48fa21f47833c6292576ef6a286de22
295
321
  rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
296
- rbacan (0.3.1)
322
+ rbacan (0.4.0)
297
323
  rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
298
324
  reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
299
325
  rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
@@ -302,8 +328,14 @@ CHECKSUMS
302
328
  rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
303
329
  rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
304
330
  securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
305
- sqlite3 (2.9.2) sha256=86814150714b6b06a328d083f46408e7a4a83b5f0a9673ed934ee3a1cb7a73b1
331
+ sqlite3 (2.9.2-aarch64-linux-gnu) sha256=eeb86db55645b85327ba75129e3614658d974bf4da8fdc87018a0d42c59f6e42
332
+ sqlite3 (2.9.2-aarch64-linux-musl) sha256=4feff91fb8c2b13688da34b5627c9d1ed9cedb3ee87a7114ec82209147f07a6d
333
+ sqlite3 (2.9.2-arm-linux-gnu) sha256=1ee2eb06b5301aaf5ce343a6e88d99ac932d95202d7b350f0e7b6d8d588580d7
334
+ sqlite3 (2.9.2-arm-linux-musl) sha256=8ca0de6aceede968de0394e22e95d549834c4d8e318f69a92a52f049878a0057
335
+ sqlite3 (2.9.2-arm64-darwin) sha256=d15bd9609a05f9d54930babe039585efc8cadd57517c15b64ec7dfa75158a5e9
336
+ sqlite3 (2.9.2-x86_64-darwin) sha256=ed691b5021674d72582d03c5a38e89634b961902735fb6225273892805421d13
306
337
  sqlite3 (2.9.2-x86_64-linux-gnu) sha256=dce83ffcb7e72f9f7aeb6e5404f15d277a45332fe18ccce8a8b3ed51e8d23aee
338
+ sqlite3 (2.9.2-x86_64-linux-musl) sha256=e8dd906a613f13b60f6d47ae9dda376384d9de1ab3f7e3f2fdf2fd18a871a2d7
307
339
  stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
308
340
  thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
309
341
  timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
@@ -1,13 +1,26 @@
1
1
  module Rbacan
2
- class Role < ApplicationRecord
3
- self.table_name = Rbacan.role_table
2
+ class Role < ApplicationRecord
3
+ self.table_name = Rbacan.role_table
4
4
 
5
- validates :name, presence: true, uniqueness: true
6
-
7
- has_many :role_permissions, class_name: Rbacan.role_permission_class, dependent: :destroy
8
- has_many :permissions, class_name: Rbacan.permission_class, through: :role_permissions
5
+ validates :name, presence: true
6
+ validates :name, uniqueness: true, unless: -> { Rbacan.tenant_scoped && tenant_id.present? }
7
+ validates :name, uniqueness: { scope: :tenant_id }, if: -> { Rbacan.tenant_scoped && tenant_id.present? }
9
8
 
10
- has_many :user_roles, class_name: Rbacan.user_role_class, dependent: :destroy
11
- has_many :users, class_name: Rbacan.permittable_class, through: :user_roles
12
- end
9
+ has_many :role_permissions, class_name: Rbacan.role_permission_class, dependent: :destroy
10
+ has_many :permissions, class_name: Rbacan.permission_class, through: :role_permissions
11
+
12
+ has_many :user_roles, class_name: Rbacan.user_role_class, dependent: :destroy
13
+ has_many :users, class_name: Rbacan.permittable_class, through: :user_roles
14
+
15
+ # Tenant association — only meaningful when tenant_scoped is enabled.
16
+ # The column must exist in the database; it is added by the migration
17
+ # when tenant_scoped is true.
18
+ belongs_to :tenant, class_name: Rbacan.tenant_class, optional: true if Rbacan.tenant_scoped
19
+
20
+ # Global roles have no tenant (tenant_id IS NULL).
21
+ scope :global, -> { where(tenant_id: nil) }
22
+
23
+ # Returns roles available for a specific tenant: its own roles plus global roles.
24
+ scope :for_tenant, ->(tenant_id) { where(tenant_id: [tenant_id, nil]) }
25
+ end
13
26
  end
@@ -1,8 +1,11 @@
1
1
  module Rbacan
2
- class UserRole < ApplicationRecord
3
- self.table_name = Rbacan.user_role_table
2
+ class UserRole < ApplicationRecord
3
+ self.table_name = Rbacan.user_role_table
4
4
 
5
- belongs_to :role, class_name: Rbacan.role_class
6
- belongs_to :user, class_name: Rbacan.permittable_class
7
- end
5
+ belongs_to :role, class_name: Rbacan.role_class
6
+ belongs_to :user, class_name: Rbacan.permittable_class
7
+
8
+ # Tenant association — only meaningful when tenant_scoped is enabled.
9
+ belongs_to :tenant, class_name: Rbacan.tenant_class, optional: true if Rbacan.tenant_scoped
10
+ end
8
11
  end
@@ -1,6 +1,6 @@
1
1
  class CreatePermissions < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
2
  def change
3
- create_table :permissions do |t|
3
+ create_table :permissions<%= ", id: :uuid" if primary_key_type == :uuid %> do |t|
4
4
  t.string :name, null: false
5
5
 
6
6
  t.timestamps
@@ -1,8 +1,8 @@
1
1
  class CreateRolePermissions < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
2
  def change
3
- create_table :role_permissions do |t|
4
- t.references :role, null: false, index: true, foreign_key: { to_table: :roles, on_delete: :cascade }
5
- t.references :permission, null: false, index: true, foreign_key: { to_table: :permissions, on_delete: :cascade }
3
+ create_table :role_permissions<%= ", id: :uuid" if primary_key_type == :uuid %> do |t|
4
+ t.references :role, null: false, type: :<%= primary_key_type %>, index: true, foreign_key: { to_table: :roles, on_delete: :cascade }
5
+ t.references :permission, null: false, type: :<%= primary_key_type %>, index: true, foreign_key: { to_table: :permissions, on_delete: :cascade }
6
6
 
7
7
  t.timestamps
8
8
  end
@@ -1,11 +1,20 @@
1
1
  class CreateRoles < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
2
  def change
3
- create_table :roles do |t|
3
+ create_table :roles<%= ", id: :uuid" if primary_key_type == :uuid %> do |t|
4
4
  t.string :name, null: false
5
+ <% if tenant_scoped? %>
6
+ t.references :tenant, null: true, type: :<%= primary_key_type %>, index: true
7
+ <% end %>
5
8
 
6
9
  t.timestamps
7
10
  end
11
+ <% if tenant_scoped? %>
12
+
13
+ add_index :roles, :name, unique: true, where: "tenant_id IS NULL", name: "index_roles_on_name_global"
14
+ add_index :roles, [:tenant_id, :name], unique: true, name: "index_roles_on_tenant_id_and_name"
15
+ <% else %>
8
16
 
9
17
  add_index :roles, :name, unique: true
18
+ <% end %>
10
19
  end
11
20
  end
@@ -1,13 +1,20 @@
1
1
  class CreateUserRoles < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
2
  def change
3
- create_table :user_roles do |t|
4
- t.references :role, null: false, index: true, foreign_key: { to_table: :roles, on_delete: :cascade }
5
- t.bigint :user_id, null: false
3
+ create_table :user_roles<%= ", id: :uuid" if primary_key_type == :uuid %> do |t|
4
+ t.references :role, null: false, type: :<%= primary_key_type %>, index: true, foreign_key: { to_table: :roles, on_delete: :cascade }
5
+ t.references :user, null: false, type: :<%= primary_key_type %>, index: true
6
+ <% if tenant_scoped? %>
7
+ t.references :tenant, null: true, type: :<%= primary_key_type %>, index: true
8
+ <% end %>
6
9
 
7
10
  t.timestamps
8
11
  end
12
+ <% if tenant_scoped? %>
13
+
14
+ add_index :user_roles, [:user_id, :role_id, :tenant_id], unique: true, name: "index_user_roles_unique"
15
+ <% else %>
9
16
 
10
- add_index :user_roles, :user_id
11
17
  add_index :user_roles, [:user_id, :role_id], unique: true
18
+ <% end %>
12
19
  end
13
20
  end
@@ -14,6 +14,21 @@ Rbacan.configure do |config|
14
14
  # config.user_role_table = "user_roles"
15
15
  # config.role_permission_table = "role_permissions"
16
16
 
17
+ # Primary key type for generated migrations.
18
+ # Set to :uuid if your app uses UUID primary keys.
19
+ # When nil, auto-detects from your Rails generator config (primary_key_type).
20
+ # config.primary_key_type = :uuid
21
+
22
+ # Enable tenant scoping for roles and user_roles.
23
+ # When true, adds tenant_id to the roles and user_roles tables,
24
+ # allowing tenant-specific roles and role assignments.
25
+ # Permissions always remain global regardless of this setting.
26
+ # config.tenant_scoped = false
27
+
28
+ # The name of your tenant model class (default: "Tenant").
29
+ # Only used when tenant_scoped is true.
30
+ # config.tenant_class = "Tenant"
31
+
17
32
  # Authorization failure handling:
18
33
  # :raise — raises Rbacan::NotAuthorized (default)
19
34
  # :redirect — redirects to unauthorized_redirect_path
@@ -5,8 +5,8 @@ module Rbacan
5
5
  module Generators
6
6
  class InstallGenerator < ::Rails::Generators::Base
7
7
  include Rails::Generators::Migration
8
- source_root File.expand_path('../../install/templates', __FILE__)
9
- desc "Add the migrations for roles and permissions"
8
+ source_root File.expand_path('../install/templates', __dir__)
9
+ desc 'Add the migrations for roles and permissions'
10
10
 
11
11
  def self.next_migration_number(path)
12
12
  next_migration_number = current_migration_number(path) + 1
@@ -14,19 +14,38 @@ module Rbacan
14
14
  end
15
15
 
16
16
  def copy_migrations
17
- migration_template "create_permissions.rb", "db/migrate/create_permissions.rb"
18
- migration_template "create_roles.rb", "db/migrate/create_roles.rb"
19
- migration_template "create_role_permissions.rb", "db/migrate/create_role_permissions.rb"
20
- migration_template "create_user_roles.rb", "db/migrate/create_user_roles.rb"
17
+ migration_template 'create_permissions.rb', 'db/migrate/create_permissions.rb'
18
+ migration_template 'create_roles.rb', 'db/migrate/create_roles.rb'
19
+ migration_template 'create_role_permissions.rb', 'db/migrate/create_role_permissions.rb'
20
+ migration_template 'create_user_roles.rb', 'db/migrate/create_user_roles.rb'
21
21
  end
22
22
 
23
23
  def copy_seed
24
- copy_file 'copy_to_seeds.rb', "db/copy_to_seeds.rb"
24
+ copy_file 'copy_to_seeds.rb', 'db/copy_to_seeds.rb'
25
25
  end
26
26
 
27
27
  def copy_initializer
28
28
  copy_file 'rbacan.rb', 'config/initializers/rbacan.rb'
29
29
  end
30
+
31
+ private
32
+
33
+ # Resolves the primary key type for migration templates.
34
+ # Checks the explicit Rbacan config first, then the Rails generator config.
35
+ def primary_key_type
36
+ configured = Rbacan.primary_key_type
37
+ configured ||= begin
38
+ Rails.configuration.generators.options.dig(:active_record, :primary_key_type)
39
+ rescue StandardError
40
+ nil
41
+ end
42
+ configured || :bigint
43
+ end
44
+
45
+ # Whether tenant scoping is enabled.
46
+ def tenant_scoped?
47
+ Rbacan.tenant_scoped
48
+ end
30
49
  end
31
50
  end
32
51
  end
@@ -7,72 +7,99 @@ module Rbacan
7
7
  included do
8
8
  has_many :user_roles,
9
9
  class_name: Rbacan.user_role_class,
10
- dependent: :destroy
10
+ dependent: :destroy
11
11
  accepts_nested_attributes_for :user_roles
12
12
  has_many :roles,
13
13
  class_name: Rbacan.role_class,
14
- through: :user_roles
14
+ through: :user_roles
15
15
 
16
16
  # Returns users that have the given role (by name).
17
- scope :with_role, ->(role_name) {
17
+ scope :with_role, lambda { |role_name|
18
18
  joins(:roles).where(Rbacan.role_table => { name: role_name.to_s })
19
19
  }
20
20
 
21
21
  # Returns users that have the given permission via any of their roles.
22
- scope :with_permission, ->(permission_name) {
22
+ scope :with_permission, lambda { |permission_name|
23
23
  joins(roles: { role_permissions: :permission })
24
24
  .where(Rbacan.permission_table => { name: permission_name.to_s })
25
25
  }
26
26
 
27
27
  # Assigns a role to the user. Idempotent — safe to call multiple times.
28
- # Fix: was find_or_initialize_by (never persisted); now find_or_create_by.
29
- def assign_role(role_name)
28
+ # When tenant_scoped is enabled, pass tenant_id: to scope the assignment.
29
+ def assign_role(role_name, tenant_id: nil)
30
30
  assigned_role = Rbacan.role_class.constantize.find_by_name(role_name.to_s)
31
31
  raise ArgumentError, "Role '#{role_name}' not found" unless assigned_role
32
32
 
33
- self.user_roles.find_or_create_by(role_id: assigned_role.id)
33
+ attrs = { role_id: assigned_role.id }
34
+ attrs[:tenant_id] = tenant_id if Rbacan.tenant_scoped
35
+ user_roles.find_or_create_by(attrs)
34
36
  end
35
37
 
36
38
  # Removes a role from the user.
37
- # Fix: was Rbacan::UserRole.where(user_id: ...) now uses the scoped
38
- # association so it respects the configured class and any FK setup.
39
- def remove_role(role_name)
39
+ # When tenant_scoped is enabled, pass tenant_id: to remove from a specific tenant.
40
+ def remove_role(role_name, tenant_id: nil)
40
41
  removed_role = Rbacan.role_class.constantize.find_by_name(role_name.to_s)
41
42
  return unless removed_role
42
43
 
43
- self.user_roles.where(role_id: removed_role.id).destroy_all
44
+ scope = user_roles.where(role_id: removed_role.id)
45
+ scope = scope.where(tenant_id: tenant_id) if Rbacan.tenant_scoped
46
+ scope.destroy_all
44
47
  end
45
48
 
46
49
  # Returns true if the user has the named permission via any of their roles.
47
- # Fix: was two queries; now a single EXISTS query via association joins.
48
50
  def can?(permission_name)
49
- self.roles
50
- .joins(role_permissions: :permission)
51
- .where(Rbacan.permission_table => { name: permission_name.to_s })
52
- .exists?
51
+ roles
52
+ .joins(role_permissions: :permission)
53
+ .where(Rbacan.permission_table => { name: permission_name.to_s })
54
+ .exists?
55
+ end
56
+
57
+ # Returns true if the user has the named permission in a specific tenant context.
58
+ # Checks both global role assignments (tenant_id NULL) and tenant-specific ones.
59
+ def can_in_tenant?(permission_name, tenant_id)
60
+ roles
61
+ .joins(role_permissions: :permission)
62
+ .where(Rbacan.permission_table => { name: permission_name.to_s })
63
+ .where(Rbacan.user_role_table => { tenant_id: [tenant_id, nil] })
64
+ .exists?
53
65
  end
54
66
 
55
67
  # Returns true if the user has the named role.
56
68
  def has_role?(role_name)
57
- self.roles.where(name: role_name.to_s).exists?
69
+ roles.where(name: role_name.to_s).exists?
70
+ end
71
+
72
+ # Returns true if the user has the named role in a specific tenant context.
73
+ # Checks both global role assignments (tenant_id NULL) and tenant-specific ones.
74
+ def has_role_in_tenant?(role_name, tenant_id)
75
+ user_roles
76
+ .joins(:role)
77
+ .where(Rbacan.role_table => { name: role_name.to_s })
78
+ .where(Rbacan.user_role_table => { tenant_id: [tenant_id, nil] })
79
+ .exists?
58
80
  end
59
81
 
60
82
  # Returns true if the user has ANY of the listed roles.
61
83
  def has_any_role?(*role_names)
62
- self.roles.where(name: role_names.map(&:to_s)).exists?
84
+ roles.where(name: role_names.map(&:to_s)).exists?
63
85
  end
64
86
 
65
87
  # Returns true if the user has ALL of the listed permissions.
66
- # Uses a single query: counts distinct matching permissions and compares
67
- # to the requested set size.
68
88
  def can_all?(*permission_names)
69
89
  names = permission_names.map(&:to_s)
70
- self.roles
71
- .joins(role_permissions: :permission)
72
- .where(Rbacan.permission_table => { name: names })
73
- .select("#{Rbacan.permission_table}.name")
74
- .distinct
75
- .count == names.uniq.size
90
+ roles
91
+ .joins(role_permissions: :permission)
92
+ .where(Rbacan.permission_table => { name: names })
93
+ .select("#{Rbacan.permission_table}.name")
94
+ .distinct
95
+ .count == names.uniq.size
96
+ end
97
+
98
+ # Returns roles assigned to this user for a specific tenant.
99
+ # Includes global role assignments (tenant_id NULL).
100
+ def roles_for_tenant(tenant_id)
101
+ roles
102
+ .where(Rbacan.user_role_table => { tenant_id: [tenant_id, nil] })
76
103
  end
77
104
  end
78
105
  end
@@ -1,3 +1,3 @@
1
1
  module Rbacan
2
- VERSION = "0.3.1"
2
+ VERSION = '0.4.0'
3
3
  end
data/lib/rbacan.rb CHANGED
@@ -1,11 +1,11 @@
1
- require "rbacan/version"
2
- require "rbacan/not_authorized"
3
- require "rbacan/permittable"
4
- require "rbacan/engine"
5
- require "rbacan/roles_and_permissions"
6
- require "rbacan/authorization"
7
- require "rbacan/view_helpers"
8
- require "rbacan/route_constraint"
1
+ require 'rbacan/version'
2
+ require 'rbacan/not_authorized'
3
+ require 'rbacan/permittable'
4
+ require 'rbacan/engine'
5
+ require 'rbacan/roles_and_permissions'
6
+ require 'rbacan/authorization'
7
+ require 'rbacan/view_helpers'
8
+ require 'rbacan/route_constraint'
9
9
 
10
10
  module Rbacan
11
11
  mattr_accessor :permittable_class
@@ -39,6 +39,33 @@ module Rbacan
39
39
  mattr_accessor :unauthorized_redirect_path
40
40
  @@unauthorized_redirect_path = '/'
41
41
 
42
+ # Primary key type for generated migrations.
43
+ # nil = auto-detect from Rails generator config (primary_key_type).
44
+ # Set to :uuid for UUID primary keys, or :bigint for standard integer keys.
45
+ mattr_accessor :primary_key_type
46
+ @@primary_key_type = nil
47
+
48
+ # When true, adds tenant_id to roles and user_roles tables,
49
+ # enabling tenant-scoped roles and role assignments.
50
+ # Permissions remain global (no tenant_id) regardless of this setting.
51
+ mattr_accessor :tenant_scoped
52
+ @@tenant_scoped = false
53
+
54
+ # The name of your tenant model class (default: "Tenant").
55
+ # Only used when tenant_scoped is true.
56
+ mattr_accessor :tenant_class
57
+ @@tenant_class = 'Tenant'
58
+
59
+ # Resolves the effective primary key type.
60
+ # Checks the explicit config first, then falls back to the Rails generator config.
61
+ def self.resolve_primary_key_type
62
+ return primary_key_type if primary_key_type.present?
63
+
64
+ Rails.configuration.generators.options.dig(:active_record, :primary_key_type)
65
+ rescue StandardError
66
+ nil
67
+ end
68
+
42
69
  def self.create_role(role_name)
43
70
  @@role_class.constantize.create(name: role_name)
44
71
  end
@@ -51,12 +78,12 @@ module Rbacan
51
78
  chosen_role = @@role_class.constantize.find_by_name(role_name)
52
79
  given_permission = @@permission_class.constantize.find_by_name(permission_name)
53
80
  @@role_permission_class.constantize.create(
54
- role_id: chosen_role.id,
81
+ role_id: chosen_role.id,
55
82
  permission_id: given_permission.id
56
83
  )
57
84
  end
58
85
 
59
- def self.configure(&block)
86
+ def self.configure
60
87
  yield self
61
88
  end
62
89
 
data/rbacan.gemspec CHANGED
@@ -1,42 +1,39 @@
1
-
2
- lib = File.expand_path("../lib", __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "rbacan/version"
3
+ require 'rbacan/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "rbacan"
6
+ spec.name = 'rbacan'
8
7
  spec.version = Rbacan::VERSION
9
- spec.authors = ["hamdi"]
10
- spec.email = ["hamdi_amiche@outlook.fr"]
8
+ spec.authors = ['hamdi']
9
+ spec.email = ['hamdi_amiche@outlook.fr']
11
10
 
12
- spec.summary = %q{A gem to give permission access to users based on their roles}
13
- spec.description = %q{RBACan is a Role-Based Access Control tool to control user access to the functionalities of your application}
14
- spec.homepage = "https://github.com/hamdi777/RBACan"
15
- spec.licenses = ["MIT"]
11
+ spec.summary = 'A gem to give permission access to users based on their roles'
12
+ spec.description = 'RBACan is a Role-Based Access Control tool to control user access to the functionalities of your application'
13
+ spec.homepage = 'https://github.com/hamdi777/RBACan'
14
+ spec.licenses = ['MIT']
16
15
 
17
- if spec.respond_to?(:metadata)
18
- spec.metadata["allowed_push_host"] = "https://rubygems.org"
19
- spec.metadata["homepage_uri"] = spec.homepage
20
- spec.metadata["changelog_uri"] = "https://github.com/hamdi777/RBACan/blob/master/CHANGELOG.md"
21
- else
22
- raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
- end
16
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)
17
+
18
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['changelog_uri'] = 'https://github.com/hamdi777/RBACan/blob/master/CHANGELOG.md'
24
21
 
25
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
23
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
24
  end
28
- spec.bindir = "exe"
25
+ spec.bindir = 'exe'
29
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
31
- spec.required_ruby_version = ">= 3.0"
27
+ spec.require_paths = ['lib']
28
+ spec.required_ruby_version = '>= 3.0'
32
29
 
33
- spec.add_dependency 'rails', '~> 5.2'
30
+ spec.add_dependency 'rails', '>= 5.2', '< 9'
34
31
 
35
- spec.add_development_dependency "bundler", "~> 2.0"
36
- spec.add_development_dependency "rake", "~> 13.0"
37
- spec.add_development_dependency "rspec", "~> 3.0"
38
- spec.add_development_dependency "generator_spec", "~> 0.9.4"
39
- spec.add_development_dependency "railties", "~> 5.2"
40
- spec.add_development_dependency "activerecord", "~> 5.2"
41
- spec.add_development_dependency "sqlite3", "~> 1.4"
32
+ spec.add_development_dependency 'activerecord', '>= 5.2', '< 9'
33
+ spec.add_development_dependency 'bundler', '>= 2.0'
34
+ spec.add_development_dependency 'generator_spec', '~> 0.9.4'
35
+ spec.add_development_dependency 'railties', '>= 5.2', '< 9'
36
+ spec.add_development_dependency 'rake', '~> 13.0'
37
+ spec.add_development_dependency 'rspec', '~> 3.0'
38
+ spec.add_development_dependency 'sqlite3'
42
39
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbacan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hamdi
@@ -13,114 +13,132 @@ dependencies:
13
13
  name: rails
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '5.2'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9'
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
22
25
  requirements:
23
- - - "~>"
26
+ - - ">="
24
27
  - !ruby/object:Gem::Version
25
28
  version: '5.2'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9'
26
32
  - !ruby/object:Gem::Dependency
27
- name: bundler
33
+ name: activerecord
28
34
  requirement: !ruby/object:Gem::Requirement
29
35
  requirements:
30
- - - "~>"
36
+ - - ">="
31
37
  - !ruby/object:Gem::Version
32
- version: '2.0'
38
+ version: '5.2'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '9'
33
42
  type: :development
34
43
  prerelease: false
35
44
  version_requirements: !ruby/object:Gem::Requirement
36
45
  requirements:
37
- - - "~>"
46
+ - - ">="
38
47
  - !ruby/object:Gem::Version
39
- version: '2.0'
48
+ version: '5.2'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '9'
40
52
  - !ruby/object:Gem::Dependency
41
- name: rake
53
+ name: bundler
42
54
  requirement: !ruby/object:Gem::Requirement
43
55
  requirements:
44
- - - "~>"
56
+ - - ">="
45
57
  - !ruby/object:Gem::Version
46
- version: '13.0'
58
+ version: '2.0'
47
59
  type: :development
48
60
  prerelease: false
49
61
  version_requirements: !ruby/object:Gem::Requirement
50
62
  requirements:
51
- - - "~>"
63
+ - - ">="
52
64
  - !ruby/object:Gem::Version
53
- version: '13.0'
65
+ version: '2.0'
54
66
  - !ruby/object:Gem::Dependency
55
- name: rspec
67
+ name: generator_spec
56
68
  requirement: !ruby/object:Gem::Requirement
57
69
  requirements:
58
70
  - - "~>"
59
71
  - !ruby/object:Gem::Version
60
- version: '3.0'
72
+ version: 0.9.4
61
73
  type: :development
62
74
  prerelease: false
63
75
  version_requirements: !ruby/object:Gem::Requirement
64
76
  requirements:
65
77
  - - "~>"
66
78
  - !ruby/object:Gem::Version
67
- version: '3.0'
79
+ version: 0.9.4
68
80
  - !ruby/object:Gem::Dependency
69
- name: generator_spec
81
+ name: railties
70
82
  requirement: !ruby/object:Gem::Requirement
71
83
  requirements:
72
- - - "~>"
84
+ - - ">="
73
85
  - !ruby/object:Gem::Version
74
- version: 0.9.4
86
+ version: '5.2'
87
+ - - "<"
88
+ - !ruby/object:Gem::Version
89
+ version: '9'
75
90
  type: :development
76
91
  prerelease: false
77
92
  version_requirements: !ruby/object:Gem::Requirement
78
93
  requirements:
79
- - - "~>"
94
+ - - ">="
80
95
  - !ruby/object:Gem::Version
81
- version: 0.9.4
96
+ version: '5.2'
97
+ - - "<"
98
+ - !ruby/object:Gem::Version
99
+ version: '9'
82
100
  - !ruby/object:Gem::Dependency
83
- name: railties
101
+ name: rake
84
102
  requirement: !ruby/object:Gem::Requirement
85
103
  requirements:
86
104
  - - "~>"
87
105
  - !ruby/object:Gem::Version
88
- version: '5.2'
106
+ version: '13.0'
89
107
  type: :development
90
108
  prerelease: false
91
109
  version_requirements: !ruby/object:Gem::Requirement
92
110
  requirements:
93
111
  - - "~>"
94
112
  - !ruby/object:Gem::Version
95
- version: '5.2'
113
+ version: '13.0'
96
114
  - !ruby/object:Gem::Dependency
97
- name: activerecord
115
+ name: rspec
98
116
  requirement: !ruby/object:Gem::Requirement
99
117
  requirements:
100
118
  - - "~>"
101
119
  - !ruby/object:Gem::Version
102
- version: '5.2'
120
+ version: '3.0'
103
121
  type: :development
104
122
  prerelease: false
105
123
  version_requirements: !ruby/object:Gem::Requirement
106
124
  requirements:
107
125
  - - "~>"
108
126
  - !ruby/object:Gem::Version
109
- version: '5.2'
127
+ version: '3.0'
110
128
  - !ruby/object:Gem::Dependency
111
129
  name: sqlite3
112
130
  requirement: !ruby/object:Gem::Requirement
113
131
  requirements:
114
- - - "~>"
132
+ - - ">="
115
133
  - !ruby/object:Gem::Version
116
- version: '1.4'
134
+ version: '0'
117
135
  type: :development
118
136
  prerelease: false
119
137
  version_requirements: !ruby/object:Gem::Requirement
120
138
  requirements:
121
- - - "~>"
139
+ - - ">="
122
140
  - !ruby/object:Gem::Version
123
- version: '1.4'
141
+ version: '0'
124
142
  description: RBACan is a Role-Based Access Control tool to control user access to
125
143
  the functionalities of your application
126
144
  email:
@@ -133,6 +151,7 @@ files:
133
151
  - ".rspec"
134
152
  - ".travis.yml"
135
153
  - CHANGELOG.md
154
+ - CLAUDE.md
136
155
  - CODE_OF_CONDUCT.md
137
156
  - Gemfile
138
157
  - Gemfile.lock
@@ -184,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
203
  - !ruby/object:Gem::Version
185
204
  version: '0'
186
205
  requirements: []
187
- rubygems_version: 3.6.8
206
+ rubygems_version: 3.6.9
188
207
  specification_version: 4
189
208
  summary: A gem to give permission access to users based on their roles
190
209
  test_files: []