ldap_tools 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|