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,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Entitlements
|
4
|
+
class Models
|
5
|
+
class Person
|
6
|
+
include ::Contracts::Core
|
7
|
+
C = ::Contracts
|
8
|
+
|
9
|
+
attr_reader :uid
|
10
|
+
|
11
|
+
# Constructor.
|
12
|
+
#
|
13
|
+
# uid - A String with user's ID (unique throughout Entitlements)
|
14
|
+
# attributes - Optionally a Hash of {String => String | Array<String>} with additional attributes
|
15
|
+
Contract C::KeywordArgs[
|
16
|
+
uid: String,
|
17
|
+
attributes: C::Maybe[C::HashOf[String => C::Or[String, C::ArrayOf[String], nil]]]
|
18
|
+
] => C::Any
|
19
|
+
def initialize(uid:, attributes: {})
|
20
|
+
@uid = uid
|
21
|
+
setup_attributes(attributes)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Grab any methods that have been defined by extras and dispatch them to the appropriate backend.
|
25
|
+
# The first argument sent to the method is a reference to this object (self). Any arguments passed
|
26
|
+
# by the caller are sent thereafter. For now this ignores blocks (maybe figure this out later?).
|
27
|
+
#
|
28
|
+
# Arguments and return varies.
|
29
|
+
def method_missing(m, *args, &block)
|
30
|
+
if method_class_ref = Entitlements.person_extra_methods[m]
|
31
|
+
return method_class_ref.send(m, self, *args)
|
32
|
+
end
|
33
|
+
|
34
|
+
# :nocov:
|
35
|
+
raise NoMethodError, "No method '#{m}' exists for Entitlements::Models::Person or any registered extras."
|
36
|
+
# :nocov:
|
37
|
+
end
|
38
|
+
|
39
|
+
# Hash method to get current value of an attribute. Raises if the attribute is undefined.
|
40
|
+
#
|
41
|
+
# attribute - A String with the attribute name to retrieve.
|
42
|
+
#
|
43
|
+
# Returns a String or Array<String> with the attribute's value. (nil deletes later)
|
44
|
+
Contract String => C::Or[String, C::ArrayOf[String], nil]
|
45
|
+
def [](attribute)
|
46
|
+
outward(@current_attributes.fetch(attribute))
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get current value of the original attribute.
|
50
|
+
#
|
51
|
+
# attribute - A String with the attribute name to retrieve.
|
52
|
+
#
|
53
|
+
# Returns a String or Array<String> with the attribute's value. (nil if it did not exist)
|
54
|
+
Contract String => C::Or[String, C::ArrayOf[String], nil]
|
55
|
+
def original(attribute)
|
56
|
+
outward(@original_attributes[attribute])
|
57
|
+
end
|
58
|
+
|
59
|
+
# Hash method to set current value of an attribute.
|
60
|
+
#
|
61
|
+
# attribute - A String with the attribute name to set.
|
62
|
+
# val - A String, Array<String>, Set<String>, or nil with new value. (nil deletes later)
|
63
|
+
#
|
64
|
+
# Returns nothing interesting (this method is in void context).
|
65
|
+
Contract String, C::Or[String, C::ArrayOf[String], C::SetOf[String], nil] => C::Any
|
66
|
+
def []=(attribute, val)
|
67
|
+
@touched_attributes.add(attribute)
|
68
|
+
|
69
|
+
if val.nil? && @original_attributes[attribute].nil?
|
70
|
+
@original_attributes.delete(attribute)
|
71
|
+
@current_attributes.delete(attribute)
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
@original_attributes[attribute] ||= nil
|
76
|
+
if val.nil? || val.is_a?(String)
|
77
|
+
@current_attributes[attribute] = val
|
78
|
+
elsif val.is_a?(Set)
|
79
|
+
@current_attributes[attribute] = val.dup
|
80
|
+
else
|
81
|
+
@current_attributes[attribute] = Set.new(val)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get the changes between original attributes and any attributes updated in this session.
|
86
|
+
#
|
87
|
+
# Takes no arguments.
|
88
|
+
#
|
89
|
+
# Returns a Hash of { attribute name => new value }.
|
90
|
+
Contract C::None => C::HashOf[String => C::Or[String, C::ArrayOf[String], nil]]
|
91
|
+
def attribute_changes
|
92
|
+
@current_attributes
|
93
|
+
.select { |k, _| @touched_attributes.member?(k) }
|
94
|
+
.reject { |k, v| @original_attributes[k] == v }
|
95
|
+
.reject { |k, v| (@original_attributes[k].nil? || @original_attributes[k] == Set.new) && (v.nil? || v == Set.new) }
|
96
|
+
.map { |k, _| [k, self[k]] }
|
97
|
+
.to_h
|
98
|
+
end
|
99
|
+
|
100
|
+
# Update an attribute that is an array or a set by adding a new string item to it.
|
101
|
+
#
|
102
|
+
# attribute - A String with the attribute name to set.
|
103
|
+
# val - A String to add to the array/set.
|
104
|
+
#
|
105
|
+
# Returns nothing.
|
106
|
+
Contract String, String => nil
|
107
|
+
def add(attribute, val)
|
108
|
+
@touched_attributes.add(attribute)
|
109
|
+
ca = @current_attributes.fetch(attribute) # Raises if not found
|
110
|
+
raise ArgumentError, "Called add() on attribute that is a #{ca.class}" unless ca.is_a?(Set)
|
111
|
+
ca.add(val)
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
# Convert the internal structure of an attribute to the displayed structure. Basically
|
118
|
+
# converts sets into sorted arrays and leaves everything else untouched.
|
119
|
+
#
|
120
|
+
# internal_obj - The internal structure.
|
121
|
+
#
|
122
|
+
# Returns the outward facing structure.
|
123
|
+
Contract C::Or[nil, String, C::SetOf[String]] => C::Or[nil, String, C::ArrayOf[String]]
|
124
|
+
def outward(internal_obj)
|
125
|
+
internal_obj.is_a?(Set) ? internal_obj.sort : internal_obj
|
126
|
+
end
|
127
|
+
|
128
|
+
# Construct a hash of attributes, keeping track of the original attributes as well
|
129
|
+
# as the current ones.
|
130
|
+
#
|
131
|
+
# attributes - Hash of {String => String | Array<String>} with additional attributes
|
132
|
+
#
|
133
|
+
# Returns nothing.
|
134
|
+
Contract C::HashOf[String => C::Or[String, C::ArrayOf[String], nil]] => nil
|
135
|
+
def setup_attributes(attributes)
|
136
|
+
@touched_attributes = Set.new
|
137
|
+
@original_attributes = {}
|
138
|
+
@current_attributes = {}
|
139
|
+
attributes.each do |k, v|
|
140
|
+
next if v.nil?
|
141
|
+
val = v.is_a?(Array) ? Set.new(v.sort) : v
|
142
|
+
@original_attributes[k] = val.dup
|
143
|
+
@current_attributes[k] = val.dup
|
144
|
+
end
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Entitlements
|
4
|
+
class Plugins
|
5
|
+
class Dummy < Entitlements::Plugins
|
6
|
+
include ::Contracts::Core
|
7
|
+
C = ::Contracts
|
8
|
+
|
9
|
+
# Dummy override hash.
|
10
|
+
#
|
11
|
+
# group - Entitlements::Models::Group object
|
12
|
+
# plugin_config - Additional configuration for the plugin
|
13
|
+
# ldap - Reference to the underlying Entitlements::Service::LDAP object
|
14
|
+
#
|
15
|
+
# Returns Hash with override settings.
|
16
|
+
Contract Entitlements::Models::Group, C::HashOf[String => C::Any], Entitlements::Service::LDAP => C::HashOf[String => C::Any]
|
17
|
+
def self.override_hash(_group, _plugin_config, _ldap)
|
18
|
+
{}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Entitlements
|
4
|
+
class Plugins
|
5
|
+
class GroupOfNames < Entitlements::Plugins
|
6
|
+
include ::Contracts::Core
|
7
|
+
C = ::Contracts
|
8
|
+
|
9
|
+
# Produce the override hash for an LDAP groupOfNames.
|
10
|
+
#
|
11
|
+
# group - Entitlements::Models::Group object
|
12
|
+
# plugin_config - Additional configuration for the plugin
|
13
|
+
# ldap - Reference to the underlying Entitlements::Service::LDAP object
|
14
|
+
#
|
15
|
+
# Returns Hash with override settings.
|
16
|
+
Contract Entitlements::Models::Group, C::HashOf[String => C::Any], Entitlements::Service::LDAP => C::HashOf[String => C::Any]
|
17
|
+
def self.override_hash(group, _plugin_config, ldap)
|
18
|
+
members = group.member_strings.map { |ms| ldap.person_dn_format.gsub("%KEY%", ms) }
|
19
|
+
|
20
|
+
{
|
21
|
+
"objectClass" => "GroupOfNames",
|
22
|
+
"member" => members,
|
23
|
+
"uniqueMember" => nil
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Entitlements
|
4
|
+
class Plugins
|
5
|
+
class PosixGroup < Entitlements::Plugins
|
6
|
+
include ::Contracts::Core
|
7
|
+
C = ::Contracts
|
8
|
+
|
9
|
+
# Produce the override hash for an LDAP posixGroup.
|
10
|
+
#
|
11
|
+
# group - Entitlements::Models::Group object
|
12
|
+
# plugin_config - Additional configuration for the plugin
|
13
|
+
# ldap - Reference to the underlying Entitlements::Service::LDAP object
|
14
|
+
#
|
15
|
+
# Returns Hash with override settings.
|
16
|
+
Contract Entitlements::Models::Group, C::HashOf[String => C::Any], Entitlements::Service::LDAP => C::HashOf[String => C::Any]
|
17
|
+
def self.override_hash(group, _plugin_config, ldap)
|
18
|
+
members = group.member_strings.map { |ms| ldap.person_dn_format.gsub("%KEY%", ms) }
|
19
|
+
|
20
|
+
{
|
21
|
+
"objectClass" => "PosixGroup",
|
22
|
+
"memberUid" => members,
|
23
|
+
"gidNumber" => gid_number(group).to_s,
|
24
|
+
"uniqueMember" => nil,
|
25
|
+
"owner" => nil
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get the gidNumber from the metadata in the group.
|
30
|
+
#
|
31
|
+
# group - Entitlements::Models::Group object
|
32
|
+
#
|
33
|
+
# Returns an Integer with the GID number of the group.
|
34
|
+
Contract Entitlements::Models::Group => Integer
|
35
|
+
def self.gid_number(group)
|
36
|
+
unless group.metadata.key?("gid_number")
|
37
|
+
raise ArgumentError, "POSIX Group #{group.dn} has no metadata setting for gid_number!"
|
38
|
+
end
|
39
|
+
|
40
|
+
result = group.metadata["gid_number"].to_i
|
41
|
+
return result if result >= 1 && result < 65536
|
42
|
+
raise ArgumentError, "POSIX Group #{group.dn} has GID #{result} out of 1-65535 range!"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Rules written in ruby can (and should) inherit this class.
|
4
|
+
|
5
|
+
module Entitlements
|
6
|
+
class Rule
|
7
|
+
class Base
|
8
|
+
include ::Contracts::Core
|
9
|
+
C = ::Contracts
|
10
|
+
|
11
|
+
# This method must be re-implemented by the child class.
|
12
|
+
#
|
13
|
+
# Takes no Arguments.
|
14
|
+
#
|
15
|
+
# Returns a Set[String] of the DN's of all people who satisfy the filter.
|
16
|
+
Contract C::None => C::SetOf[String]
|
17
|
+
def members
|
18
|
+
# :nocov:
|
19
|
+
raise "Must be implemented by the child class"
|
20
|
+
# :nocov:
|
21
|
+
end
|
22
|
+
|
23
|
+
# This method allows the class to contain a line like:
|
24
|
+
# description "This is some text"
|
25
|
+
# and have that description be set as a class variable. When
|
26
|
+
# called without an argument, this returns the value of the
|
27
|
+
# description, so the instance can access it.
|
28
|
+
Contract C::Maybe[String] => String
|
29
|
+
def self.description(text = nil)
|
30
|
+
if text
|
31
|
+
@description = text
|
32
|
+
else
|
33
|
+
@description ||= ""
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Retrieve the description of the group from the class variable.
|
38
|
+
#
|
39
|
+
# Takes no arguments.
|
40
|
+
#
|
41
|
+
# Returns a String with the description.
|
42
|
+
Contract C::None => String
|
43
|
+
def description
|
44
|
+
self.class.description
|
45
|
+
end
|
46
|
+
|
47
|
+
# This method allows the class to contain a line like:
|
48
|
+
# filter "foo" => :all / :none / [list of Strings]
|
49
|
+
# and have that filter be set as a class variable. When
|
50
|
+
# called without an argument, this returns the value of the
|
51
|
+
# filter, so the instance can access it.
|
52
|
+
Contract C::Maybe[C::HashOf[String => C::Or[:all, :none, C::ArrayOf[String]]]] => C::HashOf[String => C::Or[:all, :none, C::ArrayOf[String]]]
|
53
|
+
def self.filter(filter_pair = nil)
|
54
|
+
@filters ||= Entitlements::Data::Groups::Calculated.filters_default
|
55
|
+
@filters.merge!(filter_pair) if filter_pair
|
56
|
+
@filters
|
57
|
+
end
|
58
|
+
|
59
|
+
# Retrieve the filters for the group from the class variable.
|
60
|
+
#
|
61
|
+
# Takes no arguments.
|
62
|
+
#
|
63
|
+
# Returns a Hash with the filters.
|
64
|
+
Contract C::None => C::HashOf[String => C::Or[:all, :none, C::ArrayOf[String]]]
|
65
|
+
def filters
|
66
|
+
self.class.filter
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
attr_reader :cache
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|