entitlements 0.1.7
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
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Filter class to remove members of a particular Entitlements-managed group.
|
4
|
+
|
5
|
+
module Entitlements
|
6
|
+
class Data
|
7
|
+
class Groups
|
8
|
+
class Calculated
|
9
|
+
class Filters
|
10
|
+
class MemberOfGroup < Entitlements::Data::Groups::Calculated::Filters::Base
|
11
|
+
include ::Contracts::Core
|
12
|
+
C = ::Contracts
|
13
|
+
|
14
|
+
# Determine if the member is filtered as per this definition. Return true if the member
|
15
|
+
# is to be filtered out, false if the member does not match the filter.
|
16
|
+
#
|
17
|
+
# member - Entitlements::Models::Person object
|
18
|
+
#
|
19
|
+
# Returns true if the person is to be filtered out, false otherwise.
|
20
|
+
Contract Entitlements::Models::Person => C::Bool
|
21
|
+
def filtered?(member)
|
22
|
+
return false if filter == :all
|
23
|
+
return false unless member_of_named_group?(member, config.fetch("group"))
|
24
|
+
return true if filter == :none
|
25
|
+
!member_of_filter?(member)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Entitlements
|
4
|
+
class Data
|
5
|
+
class Groups
|
6
|
+
class Calculated
|
7
|
+
class Modifiers
|
8
|
+
class Base
|
9
|
+
include ::Contracts::Core
|
10
|
+
C = ::Contracts
|
11
|
+
|
12
|
+
# Constructor. Needs the cache (Hash with various objects of interest) for
|
13
|
+
# future lookups.
|
14
|
+
#
|
15
|
+
# rs - Entitlements::Data::Groups::Calculated::* object
|
16
|
+
# config - Configuration for this modifier as defined in entitlement
|
17
|
+
Contract C::KeywordArgs[
|
18
|
+
rs: C::Or[
|
19
|
+
Entitlements::Data::Groups::Calculated::Ruby,
|
20
|
+
Entitlements::Data::Groups::Calculated::Text,
|
21
|
+
Entitlements::Data::Groups::Calculated::YAML,
|
22
|
+
],
|
23
|
+
config: C::Any
|
24
|
+
] => C::Any
|
25
|
+
def initialize(rs:, config: nil)
|
26
|
+
@rs = rs
|
27
|
+
@config = config
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :config, :rs
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require_relative "../../../../util/util"
|
5
|
+
|
6
|
+
module Entitlements
|
7
|
+
class Data
|
8
|
+
class Groups
|
9
|
+
class Calculated
|
10
|
+
class Modifiers
|
11
|
+
class Expiration < Base
|
12
|
+
include ::Contracts::Core
|
13
|
+
C = ::Contracts
|
14
|
+
|
15
|
+
# Given a set of members in a group that is being calculated, modify the
|
16
|
+
# member set (the input) to be empty if the entitlement as a whole is expired.
|
17
|
+
# If we do this, add the metadata that allows an empty group, assuming there
|
18
|
+
# were in fact members in the group the first time we saw this.
|
19
|
+
#
|
20
|
+
# result - Set of Entitlements::Models::Person (mutated).
|
21
|
+
#
|
22
|
+
# Return true if we made any changes, false otherwise.
|
23
|
+
Contract C::SetOf[Entitlements::Models::Person] => C::Bool
|
24
|
+
def modify(result)
|
25
|
+
# If group is already empty, we have nothing to consider modifying, regardless
|
26
|
+
# of expiration date. Just return false right away.
|
27
|
+
if result.empty?
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
# If the date is in the future, leave the entitlement unchanged.
|
32
|
+
return false if parse_date > Time.now.utc.to_date
|
33
|
+
|
34
|
+
# Empty the group. Set metadata allowing no members. Return true to indicate modification.
|
35
|
+
rs.metadata["no_members_ok"] = true
|
36
|
+
result.clear
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Returns a date object from the configuration given to the class.
|
43
|
+
#
|
44
|
+
# Takes no arguments.
|
45
|
+
#
|
46
|
+
# Returns a date object.
|
47
|
+
Contract C::None => Date
|
48
|
+
def parse_date
|
49
|
+
Entitlements::Util::Util.parse_date(config)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Interact with rules that are stored as ruby code.
|
3
|
+
|
4
|
+
module Entitlements
|
5
|
+
class Data
|
6
|
+
class Groups
|
7
|
+
class Calculated
|
8
|
+
class Ruby < Entitlements::Data::Groups::Calculated::Base
|
9
|
+
include ::Contracts::Core
|
10
|
+
C = ::Contracts
|
11
|
+
|
12
|
+
# Standard interface: Calculate the members of this group.
|
13
|
+
#
|
14
|
+
# Takes no arguments.
|
15
|
+
#
|
16
|
+
# Returns a Set[Entitlements::Models::Person] with DN's of the people in the group.
|
17
|
+
Contract C::None => C::SetOf[Entitlements::Models::Person]
|
18
|
+
def members
|
19
|
+
@members ||= begin
|
20
|
+
Entitlements.logger.debug "Calculating members from #{filename}"
|
21
|
+
result = rule_obj.members
|
22
|
+
|
23
|
+
# Since this is user-written code not subject to contracts, do some basic
|
24
|
+
# format checking of the result, and standardize the output.
|
25
|
+
unless result.is_a?(Set)
|
26
|
+
raise "Expected Set[String|Entitlements::Models::Person] from #{ruby_class_name}.members, got #{result.class}!"
|
27
|
+
end
|
28
|
+
|
29
|
+
cleaned_set = result.map do |item|
|
30
|
+
if item.is_a?(String)
|
31
|
+
begin
|
32
|
+
Entitlements.cache[:people_obj].read.fetch(item)
|
33
|
+
rescue KeyError => exc
|
34
|
+
raise_rule_exception(exc)
|
35
|
+
end
|
36
|
+
elsif item.is_a?(Entitlements::Models::Person)
|
37
|
+
item
|
38
|
+
else
|
39
|
+
raise "In #{ruby_class_name}.members, expected String or Person but got #{item.inspect}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# All good, return the result.
|
44
|
+
Set.new(cleaned_set)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Standard interface: Get the description of this group.
|
49
|
+
#
|
50
|
+
# Takes no arguments.
|
51
|
+
#
|
52
|
+
# Returns a String with the group description, or "" if undefined.
|
53
|
+
Contract C::None => String
|
54
|
+
def description
|
55
|
+
result = rule_obj.description
|
56
|
+
return result if result.is_a?(String)
|
57
|
+
raise "Expected String from #{ruby_class_name}.description, got #{result.class}!"
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Get a hash of the filters defined in the group.
|
63
|
+
#
|
64
|
+
# Takes no arguments.
|
65
|
+
#
|
66
|
+
# Returns a Hash[String => :all/:none/List of strings].
|
67
|
+
Contract C::None => C::HashOf[String => C::Or[:all, :none, C::ArrayOf[String]]]
|
68
|
+
def initialize_filters
|
69
|
+
rule_obj.filters
|
70
|
+
end
|
71
|
+
|
72
|
+
# Files can support metadata intended for consumption by things other than LDAP.
|
73
|
+
# This returns the metadata from the file as a hash.
|
74
|
+
#
|
75
|
+
# Takes no arguments.
|
76
|
+
#
|
77
|
+
# Returns Hash[<String>key => <Object>value]
|
78
|
+
Contract C::None => C::HashOf[String => C::Any]
|
79
|
+
def initialize_metadata
|
80
|
+
return {} unless rule_obj.respond_to?(:metadata)
|
81
|
+
|
82
|
+
result = rule_obj.metadata
|
83
|
+
|
84
|
+
unless result.is_a?(Hash)
|
85
|
+
raise ArgumentError, "For metadata in #{filename}: expected Hash, got #{result.inspect}!"
|
86
|
+
end
|
87
|
+
|
88
|
+
result.each do |key, _|
|
89
|
+
next if key.is_a?(String)
|
90
|
+
raise ArgumentError, "For metadata in #{filename}: keys are expected to be strings, but #{key.inspect} is not!"
|
91
|
+
end
|
92
|
+
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
# Instantiate the object exactly once, on demand. Cache it for later.
|
97
|
+
#
|
98
|
+
# Takes no arguments.
|
99
|
+
#
|
100
|
+
# Returns an object.
|
101
|
+
Contract C::None => Object
|
102
|
+
def rule_obj
|
103
|
+
@rule_obj ||= begin
|
104
|
+
require filename
|
105
|
+
clazz = Kernel.const_get(ruby_class_name)
|
106
|
+
clazz.new
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Raise an exception which adds in the offending class name and filename (which
|
111
|
+
# is a user-defined entitlement that gave rise to a problem).
|
112
|
+
#
|
113
|
+
# exc - An Exception that is to be raised.
|
114
|
+
#
|
115
|
+
# Returns nothing (raises the exception after logging).
|
116
|
+
Contract Exception => nil
|
117
|
+
def raise_rule_exception(exc)
|
118
|
+
Entitlements.logger.fatal "#{exc.class} when processing #{ruby_class_name}!"
|
119
|
+
raise exc
|
120
|
+
end
|
121
|
+
|
122
|
+
# Turn the filename into a ruby class name. We care about the name of the last
|
123
|
+
# directory, and the name of the file itself. Convert these into CamelCase for
|
124
|
+
# the ruby class.
|
125
|
+
#
|
126
|
+
# Takes no arguments.
|
127
|
+
#
|
128
|
+
# Returns a String with the class name.
|
129
|
+
Contract C::None => String
|
130
|
+
def ruby_class_name
|
131
|
+
["Entitlements", "Rule", ou, cn].map { |x| camelize(x) }.join("::")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Base class for rules that we write.
|
3
|
+
|
4
|
+
module Entitlements
|
5
|
+
class Data
|
6
|
+
class Groups
|
7
|
+
class Calculated
|
8
|
+
class Rules
|
9
|
+
class Base
|
10
|
+
include ::Contracts::Core
|
11
|
+
C = ::Contracts
|
12
|
+
|
13
|
+
# Interface method: Get a Set[Entitlements::Models::Person] matching this condition.
|
14
|
+
#
|
15
|
+
# value - The value to match.
|
16
|
+
# filename - Name of the file resulting in this rule being called
|
17
|
+
# options - Optional hash of additional method-specific options
|
18
|
+
#
|
19
|
+
# Returns a Set[Entitlements::Models::Person].
|
20
|
+
Contract C::KeywordArgs[
|
21
|
+
value: String,
|
22
|
+
filename: C::Maybe[String],
|
23
|
+
options: C::Optional[C::HashOf[Symbol => C::Any]]
|
24
|
+
] => C::SetOf[Entitlements::Models::Person]
|
25
|
+
def self.matches(value:, filename: nil, options: {})
|
26
|
+
# :nocov:
|
27
|
+
raise "matches() must be defined in the child class #{self.class}!"
|
28
|
+
# :nocov:
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Is someone in an LDAP group?
|
3
|
+
|
4
|
+
module Entitlements
|
5
|
+
class Data
|
6
|
+
class Groups
|
7
|
+
class Calculated
|
8
|
+
class Rules
|
9
|
+
class Group < Entitlements::Data::Groups::Calculated::Rules::Base
|
10
|
+
|
11
|
+
FILE_EXTENSIONS = {
|
12
|
+
"rb" => "Entitlements::Data::Groups::Calculated::Ruby",
|
13
|
+
"txt" => "Entitlements::Data::Groups::Calculated::Text",
|
14
|
+
"yaml" => "Entitlements::Data::Groups::Calculated::YAML"
|
15
|
+
}
|
16
|
+
|
17
|
+
# Interface method: Get a Set[Entitlements::Models::Person] matching this condition.
|
18
|
+
#
|
19
|
+
# value - The value to match.
|
20
|
+
# filename - Name of the file resulting in this rule being called
|
21
|
+
# options - Optional hash of additional method-specific options
|
22
|
+
#
|
23
|
+
# Returns a Set[Entitlements::Models::Person].
|
24
|
+
Contract C::KeywordArgs[
|
25
|
+
value: String,
|
26
|
+
filename: C::Maybe[String],
|
27
|
+
options: C::Optional[C::HashOf[Symbol => C::Any]]
|
28
|
+
] => C::SetOf[Entitlements::Models::Person]
|
29
|
+
def self.matches(value:, filename: nil, options: {})
|
30
|
+
# We've asked for a managed group, so we need to calculate that group and return its members.
|
31
|
+
# First parse the value into the ou and cn.
|
32
|
+
raise "Error: Unexpected value #{value.inspect} in #{self.class}!" unless value =~ %r{\A(.+)/([^/]+)\z}
|
33
|
+
ou = value.rpartition("/").first
|
34
|
+
cn = value.rpartition("/").last
|
35
|
+
|
36
|
+
# Check the cache. If we are in the process of calculating it, it's a circular dependency that should be alerted upon.
|
37
|
+
# If we've already calculated this, just return the value.
|
38
|
+
current_value = Entitlements.cache[:calculated].fetch(ou, {}).fetch(cn, nil)
|
39
|
+
if current_value == :calculating
|
40
|
+
Entitlements.cache[:dependencies] << "#{ou}/#{cn}"
|
41
|
+
raise "Error: Circular dependency #{Entitlements.cache[:dependencies].join(' -> ')}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# If we have calculated this before, then apply modifiers and return the result. `current_value` here
|
45
|
+
# is a set with the correct answers but that does not take into effect any modifiers. Therefore we
|
46
|
+
# reference back to the object so we can have the modifiers applied. There's a cache in the object that
|
47
|
+
# remembers the value of `.modified_members` each time it's calculated, so this is inexpensive.
|
48
|
+
filebase_with_path = File.join(Entitlements::Util::Util.path_for_group(ou), cn)
|
49
|
+
if Entitlements.cache[:file_objects].key?(filebase_with_path)
|
50
|
+
return Entitlements.cache[:file_objects][filebase_with_path].modified_members
|
51
|
+
end
|
52
|
+
|
53
|
+
# We actually need to calculate this group. Find the file based on the ou and cn in the directory.
|
54
|
+
files = files_for(ou, options: options)
|
55
|
+
match_regex = Regexp.new("\\A" + Regexp.escape(cn.gsub("*", "\f")).gsub("\\f", ".*") + "\\z")
|
56
|
+
matching_files = files.select { |base, _ext| match_regex.match(base) }
|
57
|
+
if matching_files.any?
|
58
|
+
result = Set.new
|
59
|
+
matching_files.each do |filebase, ext|
|
60
|
+
filebase_with_path = File.join(Entitlements::Util::Util.path_for_group(ou), filebase)
|
61
|
+
|
62
|
+
# If the object has already been calculated then we can just merge the value from
|
63
|
+
# the cache without going any further. Otherwise, create a new object for the group
|
64
|
+
# reference and calculate them.
|
65
|
+
unless Entitlements.cache[:file_objects][filebase_with_path]
|
66
|
+
clazz = Kernel.const_get(FILE_EXTENSIONS[ext])
|
67
|
+
Entitlements.cache[:file_objects][filebase_with_path] = clazz.new(
|
68
|
+
filename: "#{filebase_with_path}.#{ext}",
|
69
|
+
)
|
70
|
+
if Entitlements.cache[:file_objects][filebase_with_path].members == :calculating
|
71
|
+
next if matching_files.size > 1
|
72
|
+
raise "Error: Invalid self-referencing wildcard in #{ou}/#{filebase}.#{ext}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
unless Entitlements.cache[:file_objects][filebase_with_path].modified_members == :calculating
|
77
|
+
result.merge Entitlements.cache[:file_objects][filebase_with_path].modified_members
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return result
|
81
|
+
end
|
82
|
+
|
83
|
+
# No file exists... handle accordingly
|
84
|
+
path = File.join(Entitlements::Util::Util.path_for_group(ou), "#{cn}.(rb|txt|yaml)")
|
85
|
+
if options[:skip_broken_references]
|
86
|
+
Entitlements.logger.warn "Could not find a configuration for #{path} - skipped (filename: #{filename.inspect})"
|
87
|
+
return Set.new
|
88
|
+
end
|
89
|
+
|
90
|
+
Entitlements.logger.fatal "Error: Could not find a configuration for #{path} (filename: #{filename.inspect})"
|
91
|
+
raise "Error: Could not find a configuration for #{path} (filename: #{filename.inspect})"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Enumerate and cache all files in a directory for more efficient processing later.
|
95
|
+
#
|
96
|
+
# path - A String with the directory structure relative to Entitlements.config_path
|
97
|
+
#
|
98
|
+
# Returns a Set of Hashes with { "file_without_extension" => "extension" }
|
99
|
+
Contract String, C::KeywordArgs[options: C::HashOf[Symbol => C::Any]] => C::HashOf[String => String]
|
100
|
+
def self.files_for(path, options:)
|
101
|
+
@files_for_cache ||= {}
|
102
|
+
@files_for_cache[path] ||= begin
|
103
|
+
full_path = Entitlements::Util::Util.path_for_group(path)
|
104
|
+
if File.directory?(full_path)
|
105
|
+
result = {}
|
106
|
+
Dir.entries(full_path).each do |name|
|
107
|
+
next if name.start_with?(".")
|
108
|
+
next unless name.end_with?(*FILE_EXTENSIONS.keys.map { |k| ".#{k}" })
|
109
|
+
next unless File.file?(File.join(full_path, name))
|
110
|
+
raise "Unparseable name: #{full_path}/#{name}" unless name =~ /\A(.+)\.(\w+)\z/
|
111
|
+
result[Regexp.last_match(1)] = Regexp.last_match(2)
|
112
|
+
end
|
113
|
+
result
|
114
|
+
elsif options[:skip_broken_references]
|
115
|
+
Entitlements.logger.warn "Could not find any configuration in #{full_path} - skipped"
|
116
|
+
{}
|
117
|
+
else
|
118
|
+
message = "Error: Could not find any configuration in #{full_path}"
|
119
|
+
Entitlements.logger.fatal message
|
120
|
+
raise RuntimeError, message
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# The simplest equality check we could imagine.
|
3
|
+
|
4
|
+
module Entitlements
|
5
|
+
class Data
|
6
|
+
class Groups
|
7
|
+
class Calculated
|
8
|
+
class Rules
|
9
|
+
class Username < Entitlements::Data::Groups::Calculated::Rules::Base
|
10
|
+
include ::Contracts::Core
|
11
|
+
C = ::Contracts
|
12
|
+
|
13
|
+
# Interface method: Get a Set[Entitlements::Models::Person] matching this condition.
|
14
|
+
#
|
15
|
+
# value - The value to match.
|
16
|
+
# filename - Name of the file resulting in this rule being called
|
17
|
+
# options - Optional hash of additional method-specific options
|
18
|
+
#
|
19
|
+
# Returns a Set[Entitlements::Models::Person].
|
20
|
+
Contract C::KeywordArgs[
|
21
|
+
value: String,
|
22
|
+
filename: C::Maybe[String],
|
23
|
+
options: C::Optional[C::HashOf[Symbol => C::Any]]
|
24
|
+
] => C::SetOf[Entitlements::Models::Person]
|
25
|
+
def self.matches(value:, filename: nil, options: {})
|
26
|
+
# Username is easy - the value is the uid.
|
27
|
+
begin
|
28
|
+
Set.new([Entitlements.cache[:people_obj].read(value)].compact)
|
29
|
+
rescue Entitlements::Data::People::NoSuchPersonError
|
30
|
+
# We are not currently treating this as a fatal error and as such we are just
|
31
|
+
# ignoring it. Implementors will want to implement CI-level checks for unknown
|
32
|
+
# people if this is a concern.
|
33
|
+
Set.new({})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|