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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/VERSION +1 -0
  3. data/bin/deploy-entitlements +18 -0
  4. data/lib/entitlements/auditor/base.rb +163 -0
  5. data/lib/entitlements/backend/base_controller.rb +171 -0
  6. data/lib/entitlements/backend/base_provider.rb +55 -0
  7. data/lib/entitlements/backend/dummy/controller.rb +89 -0
  8. data/lib/entitlements/backend/dummy.rb +3 -0
  9. data/lib/entitlements/backend/ldap/controller.rb +188 -0
  10. data/lib/entitlements/backend/ldap/provider.rb +128 -0
  11. data/lib/entitlements/backend/ldap.rb +4 -0
  12. data/lib/entitlements/backend/member_of/controller.rb +203 -0
  13. data/lib/entitlements/backend/member_of.rb +3 -0
  14. data/lib/entitlements/cli.rb +121 -0
  15. data/lib/entitlements/data/groups/cached.rb +120 -0
  16. data/lib/entitlements/data/groups/calculated/base.rb +478 -0
  17. data/lib/entitlements/data/groups/calculated/filters/base.rb +93 -0
  18. data/lib/entitlements/data/groups/calculated/filters/member_of_group.rb +32 -0
  19. data/lib/entitlements/data/groups/calculated/modifiers/base.rb +38 -0
  20. data/lib/entitlements/data/groups/calculated/modifiers/expiration.rb +56 -0
  21. data/lib/entitlements/data/groups/calculated/ruby.rb +137 -0
  22. data/lib/entitlements/data/groups/calculated/rules/base.rb +35 -0
  23. data/lib/entitlements/data/groups/calculated/rules/group.rb +129 -0
  24. data/lib/entitlements/data/groups/calculated/rules/username.rb +41 -0
  25. data/lib/entitlements/data/groups/calculated/text.rb +337 -0
  26. data/lib/entitlements/data/groups/calculated/yaml.rb +171 -0
  27. data/lib/entitlements/data/groups/calculated.rb +290 -0
  28. data/lib/entitlements/data/groups.rb +13 -0
  29. data/lib/entitlements/data/people/combined.rb +197 -0
  30. data/lib/entitlements/data/people/dummy.rb +71 -0
  31. data/lib/entitlements/data/people/ldap.rb +142 -0
  32. data/lib/entitlements/data/people/yaml.rb +102 -0
  33. data/lib/entitlements/data/people.rb +58 -0
  34. data/lib/entitlements/extras/base.rb +40 -0
  35. data/lib/entitlements/extras/ldap_group/base.rb +20 -0
  36. data/lib/entitlements/extras/ldap_group/filters/member_of_ldap_group.rb +50 -0
  37. data/lib/entitlements/extras/ldap_group/rules/ldap_group.rb +69 -0
  38. data/lib/entitlements/extras/orgchart/base.rb +32 -0
  39. data/lib/entitlements/extras/orgchart/logic.rb +171 -0
  40. data/lib/entitlements/extras/orgchart/person_methods.rb +55 -0
  41. data/lib/entitlements/extras/orgchart/rules/direct_report.rb +62 -0
  42. data/lib/entitlements/extras/orgchart/rules/management.rb +59 -0
  43. data/lib/entitlements/extras.rb +82 -0
  44. data/lib/entitlements/models/action.rb +82 -0
  45. data/lib/entitlements/models/group.rb +280 -0
  46. data/lib/entitlements/models/person.rb +149 -0
  47. data/lib/entitlements/plugins/dummy.rb +22 -0
  48. data/lib/entitlements/plugins/group_of_names.rb +28 -0
  49. data/lib/entitlements/plugins/posix_group.rb +46 -0
  50. data/lib/entitlements/plugins.rb +13 -0
  51. data/lib/entitlements/rule/base.rb +74 -0
  52. data/lib/entitlements/service/ldap.rb +405 -0
  53. data/lib/entitlements/util/mirror.rb +42 -0
  54. data/lib/entitlements/util/override.rb +64 -0
  55. data/lib/entitlements/util/util.rb +219 -0
  56. data/lib/entitlements.rb +606 -0
  57. 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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Entitlements
4
+ class Plugins
5
+ def self.loaded?
6
+ true
7
+ end
8
+
9
+ def self.override_hash(*args)
10
+ raise "Please define override_hash in the child class #{self}!"
11
+ end
12
+ end
13
+ 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