cratus 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|