"Fossies" - the Fresh Open Source Software Archive 
Member "mkapachepw-1.121/mkapachepw.py" (12 Apr 2005, 12737 Bytes) of package /linux/www/old/mkapachepw-1.121.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "mkapachepw.py" see the
Fossies "Dox" file reference documentation.
1 #!/usr/bin/env python
2 # mkapachepw.py
3 # Generate Apache-Compatible Password And Group Files
4 # From Unix System Passwords And Groups.
5 #
6 # Copyright (c) 2005 TundraWare Inc. All Rights Reserved.
7 # For Updates See: http://www.tundraware.com/Software/mkapachepw
8
9 # Program Information
10
11 PROGNAME = "mkapachepw"
12 RCSID = "$Id: mkapachepw.py,v 1.121 2005/04/12 07:10:35 root Exp $"
13 VERSION = RCSID.split()[2]
14
15 # Copyright Information
16
17 CPRT = "(c)"
18 DATE = "2005"
19 OWNER = "TundraWare Inc."
20 RIGHTS = "All Rights Reserved"
21 COPYRIGHT = "Copyright %s %s %s %s. " % (CPRT, DATE, OWNER, RIGHTS)
22
23
24 #----------------------------------------------------------#
25 # Variables User Might Change #
26 #----------------------------------------------------------#
27
28 BOGUSID = 100000 # Fake GID/UID used when external files
29 GRFILE = "./.htgroups" # Group output file
30 USRFILE = "./.htusers" # User output file
31 STARTUID = 100 # User IDs below this ignored
32 STARTGID = 100 # Group IDS below this ignored
33
34
35
36 #------------------- Nothing Below Here Should Need Changing ------------------#
37
38
39 #----------------------------------------------------------#
40 # Imports #
41 #----------------------------------------------------------#
42
43 import getopt
44 import grp
45 import os
46 import pwd
47 from socket import getfqdn
48 import sys
49 import time
50
51
52 #----------------------------------------------------------#
53 # Aliases & Redefinitions #
54 #----------------------------------------------------------#
55
56
57
58 #----------------------------------------------------------#
59 # Constants & Literals #
60 #----------------------------------------------------------#
61
62
63
64 #####
65 # Constants
66 #####
67
68
69
70 #####
71 # Literals
72 #####
73
74 CMDLINE = "# Command Line: %s\n" % " ".join(sys.argv)
75 TIMESTAMP = "# Created By %s %s On %s At %s\n" % (PROGNAME, VERSION, getfqdn(), time.asctime())
76
77 GID = 'GID'
78 GROUP = 'Group'
79 UID = 'UID'
80 USER = 'User'
81
82
83 #----------------------------------------------------------#
84 # Prompts, & Application Strings #
85 #----------------------------------------------------------#
86
87
88 #####
89 # Error And Warning Messages
90 #####
91
92 eABORT = "Aborting ..."
93 eERROR = "ERROR"
94 eFILEOPEN = "Cannot Open File '%s'."
95 eINVALIDID = "'%s' Is An Invalid %s ID."
96 eINVALIDNAME = "'%s' Is An Invalid %s Name."
97 eINVALIDSTART = "Invalid Starting %s, '%s' - Must Be An Integer Value."
98 eNOPREFIX = "'%s' Must Be Prefixed With '+' or '-' To Indicate Desired Action."
99
100 wCOLLIDE = "'%s' Entry In %s Conflicts With Entry Already In %s List."
101 wOVERWRITE = "Overwriting..."
102 wWARNING = "WARNING"
103
104
105 #####
106 # Usage Prompts
107 #####
108
109 uTable = [PROGNAME + " " + VERSION + " - %s\n" % COPYRIGHT,
110 "usage: " + PROGNAME + " [-sGUguIicqOohv]",
111 " where,",
112 " -s do not process system password/group files (default: process these files)",
113 " -G list of groups to include (+group | +GID) or exclude (-group | -GID) (default: none)",
114 " -U list of users to include (+user | + UID) or exclude (-user | -UID) (default: none)",
115 " -g # smallest GID to include in output (default: 100)",
116 " -u # smallest UID to include in output (default: 100)",
117 " -I file include file containing other group information (default: none)",
118 " -i file include file containing other user information (default: none)",
119 " -c do not permit entries to be overwritten (default: allow - only warn)",
120 " -q quiet mode - suppresses warning messages",
121 " -O file group file name, - selects stdout (default: ./.htgroups)",
122 " -o file user file name, - selects stdout (default: ./.htusers)",
123 " -h print this help information",
124 " -v print detailed version information",
125 ]
126
127
128 #----------------------------------------------------------#
129 # Global Variables & Data Structures #
130 #----------------------------------------------------------#
131
132 enumerated = [] # Place to store command line in/exclude enumerations
133 includes = [] # Place to store names of files to include
134
135 groups = {} # Place to store group information
136 users = {} # Place to store user information
137
138 ALLOWCOLLISIONS = True # Allow entries to overwrite each other (with warning)
139 QUIET = False # Suppress display of warning messages
140 SYSFILES = True # Flag to enable/disable inclusion of system group/pw
141
142
143 #--------------------------- Code Begins Here ---------------------------------#
144
145
146 #----------------------------------------------------------#
147 # Object Base Class Definitions #
148 #----------------------------------------------------------#
149
150
151
152 #----------------------------------------------------------#
153 # Supporting Function Definitions #
154 #----------------------------------------------------------#
155
156
157 #####
158 # Print An Error Message
159 #####
160
161 def ErrorMsg(emsg, Warning=False, Action=eABORT):
162
163 if Warning:
164 if QUIET: # Quiet mode suppresses warning messages
165 return
166 prompt = wWARNING
167 else:
168 prompt = eERROR
169
170 print PROGNAME + " " + VERSION + " " + prompt + ": " + emsg + " " + Action
171
172 # End of 'ErrorMsg()'
173
174
175 #####
176 # Print Usage Information
177 #####
178
179 def Usage():
180 for line in uTable:
181 print line
182
183 # End of 'Usage()'
184
185
186 #####
187 # Process An Enumerated List Of Groups/Users To Include Or Exclude.
188 #
189 # The 'items' argument must be a string with the names or numbers to
190 # process, with a '-' or '+' prepended to indicate Delete or Add,
191 # respectively.
192 #####
193
194 def ProcessEnumeratedList(items, lookup, name):
195
196 if name == GROUP:
197 master = groups
198 else:
199 master = users
200
201 for item in items.split():
202 orig = item
203
204 # Verify argument is in correct format and determine type of
205 # operation desired.
206
207 if item[0] == '-':
208 additem = False
209
210 elif item[0] == '+':
211 additem = True
212
213 else:
214 ErrorMsg(eNOPREFIX % item)
215 sys.exit(2)
216
217 item = item[1:] # We just need the item Name/ID portion
218
219 # Convert GIDs and UIDs to names first
220 try:
221 item = int(item)
222
223 # Handle the case where the ID does not exist
224 try:
225 item = lookup(item)[0]
226
227 except:
228 ErrorMsg(eINVALIDID % (orig[1:], name))
229 sys.exit(2)
230
231 # If not, assume it is a name and look it up
232 except ValueError:
233
234 # Make sure it even exists
235
236 if item not in master:
237 ErrorMsg(eINVALIDNAME % (item, name))
238 sys.exit(2)
239
240 # Do the actual in/exclusion
241
242 # Include
243 if additem:
244 master[item][2] = True # Mark entry as protected
245
246 # Exclude
247 else:
248 del master[item]
249
250 # End of 'ProcessEnumeratedList()'
251
252
253 #####
254 # Read A File Into A Local List, Stripping Newlines, And
255 # Suppressing Blank Lines
256 #####
257
258 def ReadFile(filename):
259
260 temp = []
261 try:
262 f = open(filename)
263 for l in f.readlines():
264 if l[-1] == '\n': # Get rid of trailing newlines
265 l = l[:-1]
266 l = l.split('#')[0].strip() # Get rid of comments
267
268 if l: # Add any non-blank lines
269 name, members = l.split(':')
270 members = members.split()
271 temp.append([name, members])
272 f.close()
273
274 except:
275 ErrorMsg(eFILEOPEN % filename)
276 sys.exit(1)
277
278 return temp
279
280 # End of 'ReadFile()'
281
282
283 #----------------------------------------------------------#
284 # Program Entry Point #
285 #----------------------------------------------------------#
286
287
288 #####
289 # Command line processing - Process any options set in the
290 # environment first, and then those given on the command line
291 #####
292
293 OPTIONS = sys.argv[1:]
294 envopt = os.getenv(PROGNAME.upper())
295 if envopt:
296 OPTIONS = envopt.split() + OPTIONS
297
298 try:
299 opts, args = getopt.getopt(OPTIONS, '-sG:U:g:u:I:i:cqO:o:hv')
300 except getopt.GetoptError:
301 Usage()
302 sys.exit(1)
303
304 # This command line accepts no args
305 if args:
306 Usage()
307 sys.exit(1)
308
309
310 for opt, val in opts:
311 if opt == "-s":
312 SYSFILES = False
313 if opt == "-G":
314 enumerated.append([val, grp.getgrgid, GROUP])
315 if opt == "-U":
316 enumerated.append([val, pwd.getpwuid, USER])
317 if opt == "-g":
318 try:
319 STARTGID=int(val)
320 except:
321 ErrorMsg(eINVALIDSTART % (GID, val))
322 sys.exit(1)
323 if opt == "-u":
324 try:
325 STARTUID=int(val)
326 except:
327 ErrorMsg(eINVALIDSTART % (UID, val))
328 sys.exit(1)
329 if opt == "-I":
330 includes.append([val, groups])
331 if opt == "-i":
332 includes.append([val, users])
333 if opt == "-c":
334 ALLOWCOLLISIONS = False
335 if opt == "-q":
336 QUIET = True
337 if opt == "-O":
338 GRFILE = val
339 if opt == "-o":
340 USRFILE = val
341 if opt == "-h":
342 Usage()
343 sys.exit(0)
344 if opt == "-v":
345 print RCSID
346 sys.exit(0)
347
348 #####
349 # Build List Of System Groups And Users
350 #####
351
352
353 # Can be suppressed with the -s command line argument
354
355 if SYSFILES:
356
357 Protected = False
358
359 #####
360 # Build List Of Groups
361 #####
362
363 for group in grp.getgrall():
364
365 gname, gpw, gid, gmembers = group[:4]
366
367 groups[gname] = [gid, [], Protected]
368 for member in gmembers:
369 groups[gname][1].append(member)
370
371 #####
372 # Build A List Of Users
373 #####
374
375 for user in pwd.getpwall():
376
377 uname, pw, uid, gid = user[:4]
378 gname = grp.getgrgid(gid)[0]
379
380 users[uname] = [uid, pw, Protected]
381
382 if uname not in groups[gname][1]:
383 groups[gname][1].append(uname)
384
385
386 #####
387 # Process Included Files
388 #####
389
390 for include in includes:
391
392 # Read the file into a temporary list
393 filename, db = include[:]
394 temp = ReadFile(filename)
395
396 # Add each entry to the appropriate in-memory database
397 for entry in temp:
398
399 # Group entries have a list of members
400 if db == groups:
401 members = entry[1]
402 name = GROUP
403
404 # User entries have a single password
405 else:
406 members = entry[1][0]
407 name = USER
408
409 # See if this entry will overwrite an existing one
410 # If it will, warn if collisions are permitted
411 # Error out otherwise
412
413 if entry[0] in db:
414
415 if ALLOWCOLLISIONS:
416 ErrorMsg(wCOLLIDE % (entry[0], filename, name), Warning=True, Action=wOVERWRITE)
417 else:
418 ErrorMsg(wCOLLIDE % (entry[0], filename, name))
419 sys.exit(4)
420
421 db[entry[0]] = [BOGUSID, members, False]
422
423
424 #####
425 # Process Any Enumerated Inclusions/Exclusions
426 #####
427
428 for enum in enumerated:
429 ProcessEnumeratedList(*enum)
430
431
432 #####
433 # Write Out The Files
434 #####
435
436 # Files Should Be Read-Only
437
438 os.umask(0377)
439
440 # Group File
441
442 try:
443 if GRFILE == '-':
444 grfile = sys.stdout
445 else:
446 grfile = open(GRFILE, "w")
447 except:
448 ErrorMsg(eFILEOPEN % GRFILE)
449 sys.exit(3)
450
451 grfile.write(TIMESTAMP)
452 grfile.write(CMDLINE)
453
454 # Write out groups if they are either protected or >= specified starting ID
455
456 gnames = groups.keys()
457 gnames.sort()
458 for gname in gnames:
459 if (groups[gname][2]) or (groups[gname][0] >= STARTGID):
460 grfile.write("%s: %s\n" % (gname, " ".join(groups[gname][1])))
461
462 grfile.close()
463
464
465 # Password File
466
467 try:
468 if USRFILE == '-':
469 pwfile = sys.stdout
470 else:
471 pwfile = open(USRFILE, "w")
472 except:
473 ErrorMsg(eFILEOPEN % USRFILE)
474 sys.exit(3)
475
476 pwfile.write(TIMESTAMP)
477 pwfile.write(CMDLINE)
478
479 # Write out users if they are either protected or >= specified starting ID
480 # Unless explicitly protected, any account that has '*' as a password
481 # (thus indicating it does not support login), will be suppressed.
482
483 unames = users.keys()
484 unames.sort()
485 for uname in unames:
486 if (users[uname][2]) or ((users[uname][0] >= STARTUID) and (users[uname][1] != '*')):
487 pwfile.write("%s:%s\n" % (uname, users[uname][1]))
488
489 pwfile.close()