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