1#! /usr/bin/env python3 

2 

3""" Sync PostgreSQL users with system users """ 

4# Copyright (C) 2001, 2002, 2006 James Troup <james@nocrew.org> 

5 

6# This program is free software; you can redistribute it and/or modify 

7# it under the terms of the GNU General Public License as published by 

8# the Free Software Foundation; either version 2 of the License, or 

9# (at your option) any later version. 

10 

11# This program is distributed in the hope that it will be useful, 

12# but WITHOUT ANY WARRANTY; without even the implied warranty of 

13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

14# GNU General Public License for more details. 

15 

16# You should have received a copy of the GNU General Public License 

17# along with this program; if not, write to the Free Software 

18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

19 

20################################################################################ 

21 

22import pwd 

23import grp 

24import sys 

25import re 

26import apt_pkg 

27 

28from daklib.config import Config 

29from daklib.dbconn import * 

30from daklib import utils 

31 

32################################################################################ 

33 

34 

35def usage(exit_code=0): 

36 print("""Usage: dak import-users-from-passwd [OPTION]... 

37Sync PostgreSQL's users with system users. 

38 

39 -h, --help show this help and exit 

40 -n, --no-action don't do anything 

41 -q, --quiet be quiet about what is being done 

42 -v, --verbose explain what is being done""") 

43 sys.exit(exit_code) 

44 

45################################################################################ 

46 

47 

48def main(): 

49 cnf = Config() 

50 

51 Arguments = [('n', "no-action", "Import-Users-From-Passwd::Options::No-Action"), 

52 ('q', "quiet", "Import-Users-From-Passwd::Options::Quiet"), 

53 ('v', "verbose", "Import-Users-From-Passwd::Options::Verbose"), 

54 ('h', "help", "Import-Users-From-Passwd::Options::Help")] 

55 for i in ["no-action", "quiet", "verbose", "help"]: 

56 key = "Import-Users-From-Passwd::Options::%s" % i 

57 if key not in cnf: 57 ↛ 55line 57 didn't jump to line 55, because the condition on line 57 was never false

58 cnf[key] = "" 

59 

60 arguments = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) 

61 Options = cnf.subtree("Import-Users-From-Passwd::Options") 

62 

63 if Options["Help"]: 63 ↛ 65line 63 didn't jump to line 65, because the condition on line 63 was never false

64 usage() 

65 elif arguments: 

66 utils.warn("dak import-users-from-passwd takes no non-option arguments.") 

67 usage(1) 

68 

69 session = DBConn().session() 

70 valid_gid = cnf.get("Import-Users-From-Passwd::ValidGID", "") 

71 if valid_gid: 

72 debiangrp = grp.getgrnam(valid_gid).gr_mem 

73 else: 

74 debiangrp = [] 

75 

76 passwd_unames = {} 

77 for entry in pwd.getpwall(): 

78 uname = entry[0] 

79 if uname not in debiangrp: 

80 if Options["Verbose"]: 

81 print("Skipping %s (Not in group %s)." % (uname, valid_gid)) 

82 continue 

83 passwd_unames[uname] = "" 

84 

85 postgres_unames = {} 

86 q = session.execute("SELECT usename FROM pg_user") 

87 for i in q.fetchall(): 

88 uname = i[0] 

89 postgres_unames[uname] = "" 

90 

91 known_postgres_unames = {} 

92 for i in cnf.get("Import-Users-From-Passwd::KnownPostgres", "").split(","): 

93 uname = i.strip() 

94 known_postgres_unames[uname] = "" 

95 

96 for uname in sorted(postgres_unames): 

97 if uname not in passwd_unames and uname not in known_postgres_unames: 

98 print("I: Deleting %s from Postgres, no longer in passwd or list of known Postgres users" % (uname)) 

99 q = session.execute('DROP USER "%s"' % (uname)) 

100 

101 safe_name = re.compile('^[A-Za-z0-9]+$') 

102 for uname in sorted(passwd_unames): 

103 if uname not in postgres_unames: 

104 if not Options["Quiet"]: 

105 print("Creating %s user in Postgres." % (uname)) 

106 if not Options["No-Action"]: 

107 if safe_name.match(uname): 

108 # NB: I never figured out how to use a bind parameter for this query 

109 # XXX: Fix this as it looks like a potential SQL injection attack to me 

110 # (hence the safe_name match we do) 

111 try: 

112 q = session.execute('CREATE USER "%s"' % (uname)) 

113 session.commit() 

114 except Exception as e: 

115 utils.warn("Could not create user %s (%s)" % (uname, str(e))) 

116 session.rollback() 

117 else: 

118 print("NOT CREATING USER %s. Doesn't match safety regex" % uname) 

119 

120 session.commit() 

121 

122####################################################################################### 

123 

124 

125if __name__ == '__main__': 

126 main()