cratus 0.2.1 → 0.2.2
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 +4 -4
- data/bin/cratus +2 -2
- data/bin/cratus-compare +7 -10
- data/lib/cratus/config.rb +54 -49
- data/lib/cratus/group.rb +11 -9
- data/lib/cratus/ldap.rb +21 -19
- data/lib/cratus/user.rb +5 -4
- data/lib/cratus/version.rb +2 -1
- metadata +48 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 288c9ccd03e5e93a3568df29460e8c2ae5d65f0e
|
4
|
+
data.tar.gz: 2a7796b78a4fe9037b90558e21b7374444d28a99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66cebb52b25cfb25ebcbfcdc66fc2ac1f0b219e28588dba6962b7c1dac3e08d4658c37ba57c09ba5ff49841e4d57f6272d05101315b005c5c5385ee4640851a8
|
7
|
+
data.tar.gz: f071f32fe719429bc04c24569e77b70a81a8f715f7d4140bae6a7c00256c0ab3c38e61d95058f5c13b740f95801ebef6fbad89ff0298c84ee073082d17a2e20b
|
data/bin/cratus
CHANGED
@@ -8,7 +8,7 @@ LDAP.connect
|
|
8
8
|
|
9
9
|
# Read in arguments
|
10
10
|
group_mapping_arg = ARGV[0]
|
11
|
-
raise
|
11
|
+
raise 'Missing Group Mapping Argument!' unless group_mapping_arg
|
12
12
|
group_mapping_file = File.expand_path(group_mapping_arg)
|
13
13
|
raise "Invalid Group Mapping File #{group_mapping_file}" unless File.readable?(group_mapping_file)
|
14
14
|
|
@@ -21,7 +21,7 @@ User.all.sort.each do |user|
|
|
21
21
|
key = user.username.to_s
|
22
22
|
user_groups = user.member_of.map { |g| g.name.to_s }
|
23
23
|
|
24
|
-
@results[key] = {'groups' => {}}
|
24
|
+
@results[key] = { 'groups' => {} }
|
25
25
|
user_groups.sort.each do |ugroup|
|
26
26
|
group_perm_set = group_permissions[ugroup] ? group_permissions[ugroup].sort : []
|
27
27
|
@results[key]['groups'][ugroup] = group_perm_set
|
data/bin/cratus-compare
CHANGED
@@ -10,7 +10,7 @@ input2 = ARGV[1]
|
|
10
10
|
|
11
11
|
# Find our diff command and break if we don't have one
|
12
12
|
diff_path = `which diff`.chomp
|
13
|
-
raise
|
13
|
+
raise 'Missing diff command in PATH!' unless $CHILD_STATUS.success?
|
14
14
|
|
15
15
|
# The command we'll use later to see what has changed
|
16
16
|
diff_cmd = "#{diff_path} -U 999999"
|
@@ -18,7 +18,7 @@ diff_cmd = "#{diff_path} -U 999999"
|
|
18
18
|
## Methods / Functions
|
19
19
|
def validate_input(input)
|
20
20
|
# Make sure the input is set
|
21
|
-
raise
|
21
|
+
raise 'Missing First Input!' unless input
|
22
22
|
# Make sure the input is a valid file that we can read
|
23
23
|
raise "Invalid Input File #{input}" unless File.readable?(input)
|
24
24
|
# TODO: make sure the input file is valid YAML
|
@@ -26,11 +26,9 @@ end
|
|
26
26
|
|
27
27
|
# Read in some input
|
28
28
|
def read_input(input)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
raise "Unable to open #{input}: #{e.message}"
|
33
|
-
end
|
29
|
+
YAML.load_file(input)
|
30
|
+
rescue => e
|
31
|
+
raise "Unable to open #{input}: #{e.message}"
|
34
32
|
end
|
35
33
|
|
36
34
|
## Execution
|
@@ -43,7 +41,7 @@ data2 = read_input(input2)
|
|
43
41
|
if data1 == data2
|
44
42
|
exit 0
|
45
43
|
else
|
46
|
-
STDERR.puts
|
44
|
+
STDERR.puts 'Looks like things have changed!'
|
47
45
|
|
48
46
|
# Calculate what has actually changed
|
49
47
|
additions_and_differences = (data2.to_a - data1.to_a)
|
@@ -62,7 +60,7 @@ else
|
|
62
60
|
|
63
61
|
# Grab the old data for just our changed users and put it into a new hash
|
64
62
|
oldhash = {}
|
65
|
-
results.each do |user,
|
63
|
+
results.each do |user, _data|
|
66
64
|
oldhash[user] = data1[user] if data1.key?(user)
|
67
65
|
end
|
68
66
|
# Add things that were removed from the old data (removed users)
|
@@ -84,4 +82,3 @@ else
|
|
84
82
|
newdata.unlink
|
85
83
|
olddata.unlink
|
86
84
|
end
|
87
|
-
|
data/lib/cratus/config.rb
CHANGED
@@ -1,56 +1,61 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
basedn: 'dc=example,dc=com',
|
27
|
-
username: 'username',
|
28
|
-
password: 'p@assedWard!',
|
29
|
-
}
|
30
|
-
merge defaults
|
31
|
-
|
32
|
-
# Then apply the config file, if one exists
|
33
|
-
begin
|
34
|
-
apprc_dir = File.expand_path('~')
|
35
|
-
config_file = File.expand_path(File.join(apprc_dir, '.cratus.yml'))
|
36
|
-
merge YAML.load_file(config_file) if File.readable?(config_file)
|
37
|
-
rescue => e
|
38
|
-
puts "WARNING: Unable to read from #{config_file}"
|
1
|
+
module Cratus
|
2
|
+
# A generic way of constructing a mergeable configuration
|
3
|
+
class Config < OpenStruct
|
4
|
+
# A Hash of the default configuration options
|
5
|
+
def defaults
|
6
|
+
{
|
7
|
+
group_dn_attribute: :cn,
|
8
|
+
group_member_attribute: :member,
|
9
|
+
group_description_attribute: :description,
|
10
|
+
group_objectclass: :group,
|
11
|
+
group_basedn: 'ou=groups,dc=example,dc=com',
|
12
|
+
group_memberof_attribute: :memberOf,
|
13
|
+
user_dn_attribute: :samaccountname,
|
14
|
+
user_objectclass: :user,
|
15
|
+
user_basedn: 'ou=users,dc=example,dc=com',
|
16
|
+
user_department_attribute: :department,
|
17
|
+
user_lockout_attribute: :lockouttime,
|
18
|
+
user_mail_attribute: :mail,
|
19
|
+
user_displayname_attribute: :displayName,
|
20
|
+
user_memberof_attribute: :memberOf,
|
21
|
+
host: 'ldap.example.com', port: 389,
|
22
|
+
basedn: 'dc=example,dc=com',
|
23
|
+
username: 'username',
|
24
|
+
password: 'p@assedWard!'
|
25
|
+
}
|
39
26
|
end
|
40
27
|
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
28
|
+
# Construct a base config using the following order of precedence:
|
29
|
+
# * environment variables
|
30
|
+
# * YAML file
|
31
|
+
# * defaults
|
32
|
+
def load
|
33
|
+
# First, apply the defaults
|
34
|
+
merge defaults
|
35
|
+
|
36
|
+
# Then apply the config file, if one exists
|
37
|
+
begin
|
38
|
+
apprc_dir = File.expand_path('~')
|
39
|
+
config_file = File.expand_path(File.join(apprc_dir, '.cratus.yml'))
|
40
|
+
merge YAML.load_file(config_file) if File.readable?(config_file)
|
41
|
+
rescue => e
|
42
|
+
puts "WARNING: Unable to read from #{config_file}: #{e.message}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Finally, apply any environment variables specified
|
46
|
+
env_conf = {}
|
47
|
+
defaults.keys.each do |key|
|
48
|
+
cratus_key = "CRATUS_#{key}".upcase
|
49
|
+
env_conf[key] = ENV[cratus_key] if ENV.key?(cratus_key)
|
50
|
+
end
|
51
|
+
merge env_conf unless env_conf.empty?
|
46
52
|
end
|
47
|
-
merge env_conf unless env_conf.empty?
|
48
|
-
end
|
49
53
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
+
def merge(data)
|
55
|
+
raise 'Invalid Config Data' unless data.is_a?(Hash)
|
56
|
+
data.each do |k, v|
|
57
|
+
self[k.to_sym] = v
|
58
|
+
end
|
54
59
|
end
|
55
60
|
end
|
56
61
|
end
|
data/lib/cratus/group.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Cratus
|
2
|
+
# An LDAP Group representation
|
2
3
|
class Group
|
3
4
|
include Comparable
|
4
5
|
attr_reader :name, :search_base
|
@@ -25,14 +26,14 @@ module Cratus
|
|
25
26
|
def member_of
|
26
27
|
memrof_attr = Cratus.config.group_memberof_attribute
|
27
28
|
|
28
|
-
# TODO make this work with more things...
|
29
|
+
# TODO: make this work with more things...
|
29
30
|
unless @raw_ldap_data
|
30
31
|
STDERR.puts "WARNING: Group '#{@name}' appears to be invalid or beyond the search scope!"
|
31
32
|
return []
|
32
33
|
end
|
33
34
|
|
34
35
|
# TODO: move the search filter to a configurable param
|
35
|
-
raw_groups = @raw_ldap_data[memrof_attr].reject {|g| g.match
|
36
|
+
raw_groups = @raw_ldap_data[memrof_attr].reject { |g| g.match(/OU=Distribution Groups/) }
|
36
37
|
initial_groups = raw_groups.map do |raw_group|
|
37
38
|
Group.new(raw_group.match(/^#{Group.ldap_dn_attribute.to_s.upcase}=([^,]+),/)[1])
|
38
39
|
end
|
@@ -40,7 +41,7 @@ module Cratus
|
|
40
41
|
initial_groups.each do |group|
|
41
42
|
all_the_groups.concat(group.member_of) # recursion!
|
42
43
|
end
|
43
|
-
all_the_groups.uniq
|
44
|
+
all_the_groups.uniq(&:name)
|
44
45
|
end
|
45
46
|
|
46
47
|
# LDAP description attribute
|
@@ -52,7 +53,7 @@ module Cratus
|
|
52
53
|
def self.all
|
53
54
|
filter = "(#{ldap_dn_attribute}=*)"
|
54
55
|
Cratus::LDAP.search(filter, basedn: ldap_search_base, attrs: ldap_dn_attribute).map do |entry|
|
55
|
-
|
56
|
+
new(entry[ldap_dn_attribute].last)
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
@@ -84,10 +85,11 @@ module Cratus
|
|
84
85
|
private
|
85
86
|
|
86
87
|
# provides a Hash of member users and groups
|
88
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
87
89
|
def all_members
|
88
90
|
# filters used to determine if each group member is a User or Group
|
89
|
-
group_filter = "(objectClass=#{Cratus.config.group_objectclass
|
90
|
-
user_filter = "(objectClass=#{Cratus.config.user_objectclass
|
91
|
+
group_filter = "(objectClass=#{Cratus.config.group_objectclass})"
|
92
|
+
user_filter = "(objectClass=#{Cratus.config.user_objectclass})"
|
91
93
|
|
92
94
|
# The raw LDAP data (a list of DNs)
|
93
95
|
raw_members = @raw_ldap_data[Cratus.config.group_member_attribute]
|
@@ -123,9 +125,9 @@ module Cratus
|
|
123
125
|
end
|
124
126
|
|
125
127
|
# deliver the results
|
126
|
-
results[:groups].uniq!
|
127
|
-
results[:users].uniq!
|
128
|
-
|
128
|
+
results[:groups].uniq!(&:name)
|
129
|
+
results[:users].uniq!(&:username)
|
130
|
+
results
|
129
131
|
end
|
130
132
|
end
|
131
133
|
end
|
data/lib/cratus/ldap.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Cratus
|
2
|
+
# The LDAP swiss-army knife for cratus
|
2
3
|
module LDAP
|
3
4
|
# Define the LDAP connection
|
4
5
|
# Note: does not actually connect (bind), just sets up the connection
|
@@ -14,16 +15,20 @@ module Cratus
|
|
14
15
|
}
|
15
16
|
}
|
16
17
|
# TODO: make the validations do something useful
|
17
|
-
#validate_connection_options(options)
|
18
|
-
|
18
|
+
# validate_connection_options(options)
|
19
|
+
@ldap_connection ||= Net::LDAP.new(options)
|
19
20
|
end
|
20
21
|
|
21
22
|
# Actually connect (bind) to LDAP
|
22
23
|
def self.connect
|
23
24
|
connection
|
24
25
|
validate_ldap_connection
|
25
|
-
|
26
|
-
|
26
|
+
@ldap_connection.bind
|
27
|
+
@ldap_bound = true
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.connected?
|
31
|
+
@ldap_bound.dup
|
27
32
|
end
|
28
33
|
|
29
34
|
# Perform an LDAP search
|
@@ -39,38 +44,35 @@ module Cratus
|
|
39
44
|
scope = options.key?(:scope) ? options[:scope] : 'subtree'
|
40
45
|
|
41
46
|
scope_class = case scope.to_s
|
42
|
-
when 'subtree','recursive','whole_subtree'
|
47
|
+
when 'subtree', 'recursive', 'whole_subtree'
|
43
48
|
Net::LDAP::SearchScope_WholeSubtree
|
44
|
-
when 'single','single_level'
|
49
|
+
when 'single', 'single_level'
|
45
50
|
Net::LDAP::SearchScope_SingleLevel
|
46
|
-
when 'object','base_object'
|
51
|
+
when 'object', 'base_object'
|
47
52
|
Net::LDAP::SearchScope_BaseObject
|
48
53
|
else
|
49
|
-
|
54
|
+
raise 'Invalid LDAP Scope!'
|
50
55
|
end
|
51
56
|
|
52
|
-
results =
|
53
|
-
base: options[:basedn],
|
54
|
-
|
55
|
-
scope: scope_class,
|
56
|
-
attributes: [*attrs].map(&:to_s)
|
57
|
+
results = connection.search(
|
58
|
+
base: options[:basedn], filter: filter,
|
59
|
+
scope: scope_class, attributes: [*attrs].map(&:to_s)
|
57
60
|
)
|
58
|
-
raise
|
59
|
-
results.compact
|
61
|
+
results.nil? ? raise('Search Failed') : results.compact
|
60
62
|
end
|
61
63
|
|
62
64
|
# Validation Methods
|
63
65
|
|
64
66
|
def self.validate_ldap_bound
|
65
|
-
raise
|
67
|
+
raise 'LDAP Not Connected' unless connected?
|
66
68
|
end
|
67
69
|
|
68
70
|
def self.validate_ldap_connection
|
69
|
-
raise
|
71
|
+
raise 'No LDAP Connection' unless connection
|
70
72
|
end
|
71
73
|
|
72
74
|
def self.validate_search_options(options)
|
73
|
-
raise
|
75
|
+
raise 'Invalid Options' unless options.respond_to?(:key?)
|
74
76
|
|
75
77
|
[:basedn].each do |key|
|
76
78
|
raise "Missing Option: #{key}" unless options.key?(key)
|
@@ -78,7 +80,7 @@ module Cratus
|
|
78
80
|
end
|
79
81
|
|
80
82
|
def self.validate_connection_options(options)
|
81
|
-
raise
|
83
|
+
raise 'Invalid Options' unless options.respond_to?(:key?)
|
82
84
|
|
83
85
|
[:host, :port, :basedn, :username, :password].each do |key|
|
84
86
|
raise "Missing Option: #{key}" unless options.key?(key)
|
data/lib/cratus/user.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Cratus
|
2
|
+
# An LDAP User representation
|
2
3
|
class User
|
3
4
|
include Comparable
|
4
5
|
attr_reader :username, :search_base
|
@@ -36,7 +37,7 @@ module Cratus
|
|
36
37
|
def member_of
|
37
38
|
memrof_attr = Cratus.config.user_memberof_attribute
|
38
39
|
# TODO: move the search filter to a configurable param
|
39
|
-
raw_groups = @raw_ldap_data[memrof_attr].reject {|g| g.match
|
40
|
+
raw_groups = @raw_ldap_data[memrof_attr].reject { |g| g.match(/OU=Distribution Groups/) }
|
40
41
|
initial_groups = raw_groups.map do |raw_group|
|
41
42
|
Group.new(raw_group.match(/^#{Group.ldap_dn_attribute.to_s.upcase}=([^,]+),/)[1])
|
42
43
|
end
|
@@ -44,10 +45,10 @@ module Cratus
|
|
44
45
|
initial_groups.each do |group|
|
45
46
|
all_the_groups.concat(group.member_of)
|
46
47
|
end
|
47
|
-
all_the_groups.uniq
|
48
|
+
all_the_groups.uniq(&:name)
|
48
49
|
end
|
49
50
|
|
50
|
-
|
51
|
+
alias groups member_of
|
51
52
|
|
52
53
|
def <=>(other)
|
53
54
|
@username <=> other.username
|
@@ -61,7 +62,7 @@ module Cratus
|
|
61
62
|
attrs: ldap_dn_attribute
|
62
63
|
)
|
63
64
|
raw_results.map do |entry|
|
64
|
-
|
65
|
+
new(entry[ldap_dn_attribute.to_sym].last)
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
data/lib/cratus/version.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# The Cratus module
|
1
2
|
module Cratus
|
2
3
|
def self.version
|
3
4
|
major = 0 # Breaking, incompatible releases
|
4
5
|
minor = 2 # Compatible, but new features
|
5
|
-
patch =
|
6
|
+
patch = 2 # Fixes to existing features
|
6
7
|
[major, minor, patch].map(&:to_s).join('.')
|
7
8
|
end
|
8
9
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cratus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Gnagy
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-11-
|
12
|
+
date: 2016-11-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: colorize
|
@@ -39,6 +39,20 @@ dependencies:
|
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0.10'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '10.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '10.0'
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
57
|
name: rspec
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,6 +95,34 @@ dependencies:
|
|
81
95
|
- - "~>"
|
82
96
|
- !ruby/object:Gem::Version
|
83
97
|
version: '0.8'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: travis
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '1.8'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '1.8'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: simplecov
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
84
126
|
description: The Ruby tool for auditing and reporting on user permissions based on
|
85
127
|
groups
|
86
128
|
email: jgnagy@knuedge.com
|
@@ -99,10 +141,11 @@ files:
|
|
99
141
|
- lib/cratus/ldap.rb
|
100
142
|
- lib/cratus/user.rb
|
101
143
|
- lib/cratus/version.rb
|
102
|
-
homepage:
|
144
|
+
homepage: https://github.com/knuedge/cratus
|
103
145
|
licenses:
|
104
146
|
- MIT
|
105
|
-
metadata:
|
147
|
+
metadata:
|
148
|
+
yard.run: yri
|
106
149
|
post_install_message: Thanks for installing Cratus!
|
107
150
|
rdoc_options: []
|
108
151
|
require_paths:
|
@@ -119,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
162
|
version: '0'
|
120
163
|
requirements: []
|
121
164
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.5
|
165
|
+
rubygems_version: 2.4.5
|
123
166
|
signing_key:
|
124
167
|
specification_version: 4
|
125
168
|
summary: Cratus queries LDAP for users and their memberships, then reports on it.
|