entitlements-app 0.1.6
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/VERSION +1 -0
 - data/bin/deploy-entitlements +18 -0
 - data/lib/entitlements/auditor/base.rb +163 -0
 - data/lib/entitlements/backend/base_controller.rb +171 -0
 - data/lib/entitlements/backend/base_provider.rb +55 -0
 - data/lib/entitlements/backend/dummy/controller.rb +89 -0
 - data/lib/entitlements/backend/dummy.rb +3 -0
 - data/lib/entitlements/backend/ldap/controller.rb +188 -0
 - data/lib/entitlements/backend/ldap/provider.rb +128 -0
 - data/lib/entitlements/backend/ldap.rb +4 -0
 - data/lib/entitlements/backend/member_of/controller.rb +203 -0
 - data/lib/entitlements/backend/member_of.rb +3 -0
 - data/lib/entitlements/cli.rb +121 -0
 - data/lib/entitlements/data/groups/cached.rb +120 -0
 - data/lib/entitlements/data/groups/calculated/base.rb +478 -0
 - data/lib/entitlements/data/groups/calculated/filters/base.rb +93 -0
 - data/lib/entitlements/data/groups/calculated/filters/member_of_group.rb +32 -0
 - data/lib/entitlements/data/groups/calculated/modifiers/base.rb +38 -0
 - data/lib/entitlements/data/groups/calculated/modifiers/expiration.rb +56 -0
 - data/lib/entitlements/data/groups/calculated/ruby.rb +137 -0
 - data/lib/entitlements/data/groups/calculated/rules/base.rb +35 -0
 - data/lib/entitlements/data/groups/calculated/rules/group.rb +129 -0
 - data/lib/entitlements/data/groups/calculated/rules/username.rb +41 -0
 - data/lib/entitlements/data/groups/calculated/text.rb +337 -0
 - data/lib/entitlements/data/groups/calculated/yaml.rb +171 -0
 - data/lib/entitlements/data/groups/calculated.rb +290 -0
 - data/lib/entitlements/data/groups.rb +13 -0
 - data/lib/entitlements/data/people/combined.rb +197 -0
 - data/lib/entitlements/data/people/dummy.rb +71 -0
 - data/lib/entitlements/data/people/ldap.rb +142 -0
 - data/lib/entitlements/data/people/yaml.rb +102 -0
 - data/lib/entitlements/data/people.rb +58 -0
 - data/lib/entitlements/extras/base.rb +40 -0
 - data/lib/entitlements/extras/ldap_group/base.rb +20 -0
 - data/lib/entitlements/extras/ldap_group/filters/member_of_ldap_group.rb +50 -0
 - data/lib/entitlements/extras/ldap_group/rules/ldap_group.rb +69 -0
 - data/lib/entitlements/extras/orgchart/base.rb +32 -0
 - data/lib/entitlements/extras/orgchart/logic.rb +171 -0
 - data/lib/entitlements/extras/orgchart/person_methods.rb +55 -0
 - data/lib/entitlements/extras/orgchart/rules/direct_report.rb +62 -0
 - data/lib/entitlements/extras/orgchart/rules/management.rb +59 -0
 - data/lib/entitlements/extras.rb +82 -0
 - data/lib/entitlements/models/action.rb +82 -0
 - data/lib/entitlements/models/group.rb +280 -0
 - data/lib/entitlements/models/person.rb +149 -0
 - data/lib/entitlements/plugins/dummy.rb +22 -0
 - data/lib/entitlements/plugins/group_of_names.rb +28 -0
 - data/lib/entitlements/plugins/posix_group.rb +46 -0
 - data/lib/entitlements/plugins.rb +13 -0
 - data/lib/entitlements/rule/base.rb +74 -0
 - data/lib/entitlements/service/ldap.rb +405 -0
 - data/lib/entitlements/util/mirror.rb +42 -0
 - data/lib/entitlements/util/override.rb +64 -0
 - data/lib/entitlements/util/util.rb +219 -0
 - data/lib/entitlements.rb +606 -0
 - metadata +343 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: eae412adac1519d3fd52aaf3b53bc62fe80dc22d6da4508ecba7fe08f424e4c3
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 364c116281789b97b1b21d30542f7c5abdd7fb5ef9b6a5f0aacc4971d6d05b48
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: aeea39803eebade5d4446477f361beafb64a187ad436680dcdcb12a9792a10d9ec039e42a84ac2171e155fc8791f031c4ce3b7f52d19677032acf96c19b4a44f
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 79915c3c077e52ac95de932e3fee76fa030f977cbac79866d7de79589a27ca19334082041e30c2df1c4c7a1e174b43bca974e20da28e05009f3a46a579550d85
         
     | 
    
        data/VERSION
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            0.1.6
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            ENV["BUNDLE_GEMFILE"] = File.expand_path("../Gemfile", File.dirname(__FILE__))
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "bundler/setup"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "contracts"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            # We don't need Contract outside of normal development
         
     | 
| 
      
 8 
     | 
    
         
            +
            VALID = [true, nil]
         
     | 
| 
      
 9 
     | 
    
         
            +
            class Contract
         
     | 
| 
      
 10 
     | 
    
         
            +
              def self.valid?(arg, contract)
         
     | 
| 
      
 11 
     | 
    
         
            +
                VALID
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            require "entitlements"
         
     | 
| 
      
 16 
     | 
    
         
            +
            exitcode = Entitlements::Cli.run
         
     | 
| 
      
 17 
     | 
    
         
            +
            exitcode ||= 0
         
     | 
| 
      
 18 
     | 
    
         
            +
            exit exitcode
         
     | 
| 
         @@ -0,0 +1,163 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # This class provides common methods and is intended to be inherited by other audit providers.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Entitlements
         
     | 
| 
      
 6 
     | 
    
         
            +
              class Auditor
         
     | 
| 
      
 7 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include ::Contracts::Core
         
     | 
| 
      
 9 
     | 
    
         
            +
                  C = ::Contracts
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  attr_reader :description, :provider_id
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  # ---------
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # Interface
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # ---------
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  # Constructor.
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # config - A Hash with configuration options
         
     | 
| 
      
 20 
     | 
    
         
            +
                  Contract Logger, C::HashOf[String => C::Any] => C::Any
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def initialize(logger, config)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @logger = logger
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @description = config["description"] || self.class.to_s
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @provider_id = config["provider_id"] || self.class.to_s.split("::").last
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @config = config
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  # Setup. This sets up the audit provider before any action takes place. This may be
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # declared in the child class.
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # Takes no arguments.
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # Returns nothing.
         
     | 
| 
      
 34 
     | 
    
         
            +
                  Contract C::None => nil
         
     | 
| 
      
 35 
     | 
    
         
            +
                  def setup
         
     | 
| 
      
 36 
     | 
    
         
            +
                    # :nocov:
         
     | 
| 
      
 37 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 38 
     | 
    
         
            +
                    # :nocov:
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  # Commit. This takes the entirety of group objects and actions and records them in
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # whatever methodology the audit provider uses.
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # actions            - Array of Entitlements::Models::Action (all requested actions)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # successful_actions - Array of Entitlements::Models::Action (successfully applied actions)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # provider_exception - Exception raised by a provider when applying (hopefully nil)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # Returns nothing.
         
     | 
| 
      
 49 
     | 
    
         
            +
                  Contract C::KeywordArgs[
         
     | 
| 
      
 50 
     | 
    
         
            +
                    actions: C::ArrayOf[Entitlements::Models::Action],
         
     | 
| 
      
 51 
     | 
    
         
            +
                    successful_actions: C::ArrayOf[Entitlements::Models::Action],
         
     | 
| 
      
 52 
     | 
    
         
            +
                    provider_exception: C::Or[nil, Exception]
         
     | 
| 
      
 53 
     | 
    
         
            +
                  ] => nil
         
     | 
| 
      
 54 
     | 
    
         
            +
                  def commit(actions:, successful_actions:, provider_exception:)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    # :nocov:
         
     | 
| 
      
 56 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 57 
     | 
    
         
            +
                    # :nocov:
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  # Set up a logger class that wraps incoming messages with the prefix and (if meaningful) the
         
     | 
| 
      
 61 
     | 
    
         
            +
                  # provider ID. Messages are then sent with the requested priority to the actual logger object.
         
     | 
| 
      
 62 
     | 
    
         
            +
                  class CustomLogger
         
     | 
| 
      
 63 
     | 
    
         
            +
                    def initialize(underlying_object, underlying_logger)
         
     | 
| 
      
 64 
     | 
    
         
            +
                      @underlying_object = underlying_object
         
     | 
| 
      
 65 
     | 
    
         
            +
                      @underlying_logger = underlying_logger
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    def prefix
         
     | 
| 
      
 69 
     | 
    
         
            +
                      @prefix ||= begin
         
     | 
| 
      
 70 
     | 
    
         
            +
                        if @underlying_object.provider_id == @underlying_object.class.to_s.split("::").last
         
     | 
| 
      
 71 
     | 
    
         
            +
                          @underlying_object.class.to_s
         
     | 
| 
      
 72 
     | 
    
         
            +
                        else
         
     | 
| 
      
 73 
     | 
    
         
            +
                          "#{@underlying_object.class}[#{@underlying_object.provider_id}]"
         
     | 
| 
      
 74 
     | 
    
         
            +
                        end
         
     | 
| 
      
 75 
     | 
    
         
            +
                      end
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                    def method_missing(m, *args, &block)
         
     | 
| 
      
 79 
     | 
    
         
            +
                      args[0] = "#{prefix}: #{args.first}"
         
     | 
| 
      
 80 
     | 
    
         
            +
                      @underlying_logger.send(m, *args, &block)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  private
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  attr_reader :config
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  # Intercept calls to logger to wrap through the custom class.
         
     | 
| 
      
 89 
     | 
    
         
            +
                  def logger
         
     | 
| 
      
 90 
     | 
    
         
            +
                    @logger_class ||= CustomLogger.new(self, @logger)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  # Raise a configuration error message.
         
     | 
| 
      
 94 
     | 
    
         
            +
                  #
         
     | 
| 
      
 95 
     | 
    
         
            +
                  # message - A String with the error message to be logged and raised.
         
     | 
| 
      
 96 
     | 
    
         
            +
                  #
         
     | 
| 
      
 97 
     | 
    
         
            +
                  # Returns nothing because it raises an error.
         
     | 
| 
      
 98 
     | 
    
         
            +
                  Contract String => C::Any
         
     | 
| 
      
 99 
     | 
    
         
            +
                  def configuration_error(message)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    provider = self.class.to_s.split("::").last
         
     | 
| 
      
 101 
     | 
    
         
            +
                    error_message = "Configuration error for provider=#{provider} id=#{provider_id}: #{message}"
         
     | 
| 
      
 102 
     | 
    
         
            +
                    logger.fatal "Configuration error: #{message}"
         
     | 
| 
      
 103 
     | 
    
         
            +
                    raise ArgumentError, error_message
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  # Require the the configuration contain certain keys (no validation is performed on the
         
     | 
| 
      
 107 
     | 
    
         
            +
                  # values - just make sure the key exists).
         
     | 
| 
      
 108 
     | 
    
         
            +
                  #
         
     | 
| 
      
 109 
     | 
    
         
            +
                  # required_keys - An Array of Strings with the required keys.
         
     | 
| 
      
 110 
     | 
    
         
            +
                  #
         
     | 
| 
      
 111 
     | 
    
         
            +
                  # Returns nothing.
         
     | 
| 
      
 112 
     | 
    
         
            +
                  Contract C::ArrayOf[String] => nil
         
     | 
| 
      
 113 
     | 
    
         
            +
                  def require_config_keys(required_keys)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    missing_keys = required_keys - config.keys
         
     | 
| 
      
 115 
     | 
    
         
            +
                    return unless missing_keys.any?
         
     | 
| 
      
 116 
     | 
    
         
            +
                    configuration_error "Not all required keys are defined. Missing: #{missing_keys.join(',')}."
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  # Convert a distinguished name (cn=something,ou=foo,dc=example,dc=net) to a file
         
     | 
| 
      
 120 
     | 
    
         
            +
                  # path (dc=net/dc=example/ou=foo/cn=something).
         
     | 
| 
      
 121 
     | 
    
         
            +
                  #
         
     | 
| 
      
 122 
     | 
    
         
            +
                  # dn - A String with the distinguished name.
         
     | 
| 
      
 123 
     | 
    
         
            +
                  #
         
     | 
| 
      
 124 
     | 
    
         
            +
                  # Returns a String with the path.
         
     | 
| 
      
 125 
     | 
    
         
            +
                  Contract String => String
         
     | 
| 
      
 126 
     | 
    
         
            +
                  def path_from_dn(dn)
         
     | 
| 
      
 127 
     | 
    
         
            +
                    File.join(dn.split(",").reverse)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  # Convert a path (dc=net/dc=example/ou=foo/cn=something) to a distinguished name
         
     | 
| 
      
 131 
     | 
    
         
            +
                  # (cn=something,ou=foo,dc=example,dc=net).
         
     | 
| 
      
 132 
     | 
    
         
            +
                  #
         
     | 
| 
      
 133 
     | 
    
         
            +
                  # path - A String with the path name.
         
     | 
| 
      
 134 
     | 
    
         
            +
                  #
         
     | 
| 
      
 135 
     | 
    
         
            +
                  # Returns a String with the distinguished name.
         
     | 
| 
      
 136 
     | 
    
         
            +
                  Contract String => String
         
     | 
| 
      
 137 
     | 
    
         
            +
                  def dn_from_path(path)
         
     | 
| 
      
 138 
     | 
    
         
            +
                    path.split("/").reject { |i| i.empty? }.reverse.join(",")
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                  # From a list of actions, return only the ones that have a net change in membership.
         
     | 
| 
      
 142 
     | 
    
         
            +
                  # In other words, filter out the ones where only metadata / description / etc. has changed.
         
     | 
| 
      
 143 
     | 
    
         
            +
                  #
         
     | 
| 
      
 144 
     | 
    
         
            +
                  # actions - Incoming array of Entitlements::Models::Action
         
     | 
| 
      
 145 
     | 
    
         
            +
                  #
         
     | 
| 
      
 146 
     | 
    
         
            +
                  # Returns an array of Entitlements::Models::Action
         
     | 
| 
      
 147 
     | 
    
         
            +
                  Contract C::ArrayOf[Entitlements::Models::Action] => C::ArrayOf[Entitlements::Models::Action]
         
     | 
| 
      
 148 
     | 
    
         
            +
                  def actions_with_membership_change(actions)
         
     | 
| 
      
 149 
     | 
    
         
            +
                    actions.select do |action|
         
     | 
| 
      
 150 
     | 
    
         
            +
                      if action.updated.is_a?(Entitlements::Models::Person) || action.existing == :none
         
     | 
| 
      
 151 
     | 
    
         
            +
                        # MemberOf or other modification to the person itself, not handled by this auditor
         
     | 
| 
      
 152 
     | 
    
         
            +
                        false
         
     | 
| 
      
 153 
     | 
    
         
            +
                      elsif action.updated.nil? || action.existing.nil?
         
     | 
| 
      
 154 
     | 
    
         
            +
                        # Add/remove group always triggers a commit
         
     | 
| 
      
 155 
     | 
    
         
            +
                        true
         
     | 
| 
      
 156 
     | 
    
         
            +
                      else
         
     | 
| 
      
 157 
     | 
    
         
            +
                        action.updated.member_strings_insensitive != action.existing.member_strings_insensitive
         
     | 
| 
      
 158 
     | 
    
         
            +
                      end
         
     | 
| 
      
 159 
     | 
    
         
            +
                    end
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
              end
         
     | 
| 
      
 163 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,171 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # To add a new backend, make its "Controller" class inherit from Entitlements::Backend::BaseController.
         
     | 
| 
      
 4 
     | 
    
         
            +
            # Consider using dummy/controller.rb as a template for a brand new class.
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # Needed to register backends
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative "../cli"
         
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative "../util/util"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            module Entitlements
         
     | 
| 
      
 11 
     | 
    
         
            +
              class Backend
         
     | 
| 
      
 12 
     | 
    
         
            +
                class BaseController
         
     | 
| 
      
 13 
     | 
    
         
            +
                  include ::Contracts::Core
         
     | 
| 
      
 14 
     | 
    
         
            +
                  C = ::Contracts
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  # Upon loading of the class itself, register the class in the list of available
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # backends that is tracked in the Entitlements class.
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def self.register
         
     | 
| 
      
 19 
     | 
    
         
            +
                    Entitlements.register_backend(identifier, self, priority)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  # Default priority is 10 - override by defining this method in the child class.
         
     | 
| 
      
 23 
     | 
    
         
            +
                  def self.priority
         
     | 
| 
      
 24 
     | 
    
         
            +
                    10
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  # :nocov:
         
     | 
| 
      
 28 
     | 
    
         
            +
                  def priority
         
     | 
| 
      
 29 
     | 
    
         
            +
                    self.class.priority
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # :nocov:
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  # Default identifier is the de-camelized name of the class - override by defining this method in the child class.
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def self.identifier
         
     | 
| 
      
 35 
     | 
    
         
            +
                    classname = self.to_s.split("::")[-2]
         
     | 
| 
      
 36 
     | 
    
         
            +
                    Entitlements::Util::Util.decamelize(classname)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  COMMON_GROUP_CONFIG = {
         
     | 
| 
      
 40 
     | 
    
         
            +
                    "allowed_methods" => { required: false, type: Array },
         
     | 
| 
      
 41 
     | 
    
         
            +
                    "allowed_types"   => { required: false, type: Array },
         
     | 
| 
      
 42 
     | 
    
         
            +
                    "dir"             => { required: false, type: String }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  }
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  # Constructor. Generic constructor that takes a hash of configuration options.
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # group_name - Name of the corresponding group in the entitlements configuration file.
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # config     - Optionally, a Hash of configuration information (configuration is referenced if empty).
         
     | 
| 
      
 49 
     | 
    
         
            +
                  Contract String, C::Maybe[C::HashOf[String => C::Any]] => C::Any
         
     | 
| 
      
 50 
     | 
    
         
            +
                  def initialize(group_name, config = nil)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @group_name = group_name
         
     | 
| 
      
 52 
     | 
    
         
            +
                    @config = config ? config.dup : Entitlements.config["groups"].fetch(group_name).dup
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @config.delete("type")
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @actions = []
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @logger = Entitlements.logger
         
     | 
| 
      
 56 
     | 
    
         
            +
                    validate_config!(@group_name, @config)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  attr_reader :actions
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  # Print difference array.
         
     | 
| 
      
 62 
     | 
    
         
            +
                  #
         
     | 
| 
      
 63 
     | 
    
         
            +
                  # key           - String with the key identifying the OU
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # added         - Array[Entitlements::Models::Action]
         
     | 
| 
      
 65 
     | 
    
         
            +
                  # removed       - Array[Entitlements::Models::Action]
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # changed       - Array[Entitlements::Models::Action]
         
     | 
| 
      
 67 
     | 
    
         
            +
                  # ignored_users - Optionally a Set of Strings with usernames to ignore
         
     | 
| 
      
 68 
     | 
    
         
            +
                  #
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # Returns nothing (this just prints to logger).
         
     | 
| 
      
 70 
     | 
    
         
            +
                  Contract C::KeywordArgs[
         
     | 
| 
      
 71 
     | 
    
         
            +
                    key: String,
         
     | 
| 
      
 72 
     | 
    
         
            +
                    added:   C::ArrayOf[Entitlements::Models::Action],
         
     | 
| 
      
 73 
     | 
    
         
            +
                    removed: C::ArrayOf[Entitlements::Models::Action],
         
     | 
| 
      
 74 
     | 
    
         
            +
                    changed: C::ArrayOf[Entitlements::Models::Action],
         
     | 
| 
      
 75 
     | 
    
         
            +
                    ignored_users: C::Maybe[C::SetOf[String]]
         
     | 
| 
      
 76 
     | 
    
         
            +
                  ] => C::Any
         
     | 
| 
      
 77 
     | 
    
         
            +
                  def print_differences(key:, added:, removed:, changed:, ignored_users: Set.new)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    added_array   = added.map   { |i| [i.dn, :added,   i] }
         
     | 
| 
      
 79 
     | 
    
         
            +
                    removed_array = removed.map { |i| [i.dn, :removed, i] }
         
     | 
| 
      
 80 
     | 
    
         
            +
                    changed_array = changed.map { |i| [i.dn, :changed, i] }
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    combined = (added_array + removed_array + changed_array).sort_by { |i| i.first.to_s.downcase }
         
     | 
| 
      
 83 
     | 
    
         
            +
                    combined.each do |entry|
         
     | 
| 
      
 84 
     | 
    
         
            +
                      identifier = entry[0]
         
     | 
| 
      
 85 
     | 
    
         
            +
                      changetype = entry[1]
         
     | 
| 
      
 86 
     | 
    
         
            +
                      obj        = entry[2]
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                      if changetype == :added
         
     | 
| 
      
 89 
     | 
    
         
            +
                        members = obj.updated.member_strings.map { |i| i =~ /\Auid=(.+?),/ ? Regexp.last_match(1) : i }
         
     | 
| 
      
 90 
     | 
    
         
            +
                        Entitlements.logger.info "ADD #{identifier} to #{key} (Members: #{members.sort.join(',')})"
         
     | 
| 
      
 91 
     | 
    
         
            +
                      elsif changetype == :removed
         
     | 
| 
      
 92 
     | 
    
         
            +
                        Entitlements.logger.info "DELETE #{identifier} from #{key}"
         
     | 
| 
      
 93 
     | 
    
         
            +
                      else
         
     | 
| 
      
 94 
     | 
    
         
            +
                        ignored_users.merge obj.ignored_users
         
     | 
| 
      
 95 
     | 
    
         
            +
                        existing_members = obj.existing.member_strings
         
     | 
| 
      
 96 
     | 
    
         
            +
                        Entitlements::Util::Util.remove_uids(existing_members, ignored_users)
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                        proposed_members = obj.updated.member_strings
         
     | 
| 
      
 99 
     | 
    
         
            +
                        Entitlements::Util::Util.remove_uids(proposed_members, ignored_users)
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                        added_to_group = (proposed_members - existing_members).map { |i| [i, "+"] }
         
     | 
| 
      
 102 
     | 
    
         
            +
                        removed_from_group = (existing_members - proposed_members).map { |i| [i, "-"] }
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                        # Filter out case-only differences. For example if "bob" is in existing and "BOB" is in proposed,
         
     | 
| 
      
 105 
     | 
    
         
            +
                        # we don't want to show this as a difference.
         
     | 
| 
      
 106 
     | 
    
         
            +
                        downcase_proposed_members = proposed_members.map { |m| m.downcase }
         
     | 
| 
      
 107 
     | 
    
         
            +
                        downcase_existing_members = existing_members.map { |m| m.downcase }
         
     | 
| 
      
 108 
     | 
    
         
            +
                        duplicated = downcase_proposed_members & downcase_existing_members
         
     | 
| 
      
 109 
     | 
    
         
            +
                        added_to_group.reject! { |m| duplicated.include?(m.first.downcase) }
         
     | 
| 
      
 110 
     | 
    
         
            +
                        removed_from_group.reject! { |m| duplicated.include?(m.first.downcase) }
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                        # What's left is actual changes.
         
     | 
| 
      
 113 
     | 
    
         
            +
                        combined_group = (added_to_group + removed_from_group).sort_by { |i| i.first.downcase }
         
     | 
| 
      
 114 
     | 
    
         
            +
                        if combined_group.any?
         
     | 
| 
      
 115 
     | 
    
         
            +
                          Entitlements.logger.info "CHANGE #{identifier} in #{key}"
         
     | 
| 
      
 116 
     | 
    
         
            +
                          combined_group.each do |item, item_changetype|
         
     | 
| 
      
 117 
     | 
    
         
            +
                            Entitlements.logger.info ".  #{item_changetype} #{item}"
         
     | 
| 
      
 118 
     | 
    
         
            +
                          end
         
     | 
| 
      
 119 
     | 
    
         
            +
                        end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                        if obj.existing.description != obj.updated.description && obj.ou_type == "ldap"
         
     | 
| 
      
 122 
     | 
    
         
            +
                          Entitlements.logger.info "METADATA CHANGE #{identifier} in #{key}"
         
     | 
| 
      
 123 
     | 
    
         
            +
                          Entitlements.logger.info "- Old description: #{obj.existing.description.inspect}"
         
     | 
| 
      
 124 
     | 
    
         
            +
                          Entitlements.logger.info "+ New description: #{obj.updated.description.inspect}"
         
     | 
| 
      
 125 
     | 
    
         
            +
                        end
         
     | 
| 
      
 126 
     | 
    
         
            +
                      end
         
     | 
| 
      
 127 
     | 
    
         
            +
                    end
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  # Get count of changes.
         
     | 
| 
      
 131 
     | 
    
         
            +
                  #
         
     | 
| 
      
 132 
     | 
    
         
            +
                  # Takes no arguments.
         
     | 
| 
      
 133 
     | 
    
         
            +
                  #
         
     | 
| 
      
 134 
     | 
    
         
            +
                  # Returns an Integer.
         
     | 
| 
      
 135 
     | 
    
         
            +
                  Contract C::None => Integer
         
     | 
| 
      
 136 
     | 
    
         
            +
                  def change_count
         
     | 
| 
      
 137 
     | 
    
         
            +
                    actions.size
         
     | 
| 
      
 138 
     | 
    
         
            +
                  end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                  # Stub methods
         
     | 
| 
      
 141 
     | 
    
         
            +
                  # :nocov:
         
     | 
| 
      
 142 
     | 
    
         
            +
                  def prefetch
         
     | 
| 
      
 143 
     | 
    
         
            +
                    # Can be left undefined
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                  def validate
         
     | 
| 
      
 147 
     | 
    
         
            +
                    # Can be left undefined
         
     | 
| 
      
 148 
     | 
    
         
            +
                  end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                  def calculate
         
     | 
| 
      
 151 
     | 
    
         
            +
                    raise "Must be defined in child class"
         
     | 
| 
      
 152 
     | 
    
         
            +
                  end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                  def preapply
         
     | 
| 
      
 155 
     | 
    
         
            +
                    # Can be left undefined
         
     | 
| 
      
 156 
     | 
    
         
            +
                  end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                  def apply(action)
         
     | 
| 
      
 159 
     | 
    
         
            +
                    raise "Must be defined in child class"
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                  def validate_config!(key, data)
         
     | 
| 
      
 163 
     | 
    
         
            +
                    # Can be left undefined (but really shouldn't)
         
     | 
| 
      
 164 
     | 
    
         
            +
                  end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                  private
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                  attr_reader :config, :group_name, :logger
         
     | 
| 
      
 169 
     | 
    
         
            +
                end
         
     | 
| 
      
 170 
     | 
    
         
            +
              end
         
     | 
| 
      
 171 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "set"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Entitlements
         
     | 
| 
      
 6 
     | 
    
         
            +
              class Backend
         
     | 
| 
      
 7 
     | 
    
         
            +
                class BaseProvider
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include ::Contracts::Core
         
     | 
| 
      
 9 
     | 
    
         
            +
                  C = ::Contracts
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  # Dry run of committing changes. Returns a list of users added or removed.
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # Takes a group; looks up that same group in the appropriate backend.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # group         - An Entitlements::Models::Group object.
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # ignored_users - Optionally, a Set of lower-case Strings of users to ignore.
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # Returns added / removed hash.
         
     | 
| 
      
 18 
     | 
    
         
            +
                  Contract Entitlements::Models::Group, C::Maybe[C::SetOf[String]] => Hash[added: C::SetOf[String], removed: C::SetOf[String]]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  def diff(group, ignored_users = Set.new)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    existing_group = read(group.cn.downcase)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    return diff_existing_updated(existing_group, group, ignored_users)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # Dry run of committing changes. Returns a list of users added or removed.
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # Takes an existing and an updated group object, avoiding a lookup in the backend.
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # existing_group - An Entitlements::Models::Group object.
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # group          - An Entitlements::Models::Group object.
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # ignored_users  - Optionally, a Set of lower-case Strings of users to ignore.
         
     | 
| 
      
 30 
     | 
    
         
            +
                  Contract Entitlements::Models::Group, Entitlements::Models::Group, C::Maybe[C::SetOf[String]] => Hash[added: C::SetOf[String], removed: C::SetOf[String]]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def diff_existing_updated(existing_group, group, ignored_users = Set.new)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    # The comparison needs to be done case-insensitive because some backends (e.g. GitHub organizations or teams)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    # may report members with different capitalization than is used in Entitlements. Keep track of correct capitalization
         
     | 
| 
      
 34 
     | 
    
         
            +
                    # of member names here so they can be applied later. Note that `group` (from Entitlements) overrides `existing_group`
         
     | 
| 
      
 35 
     | 
    
         
            +
                    # (from the backend).
         
     | 
| 
      
 36 
     | 
    
         
            +
                    member_with_correct_capitalization = existing_group.member_strings.map { |ms| [ms.downcase, ms] }.to_h
         
     | 
| 
      
 37 
     | 
    
         
            +
                    member_with_correct_capitalization.merge! group.member_strings.map { |ms| [ms.downcase, ms] }.to_h
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    existing_members = existing_group.member_strings.map { |u| u.downcase }
         
     | 
| 
      
 40 
     | 
    
         
            +
                    Entitlements::Util::Util.remove_uids(existing_members, ignored_users)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    proposed_members = group.member_strings.map { |u| u.downcase }
         
     | 
| 
      
 43 
     | 
    
         
            +
                    Entitlements::Util::Util.remove_uids(proposed_members, ignored_users)
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    added_members = proposed_members - existing_members
         
     | 
| 
      
 46 
     | 
    
         
            +
                    removed_members = existing_members - proposed_members
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    {
         
     | 
| 
      
 49 
     | 
    
         
            +
                      added: Set.new(added_members.map { |ms| member_with_correct_capitalization[ms] }),
         
     | 
| 
      
 50 
     | 
    
         
            +
                      removed: Set.new(removed_members.map { |ms| member_with_correct_capitalization[ms] })
         
     | 
| 
      
 51 
     | 
    
         
            +
                    }
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,89 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Entitlements
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Backend
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Dummy
         
     | 
| 
      
 6 
     | 
    
         
            +
                  class Controller < Entitlements::Backend::BaseController
         
     | 
| 
      
 7 
     | 
    
         
            +
                    register
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    # :nocov:
         
     | 
| 
      
 10 
     | 
    
         
            +
                    include ::Contracts::Core
         
     | 
| 
      
 11 
     | 
    
         
            +
                    C = ::Contracts
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    # Pre-fetch the existing group membership in each OU.
         
     | 
| 
      
 14 
     | 
    
         
            +
                    #
         
     | 
| 
      
 15 
     | 
    
         
            +
                    # Takes no arguments.
         
     | 
| 
      
 16 
     | 
    
         
            +
                    #
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # Returns nothing. (Populates cache.)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    Contract C::None => C::Any
         
     | 
| 
      
 19 
     | 
    
         
            +
                    def prefetch
         
     | 
| 
      
 20 
     | 
    
         
            +
                      # This does nothing.
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    # Validation routines.
         
     | 
| 
      
 24 
     | 
    
         
            +
                    #
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # Takes no arguments.
         
     | 
| 
      
 26 
     | 
    
         
            +
                    #
         
     | 
| 
      
 27 
     | 
    
         
            +
                    # Returns nothing. (Populates cache.)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    Contract C::None => C::Any
         
     | 
| 
      
 29 
     | 
    
         
            +
                    def validate
         
     | 
| 
      
 30 
     | 
    
         
            +
                      # This does nothing.
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    # Get count of changes.
         
     | 
| 
      
 34 
     | 
    
         
            +
                    #
         
     | 
| 
      
 35 
     | 
    
         
            +
                    # Takes no arguments.
         
     | 
| 
      
 36 
     | 
    
         
            +
                    #
         
     | 
| 
      
 37 
     | 
    
         
            +
                    # Returns an Integer.
         
     | 
| 
      
 38 
     | 
    
         
            +
                    Contract C::None => Integer
         
     | 
| 
      
 39 
     | 
    
         
            +
                    def change_count
         
     | 
| 
      
 40 
     | 
    
         
            +
                      super
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    # Calculation routines.
         
     | 
| 
      
 44 
     | 
    
         
            +
                    #
         
     | 
| 
      
 45 
     | 
    
         
            +
                    # Takes no arguments.
         
     | 
| 
      
 46 
     | 
    
         
            +
                    #
         
     | 
| 
      
 47 
     | 
    
         
            +
                    # Returns nothing (populates @actions).
         
     | 
| 
      
 48 
     | 
    
         
            +
                    Contract C::None => C::Any
         
     | 
| 
      
 49 
     | 
    
         
            +
                    def calculate
         
     | 
| 
      
 50 
     | 
    
         
            +
                      # No point in calculating anything. Any references herein will be calculated automatically.
         
     | 
| 
      
 51 
     | 
    
         
            +
                      @actions = []
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    # Pre-apply routines.
         
     | 
| 
      
 55 
     | 
    
         
            +
                    #
         
     | 
| 
      
 56 
     | 
    
         
            +
                    # Takes no arguments.
         
     | 
| 
      
 57 
     | 
    
         
            +
                    #
         
     | 
| 
      
 58 
     | 
    
         
            +
                    # Returns nothing.
         
     | 
| 
      
 59 
     | 
    
         
            +
                    Contract C::None => C::Any
         
     | 
| 
      
 60 
     | 
    
         
            +
                    def preapply
         
     | 
| 
      
 61 
     | 
    
         
            +
                      # This does nothing.
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    # Apply changes.
         
     | 
| 
      
 65 
     | 
    
         
            +
                    #
         
     | 
| 
      
 66 
     | 
    
         
            +
                    # action - Action array.
         
     | 
| 
      
 67 
     | 
    
         
            +
                    #
         
     | 
| 
      
 68 
     | 
    
         
            +
                    # Returns nothing.
         
     | 
| 
      
 69 
     | 
    
         
            +
                    Contract Entitlements::Models::Action => C::Any
         
     | 
| 
      
 70 
     | 
    
         
            +
                    def apply(caction)
         
     | 
| 
      
 71 
     | 
    
         
            +
                      # This does nothing.
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    # Validate configuration options.
         
     | 
| 
      
 75 
     | 
    
         
            +
                    #
         
     | 
| 
      
 76 
     | 
    
         
            +
                    # key  - String with the name of the group.
         
     | 
| 
      
 77 
     | 
    
         
            +
                    # data - Hash with the configuration data.
         
     | 
| 
      
 78 
     | 
    
         
            +
                    #
         
     | 
| 
      
 79 
     | 
    
         
            +
                    # Returns nothing.
         
     | 
| 
      
 80 
     | 
    
         
            +
                    Contract String, C::HashOf[String => C::Any] => nil
         
     | 
| 
      
 81 
     | 
    
         
            +
                    def validate_config!(key, data)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      # Do nothing to validate. Pass whatever arguments you want, and this will just ignore them!
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    # :nocov:
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
              end
         
     | 
| 
      
 89 
     | 
    
         
            +
            end
         
     |