launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #21918
[Merge] lp:~cjwatson/launchpad/htpasswd-salt into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/htpasswd-salt into lp:launchpad.
Commit message:
Ensure that PPA .htpasswd salts are drawn from the correct alphabet.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1722209 in Launchpad itself: "crypt.crypt() changed in Xenial causing incorrectly generated .htpasswd entries"
https://bugs.launchpad.net/launchpad/+bug/1722209
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/htpasswd-salt/+merge/332013
This is all a horrible hack, but I think it'll do until we have a better authentication system.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/htpasswd-salt into lp:launchpad.
=== modified file 'lib/lp/archivepublisher/htaccess.py'
--- lib/lp/archivepublisher/htaccess.py 2016-07-13 16:42:34 +0000
+++ lib/lp/archivepublisher/htaccess.py 2017-10-09 12:45:58 +0000
@@ -1,6 +1,6 @@
#!/usr/bin/python
#
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Writing of htaccess and htpasswd files."""
@@ -13,7 +13,7 @@
'write_htpasswd',
]
-
+import base64
import crypt
from operator import itemgetter
import os
@@ -65,6 +65,21 @@
file.close()
+# XXX cjwatson 2017-10-09: This whole mechanism of writing password files to
+# disk (as opposed to e.g. using a WSGI authentication provider that checks
+# passwords against the database) is terrible, but as long as we're using it
+# we should use something like bcrypt rather than DES-based crypt.
+def make_salt(s):
+ """Produce a salt from an input string.
+
+ This ensures that salts are drawn from the correct alphabet
+ ([./a-zA-Z0-9]).
+ """
+ # As long as the input string is at least one character long, there will
+ # be no padding within the first two characters.
+ return base64.b64encode((s or " ").encode("UTF-8"), altchars=b"./")[:2]
+
+
def htpasswd_credentials_for_archive(archive):
"""Return credentials for an archive for use with write_htpasswd.
@@ -94,10 +109,11 @@
for person_id, token_name, token in tokens:
if token_name:
# A named auth token.
- output.append(('+' + token_name, token, token_name[:2]))
+ output.append(('+' + token_name, token, make_salt(token_name)))
else:
# A subscription auth token.
- output.append((names[person_id], token, names[person_id][:2]))
+ output.append(
+ (names[person_id], token, make_salt(names[person_id])))
# The first .htpasswd entry is the buildd_secret.
yield (BUILDD_USER_NAME, archive.buildd_secret, BUILDD_USER_NAME[:2])
=== modified file 'lib/lp/archivepublisher/tests/test_htaccess.py'
--- lib/lp/archivepublisher/tests/test_htaccess.py 2016-07-13 16:42:34 +0000
+++ lib/lp/archivepublisher/tests/test_htaccess.py 2017-10-09 12:45:58 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Test htaccess/htpasswd file generation. """
@@ -111,23 +111,27 @@
self.ppa.buildd_secret = "geheim"
name12 = getUtility(IPersonSet).getByName("name12")
name16 = getUtility(IPersonSet).getByName("name16")
+ hyphenated = self.factory.makePerson(name="a-b-c")
self.ppa.newSubscription(name12, self.ppa.owner)
self.ppa.newSubscription(name16, self.ppa.owner)
+ self.ppa.newSubscription(hyphenated, self.ppa.owner)
first_created_token = self.ppa.newAuthToken(name16)
second_created_token = self.ppa.newAuthToken(name12)
+ third_created_token = self.ppa.newAuthToken(hyphenated)
named_token_20 = self.ppa.newNamedAuthToken(u"name20", as_dict=False)
named_token_14 = self.ppa.newNamedAuthToken(u"name14", as_dict=False)
named_token_99 = self.ppa.newNamedAuthToken(u"name99", as_dict=False)
named_token_99.deactivate()
+ expected_credentials = [
+ ("buildd", "geheim", "bu"),
+ ("+name14", named_token_14.token, "bm"),
+ ("+name20", named_token_20.token, "bm"),
+ ("a-b-c", third_created_token.token, "YS"),
+ ("name12", second_created_token.token, "bm"),
+ ("name16", first_created_token.token, "bm"),
+ ]
credentials = list(htpasswd_credentials_for_archive(self.ppa))
# Use assertEqual instead of assertContentEqual to verify order.
- self.assertEqual(
- credentials, [
- ("buildd", "geheim", "bu"),
- ("+name14", named_token_14.token, "na"),
- ("+name20", named_token_20.token, "na"),
- ("name12", second_created_token.token, "na"),
- ("name16", first_created_token.token, "na"),
- ])
+ self.assertEqual(expected_credentials, credentials)
Follow ups