Merge lp:~maxiberta/launchpad/named-auth-tokens-htaccess into lp:launchpad
- named-auth-tokens-htaccess
- Merge into devel
Proposed by
Maximiliano Bertacchini
Status: | Merged |
---|---|
Merged at revision: | 18137 |
Proposed branch: | lp:~maxiberta/launchpad/named-auth-tokens-htaccess |
Merge into: | lp:launchpad |
Prerequisite: | lp:~maxiberta/launchpad/named-auth-tokens |
Diff against target: |
465 lines (+174/-55) 7 files modified
lib/lp/archivepublisher/htaccess.py (+27/-19) lib/lp/archivepublisher/scripts/generate_ppa_htaccess.py (+32/-3) lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py (+77/-7) lib/lp/archivepublisher/tests/test_htaccess.py (+19/-9) lib/lp/soyuz/interfaces/archive.py (+7/-4) lib/lp/soyuz/model/archive.py (+5/-2) lib/lp/soyuz/tests/test_archive.py (+7/-11) |
To merge this branch: | bzr merge lp:~maxiberta/launchpad/named-auth-tokens-htaccess |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Maximiliano Bertacchini | Pending | ||
Review via email: mp+300002@code.launchpad.net |
This proposal supersedes a proposal from 2016-07-13.
Commit message
Add support for named auth tokens in ppa htpasswd creation.
Description of the change
Add support for named auth tokens in ppa htpasswd creation (including the removal of revoked named tokens).
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
review:
Approve
Revision history for this message
Maximiliano Bertacchini (maxiberta) : | # |
Revision history for this message
Colin Watson (cjwatson) : | # |
Revision history for this message
Maximiliano Bertacchini (maxiberta) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/archivepublisher/htaccess.py' |
2 | --- lib/lp/archivepublisher/htaccess.py 2013-06-20 05:50:00 +0000 |
3 | +++ lib/lp/archivepublisher/htaccess.py 2016-07-14 16:06:58 +0000 |
4 | @@ -58,43 +58,51 @@ |
5 | |
6 | file = open(filename, "a") |
7 | try: |
8 | - for entry in users: |
9 | - user, password, salt = entry |
10 | + for user, password, salt in users: |
11 | encrypted = crypt.crypt(password, salt) |
12 | file.write("%s:%s\n" % (user, encrypted)) |
13 | finally: |
14 | file.close() |
15 | |
16 | |
17 | -def htpasswd_credentials_for_archive(archive, tokens=None): |
18 | +def htpasswd_credentials_for_archive(archive): |
19 | """Return credentials for an archive for use with write_htpasswd. |
20 | |
21 | :param archive: An `IArchive` (must be private) |
22 | - :param tokens: Optional iterable of `IArchiveAuthToken`s. |
23 | :return: Iterable of tuples with (user, password, salt) for use with |
24 | write_htpasswd. |
25 | """ |
26 | assert archive.private, "Archive %r must be private" % archive |
27 | |
28 | - if tokens is None: |
29 | - tokens = IStore(ArchiveAuthToken).find( |
30 | - (ArchiveAuthToken.person_id, ArchiveAuthToken.token), |
31 | - ArchiveAuthToken.archive == archive, |
32 | - ArchiveAuthToken.date_deactivated == None) |
33 | + tokens = IStore(ArchiveAuthToken).find( |
34 | + (ArchiveAuthToken.person_id, ArchiveAuthToken.name, |
35 | + ArchiveAuthToken.token), |
36 | + ArchiveAuthToken.archive == archive, |
37 | + ArchiveAuthToken.date_deactivated == None) |
38 | # We iterate tokens more than once - materialise it. |
39 | tokens = list(tokens) |
40 | |
41 | - # The first .htpasswd entry is the buildd_secret. |
42 | - yield (BUILDD_USER_NAME, archive.buildd_secret, BUILDD_USER_NAME[:2]) |
43 | - |
44 | + # Preload map with person ID to person name. |
45 | person_ids = map(itemgetter(0), tokens) |
46 | names = dict( |
47 | IStore(Person).find( |
48 | (Person.id, Person.name), Person.id.is_in(set(person_ids)))) |
49 | - # Combine the token list with the person list, sorting by person ID |
50 | - # so the file can be compared later. |
51 | - sorted_tokens = [(token[1], names[token[0]]) for token in sorted(tokens)] |
52 | - # Iterate over tokens and write the appropriate htpasswd |
53 | - # entries for them. |
54 | - for token, person_name in sorted_tokens: |
55 | - yield (person_name, token, person_name[:2]) |
56 | + |
57 | + # Format the user field by combining the token list with the person list |
58 | + # (when token has person_id) or prepending a '+' (for named tokens). |
59 | + output = [] |
60 | + for person_id, token_name, token in tokens: |
61 | + if token_name: |
62 | + # A named auth token. |
63 | + output.append(('+' + token_name, token, token_name[:2])) |
64 | + else: |
65 | + # A subscription auth token. |
66 | + output.append((names[person_id], token, names[person_id][:2])) |
67 | + |
68 | + # The first .htpasswd entry is the buildd_secret. |
69 | + yield (BUILDD_USER_NAME, archive.buildd_secret, BUILDD_USER_NAME[:2]) |
70 | + |
71 | + # Iterate over tokens and write the appropriate htpasswd entries for them. |
72 | + # Sort by name/person ID so the file can be compared later. |
73 | + for user, password, salt in sorted(output): |
74 | + yield (user, password, salt) |
75 | |
76 | === modified file 'lib/lp/archivepublisher/scripts/generate_ppa_htaccess.py' |
77 | --- lib/lp/archivepublisher/scripts/generate_ppa_htaccess.py 2014-10-29 06:04:09 +0000 |
78 | +++ lib/lp/archivepublisher/scripts/generate_ppa_htaccess.py 2016-07-14 16:06:58 +0000 |
79 | @@ -89,8 +89,7 @@ |
80 | fd, temp_filename = tempfile.mkstemp(dir=pub_config.temproot) |
81 | os.close(fd) |
82 | |
83 | - write_htpasswd( |
84 | - temp_filename, htpasswd_credentials_for_archive(ppa)) |
85 | + write_htpasswd(temp_filename, htpasswd_credentials_for_archive(ppa)) |
86 | |
87 | return temp_filename |
88 | |
89 | @@ -176,6 +175,7 @@ |
90 | store = IStore(ArchiveSubscriber) |
91 | valid_tokens = store.find( |
92 | ArchiveAuthToken, |
93 | + ArchiveAuthToken.name == None, |
94 | ArchiveAuthToken.date_deactivated == None, |
95 | ArchiveAuthToken.archive_id == ArchiveSubscriber.archive_id, |
96 | ArchiveSubscriber.status == ArchiveSubscriberStatus.CURRENT, |
97 | @@ -186,6 +186,7 @@ |
98 | # all active tokens and valid tokens. |
99 | all_active_tokens = store.find( |
100 | ArchiveAuthToken, |
101 | + ArchiveAuthToken.name == None, |
102 | ArchiveAuthToken.date_deactivated == None) |
103 | |
104 | return all_active_tokens.difference(valid_tokens) |
105 | @@ -274,6 +275,22 @@ |
106 | *extra_expr) |
107 | return new_ppa_tokens |
108 | |
109 | + def getDeactivatedNamedTokens(self, since=None): |
110 | + """Return result set of named tokens deactivated since given time.""" |
111 | + now = datetime.now(pytz.UTC) |
112 | + |
113 | + store = IStore(ArchiveAuthToken) |
114 | + extra_expr = [] |
115 | + if since: |
116 | + extra_expr = [ArchiveAuthToken.date_deactivated >= since] |
117 | + tokens = store.find( |
118 | + ArchiveAuthToken, |
119 | + ArchiveAuthToken.name != None, |
120 | + ArchiveAuthToken.date_deactivated != None, |
121 | + ArchiveAuthToken.date_deactivated <= now, |
122 | + *extra_expr) |
123 | + return tokens |
124 | + |
125 | def getNewPrivatePPAs(self, since=None): |
126 | """Return the recently created private PPAs.""" |
127 | store = IStore(Archive) |
128 | @@ -294,6 +311,18 @@ |
129 | |
130 | last_success = self.getTimeToSyncFrom() |
131 | |
132 | + # Include ppas with named tokens deactivated since last time we ran. |
133 | + num_tokens = 0 |
134 | + for token in self.getDeactivatedNamedTokens(since=last_success): |
135 | + affected_ppas.add(token.archive) |
136 | + num_tokens += 1 |
137 | + |
138 | + new_ppa_count = len(affected_ppas) |
139 | + self.logger.debug( |
140 | + "%s deactivated named tokens since last run, %s PPAs affected" |
141 | + % (num_tokens, new_ppa_count - current_ppa_count)) |
142 | + current_ppa_count = new_ppa_count |
143 | + |
144 | # In addition to the ppas that are affected by deactivated |
145 | # tokens, we also want to include any ppas that have tokens |
146 | # created since the last time we ran. |
147 | @@ -316,7 +345,7 @@ |
148 | |
149 | self.logger.debug('%s PPAs require updating' % new_ppa_count) |
150 | for ppa in affected_ppas: |
151 | - # If this PPA is blacklisted, do not touch it's htaccess/pwd |
152 | + # If this PPA is blacklisted, do not touch its htaccess/pwd |
153 | # files. |
154 | blacklisted_ppa_names_for_owner = self.blacklist.get( |
155 | ppa.owner.name, []) |
156 | |
157 | === modified file 'lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py' |
158 | --- lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py 2016-01-26 15:47:37 +0000 |
159 | +++ lib/lp/archivepublisher/tests/test_generate_ppa_htaccess.py 2016-07-14 16:06:58 +0000 |
160 | @@ -32,6 +32,7 @@ |
161 | from lp.registry.interfaces.person import IPersonSet |
162 | from lp.registry.interfaces.teammembership import TeamMembershipStatus |
163 | from lp.services.config import config |
164 | +from lp.services.features.testing import FeatureFixture |
165 | from lp.services.log.logger import BufferLogger |
166 | from lp.services.osutils import ( |
167 | ensure_directory_exists, |
168 | @@ -43,6 +44,7 @@ |
169 | ArchiveStatus, |
170 | ArchiveSubscriberStatus, |
171 | ) |
172 | +from lp.soyuz.interfaces.archive import NAMED_AUTH_TOKEN_FEATURE_FLAG |
173 | from lp.testing import TestCaseWithFactory |
174 | from lp.testing.dbuser import ( |
175 | lp_dbuser, |
176 | @@ -72,6 +74,9 @@ |
177 | ubuntutest = getUtility(IDistributionSet)['ubuntutest'] |
178 | self.ppa.distribution = ubuntutest |
179 | |
180 | + # Enable named auth tokens. |
181 | + self.useFixture(FeatureFixture({NAMED_AUTH_TOKEN_FEATURE_FLAG: u"on"})) |
182 | + |
183 | def getScript(self, test_args=None): |
184 | """Return a HtaccessTokenGenerator instance.""" |
185 | if test_args is None: |
186 | @@ -260,6 +265,11 @@ |
187 | all_persons, persons1, persons2, tokens) = data |
188 | team1_person = persons1[0] |
189 | |
190 | + # Named tokens should be ignored for deactivation. |
191 | + self.ppa.newNamedAuthToken(u"tokenname1") |
192 | + named_token = self.ppa.newNamedAuthToken(u"tokenname2") |
193 | + named_token.deactivate() |
194 | + |
195 | # Initially, nothing is eligible for deactivation. |
196 | script = self.getScript() |
197 | script.deactivateInvalidTokens() |
198 | @@ -325,12 +335,9 @@ |
199 | sub2 = self.ppa.newSubscription(name16, self.ppa.owner) |
200 | token1 = self.ppa.newAuthToken(name12) |
201 | token2 = self.ppa.newAuthToken(name16) |
202 | + token3 = self.ppa.newNamedAuthToken(u"tokenname3") |
203 | self.layer.txn.commit() |
204 | - subs = [sub1] |
205 | - subs.append(sub2) |
206 | - tokens = [token1] |
207 | - tokens.append(token2) |
208 | - return subs, tokens |
209 | + return (sub1, sub2), (token1, token2, token3) |
210 | |
211 | def ensureNoFiles(self): |
212 | """Ensure the .ht* files don't already exist.""" |
213 | @@ -375,6 +382,36 @@ |
214 | os.remove(htaccess) |
215 | os.remove(htpasswd) |
216 | |
217 | + def testBasicOperation_with_named_tokens(self): |
218 | + """Invoke the actual script and make sure it generates some files.""" |
219 | + token1 = self.ppa.newNamedAuthToken(u"tokenname1") |
220 | + token2 = self.ppa.newNamedAuthToken(u"tokenname2") |
221 | + token3 = self.ppa.newNamedAuthToken(u"tokenname3") |
222 | + token3.deactivate() |
223 | + |
224 | + # Call the script and check that we have a .htaccess and a .htpasswd. |
225 | + htaccess, htpasswd = self.ensureNoFiles() |
226 | + script = self.getScript() |
227 | + script.main() |
228 | + self.assertThat([htaccess, htpasswd], AllMatch(FileExists())) |
229 | + with open(htpasswd) as htpasswd_file: |
230 | + contents = htpasswd_file.read() |
231 | + self.assertIn('+' + token1.name, contents) |
232 | + self.assertIn('+' + token2.name, contents) |
233 | + self.assertNotIn('+' + token3.name, contents) |
234 | + |
235 | + # Deactivate a named token and verify it is removed from .htpasswd. |
236 | + token2.deactivate() |
237 | + script.main() |
238 | + self.assertThat([htaccess, htpasswd], AllMatch(FileExists())) |
239 | + with open(htpasswd) as htpasswd_file: |
240 | + contents = htpasswd_file.read() |
241 | + self.assertIn('+' + token1.name, contents) |
242 | + self.assertNotIn('+' + token2.name, contents) |
243 | + self.assertNotIn('+' + token3.name, contents) |
244 | + os.remove(htaccess) |
245 | + os.remove(htpasswd) |
246 | + |
247 | def _setupOptionsData(self): |
248 | """Setup test data for option testing.""" |
249 | subs, tokens = self.setupDummyTokens() |
250 | @@ -598,14 +635,47 @@ |
251 | script = self.getScript() |
252 | self.assertContentEqual(tokens[1:], script.getNewTokens()) |
253 | |
254 | + def test_getDeactivatedNamedTokens_no_previous_run(self): |
255 | + """All deactivated named tokens returned if there is no record |
256 | + of previous run.""" |
257 | + last_start = datetime.now(pytz.UTC) - timedelta(seconds=90) |
258 | + before_last_start = last_start - timedelta(seconds=30) |
259 | + |
260 | + self.ppa.newNamedAuthToken(u"tokenname1") |
261 | + token2 = self.ppa.newNamedAuthToken(u"tokenname2") |
262 | + token2.deactivate() |
263 | + token3 = self.ppa.newNamedAuthToken(u"tokenname3") |
264 | + token3.date_deactivated = before_last_start |
265 | + |
266 | + script = self.getScript() |
267 | + self.assertContentEqual( |
268 | + [token2, token3], script.getDeactivatedNamedTokens()) |
269 | + |
270 | + def test_getDeactivatedNamedTokens_only_those_since_last_run(self): |
271 | + """Only named tokens deactivated since last run are returned.""" |
272 | + last_start = datetime.now(pytz.UTC) - timedelta(seconds=90) |
273 | + before_last_start = last_start - timedelta(seconds=30) |
274 | + tomorrow = datetime.now(pytz.UTC) + timedelta(days=1) |
275 | + |
276 | + self.ppa.newNamedAuthToken(u"tokenname1") |
277 | + token2 = self.ppa.newNamedAuthToken(u"tokenname2") |
278 | + token2.deactivate() |
279 | + token3 = self.ppa.newNamedAuthToken(u"tokenname3") |
280 | + token3.date_deactivated = before_last_start |
281 | + token4 = self.ppa.newNamedAuthToken(u"tokenname4") |
282 | + token4.date_deactivated = tomorrow |
283 | + |
284 | + script = self.getScript() |
285 | + self.assertContentEqual( |
286 | + [token2], script.getDeactivatedNamedTokens(last_start)) |
287 | + |
288 | def test_processes_PPAs_without_subscription(self): |
289 | # A .htaccess file is written for Private PPAs even if they don't have |
290 | # any subscriptions. |
291 | htaccess, htpasswd = self.ensureNoFiles() |
292 | transaction.commit() |
293 | |
294 | - # Call the script and check that we have a .htaccess and a |
295 | - # .htpasswd. |
296 | + # Call the script and check that we have a .htaccess and a .htpasswd. |
297 | return_code, stdout, stderr = self.runScript() |
298 | self.assertEqual( |
299 | return_code, 0, "Got a bad return code of %s\nOutput:\n%s" % |
300 | |
301 | === modified file 'lib/lp/archivepublisher/tests/test_htaccess.py' |
302 | --- lib/lp/archivepublisher/tests/test_htaccess.py 2012-05-31 01:57:07 +0000 |
303 | +++ lib/lp/archivepublisher/tests/test_htaccess.py 2016-07-14 16:06:58 +0000 |
304 | @@ -15,6 +15,8 @@ |
305 | ) |
306 | from lp.registry.interfaces.distribution import IDistributionSet |
307 | from lp.registry.interfaces.person import IPersonSet |
308 | +from lp.services.features.testing import FeatureFixture |
309 | +from lp.soyuz.interfaces.archive import NAMED_AUTH_TOKEN_FEATURE_FLAG |
310 | from lp.testing import TestCaseWithFactory |
311 | from lp.testing.layers import LaunchpadZopelessLayer |
312 | |
313 | @@ -36,6 +38,9 @@ |
314 | ubuntutest = getUtility(IDistributionSet)['ubuntutest'] |
315 | self.ppa.distribution = ubuntutest |
316 | |
317 | + # Enable named auth tokens. |
318 | + self.useFixture(FeatureFixture({NAMED_AUTH_TOKEN_FEATURE_FLAG: u"on"})) |
319 | + |
320 | def test_write_htpasswd(self): |
321 | """Test that writing the .htpasswd file works properly.""" |
322 | fd, filename = tempfile.mkstemp() |
323 | @@ -109,15 +114,20 @@ |
324 | self.ppa.newSubscription(name12, self.ppa.owner) |
325 | self.ppa.newSubscription(name16, self.ppa.owner) |
326 | first_created_token = self.ppa.newAuthToken(name16) |
327 | - tokens = [ |
328 | - (token.person_id, token.token) |
329 | - for token in [self.ppa.newAuthToken(name12), first_created_token]] |
330 | - |
331 | - credentials = list(htpasswd_credentials_for_archive(self.ppa, tokens)) |
332 | - |
333 | - self.assertContentEqual( |
334 | + second_created_token = self.ppa.newAuthToken(name12) |
335 | + named_token_20 = self.ppa.newNamedAuthToken(u"name20", as_dict=False) |
336 | + named_token_14 = self.ppa.newNamedAuthToken(u"name14", as_dict=False) |
337 | + named_token_99 = self.ppa.newNamedAuthToken(u"name99", as_dict=False) |
338 | + named_token_99.deactivate() |
339 | + |
340 | + credentials = list(htpasswd_credentials_for_archive(self.ppa)) |
341 | + |
342 | + # Use assertEqual instead of assertContentEqual to verify order. |
343 | + self.assertEqual( |
344 | credentials, [ |
345 | ("buildd", "geheim", "bu"), |
346 | - ("name12", tokens[0][1], "na"), |
347 | - ("name16", tokens[1][1], "na") |
348 | + ("+name14", named_token_14.token, "na"), |
349 | + ("+name20", named_token_20.token, "na"), |
350 | + ("name12", second_created_token.token, "na"), |
351 | + ("name16", first_created_token.token, "na"), |
352 | ]) |
353 | |
354 | === modified file 'lib/lp/soyuz/interfaces/archive.py' |
355 | --- lib/lp/soyuz/interfaces/archive.py 2016-07-07 21:32:09 +0000 |
356 | +++ lib/lp/soyuz/interfaces/archive.py 2016-07-14 16:06:58 +0000 |
357 | @@ -2097,22 +2097,25 @@ |
358 | :param dependency: is an `IArchive` object. |
359 | """ |
360 | |
361 | + @call_with(as_dict=True) |
362 | @operation_parameters( |
363 | name=TextLine(title=_("Authorization token name"), required=True), |
364 | token=TextLine( |
365 | title=_("Optional secret for this named token"), required=False)) |
366 | @export_write_operation() |
367 | @operation_for_version("devel") |
368 | - def newNamedAuthToken(name, token=None): |
369 | + def newNamedAuthToken(name, token=None, as_dict=False): |
370 | """Create a new named authorization token. |
371 | |
372 | :param name: An identifier string for this token. |
373 | :param token: Optional unicode text to use as the token. One will be |
374 | generated if not given. |
375 | + :param as_dict: Optional boolean, controls whether the return value is |
376 | + a dictionary or a full object. |
377 | |
378 | - :return: A dictionary where the value of `token` is the secret and |
379 | - the value of `archive_url` is the externally-usable archive URL |
380 | - including basic auth. |
381 | + :return: An `ArchiveAuthToken` object or a dictionary where the value |
382 | + of `token` is the secret and the value of `archive_url` is the |
383 | + externally-usable archive URL including basic auth. |
384 | """ |
385 | |
386 | @operation_parameters( |
387 | |
388 | === modified file 'lib/lp/soyuz/model/archive.py' |
389 | --- lib/lp/soyuz/model/archive.py 2016-07-07 21:32:09 +0000 |
390 | +++ lib/lp/soyuz/model/archive.py 2016-07-14 16:06:58 +0000 |
391 | @@ -1952,7 +1952,7 @@ |
392 | IStore(ArchiveAuthToken).add(archive_auth_token) |
393 | return archive_auth_token |
394 | |
395 | - def newNamedAuthToken(self, name, token=None): |
396 | + def newNamedAuthToken(self, name, token=None, as_dict=False): |
397 | """See `IArchive`.""" |
398 | |
399 | if not getFeatureFlag(NAMED_AUTH_TOKEN_FEATURE_FLAG): |
400 | @@ -1980,7 +1980,10 @@ |
401 | archive_auth_token.name = name |
402 | archive_auth_token.token = token |
403 | IStore(ArchiveAuthToken).add(archive_auth_token) |
404 | - return archive_auth_token.asDict() |
405 | + if as_dict: |
406 | + return archive_auth_token.asDict() |
407 | + else: |
408 | + return archive_auth_token |
409 | |
410 | def getNamedAuthToken(self, name): |
411 | """See `IArchive`.""" |
412 | |
413 | === modified file 'lib/lp/soyuz/tests/test_archive.py' |
414 | --- lib/lp/soyuz/tests/test_archive.py 2016-07-07 21:32:09 +0000 |
415 | +++ lib/lp/soyuz/tests/test_archive.py 2016-07-14 16:06:58 +0000 |
416 | @@ -1294,7 +1294,7 @@ |
417 | self.assertEqual(token.archive_url, url) |
418 | |
419 | def test_newNamedAuthToken_private_archive(self): |
420 | - res = self.private_ppa.newNamedAuthToken(u"tokenname") |
421 | + res = self.private_ppa.newNamedAuthToken(u"tokenname", as_dict=True) |
422 | token = getUtility(IArchiveAuthTokenSet).getActiveNamedTokenForArchive( |
423 | self.private_ppa, u"tokenname") |
424 | self.assertIsNotNone(token) |
425 | @@ -1320,25 +1320,21 @@ |
426 | self.private_ppa.newNamedAuthToken, u"tokenname") |
427 | |
428 | def test_newNamedAuthToken_with_custom_secret(self): |
429 | - self.private_ppa.newNamedAuthToken(u"tokenname", u"somesecret") |
430 | - token = getUtility(IArchiveAuthTokenSet).getActiveNamedTokenForArchive( |
431 | - self.private_ppa, u"tokenname") |
432 | - self.assertEqual(u"somesecret", token.token) |
433 | + token = self.private_ppa.newNamedAuthToken(u"tokenname", u"secret") |
434 | + self.assertEqual(u"secret", token.token) |
435 | |
436 | def test_getNamedAuthToken_with_no_token(self): |
437 | self.assertRaises( |
438 | NotFoundError, self.private_ppa.getNamedAuthToken, u"tokenname") |
439 | |
440 | def test_getNamedAuthToken_with_token(self): |
441 | - res = self.private_ppa.newNamedAuthToken(u"tokenname") |
442 | + res = self.private_ppa.newNamedAuthToken(u"tokenname", as_dict=True) |
443 | self.assertEqual( |
444 | self.private_ppa.getNamedAuthToken(u"tokenname"), |
445 | res) |
446 | |
447 | def test_revokeNamedAuthToken_with_token(self): |
448 | - self.private_ppa.newNamedAuthToken(u"tokenname") |
449 | - token = getUtility(IArchiveAuthTokenSet).getActiveNamedTokenForArchive( |
450 | - self.private_ppa, u"tokenname") |
451 | + token = self.private_ppa.newNamedAuthToken(u"tokenname") |
452 | self.private_ppa.revokeNamedAuthToken(u"tokenname") |
453 | self.assertIsNotNone(token.date_deactivated) |
454 | |
455 | @@ -1353,8 +1349,8 @@ |
456 | NotFoundError, self.private_ppa.getNamedAuthToken, u"tokenname") |
457 | |
458 | def test_getNamedAuthTokens(self): |
459 | - res1 = self.private_ppa.newNamedAuthToken(u"tokenname1") |
460 | - res2 = self.private_ppa.newNamedAuthToken(u"tokenname2") |
461 | + res1 = self.private_ppa.newNamedAuthToken(u"tokenname1", as_dict=True) |
462 | + res2 = self.private_ppa.newNamedAuthToken(u"tokenname2", as_dict=True) |
463 | self.private_ppa.newNamedAuthToken(u"tokenname3") |
464 | self.private_ppa.revokeNamedAuthToken(u"tokenname3") |
465 | self.assertContentEqual( |
Updated branch with fixes and improvements based on code review.