1""" 

2switch to new ACL implementation and add pre-suite NEW 

3 

4@contact: Debian FTP Master <ftpmaster@debian.org> 

5@copyright: 2012 Ansgar Burchardt <ansgar@debian.org> 

6@license: GNU General Public License version 2 or later 

7""" 

8 

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

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

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

12# (at your option) any later version. 

13 

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

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

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

17# GNU General Public License for more details. 

18 

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

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

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

22 

23################################################################################ 

24 

25import psycopg2 

26 

27from daklib.dak_exceptions import DBUpdateError 

28 

29statements = [ 

30 """ALTER TABLE suite ADD COLUMN new_queue_id INT REFERENCES policy_queue(id)""", 

31 """CREATE TABLE acl ( 

32 id SERIAL PRIMARY KEY NOT NULL, 

33 name TEXT NOT NULL, 

34 is_global BOOLEAN NOT NULL DEFAULT 'f', 

35 

36 match_fingerprint BOOLEAN NOT NULL DEFAULT 'f', 

37 match_keyring_id INTEGER REFERENCES keyrings(id), 

38 

39 allow_new BOOLEAN NOT NULL DEFAULT 'f', 

40 allow_source BOOLEAN NOT NULL DEFAULT 'f', 

41 allow_binary BOOLEAN NOT NULL DEFAULT 'f', 

42 allow_binary_all BOOLEAN NOT NULL DEFAULT 'f', 

43 allow_binary_only BOOLEAN NOT NULL DEFAULT 'f', 

44 allow_hijack BOOLEAN NOT NULL DEFAULT 'f', 

45 allow_per_source BOOLEAN NOT NULL DEFAULT 'f', 

46 deny_per_source BOOLEAN NOT NULL DEFAULT 'f' 

47 )""", 

48 """CREATE TABLE acl_architecture_map ( 

49 acl_id INTEGER NOT NULL REFERENCES acl(id) ON DELETE CASCADE, 

50 architecture_id INTEGER NOT NULL REFERENCES architecture(id) ON DELETE CASCADE, 

51 PRIMARY KEY (acl_id, architecture_id) 

52 )""", 

53 """CREATE TABLE acl_fingerprint_map ( 

54 acl_id INTEGER NOT NULL REFERENCES acl(id) ON DELETE CASCADE, 

55 fingerprint_id INTEGER NOT NULL REFERENCES fingerprint(id) ON DELETE CASCADE, 

56 PRIMARY KEY (acl_id, fingerprint_id) 

57 )""", 

58 """CREATE TABLE acl_per_source ( 

59 acl_id INTEGER NOT NULL REFERENCES acl(id) ON DELETE CASCADE, 

60 fingerprint_id INTEGER NOT NULL REFERENCES fingerprint(id) ON DELETE CASCADE, 

61 source TEXT NOT NULL, 

62 reason TEXT, 

63 PRIMARY KEY (acl_id, fingerprint_id, source) 

64 )""", 

65 """CREATE TABLE suite_acl_map ( 

66 suite_id INTEGER NOT NULL REFERENCES suite(id) ON DELETE CASCADE, 

67 acl_id INTEGER NOT NULL REFERENCES acl(id), 

68 PRIMARY KEY (suite_id, acl_id) 

69 )""", 

70] 

71 

72################################################################################ 

73 

74 

75def get_buildd_acl_id(c, keyring_id): 

76 c.execute( 

77 """ 

78 SELECT 'buildd-' || STRING_AGG(a.arch_string, '+' ORDER BY a.arch_string) 

79 FROM keyring_acl_map kam 

80 JOIN architecture a ON kam.architecture_id = a.id 

81 WHERE kam.keyring_id = %(keyring_id)s 

82 """, 

83 {"keyring_id": keyring_id}, 

84 ) 

85 (acl_name,) = c.fetchone() 

86 

87 c.execute("SELECT id FROM acl WHERE name = %(acl_name)s", {"acl_name": acl_name}) 

88 row = c.fetchone() 

89 if row is not None: 

90 return row[0] 

91 

92 c.execute( 

93 """ 

94 INSERT INTO acl 

95 ( name, allow_new, allow_source, allow_binary, allow_binary_all, allow_binary_only, allow_hijack) 

96 VALUES (%(acl_name)s, 't', 'f', 't', 'f', 't', 't') 

97 RETURNING id""", 

98 {"acl_name": acl_name}, 

99 ) 

100 (acl_id,) = c.fetchone() 

101 

102 c.execute( 

103 """INSERT INTO acl_architecture_map (acl_id, architecture_id) 

104 SELECT %(acl_id)s, architecture_id 

105 FROM keyring_acl_map 

106 WHERE keyring_id = %(keyring_id)s""", 

107 {"acl_id": acl_id, "keyring_id": keyring_id}, 

108 ) 

109 

110 return acl_id 

111 

112 

113def get_acl_id(c, acl_dd, acl_dm, keyring_id, source_acl_id, binary_acl_id): 

114 c.execute( 

115 "SELECT access_level FROM source_acl WHERE id = %(source_acl_id)s", 

116 {"source_acl_id": source_acl_id}, 

117 ) 

118 row = c.fetchone() 

119 if row is not None: 119 ↛ 122line 119 didn't jump to line 122, because the condition on line 119 was never false

120 source_acl = row[0] 

121 else: 

122 source_acl = None 

123 

124 c.execute( 

125 "SELECT access_level FROM binary_acl WHERE id = %(binary_acl_id)s", 

126 {"binary_acl_id": binary_acl_id}, 

127 ) 

128 row = c.fetchone() 

129 if row is not None: 129 ↛ 132line 129 didn't jump to line 132, because the condition on line 129 was never false

130 binary_acl = row[0] 

131 else: 

132 binary_acl = None 

133 

134 if source_acl == "full" and binary_acl == "full": 134 ↛ 136line 134 didn't jump to line 136, because the condition on line 134 was never false

135 return acl_dd 

136 elif source_acl == "dm" and binary_acl == "full": 

137 return acl_dm 

138 elif source_acl is None and binary_acl == "map": 

139 return get_buildd_acl_id(c, keyring_id) 

140 

141 raise Exception( 

142 "Cannot convert ACL combination automatically: binary_acl={0}, source_acl={1}".format( 

143 binary_acl, source_acl 

144 ) 

145 ) 

146 

147 

148def do_update(self): 

149 print(__doc__) 

150 try: 

151 c = self.db.cursor() 

152 

153 for stmt in statements: 

154 c.execute(stmt) 

155 

156 c.execute( 

157 """ 

158 INSERT INTO acl 

159 (name, allow_new, allow_source, allow_binary, allow_binary_all, allow_binary_only, allow_hijack) 

160 VALUES ('dd', 't', 't', 't', 't', 't', 't') 

161 RETURNING id""" 

162 ) 

163 (acl_dd,) = c.fetchone() 

164 

165 c.execute( 

166 """ 

167 INSERT INTO acl 

168 (name, allow_new, allow_source, allow_binary, allow_binary_all, allow_binary_only, allow_per_source, allow_hijack) 

169 VALUES ('dm', 'f', 't', 't', 't', 'f', 't', 'f') 

170 RETURNING id""" 

171 ) 

172 (acl_dm,) = c.fetchone() 

173 

174 # convert per-fingerprint ACLs 

175 

176 c.execute( 

177 "ALTER TABLE fingerprint ADD COLUMN acl_id INTEGER REFERENCES acl(id)" 

178 ) 

179 c.execute( 

180 """SELECT id, keyring, source_acl_id, binary_acl_id 

181 FROM fingerprint 

182 WHERE source_acl_id IS NOT NULL OR binary_acl_id IS NOT NULL""" 

183 ) 

184 for fingerprint_id, keyring_id, source_acl_id, binary_acl_id in c.fetchall(): 184 ↛ 185line 184 didn't jump to line 185, because the loop on line 184 never started

185 acl_id = get_acl_id( 

186 c, acl_dd, acl_dm, keyring_id, source_acl_id, binary_acl_id 

187 ) 

188 c.execute( 

189 "UPDATE fingerprint SET acl_id = %(acl_id)s WHERE id = %(fingerprint_id)s", 

190 {"acl_id": acl_id, "fingerprint_id": fingerprint_id}, 

191 ) 

192 c.execute( 

193 """ALTER TABLE fingerprint 

194 DROP COLUMN source_acl_id, 

195 DROP COLUMN binary_acl_id, 

196 DROP COLUMN binary_reject""" 

197 ) 

198 

199 # convert per-keyring ACLs 

200 c.execute("ALTER TABLE keyrings ADD COLUMN acl_id INTEGER REFERENCES acl(id)") 

201 c.execute( 

202 "SELECT id, default_source_acl_id, default_binary_acl_id FROM keyrings" 

203 ) 

204 for keyring_id, source_acl_id, binary_acl_id in c.fetchall(): 

205 acl_id = get_acl_id( 

206 c, acl_dd, acl_dm, keyring_id, source_acl_id, binary_acl_id 

207 ) 

208 c.execute( 

209 "UPDATE keyrings SET acl_id = %(acl_id)s WHERE id = %(keyring_id)s", 

210 {"acl_id": acl_id, "keyring_id": keyring_id}, 

211 ) 

212 c.execute( 

213 """ALTER TABLE keyrings 

214 DROP COLUMN default_source_acl_id, 

215 DROP COLUMN default_binary_acl_id, 

216 DROP COLUMN default_binary_reject""" 

217 ) 

218 

219 c.execute("DROP TABLE keyring_acl_map") 

220 c.execute("DROP TABLE binary_acl_map") 

221 c.execute("DROP TABLE binary_acl") 

222 c.execute("DROP TABLE source_acl") 

223 

224 # convert upload blocks 

225 c.execute( 

226 """ 

227 INSERT INTO acl 

228 ( name, is_global, allow_new, allow_source, allow_binary, allow_binary_all, allow_hijack, allow_binary_only, deny_per_source) 

229 VALUES ('blocks', 't', 't', 't', 't', 't', 't', 't', 't') 

230 RETURNING id""" 

231 ) 

232 (acl_block,) = c.fetchone() 

233 c.execute("SELECT source, fingerprint_id, reason FROM upload_blocks") 

234 for source, fingerprint_id, reason in c.fetchall(): 234 ↛ 235line 234 didn't jump to line 235, because the loop on line 234 never started

235 if fingerprint_id is None: 

236 raise Exception( 

237 "ERROR: upload blocks based on uid are no longer supported\n" 

238 "=========================================================\n" 

239 "\n" 

240 "dak now only supports upload blocks based on fingerprints. Please remove\n" 

241 "any uid-specific block by running\n" 

242 " DELETE FROM upload_blocks WHERE fingerprint_id IS NULL\n" 

243 "and try again." 

244 ) 

245 

246 c.execute( 

247 "INSERT INTO acl_match_source_map (acl_id, fingerprint_id, source, reason) VALUES (%(acl_id)s, %(fingerprint_id)s, %(source)s, %(reason)s)", 

248 { 

249 "acl_id": acl_block, 

250 "fingerprint_id": fingerprint_id, 

251 "source": source, 

252 "reason": reason, 

253 }, 

254 ) 

255 c.execute("DROP TABLE upload_blocks") 

256 

257 c.execute("UPDATE config SET value = '83' WHERE name = 'db_revision'") 

258 self.db.commit() 

259 

260 except psycopg2.ProgrammingError as msg: 

261 self.db.rollback() 

262 raise DBUpdateError( 

263 "Unable to apply sick update 83, rollback issued. Error message: {0}".format( 

264 msg 

265 ) 

266 )