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
@@ -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
|