4 # Copyright (c) 2000-2001 Jason Gunthorpe <jgg@debian.org>
5 # Copyright (c) 2001 Ryan Murray <rmurray@debian.org>
6 # Copyright (c) 2003 James Troup <troup@debian.org>
7 # Copyright (c) 2004-2005 Joey Schulze <joey@infodrom.org>
8 # Copyright (c) 2008,2009 Peter Palfrader <peter@palfrader.org>
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 # This script is an interactive way to manipulate fields in the LDAP directory.
25 # When run it connects to the directory using the current users ID and fetches
26 # all the attributes for the first machine. It then formats them nicely and
27 # allows the user to change them.
29 # Usage: userinfo -a <user> -u <user> -c <user> -r
30 # -a Set the authentication user (the user whose password you are
32 # -h Set the host to display
33 # -l list all hosts and their status
34 # -f list all SSH fingerprints
36 import time, os, pwd, sys, getopt, ldap, crypt, readline, copy, subprocess
37 from os import O_CREAT, O_EXCL, O_WRONLY
38 from userdir_ldap import *
41 AttrInfo = {"description": ["Machine Descr.", 1],
42 "hostname": ["Host names", 2],
43 "status": ["Status", 3],
45 "sponsor": ["Sponsors", 5],
46 "distribution": ["Distribution", 6],
47 "access": ["Access", 7],
48 "admin": ["Admin", 8],
49 "architecture": ["Architecture", 9],
50 "machine": ["Machine Hardware", 10],
51 "memory": ["Memory", 11],
53 "physicalHost": ["Physical Host", 13],
54 "sshRSAHostKey": ["SSH Host Keys", 14],
55 "bandwidth": ["Bandwidth", 15],
56 "purpose": ["Purposes", 16],
57 "allowedGroups": ["Groups", 17],
58 "exportOptions": ["Export-Opts", 18],
59 "ipHostNumber": ["IP Address", 19],
60 "mXRecord": ["MXRecord", 20],
61 "dnsTTL": ["dnsTTL", 21],
62 "sshdistAuthKeysHost": ["extra authkeys ip", 22],
65 AttrPrompt = {"description": ["Purpose of the machine"],
66 "hostname": ["The hostnames for the box (ipv4/ipv6)"],
67 "status": ["Blank if Up, explaination if not"],
68 "l": ["Physical location"],
69 "sponsor": ["Sponsors and their URLs"],
70 "distribution": ["The distribution version"],
71 "access": ["all, developer only, restricted"],
72 "admin": ["Admin email address"],
73 "architecture": ["Debian Architecture string"],
74 "machine": ["Hardware description"],
75 "memory": ["Installed RAM"],
76 "disk": ["Disk Space, RAID levels, etc"],
77 "physicalHost": ["The box hosting this virtual server"],
78 "sshRSAHostKey": ["A copy of /etc/ssh/ssh_*host_key.pub"],
79 "bandwidth": ["Available outbound"],
80 "purpose": ["The purposes of this host"],
81 "allowedGroups": ["allowed Groups on this host"],
82 "exportOptions": ["additional export options"],
83 "ipHostNumber": ["IP Addresses(es) of the machine"],
84 "mXRecord": ["Mail Exchanger for this machine"],
85 "dnsTTL": ["dns TTL value"],
86 "sshdistAuthKeysHost": ["additional hosts for sshdist's authkeys file"],
89 # Create a map of IDs to desc,value,attr
91 for at in AttrInfo.keys():
92 if (AttrInfo[at][1] != 0):
93 OrderedIndex[AttrInfo[at][1]] = [AttrInfo[at][0], "", at]
94 OrigOrderedIndex = copy.deepcopy(OrderedIndex)
96 # Print out the automatic time stamp information
97 def PrintModTime(Attrs):
98 Stamp = GetAttr(Attrs,"modifyTimestamp","")
100 Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
101 int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1)
102 print "%-24s:" % ("Record last modified on"), time.strftime("%a %d/%m/%Y %X UTC",Time),
103 print "by",ldap.explode_dn(GetAttr(Attrs,"modifiersName"),1)[0]
105 Stamp = GetAttr(Attrs,"createTimestamp","")
107 Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
108 int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1)
109 print "%-24s:" % ("Record created on"), time.strftime("%a %d/%m/%Y %X UTC",Time)
111 # Display all of the attributes in a numbered list
112 def ShowAttrs(Attrs):
116 for at in Attrs[1].keys():
117 if AttrInfo.has_key(at):
118 if AttrInfo[at][1] == 0:
119 print " %-18s:" % (AttrInfo[at][0]),
120 for x in Attrs[1][at]:
124 OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at]
126 Keys = OrderedIndex.keys()
129 if at < 100 or RootMode != 0:
130 print " %3u) %-18s: " % (at,OrderedIndex[at][0]),
131 for x in OrderedIndex[at][1]:
132 print "'%s'" % (re.sub('[\n\r]','?',x)),
136 """Display a one-line overview for a given host"""
137 for i in ['host','architecture','distribution','access','status']:
138 if i not in Attrs[1].keys():
140 print "%-12s %-10s %-38s %-25s %s" % (\
141 Attrs[1]['host'][0], \
142 Attrs[1]['architecture'][0], \
143 Attrs[1]['distribution'][0], \
144 Attrs[1]['access'][0], \
145 Attrs[1]['status'][0])
147 # Change a single attribute
148 def ChangeAttr(Attrs,Attr):
149 if (Attr in ["sponsor", "sshRSAHostKey", "purpose", "allowedGroups", "exportOptions", "ipHostNumber", "mXRecord", "sshdistAuthKeysHost"]):
150 return MultiChangeAttr(Attrs,Attr)
152 print "Old value: '%s'" % (GetAttr(Attrs,Attr,""))
153 print "Press enter to leave unchanged and a single space to set to empty"
154 NewValue = raw_input("New? ")
158 print "Leaving unchanged."
161 # Single space designates delete, trap the delete error
162 if (NewValue == " "):
165 l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,None)])
166 except ldap.NO_SUCH_ATTRIBUTE:
170 Attrs[1][Attr] = [""]
175 l.modify_s(HostDn,[(ldap.MOD_REPLACE,Attr,NewValue)])
176 Attrs[1][Attr] = [NewValue]
179 def MultiChangeAttr(Attrs,Attr):
180 # Make sure that we have an entry
181 if not Attrs[1].has_key(Attr):
184 Attrs[1][Attr].sort()
185 print "Old values: ",Attrs[1][Attr]
187 Mode = raw_input("[D]elete or [A]dd? ").upper()
188 if (Mode != 'D' and Mode != 'A'):
191 NewValue = raw_input("Value? ")
194 print "Leaving unchanged."
201 l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,NewValue)])
202 except ldap.NO_SUCH_ATTRIBUTE:
206 Attrs[1][Attr].remove(NewValue)
211 l.modify_s(HostDn,[(ldap.MOD_ADD,Attr,NewValue)])
212 Attrs[1][Attr].append(NewValue)
216 # Main program starts here
217 User = pwd.getpwuid(os.getuid())[0]
224 (options, arguments) = getopt.getopt(sys.argv[1:], "nh:a:rlf")
225 except getopt.GetoptError, data:
229 for (switch, val) in options:
232 elif (switch == '-a'):
234 elif (switch == '-r'):
236 elif (switch == '-n'):
238 elif (switch == '-l'):
241 elif (switch == '-f'):
246 l = passwdAccessLDAP(BaseDn, BindUser)
249 l.simple_bind_s("","")
252 Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=*")
255 hosts.append(hAttrs[1]['host'][0])
258 print "%-12s %-10s %-38s %-25s %s" % ("Host name","Arch","Distribution","Access","Status")
262 if host == hAttrs[1]['host'][0]:
265 elif FingerPrints == 1:
267 Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host)
269 Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=*")
272 hosts.append(hAttrs[1]['host'][0])
277 if host == hAttrs[1]['host'][0]:
278 if 'sshRSAHostKey' in hAttrs[1].keys():
279 for key in hAttrs[1]['sshRSAHostKey']:
280 keygen = subprocess.Popen(['/usr/bin/ssh-keygen', '-l', '-f', '-'],
281 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
282 keygen.stdin.write(key + '\n')
284 input = keygen.stdout.readline()
286 fingerprint = input.split(' ')
287 print "%s %s root@%s" % (fingerprint[0], fingerprint[1], host)
290 HostDn = "host=" + Host + "," + HostBaseDn
292 # Query the server for all of the attributes
293 Attrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host)
295 print "Host",Host,"was not found."
298 # repeatedly show the account configuration
305 print " a) Arbitary Change"
307 print " d) Delete Host"
308 print " u) Switch Hosts"
312 Response = raw_input("Change? ")
313 if (Response == "x" or Response == "X" or Response == "q" or
314 Response == "quit" or Response == "exit"):
317 # Change who we are looking at
318 if (Response == 'u' or Response == 'U'):
319 NewHost = raw_input("Host? ")
322 NAttrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost)
324 print "Host",NewHost,"was not found."
328 HostDn = "host=" + Host + "," + HostBaseDn
329 OrderedIndex = copy.deepcopy(OrigOrderedIndex)
332 # Create a new entry and change to it Change who we are looking at
333 if (Response == 'n' or Response == 'N'):
334 NewHost = raw_input("Host? ")
337 NAttrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost)
339 print "Host",NewHost,"already exists."
341 NewHostName = raw_input("Hostname? ")
344 Dn = "host=" + NewHost + "," + HostBaseDn
345 l.add_s(Dn,[("host", NewHost),
346 ("hostname", NewHostName),
347 ("objectClass", ("top", "debianServer"))])
350 NAttrs = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost)
352 print "Host",NewHost,"was not found."
356 HostDn = "host=" + Host + "," + HostBaseDn
357 OrderedIndex = copy.deepcopy(OrigOrderedIndex)
360 # Handle changing an arbitary value
361 if (Response == "a"):
362 Attr = raw_input("Attr? ")
363 ChangeAttr(Attrs[0],Attr)
366 if (Response == 'd'):
367 Really = raw_input("Really (type yes)? ")
370 print "Deleting",HostDn
374 # Convert the integer response
377 if (not OrderedIndex.has_key(ID) or (ID > 100 and RootMode == 0)):
383 # Print the what to do prompt
384 print "Changing LDAP entry '%s' (%s)" % (OrderedIndex[ID][0],OrderedIndex[ID][2])
385 print AttrPrompt[OrderedIndex[ID][2]][0]
386 ChangeAttr(Attrs[0],OrderedIndex[ID][2])