ldap_tools 0.1.0
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/bin/ldaptools +35 -0
- data/lib/tapjoy/ldap/audit.rb +88 -0
- data/lib/tapjoy/ldap/base.rb +180 -0
- data/lib/tapjoy/ldap/group.rb +109 -0
- data/lib/tapjoy/ldap/key.rb +187 -0
- data/lib/tapjoy/ldap/user.rb +114 -0
- data/lib/tapjoy/ldap.rb +23 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: caa6dd479a62ca4b75d94ec3a14d3f5087b09b06
|
4
|
+
data.tar.gz: 8aab3638ac10b7346e61d80b48e99bdd1b020541
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 931365eb56cf362723e430bab17e881d624cc4bde9a988c17bf2b31ee4e8169321b8d96c64ec454f862b54a16924a1b1d4ae661043f78611faeb49b6e871d78e
|
7
|
+
data.tar.gz: 7f0f97d5a554fbe5a2bf2d1e84010a2f3c285d87f3a206dc75cdd5be75560731a2a0df65c79cc7bc03f0ef42ac35cd79143bbcf6097af47e76e5108e7e495d63
|
data/bin/ldaptools
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'tapjoy/ldap'
|
3
|
+
|
4
|
+
def commands(synopsis, object, sub_commands)
|
5
|
+
Trollop::options do
|
6
|
+
usage "#{object} [SUB_COMMAND] [options]"
|
7
|
+
synopsis "\n#{synopsis}.\nAvailable subcommands are: #{sub_commands}"
|
8
|
+
stop_on sub_commands
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
SUB_COMMANDS = %w(user group key audit)
|
13
|
+
commands('Tool to manage LDAP resources', '', SUB_COMMANDS)
|
14
|
+
|
15
|
+
cmd = ARGV.shift # get the subcommand
|
16
|
+
case cmd
|
17
|
+
when "user" # run commands associated with user object
|
18
|
+
USER_SUB_COMMANDS = %w(create delete)
|
19
|
+
commands('This object is used for user management', cmd, USER_SUB_COMMANDS)
|
20
|
+
Tapjoy::LDAP::User.new
|
21
|
+
when 'group'
|
22
|
+
GROUP_SUB_COMMANDS = %w(create delete add_user)
|
23
|
+
commands('This object is used for group management', cmd, GROUP_SUB_COMMANDS)
|
24
|
+
Tapjoy::LDAP::Group.new
|
25
|
+
when 'key'
|
26
|
+
KEY_SUB_COMMANDS = %w(add remove install)
|
27
|
+
commands('This object is used for group management', cmd, KEY_SUB_COMMANDS)
|
28
|
+
Tapjoy::LDAP::Key.new
|
29
|
+
when 'audit'
|
30
|
+
AUDIT_SUB_COMMANDS = %w(by_user by_group raw)
|
31
|
+
commands('This object is used for auditing LDAP permissions', cmd, AUDIT_SUB_COMMANDS)
|
32
|
+
Tapjoy::LDAP::Audit.new
|
33
|
+
else
|
34
|
+
raise Tapjoy::LDAP::InvalidArgument
|
35
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Tapjoy
|
2
|
+
module LDAP
|
3
|
+
class Audit
|
4
|
+
|
5
|
+
# Instantiate class
|
6
|
+
def initialize
|
7
|
+
command = ARGV.shift
|
8
|
+
|
9
|
+
case command
|
10
|
+
when 'by_user', 'by_group', 'raw'
|
11
|
+
send(command)
|
12
|
+
else
|
13
|
+
raise Tapjoy::LDAP::InvalidArgument
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Clean output of hashes
|
20
|
+
def print_hash(header_string, object_hash)
|
21
|
+
puts header_string
|
22
|
+
puts "=" * header_string.length
|
23
|
+
object_hash.each_pair do |key, values|
|
24
|
+
next if values.empty?
|
25
|
+
puts "- #{key}"
|
26
|
+
values.each { |value| puts " - #{value}" }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get list of users
|
31
|
+
def get_users
|
32
|
+
user_list = Array.new
|
33
|
+
|
34
|
+
filter = Net::LDAP::Filter.eq('objectclass', 'posixAccount')
|
35
|
+
attributes = ['uid']
|
36
|
+
|
37
|
+
results = Tapjoy::LDAP::client.search(attributes, filter)
|
38
|
+
results.each do |entry|
|
39
|
+
user_list << entry['uid'].first
|
40
|
+
end
|
41
|
+
|
42
|
+
return user_list.sort
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get hash of groups with list of members of each group
|
46
|
+
def get_groups_with_membership
|
47
|
+
filter = Net::LDAP::Filter.eq('objectclass', 'posixGroup')
|
48
|
+
attributes = ['cn', 'memberUid']
|
49
|
+
|
50
|
+
results = Tapjoy::LDAP::client.search(attributes, filter)
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get a group to user mapping
|
55
|
+
def by_user
|
56
|
+
user_groups = Hash.new
|
57
|
+
user_list = get_users
|
58
|
+
group_results = get_groups_with_membership
|
59
|
+
|
60
|
+
user_list.each do |user|
|
61
|
+
user_groups[user] = Array.new
|
62
|
+
group_results.each do |entry|
|
63
|
+
user_groups[user] << entry['cn'].first if entry['memberUid'].include?(user)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
print_hash('Groups by user', user_groups)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get a user to group mapping
|
71
|
+
def by_group
|
72
|
+
group_membership = Hash.new
|
73
|
+
|
74
|
+
get_groups_with_membership.each do |entry|
|
75
|
+
group_membership[entry['cn'].first] = entry['memberUid']
|
76
|
+
end
|
77
|
+
|
78
|
+
print_hash('Users in groups', group_membership)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Print raw output
|
82
|
+
def raw
|
83
|
+
results = Tapjoy::LDAP::client.search
|
84
|
+
puts results.inspect
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
module Tapjoy
|
2
|
+
module LDAP
|
3
|
+
class Base
|
4
|
+
|
5
|
+
attr_reader :hosts, :basedn, :conn, :service_ou, :group, :key
|
6
|
+
|
7
|
+
# Instantiate class
|
8
|
+
def initialize
|
9
|
+
ldap_config_file = "#{ldap_config_directory}/ldap_info.yaml"
|
10
|
+
ldap_password_file = "#{ldap_config_directory}/ldap.secret"
|
11
|
+
@ldap_info = YAML.load_file(ldap_config_file)
|
12
|
+
@hosts = @ldap_info['servers']
|
13
|
+
@basedn = @ldap_info['basedn']
|
14
|
+
@conn = find_valid_host(ldap_password_file)
|
15
|
+
@service_ou = @ldap_info['service_ou']
|
16
|
+
@email_domain = @ldap_info['email_domain']
|
17
|
+
end
|
18
|
+
|
19
|
+
# Set LDAP Config Directory
|
20
|
+
def ldap_config_directory
|
21
|
+
return "#{ENV['LDAP_CONFIG_DIR'] ? ENV['LDAP_CONFIG_DIR'] : ENV['HOME'] + '/.ldap'}"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Search the LDAP directory
|
25
|
+
def search(attributes = ['*'],
|
26
|
+
filter = Net::LDAP::Filter.eq('objectclass','*'))
|
27
|
+
@entries = []
|
28
|
+
if @conn
|
29
|
+
@conn.search :base => @basedn,
|
30
|
+
:filter => filter,
|
31
|
+
:attributes => attributes do |entry|
|
32
|
+
@entries.push(entry)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
abort('Could not connect to any LDAP servers')
|
36
|
+
end
|
37
|
+
|
38
|
+
return @entries
|
39
|
+
end
|
40
|
+
|
41
|
+
# Add objects to LDAP
|
42
|
+
def add(dn, attributes)
|
43
|
+
@conn.add(:dn => dn, :attributes => attributes)
|
44
|
+
return return_result
|
45
|
+
end
|
46
|
+
|
47
|
+
# Modify objects in LDAP
|
48
|
+
def modify(dn, operations)
|
49
|
+
@conn.modify(:dn => dn, :operations => operations)
|
50
|
+
return return_result
|
51
|
+
end
|
52
|
+
|
53
|
+
# Delete objects from LDAP
|
54
|
+
def delete(dn)
|
55
|
+
@conn.delete(:dn => dn)
|
56
|
+
return return_result
|
57
|
+
end
|
58
|
+
|
59
|
+
# Format return codes
|
60
|
+
def return_result
|
61
|
+
msg1 = "Return Code: #{ @conn.get_operation_result.code }\n"
|
62
|
+
msg2 = "Message: #{ @conn.get_operation_result.message }"
|
63
|
+
return msg1 + msg2
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get highest used ID
|
67
|
+
def get_max_id(object_type, role)
|
68
|
+
case object_type
|
69
|
+
when 'user'
|
70
|
+
objectclass = 'person'
|
71
|
+
ldap_attr = 'uidNumber'
|
72
|
+
when 'group'
|
73
|
+
objectclass = 'posixGroup'
|
74
|
+
ldap_attr = 'gidNumber'
|
75
|
+
else
|
76
|
+
abort('Unknown object type')
|
77
|
+
end
|
78
|
+
|
79
|
+
minID, maxID = set_id_boundary(role)
|
80
|
+
|
81
|
+
# LDAP Filters
|
82
|
+
oc_filter = Net::LDAP::Filter.eq('objectclass', objectclass)
|
83
|
+
attr_filter = Net::LDAP::Filter.eq(ldap_attr, '*')
|
84
|
+
filter = Net::LDAP::Filter.join(oc_filter, attr_filter)
|
85
|
+
|
86
|
+
highid = minID - 1 #subtract 1, so we can add 1 later
|
87
|
+
|
88
|
+
id_list = search([ldap_attr], filter)
|
89
|
+
id_list.each do |item|
|
90
|
+
|
91
|
+
# parse attribute associated with object
|
92
|
+
# users => uidnumber
|
93
|
+
# groups => gidnumber
|
94
|
+
if object_type == 'user'
|
95
|
+
id = item.uidnumber[0].to_i
|
96
|
+
elsif object_type == 'group'
|
97
|
+
id = item.gidnumber[0].to_i
|
98
|
+
else
|
99
|
+
abort('Unknown object')
|
100
|
+
end
|
101
|
+
|
102
|
+
# Now that we have the appropriate attribute
|
103
|
+
# let's find the first useable id.
|
104
|
+
# I *really* hate the pattern I use here, but
|
105
|
+
# can't think of a better one atm.
|
106
|
+
if id > highid
|
107
|
+
highid = id
|
108
|
+
end
|
109
|
+
if maxID.nil?
|
110
|
+
next
|
111
|
+
else
|
112
|
+
if id > maxID
|
113
|
+
highid = maxID
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if !highid.nil?
|
119
|
+
id = highid + 1
|
120
|
+
return id.to_s
|
121
|
+
else
|
122
|
+
abort("Unable to find highest #{ldap_attr}")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
## Private methods start here ##
|
128
|
+
private
|
129
|
+
|
130
|
+
# Connect to LDAP server
|
131
|
+
def ldap_connect(host, ldap_password_file)
|
132
|
+
port = @ldap_info['port']
|
133
|
+
auth = {
|
134
|
+
:method => :simple,
|
135
|
+
:username => @ldap_info['rootdn'],
|
136
|
+
:password => File.read(ldap_password_file).chomp
|
137
|
+
}
|
138
|
+
|
139
|
+
ldap = Net::LDAP.new :host => host,
|
140
|
+
:port => port,
|
141
|
+
:base => @basedn,
|
142
|
+
:auth => auth
|
143
|
+
return ldap
|
144
|
+
end
|
145
|
+
|
146
|
+
# Find valid LDAP host
|
147
|
+
def find_valid_host(ldap_password_file)
|
148
|
+
@hosts.each do |host|
|
149
|
+
@ldap = ldap_connect(host, ldap_password_file)
|
150
|
+
begin
|
151
|
+
if @ldap.bind
|
152
|
+
return @ldap
|
153
|
+
else
|
154
|
+
next
|
155
|
+
end
|
156
|
+
rescue Net::LDAP::LdapError
|
157
|
+
next
|
158
|
+
end
|
159
|
+
end
|
160
|
+
abort('Could not connect to any LDAP servers')
|
161
|
+
end
|
162
|
+
|
163
|
+
# Set acceptable range for IDs
|
164
|
+
def set_id_boundary(role)
|
165
|
+
case role
|
166
|
+
when 'user'
|
167
|
+
minID = 10000
|
168
|
+
maxID = 19999
|
169
|
+
when 'service'
|
170
|
+
minID = 20000
|
171
|
+
maxID = nil
|
172
|
+
else
|
173
|
+
abort('Unknown role')
|
174
|
+
end
|
175
|
+
|
176
|
+
return minID, maxID
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Tapjoy
|
2
|
+
module LDAP
|
3
|
+
class Group
|
4
|
+
|
5
|
+
attr_reader :groupname, :servers, :conn
|
6
|
+
|
7
|
+
# Instantiate class
|
8
|
+
def initialize
|
9
|
+
# This is a necessary construct, because init could be called from
|
10
|
+
# places other than the commandline. As result, we want to overload
|
11
|
+
# init, without *really* overloading it.
|
12
|
+
if ARGV.length >= 1
|
13
|
+
command = ARGV.shift
|
14
|
+
|
15
|
+
case command
|
16
|
+
when 'create', 'delete', 'add_user'
|
17
|
+
send(command)
|
18
|
+
else
|
19
|
+
raise Tapjoy::LDAP::InvalidArgument
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Lookup GID for the given group
|
25
|
+
def lookup_id(groupname)
|
26
|
+
gidnumber = []
|
27
|
+
|
28
|
+
oc_filter = Net::LDAP::Filter.eq('objectclass', 'posixGroup')
|
29
|
+
cn_filter = Net::LDAP::Filter.eq('cn', groupname)
|
30
|
+
filter = Net::LDAP::Filter.join(oc_filter, cn_filter)
|
31
|
+
|
32
|
+
results = Tapjoy::LDAP::client.search(['gidNumber'], filter)
|
33
|
+
|
34
|
+
# Make sure we return one, and only one group
|
35
|
+
if results.size < 1
|
36
|
+
abort('Group not found')
|
37
|
+
elsif results.size > 1
|
38
|
+
abort('Multiple groups found. Please narrow your search.')
|
39
|
+
end
|
40
|
+
|
41
|
+
results.each { |result| gidnumber = result.gidnumber }
|
42
|
+
return gidnumber[0]
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
# Create group
|
47
|
+
def create
|
48
|
+
opts = Trollop::options do
|
49
|
+
# Set help message
|
50
|
+
banner("#{$0} group create [options]")
|
51
|
+
|
52
|
+
opt :name, 'Specify group to create', :type => :string
|
53
|
+
opt :type, 'Specfy if this is a user or service group', :type => :string, :default => 'user'
|
54
|
+
end
|
55
|
+
|
56
|
+
Trollop::die :name, 'argument count must be one' if opts[:name].nil?
|
57
|
+
Trollop::die :type, "argument must be 'user' or 'service'" unless ['user', 'service'].include?opts[:type]
|
58
|
+
|
59
|
+
dn = "cn=#{ opts[:name] },ou=Group,#{ Tapjoy::LDAP::client.basedn }"
|
60
|
+
|
61
|
+
ldap_attr = {
|
62
|
+
:cn => opts[:name],
|
63
|
+
:objectclass => ['top','posixGroup'],
|
64
|
+
:gidnumber => Tapjoy::LDAP::client.get_max_id('group', opts[:type])
|
65
|
+
}
|
66
|
+
puts Tapjoy::LDAP::client.add(dn, ldap_attr)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Delete group
|
70
|
+
def delete
|
71
|
+
opts = Trollop::options do
|
72
|
+
# Set help message
|
73
|
+
banner("#{$0} group delete [options]")
|
74
|
+
|
75
|
+
opt(:group, 'Specify group', :type => :string, :required => true)
|
76
|
+
opt(:force, 'Force delete')
|
77
|
+
end
|
78
|
+
|
79
|
+
dn = "cn=#{ opts[:group] },ou=Group,#{ Tapjoy::LDAP::client.basedn }"
|
80
|
+
unless opts[:force]
|
81
|
+
puts "Confirm that you want to delete group: #{ opts[:group] }"
|
82
|
+
print '>'
|
83
|
+
confirm = STDIN.gets.chomp().downcase
|
84
|
+
unless confirm.eql?('y') || confirm.eql?('yes')
|
85
|
+
abort("Deletion of #{ opts[:group] } aborted")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
puts Tapjoy::LDAP::client.delete(dn)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add user to group
|
93
|
+
def add_user
|
94
|
+
opts = Trollop::options do
|
95
|
+
banner("#{0} group add_user [options]")
|
96
|
+
|
97
|
+
opt(:group, 'Specify group', :type => :string, :required => true)
|
98
|
+
opt(:username, 'Specify username', :type => :string, :required => true)
|
99
|
+
end
|
100
|
+
|
101
|
+
dn = "cn=#{ opts[:group] },ou=Group,#{ Tapjoy::LDAP::client.basedn }"
|
102
|
+
operations = [
|
103
|
+
[:add, :memberUid, opts[:username]]
|
104
|
+
]
|
105
|
+
puts Tapjoy::LDAP::client.modify(dn, operations)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module Tapjoy
|
2
|
+
module LDAP
|
3
|
+
class Key
|
4
|
+
|
5
|
+
# Instantiate class
|
6
|
+
def initialize
|
7
|
+
command = ARGV.shift
|
8
|
+
|
9
|
+
case command
|
10
|
+
when 'add', 'remove', 'install'
|
11
|
+
send(command)
|
12
|
+
else
|
13
|
+
raise Tapjoy::LDAP::InvalidArgument
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
## Private methods start here ##
|
18
|
+
private
|
19
|
+
# Get key listing from LDAP
|
20
|
+
def get_keys_from_ldap
|
21
|
+
|
22
|
+
key_results = {}
|
23
|
+
|
24
|
+
results = Tapjoy::LDAP::client.search(attributes = ['uid', 'sshPublicKey'],
|
25
|
+
filter = Net::LDAP::Filter.eq('sshPublicKey', '*'))
|
26
|
+
|
27
|
+
results.each { |result| key_results[result.uid[0]] = result.sshPublicKey }
|
28
|
+
|
29
|
+
return key_results
|
30
|
+
end
|
31
|
+
|
32
|
+
# Retrieve keys from file/stdin
|
33
|
+
def get_keys_from_commandline(filename)
|
34
|
+
return_keys = []
|
35
|
+
|
36
|
+
if filename.eql?('-')
|
37
|
+
STDIN.each do |str|
|
38
|
+
return_keys << str.chomp!
|
39
|
+
end
|
40
|
+
else
|
41
|
+
return_keys = Array(File.open(filename))
|
42
|
+
end
|
43
|
+
|
44
|
+
return_keys.each { |key| verify(key) }
|
45
|
+
return return_keys
|
46
|
+
end
|
47
|
+
|
48
|
+
# Verify key format
|
49
|
+
def verify(key)
|
50
|
+
unless key.start_with?('ssh')
|
51
|
+
puts "Invalid key due to missing ssh key type:\n\n"
|
52
|
+
puts "\t#{ key }\n\n"
|
53
|
+
abort "Please verify your key and try again"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Add key to LDAP
|
58
|
+
def add
|
59
|
+
opts = Trollop::options do
|
60
|
+
# Set help message
|
61
|
+
banner("#{$0} key add [options]")
|
62
|
+
|
63
|
+
opt :user, 'Specify username to add key to', :type => :string,
|
64
|
+
:required => true
|
65
|
+
opt :filename, 'File to load keys from', :type => :string, :default => '-'
|
66
|
+
end
|
67
|
+
|
68
|
+
keys = get_keys_from_commandline(opts[:filename])
|
69
|
+
|
70
|
+
filter = Net::LDAP::Filter.eq('uid', opts[:user])
|
71
|
+
results = Tapjoy::LDAP::client.search(attributes = ['*'], filter = filter)
|
72
|
+
|
73
|
+
# Make sure we return one, and only one user DN
|
74
|
+
if results.size < 1
|
75
|
+
abort 'user not found'
|
76
|
+
elsif results.size > 1
|
77
|
+
abort 'Multiple users found. Please narrow your search.'
|
78
|
+
end
|
79
|
+
|
80
|
+
results.each do |result|
|
81
|
+
unless result.objectclass.include?('ldapPublicKey')
|
82
|
+
puts 'LDAP Public Key Object Class not found.'
|
83
|
+
abort 'Please ensure user was created correctly.'
|
84
|
+
end
|
85
|
+
keys.each do |key|
|
86
|
+
Tapjoy::LDAP::client.conn.add_attribute(result.dn, :sshPublicKey, key)
|
87
|
+
puts Tapjoy::LDAP::client.return_result
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Remove key from LDAP
|
93
|
+
def remove
|
94
|
+
opts = Trollop::options do
|
95
|
+
# Set help message
|
96
|
+
banner("#{$0} key remove [options]")
|
97
|
+
|
98
|
+
opt :user, 'Specify username to delete key from', :type => :string,
|
99
|
+
:required => true
|
100
|
+
opt :filename, 'File to load key deletion list from', :type => :string,
|
101
|
+
:default => '-'
|
102
|
+
opt(:force, 'Force delete')
|
103
|
+
end
|
104
|
+
|
105
|
+
keys = get_keys_from_commandline(opts[:filename])
|
106
|
+
|
107
|
+
filter = Net::LDAP::Filter.eq('uid', opts[:user])
|
108
|
+
attributes = ['sshPublicKey']
|
109
|
+
old_array = []
|
110
|
+
|
111
|
+
new_array = []
|
112
|
+
|
113
|
+
results = Tapjoy::LDAP::client.search(attributes, filter)
|
114
|
+
if results.size < 1
|
115
|
+
puts "User (#{ opts[:user] }) not found."
|
116
|
+
abort 'Please check the username and try again'
|
117
|
+
elsif results.size > 1
|
118
|
+
abort 'Multiple users found. Please narrow your search.'
|
119
|
+
end
|
120
|
+
|
121
|
+
results.each do |result|
|
122
|
+
@user_dn = result.dn
|
123
|
+
puts "User DN: #{ @user_dn }"
|
124
|
+
old_array = result.sshPublicKey
|
125
|
+
end
|
126
|
+
|
127
|
+
keep_keys = old_array - keys
|
128
|
+
delete_keys = old_array & keys
|
129
|
+
keys_not_found = keys - old_array
|
130
|
+
|
131
|
+
puts 'Please confirm the following operations:'
|
132
|
+
puts "Keep these keys:\n\n"
|
133
|
+
print "\t #{ keep_keys }\n\n"
|
134
|
+
puts "Delete these keys:\n\n"
|
135
|
+
print "\t #{ delete_keys }\n\n"
|
136
|
+
puts "Ignore these keys (not found in LDAP for #{ opts[:user]}):\n\n"
|
137
|
+
print "\t #{ keys_not_found }\n\n"
|
138
|
+
|
139
|
+
# We have to create a new stdin here, because we already use stdin
|
140
|
+
# in the get_keys_from_commandline method.
|
141
|
+
fd = IO.sysopen('/dev/tty', 'w+')
|
142
|
+
unless opts[:force]
|
143
|
+
print '>'
|
144
|
+
confirm = ''
|
145
|
+
IO.open(fd, 'w+') { |io| confirm = io.gets.chomp }
|
146
|
+
unless confirm.eql?('y') || confirm.eql?('yes')
|
147
|
+
abort("Deletion of #{ opts[:user] } aborted")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
Tapjoy::LDAP::client.conn.replace_attribute(@user_dn, :sshPublicKey, keep_keys)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Install key on localhost
|
155
|
+
def install
|
156
|
+
opts = Trollop::options do
|
157
|
+
# Set help message
|
158
|
+
banner("#{$0} key install [options]")
|
159
|
+
|
160
|
+
opt :debug, 'Enable debug/dry-run mode'
|
161
|
+
end
|
162
|
+
|
163
|
+
# Store results of query
|
164
|
+
if opts[:debug]
|
165
|
+
puts search_results
|
166
|
+
exit 1
|
167
|
+
end
|
168
|
+
|
169
|
+
get_keys_from_ldap.each do |key,values|
|
170
|
+
directory = "/etc/ssh/users/#{key}"
|
171
|
+
FileUtils.mkdir_p(directory) unless File.exists?directory
|
172
|
+
keypath = "#{directory}/authorized_keys"
|
173
|
+
if File.exists?(keypath)
|
174
|
+
keys = File.read(keypath)
|
175
|
+
else
|
176
|
+
keys = []
|
177
|
+
end
|
178
|
+
File.open(keypath, 'a+') do |file|
|
179
|
+
file.puts values.reject { |value| keys.include?(value) }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# TODO method to remove from authorized_keys any key that is not in LDAP
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module Tapjoy
|
5
|
+
module LDAP
|
6
|
+
class User
|
7
|
+
|
8
|
+
# Instantiate class
|
9
|
+
def initialize
|
10
|
+
command = ARGV.shift
|
11
|
+
|
12
|
+
case command
|
13
|
+
when 'create', 'delete'
|
14
|
+
send(command)
|
15
|
+
else
|
16
|
+
raise Tapjoy::LDAP::InvalidArgument
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
# Create user in LDAP
|
22
|
+
def create
|
23
|
+
opts = Trollop::options do
|
24
|
+
# Set help message
|
25
|
+
usage "user create [options]"
|
26
|
+
|
27
|
+
# Username is two arguments
|
28
|
+
# Trollop will accept more, but we will only parse two later
|
29
|
+
# TODO: support given names that include a space
|
30
|
+
opt(:user, "Specify user's first and last name",
|
31
|
+
:type => :strings, :required => true)
|
32
|
+
|
33
|
+
# Groupname is a single string, for primary group setting
|
34
|
+
opt(:group, 'Specify name of primary group', :type => :string, :required => true)
|
35
|
+
|
36
|
+
opt(:type, 'Specfy if this is a user or service account',
|
37
|
+
:type => :string, :default => 'user')
|
38
|
+
end
|
39
|
+
|
40
|
+
Trollop::die :user, 'argument count must be two' if opts[:user].size != 2
|
41
|
+
Trollop::die :type, "argument must be 'user' or 'service'" unless ['user', 'service'].include?opts[:type]
|
42
|
+
|
43
|
+
fname, lname = opts[:user]
|
44
|
+
|
45
|
+
# format username
|
46
|
+
username = "#{fname}.#{lname}"
|
47
|
+
username = username.downcase
|
48
|
+
group = Tapjoy::LDAP::Group.new
|
49
|
+
|
50
|
+
uidnumber = Tapjoy::LDAP::client.get_max_id('user', opts[:type])
|
51
|
+
gidnumber = group.lookup_id(opts[:group])
|
52
|
+
|
53
|
+
case opts[:type]
|
54
|
+
when 'user'
|
55
|
+
ou = 'People'
|
56
|
+
when 'service'
|
57
|
+
ou = Tapjoy::LDAP::client.service_ou
|
58
|
+
else
|
59
|
+
puts 'Unknown type'
|
60
|
+
end
|
61
|
+
|
62
|
+
# Super-Salt: bad for blood pressure, good for secure passwords
|
63
|
+
# We can get away with this, since we're not planning on using passwords
|
64
|
+
salt = SecureRandom.base64(32)
|
65
|
+
password = SecureRandom.base64(64)
|
66
|
+
password = Digest::SHA1.base64digest(password + salt)
|
67
|
+
dn = "uid=#{ username },ou=People,#{ Tapjoy::LDAP::client.basedn }"
|
68
|
+
ldap_attr = {
|
69
|
+
:uid => username,
|
70
|
+
:cn => "#{ fname } #{ lname }",
|
71
|
+
:objectclass => ['top','posixAccount','shadowAccount','inetOrgPerson',
|
72
|
+
'organizationalPerson','person', 'ldapPublicKey'],
|
73
|
+
:sn => lname,
|
74
|
+
:givenname => fname,
|
75
|
+
:homedirectory => "/home/#{ username }",
|
76
|
+
:loginshell => '/bin/bash',
|
77
|
+
:mail => "#{fname}.#{lname}@tapjoy.com".downcase,
|
78
|
+
:uidnumber => uidnumber,
|
79
|
+
:gidnumber => gidnumber,
|
80
|
+
:userpassword => '{SSHA}' + password
|
81
|
+
}
|
82
|
+
puts Tapjoy::LDAP::client.add(dn, ldap_attr)
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
# Delete user from LDAP
|
87
|
+
def delete
|
88
|
+
options = {}
|
89
|
+
prompt = '>'
|
90
|
+
|
91
|
+
opts = Trollop::options do
|
92
|
+
# Set help message
|
93
|
+
usage "user delete [options]"
|
94
|
+
|
95
|
+
opt(:user, 'Specify username', :type => :string, :required => true)
|
96
|
+
opt(:force, 'Force delete')
|
97
|
+
end
|
98
|
+
|
99
|
+
dn = "uid=#{ opts[:user] },ou=People,#{ Tapjoy::LDAP::client.basedn }"
|
100
|
+
unless opts[:force]
|
101
|
+
puts "Confirm that you want to delete user: #{ opts[:user] }"
|
102
|
+
print prompt
|
103
|
+
confirm = STDIN.gets.chomp().downcase
|
104
|
+
unless confirm.eql?('y') || confirm.eql?('yes')
|
105
|
+
abort("Deletion of #{ opts[:user] } aborted")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
puts Tapjoy::LDAP::client.delete(dn)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/tapjoy/ldap.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'net/ldap'
|
2
|
+
require 'yaml'
|
3
|
+
require 'trollop'
|
4
|
+
require_relative 'ldap/base'
|
5
|
+
require_relative 'ldap/group'
|
6
|
+
require_relative 'ldap/key'
|
7
|
+
require_relative 'ldap/user'
|
8
|
+
require_relative 'ldap/audit'
|
9
|
+
|
10
|
+
module Tapjoy
|
11
|
+
module LDAP
|
12
|
+
|
13
|
+
def self.client
|
14
|
+
@@client ||= Tapjoy::LDAP::Base.new
|
15
|
+
end
|
16
|
+
|
17
|
+
class InvalidArgument < ArgumentError
|
18
|
+
def initialize
|
19
|
+
Trollop::educate
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ldap_tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ali Tayarani
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: trollop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: net-ldap
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.11'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.11'
|
41
|
+
description: A set of tools to make managing LDAP users, groups, and keys easier
|
42
|
+
email: ali.tayarani@tapjoy.com
|
43
|
+
executables:
|
44
|
+
- ldaptools
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- bin/ldaptools
|
49
|
+
- lib/tapjoy/ldap.rb
|
50
|
+
- lib/tapjoy/ldap/audit.rb
|
51
|
+
- lib/tapjoy/ldap/base.rb
|
52
|
+
- lib/tapjoy/ldap/group.rb
|
53
|
+
- lib/tapjoy/ldap/key.rb
|
54
|
+
- lib/tapjoy/ldap/user.rb
|
55
|
+
homepage: https://github.com/Tapjoy/ops-toolbox-internal/tree/master/scripts/ldap-tools
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.1'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 2.4.6
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: Tapjoy LDAP Tools
|
79
|
+
test_files: []
|