entitlements-app 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|