rabarber 4.1.0 → 4.1.2
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/CHANGELOG.md +16 -0
- data/README.md +91 -60
- data/lib/rabarber/core/access.rb +3 -5
- data/lib/rabarber/core/cache.rb +2 -5
- data/lib/rabarber/core/permissions.rb +6 -3
- data/lib/rabarber/core/permissions_integrity_checker.rb +2 -2
- data/lib/rabarber/core/rule.rb +2 -3
- data/lib/rabarber/version.rb +1 -1
- data/rabarber.gemspec +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 73050af26c88e78f295c489ee5fc49c782b3950157db2ecd1dd48cdda5b10045
         | 
| 4 | 
            +
              data.tar.gz: '08f7da98672774c2198c155be4fb6f795c23e7da72e565631ef0f08207cd2cb9'
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d1482714aa484c800970ea77a081388a0df4b8945e8fc0931b76e8d6415ed9f88c01ad8834e873d9161a20c7a5f2208b10782fe95857024b1a6069919f83dd47
         | 
| 7 | 
            +
              data.tar.gz: '06383d81e91f872419fb567b2ef18991abf6a32263297645725e52b70a9c383a769c72d3657fb9f61a6cc11fb30b2129d813ca63e1a352761f99795f287c86b7'
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,19 @@ | |
| 1 | 
            +
            ## v4.1.2
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ### Misc:
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            - Replaced `git ls-files` with `Dir.glob` in gemspec for improved portability and compatibility
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ## v4.1.1
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ### Bugs:
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            - Fixed an issue where controller-wide `grant_access` calls would overwrite each other instead of being additive, causing inconsistent access control based on statement order
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ### Misc:
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            - Minor performance improvement for authorization checks
         | 
| 16 | 
            +
             | 
| 1 17 | 
             
            ## v4.1.0
         | 
| 2 18 |  | 
| 3 19 | 
             
            ### Features:
         | 
    
        data/README.md
    CHANGED
    
    | @@ -248,17 +248,19 @@ This adds `.grant_access(action: nil, roles: nil, context: nil, if: nil, unless: | |
| 248 248 | 
             
            The most basic usage of the method is as follows:
         | 
| 249 249 |  | 
| 250 250 | 
             
            ```rb
         | 
| 251 | 
            -
             | 
| 252 | 
            -
               | 
| 253 | 
            -
             | 
| 254 | 
            -
                 | 
| 255 | 
            -
             | 
| 256 | 
            -
             | 
| 257 | 
            -
             | 
| 258 | 
            -
             | 
| 259 | 
            -
             | 
| 260 | 
            -
             | 
| 261 | 
            -
                 | 
| 251 | 
            +
            module Crm
         | 
| 252 | 
            +
              class InvoicesController < ApplicationController
         | 
| 253 | 
            +
                grant_access action: :index, roles: [:accountant, :admin]
         | 
| 254 | 
            +
                def index
         | 
| 255 | 
            +
                  @invoices = Invoice.all
         | 
| 256 | 
            +
                  @invoices = @invoices.paid if current_user.has_role?(:accountant)
         | 
| 257 | 
            +
                  # ...
         | 
| 258 | 
            +
                end
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                grant_access action: :destroy, roles: :admin
         | 
| 261 | 
            +
                def destroy
         | 
| 262 | 
            +
                  # ...
         | 
| 263 | 
            +
                end
         | 
| 262 264 | 
             
              end
         | 
| 263 265 | 
             
            end
         | 
| 264 266 | 
             
            ```
         | 
| @@ -267,23 +269,27 @@ This grants access to `index` action for users with `accountant` or `admin` role | |
| 267 269 | 
             
            You can also define controller-wide rules (without `action` argument):
         | 
| 268 270 |  | 
| 269 271 | 
             
            ```rb
         | 
| 270 | 
            -
             | 
| 271 | 
            -
               | 
| 272 | 
            +
            module Crm
         | 
| 273 | 
            +
              class BaseController < ApplicationController
         | 
| 274 | 
            +
                grant_access roles: [:admin, :manager]
         | 
| 272 275 |  | 
| 273 | 
            -
             | 
| 274 | 
            -
             | 
| 275 | 
            -
             | 
| 276 | 
            +
                grant_access action: :dashboard, roles: :marketer
         | 
| 277 | 
            +
                def dashboard
         | 
| 278 | 
            +
                  # ...
         | 
| 279 | 
            +
                end
         | 
| 276 280 | 
             
              end
         | 
| 277 281 | 
             
            end
         | 
| 278 282 |  | 
| 279 | 
            -
             | 
| 280 | 
            -
               | 
| 281 | 
            -
             | 
| 282 | 
            -
                 | 
| 283 | 
            -
             | 
| 283 | 
            +
            module Crm
         | 
| 284 | 
            +
              class InvoicesController < Crm::BaseController
         | 
| 285 | 
            +
                grant_access roles: :accountant
         | 
| 286 | 
            +
                def index
         | 
| 287 | 
            +
                  # ...
         | 
| 288 | 
            +
                end
         | 
| 284 289 |  | 
| 285 | 
            -
             | 
| 286 | 
            -
             | 
| 290 | 
            +
                def delete
         | 
| 291 | 
            +
                  # ...
         | 
| 292 | 
            +
                end
         | 
| 287 293 | 
             
              end
         | 
| 288 294 | 
             
            end
         | 
| 289 295 | 
             
            ```
         | 
| @@ -311,55 +317,80 @@ If you've set `must_have_roles` setting to `true`, then only the users with at l | |
| 311 317 |  | 
| 312 318 | 
             
            Also keep in mind that rules defined in child classes don't override parent rules but rather add to them:
         | 
| 313 319 | 
             
            ```rb
         | 
| 314 | 
            -
             | 
| 315 | 
            -
               | 
| 320 | 
            +
            module Crm
         | 
| 321 | 
            +
              class BaseController < ApplicationController
         | 
| 322 | 
            +
                grant_access roles: :admin
         | 
| 316 323 | 
             
              # ...
         | 
| 324 | 
            +
              end
         | 
| 317 325 | 
             
            end
         | 
| 318 326 |  | 
| 319 | 
            -
             | 
| 320 | 
            -
               | 
| 327 | 
            +
            module Crm
         | 
| 328 | 
            +
              class InvoicesController < Crm::BaseController
         | 
| 329 | 
            +
                grant_access roles: :accountant
         | 
| 321 330 | 
             
              # ...
         | 
| 331 | 
            +
              end
         | 
| 322 332 | 
             
            end
         | 
| 323 333 | 
             
            ```
         | 
| 324 334 | 
             
            This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
         | 
| 325 335 |  | 
| 336 | 
            +
            This applies as well to multiple rules defined for the same controller or action:
         | 
| 337 | 
            +
            ```rb
         | 
| 338 | 
            +
            module Crm
         | 
| 339 | 
            +
              class OrdersController < ApplicationController
         | 
| 340 | 
            +
                grant_access roles: :manager, context: Order
         | 
| 341 | 
            +
                grant_access roles: :admin
         | 
| 342 | 
            +
             | 
| 343 | 
            +
                grant_access action: :show, roles: :client, context: -> { Order.find(params[:id]) }
         | 
| 344 | 
            +
                grant_access action: :show, roles: :accountant
         | 
| 345 | 
            +
                def show
         | 
| 346 | 
            +
                  # ...
         | 
| 347 | 
            +
                end
         | 
| 348 | 
            +
              end
         | 
| 349 | 
            +
            end
         | 
| 350 | 
            +
            ```
         | 
| 351 | 
            +
            This will add rules for `manager` and `admin` roles for all actions in `Crm::OrdersController`, and for `client` and `accountant` roles for the `show` action.
         | 
| 352 | 
            +
             | 
| 326 353 | 
             
            ## Dynamic Authorization Rules
         | 
| 327 354 |  | 
| 328 355 | 
             
            For more complex cases, Rabarber provides dynamic rules:
         | 
| 329 356 |  | 
| 330 357 | 
             
            ```rb
         | 
| 331 | 
            -
             | 
| 332 | 
            -
               | 
| 358 | 
            +
            module Crm
         | 
| 359 | 
            +
              class OrdersController < ApplicationController
         | 
| 360 | 
            +
                grant_access roles: :manager, if: :company_manager?, unless: :fired?
         | 
| 333 361 |  | 
| 334 | 
            -
             | 
| 335 | 
            -
             | 
| 336 | 
            -
             | 
| 362 | 
            +
                def index
         | 
| 363 | 
            +
                  # ...
         | 
| 364 | 
            +
                end
         | 
| 337 365 |  | 
| 338 | 
            -
             | 
| 366 | 
            +
                private
         | 
| 339 367 |  | 
| 340 | 
            -
             | 
| 341 | 
            -
             | 
| 342 | 
            -
             | 
| 368 | 
            +
                def company_manager?
         | 
| 369 | 
            +
                  Company.find(params[:company_id]).manager == current_user
         | 
| 370 | 
            +
                end
         | 
| 343 371 |  | 
| 344 | 
            -
             | 
| 345 | 
            -
             | 
| 372 | 
            +
                def fired?
         | 
| 373 | 
            +
                  current_user.fired?
         | 
| 374 | 
            +
                end
         | 
| 346 375 | 
             
              end
         | 
| 347 376 | 
             
            end
         | 
| 348 377 |  | 
| 349 | 
            -
             | 
| 350 | 
            -
               | 
| 351 | 
            -
             | 
| 352 | 
            -
             | 
| 353 | 
            -
             | 
| 354 | 
            -
                 | 
| 355 | 
            -
             | 
| 356 | 
            -
             | 
| 357 | 
            -
             | 
| 358 | 
            -
             | 
| 359 | 
            -
             | 
| 360 | 
            -
             | 
| 361 | 
            -
             | 
| 362 | 
            -
                 | 
| 378 | 
            +
            module Crm
         | 
| 379 | 
            +
              class InvoicesController < ApplicationController
         | 
| 380 | 
            +
                grant_access roles: :senior_accountant
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                grant_access action: :index, roles: [:secretary, :accountant], if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
         | 
| 383 | 
            +
                def index
         | 
| 384 | 
            +
                  @invoices = Invoice.all
         | 
| 385 | 
            +
                  @invoices = @invoices.where("total < 10000") if current_user.has_role?(:accountant)
         | 
| 386 | 
            +
                  @invoices = @invoices.unpaid if current_user.has_role?(:secretary)
         | 
| 387 | 
            +
                  # ...
         | 
| 388 | 
            +
                end
         | 
| 389 | 
            +
             | 
| 390 | 
            +
                grant_access action: :show, roles: :accountant, unless: -> { Invoice.find(params[:id]).total > 10_000 }
         | 
| 391 | 
            +
                def show
         | 
| 392 | 
            +
                  # ...
         | 
| 393 | 
            +
                end
         | 
| 363 394 | 
             
              end
         | 
| 364 395 | 
             
            end
         | 
| 365 396 | 
             
            ```
         | 
| @@ -393,27 +424,27 @@ Every Rabarber method can accept a context as an additional keyword argument. By | |
| 393 424 | 
             
            E.g., consider a model named `Project`, where each project has its owner and regular members. Roles can be defined like this:
         | 
| 394 425 |  | 
| 395 426 | 
             
            ```rb
         | 
| 396 | 
            -
             | 
| 397 | 
            -
             | 
| 427 | 
            +
            user.assign_roles(:owner, context: project)
         | 
| 428 | 
            +
            another_user.assign_roles(:member, context: project)
         | 
| 398 429 | 
             
            ```
         | 
| 399 430 |  | 
| 400 431 | 
             
            Then the roles can be verified:
         | 
| 401 432 |  | 
| 402 433 | 
             
            ```rb
         | 
| 403 | 
            -
             | 
| 404 | 
            -
             | 
| 434 | 
            +
            user.has_role?(:owner, context: project)
         | 
| 435 | 
            +
            another_user.has_role?(:member, context: project)
         | 
| 405 436 | 
             
            ```
         | 
| 406 437 |  | 
| 407 438 | 
             
            A role can also be added using a class as a context, e.g., for project admins who can manage all projects:
         | 
| 408 439 |  | 
| 409 440 | 
             
            ```rb
         | 
| 410 | 
            -
             | 
| 441 | 
            +
            user.assign_roles(:admin, context: Project)
         | 
| 411 442 | 
             
            ```
         | 
| 412 443 |  | 
| 413 444 | 
             
            And then it can also be verified:
         | 
| 414 445 |  | 
| 415 446 | 
             
            ```rb
         | 
| 416 | 
            -
             | 
| 447 | 
            +
            user.has_role?(:admin, context: Project)
         | 
| 417 448 | 
             
            ```
         | 
| 418 449 |  | 
| 419 450 | 
             
            In authorization rules, the context can be used in the same way, but it also can be a proc or a symbol (similar to dynamic rules):
         | 
| @@ -445,13 +476,13 @@ It's important to note that role names are not unique globally but are unique wi | |
| 445 476 | 
             
            If you want to see all the roles assigned to a user within a specific context, you can use:
         | 
| 446 477 |  | 
| 447 478 | 
             
            ```rb
         | 
| 448 | 
            -
             | 
| 479 | 
            +
            user.roles(context: project)
         | 
| 449 480 | 
             
            ```
         | 
| 450 481 |  | 
| 451 482 | 
             
            Or if you want to get all the roles available in a specific context, you can use:
         | 
| 452 483 |  | 
| 453 484 | 
             
            ```rb
         | 
| 454 | 
            -
             | 
| 485 | 
            +
            Rabarber::Role.names(context: Project)
         | 
| 455 486 | 
             
            ```
         | 
| 456 487 |  | 
| 457 488 | 
             
            ## When Unauthorized
         | 
    
        data/lib/rabarber/core/access.rb
    CHANGED
    
    | @@ -8,15 +8,13 @@ module Rabarber | |
| 8 8 | 
             
                  end
         | 
| 9 9 |  | 
| 10 10 | 
             
                  def controller_accessible?(roleable, controller_instance)
         | 
| 11 | 
            -
                    controller_rules.any? do | | 
| 12 | 
            -
                      controller_instance.is_a?( | 
| 11 | 
            +
                    controller_rules.any? do |controller, rules|
         | 
| 12 | 
            +
                      controller_instance.is_a?(controller) && rules.any? { _1.verify_access(roleable, controller_instance) }
         | 
| 13 13 | 
             
                    end
         | 
| 14 14 | 
             
                  end
         | 
| 15 15 |  | 
| 16 16 | 
             
                  def action_accessible?(roleable, action, controller_instance)
         | 
| 17 | 
            -
                    action_rules[controller_instance.class].any?  | 
| 18 | 
            -
                      rule.action == action && rule.verify_access(roleable, controller_instance)
         | 
| 19 | 
            -
                    end
         | 
| 17 | 
            +
                    action_rules[controller_instance.class][action].any? { _1.verify_access(roleable, controller_instance) }
         | 
| 20 18 | 
             
                  end
         | 
| 21 19 | 
             
                end
         | 
| 22 20 | 
             
              end
         | 
    
        data/lib/rabarber/core/cache.rb
    CHANGED
    
    
| @@ -13,13 +13,16 @@ module Rabarber | |
| 13 13 | 
             
                  attr_reader :storage
         | 
| 14 14 |  | 
| 15 15 | 
             
                  def initialize
         | 
| 16 | 
            -
                    @storage = { | 
| 16 | 
            +
                    @storage = {
         | 
| 17 | 
            +
                      controller_rules: Hash.new { |h, k| h[k] = [] },
         | 
| 18 | 
            +
                      action_rules: Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = [] } }
         | 
| 19 | 
            +
                    }
         | 
| 17 20 | 
             
                  end
         | 
| 18 21 |  | 
| 19 22 | 
             
                  class << self
         | 
| 20 23 | 
             
                    def add(controller, action, roles, context, dynamic_rule, negated_dynamic_rule)
         | 
| 21 | 
            -
                      rule = Rabarber::Core::Rule.new( | 
| 22 | 
            -
                      action ? action_rules[controller] += [rule] : controller_rules[controller]  | 
| 24 | 
            +
                      rule = Rabarber::Core::Rule.new(roles, context, dynamic_rule, negated_dynamic_rule)
         | 
| 25 | 
            +
                      action ? action_rules[controller][action] += [rule] : controller_rules[controller] += [rule]
         | 
| 23 26 | 
             
                    end
         | 
| 24 27 |  | 
| 25 28 | 
             
                    def controller_rules
         | 
| @@ -21,8 +21,8 @@ module Rabarber | |
| 21 21 | 
             
                  private
         | 
| 22 22 |  | 
| 23 23 | 
             
                  def missing_list
         | 
| 24 | 
            -
                    @missing_list ||= action_rules.each_with_object([]) do |(controller,  | 
| 25 | 
            -
                      missing_actions =  | 
| 24 | 
            +
                    @missing_list ||= action_rules.each_with_object([]) do |(controller, hash), arr|
         | 
| 25 | 
            +
                      missing_actions = hash.keys - controller.action_methods.map(&:to_sym)
         | 
| 26 26 | 
             
                      arr << { controller => missing_actions } if missing_actions.any?
         | 
| 27 27 | 
             
                    end
         | 
| 28 28 | 
             
                  end
         | 
    
        data/lib/rabarber/core/rule.rb
    CHANGED
    
    | @@ -3,10 +3,9 @@ | |
| 3 3 | 
             
            module Rabarber
         | 
| 4 4 | 
             
              module Core
         | 
| 5 5 | 
             
                class Rule
         | 
| 6 | 
            -
                  attr_reader : | 
| 6 | 
            +
                  attr_reader :roles, :context, :dynamic_rule, :negated_dynamic_rule
         | 
| 7 7 |  | 
| 8 | 
            -
                  def initialize( | 
| 9 | 
            -
                    @action = action
         | 
| 8 | 
            +
                  def initialize(roles, context, dynamic_rule, negated_dynamic_rule)
         | 
| 10 9 | 
             
                    @roles = Array(roles)
         | 
| 11 10 | 
             
                    @context = context
         | 
| 12 11 | 
             
                    @dynamic_rule = dynamic_rule || -> { true }
         | 
    
        data/lib/rabarber/version.rb
    CHANGED
    
    
    
        data/rabarber.gemspec
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: rabarber
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 4.1. | 
| 4 | 
            +
              version: 4.1.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - enjaku4
         | 
| 8 8 | 
             
            - trafium
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2025-02-21 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rails
         |