fix: remove no longer needed htpasswd infrastructure (#7590)

This commit is contained in:
Robert Sparks 2024-06-26 13:33:09 -05:00 committed by GitHub
parent a1902cfeca
commit 704f9967fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 0 additions and 168 deletions

View file

@ -1,30 +0,0 @@
# Copyright The IETF Trust 2016-2020, All Rights Reserved
# -*- coding: utf-8 -*-
import io
import subprocess, hashlib
from django.utils.encoding import force_bytes
from django.conf import settings
def update_htpasswd_file(username, password):
if getattr(settings, 'USE_PYTHON_HTDIGEST', None):
pass_file = settings.HTPASSWD_FILE
realm = settings.HTDIGEST_REALM
prefix = force_bytes('%s:%s:' % (username, realm))
key = force_bytes(hashlib.md5(prefix + force_bytes(password)).hexdigest())
f = io.open(pass_file, 'r+b')
pos = f.tell()
line = f.readline()
while line:
if line.startswith(prefix):
break
pos=f.tell()
line = f.readline()
f.seek(pos)
f.write(b'%s%s\n' % (prefix, key))
f.close()
else:
p = subprocess.Popen([settings.HTPASSWD_COMMAND, "-b", settings.HTPASSWD_FILE, username, password], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()

View file

@ -3,13 +3,10 @@
import datetime import datetime
import io
import logging # pyflakes:ignore import logging # pyflakes:ignore
import os
import re import re
import requests import requests
import requests_mock import requests_mock
import shutil
import time import time
import urllib import urllib
@ -21,7 +18,6 @@ from oic.oic.message import RegistrationResponse, AuthorizationResponse
from oic.utils.authn.client import CLIENT_AUTHN_METHOD from oic.utils.authn.client import CLIENT_AUTHN_METHOD
from oidc_provider.models import RSAKey from oidc_provider.models import RSAKey
from pyquery import PyQuery from pyquery import PyQuery
from unittest import skipIf
from urllib.parse import urlsplit from urllib.parse import urlsplit
import django.core.signing import django.core.signing
@ -35,7 +31,6 @@ import debug # pyflakes:ignore
from ietf.group.factories import GroupFactory, RoleFactory from ietf.group.factories import GroupFactory, RoleFactory
from ietf.group.models import Group, Role, RoleName from ietf.group.models import Group, Role, RoleName
from ietf.ietfauth.htpasswd import update_htpasswd_file
from ietf.ietfauth.utils import has_role from ietf.ietfauth.utils import has_role
from ietf.meeting.factories import MeetingFactory from ietf.meeting.factories import MeetingFactory
from ietf.nomcom.factories import NomComFactory from ietf.nomcom.factories import NomComFactory
@ -45,41 +40,12 @@ from ietf.person.tasks import send_apikey_usage_emails_task
from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory
from ietf.review.models import ReviewWish, UnavailablePeriod from ietf.review.models import ReviewWish, UnavailablePeriod
from ietf.stats.models import MeetingRegistration from ietf.stats.models import MeetingRegistration
from ietf.utils.decorators import skip_coverage
from ietf.utils.mail import outbox, empty_outbox, get_payload_text from ietf.utils.mail import outbox, empty_outbox, get_payload_text
from ietf.utils.test_utils import TestCase, login_testing_unauthorized from ietf.utils.test_utils import TestCase, login_testing_unauthorized
from ietf.utils.timezone import date_today from ietf.utils.timezone import date_today
if os.path.exists(settings.HTPASSWD_COMMAND):
skip_htpasswd_command = False
skip_message = ""
else:
skip_htpasswd_command = True
skip_message = ("Skipping htpasswd test: The binary for htpasswd wasn't found in the\n "
"location indicated in settings.py.")
print(" "+skip_message)
class IetfAuthTests(TestCase): class IetfAuthTests(TestCase):
def setUp(self):
super().setUp()
self.saved_use_python_htdigest = getattr(settings, "USE_PYTHON_HTDIGEST", None)
settings.USE_PYTHON_HTDIGEST = True
self.saved_htpasswd_file = settings.HTPASSWD_FILE
self.htpasswd_dir = self.tempdir('htpasswd')
settings.HTPASSWD_FILE = os.path.join(self.htpasswd_dir, "htpasswd")
io.open(settings.HTPASSWD_FILE, 'a').close() # create empty file
self.saved_htdigest_realm = getattr(settings, "HTDIGEST_REALM", None)
settings.HTDIGEST_REALM = "test-realm"
def tearDown(self):
shutil.rmtree(self.htpasswd_dir)
settings.USE_PYTHON_HTDIGEST = self.saved_use_python_htdigest
settings.HTPASSWD_FILE = self.saved_htpasswd_file
settings.HTDIGEST_REALM = self.saved_htdigest_realm
super().tearDown()
def test_index(self): def test_index(self):
self.assertEqual(self.client.get(urlreverse("ietf.ietfauth.views.index")).status_code, 200) self.assertEqual(self.client.get(urlreverse("ietf.ietfauth.views.index")).status_code, 200)
@ -162,15 +128,6 @@ class IetfAuthTests(TestCase):
return confirm_url return confirm_url
def username_in_htpasswd_file(self, username):
with io.open(settings.HTPASSWD_FILE) as f:
for l in f:
if l.startswith(username + ":"):
return True
with io.open(settings.HTPASSWD_FILE) as f:
print(f.read())
return False
# For the lowered barrier to account creation period, we are disabling this kind of failure # For the lowered barrier to account creation period, we are disabling this kind of failure
# def test_create_account_failure(self): # def test_create_account_failure(self):
@ -223,8 +180,6 @@ class IetfAuthTests(TestCase):
self.assertEqual(Person.objects.filter(user__username=email).count(), 1) self.assertEqual(Person.objects.filter(user__username=email).count(), 1)
self.assertEqual(Email.objects.filter(person__user__username=email).count(), 1) self.assertEqual(Email.objects.filter(person__user__username=email).count(), 1)
self.assertTrue(self.username_in_htpasswd_file(email))
# This also tests new account creation. # This also tests new account creation.
def test_create_existing_account(self): def test_create_existing_account(self):
@ -490,7 +445,6 @@ class IetfAuthTests(TestCase):
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)
q = PyQuery(r.content) q = PyQuery(r.content)
self.assertEqual(len(q("form .is-invalid")), 0) self.assertEqual(len(q("form .is-invalid")), 0)
self.assertTrue(self.username_in_htpasswd_file(user.username))
# reuse reset url # reuse reset url
r = self.client.get(confirm_url) r = self.client.get(confirm_url)
@ -614,23 +568,6 @@ class IetfAuthTests(TestCase):
self.assertEqual(r.status_code, 302) self.assertEqual(r.status_code, 302)
self.assertEqual(ReviewWish.objects.filter(doc=doc, team=review_req.team).count(), 0) self.assertEqual(ReviewWish.objects.filter(doc=doc, team=review_req.team).count(), 0)
def test_htpasswd_file_with_python(self):
# make sure we test both Python and call-out to binary
settings.USE_PYTHON_HTDIGEST = True
update_htpasswd_file("foo", "passwd")
self.assertTrue(self.username_in_htpasswd_file("foo"))
@skipIf(skip_htpasswd_command, skip_message)
@skip_coverage
def test_htpasswd_file_with_htpasswd_binary(self):
# make sure we test both Python and call-out to binary
settings.USE_PYTHON_HTDIGEST = False
update_htpasswd_file("foo", "passwd")
self.assertTrue(self.username_in_htpasswd_file("foo"))
def test_change_password(self): def test_change_password(self):
chpw_url = urlreverse("ietf.ietfauth.views.change_password") chpw_url = urlreverse("ietf.ietfauth.views.change_password")
prof_url = urlreverse("ietf.ietfauth.views.profile") prof_url = urlreverse("ietf.ietfauth.views.profile")

View file

@ -65,7 +65,6 @@ from ietf.group.models import Role, Group
from ietf.ietfauth.forms import ( RegistrationForm, PasswordForm, ResetPasswordForm, TestEmailForm, from ietf.ietfauth.forms import ( RegistrationForm, PasswordForm, ResetPasswordForm, TestEmailForm,
ChangePasswordForm, get_person_form, RoleEmailForm, ChangePasswordForm, get_person_form, RoleEmailForm,
NewEmailForm, ChangeUsernameForm, PersonPasswordForm) NewEmailForm, ChangeUsernameForm, PersonPasswordForm)
from ietf.ietfauth.htpasswd import update_htpasswd_file
from ietf.ietfauth.utils import has_role from ietf.ietfauth.utils import has_role
from ietf.name.models import ExtResourceName from ietf.name.models import ExtResourceName
from ietf.nomcom.models import NomCom from ietf.nomcom.models import NomCom
@ -222,8 +221,6 @@ def confirm_account(request, auth):
user = User.objects.create(username=email, email=email) user = User.objects.create(username=email, email=email)
user.set_password(password) user.set_password(password)
user.save() user.save()
# password is also stored in htpasswd file
update_htpasswd_file(email, password)
# make sure the rest of the person infrastructure is # make sure the rest of the person infrastructure is
# well-connected # well-connected
@ -552,8 +549,6 @@ def confirm_password_reset(request, auth):
user.set_password(password) user.set_password(password)
user.save() user.save()
# password is also stored in htpasswd file
update_htpasswd_file(user.username, password)
success = True success = True
else: else:
@ -693,8 +688,6 @@ def change_password(request):
user.set_password(new_password) user.set_password(new_password)
user.save() user.save()
# password is also stored in htpasswd file
update_htpasswd_file(user.username, new_password)
# keep the session # keep the session
update_session_auth_hash(request, user) update_session_auth_hash(request, user)
@ -731,13 +724,10 @@ def change_username(request):
form = ChangeUsernameForm(user, request.POST) form = ChangeUsernameForm(user, request.POST)
if form.is_valid(): if form.is_valid():
new_username = form.cleaned_data["username"] new_username = form.cleaned_data["username"]
password = form.cleaned_data["password"]
assert new_username in emails assert new_username in emails
user.username = new_username.lower() user.username = new_username.lower()
user.save() user.save()
# password is also stored in htpasswd file
update_htpasswd_file(user.username, password)
# keep the session # keep the session
update_session_auth_hash(request, user) update_session_auth_hash(request, user)

View file

@ -979,8 +979,6 @@ DE_GFM_BINARY = '/usr/bin/de-gfm.ruby2.5'
# Account settings # Account settings
DAYS_TO_EXPIRE_REGISTRATION_LINK = 3 DAYS_TO_EXPIRE_REGISTRATION_LINK = 3
MINUTES_TO_EXPIRE_RESET_PASSWORD_LINK = 60 MINUTES_TO_EXPIRE_RESET_PASSWORD_LINK = 60
HTPASSWD_COMMAND = "/usr/bin/htpasswd"
HTPASSWD_FILE = "/a/www/htpasswd"
# Generation of pdf files # Generation of pdf files
GHOSTSCRIPT_COMMAND = "/usr/bin/gs" GHOSTSCRIPT_COMMAND = "/usr/bin/gs"

View file

@ -1,63 +0,0 @@
# Copyright The IETF Trust 2014-2020, All Rights Reserved
import io
import sys
from textwrap import dedent
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
def import_htpasswd_file(filename, verbosity=1, overwrite=False):
with io.open(filename) as file:
for line in file:
if not ':' in line:
raise ValueError('Found a line without colon separator in the htpassword file %s:'
' "%s"' % (file.name, line))
username, password = line.strip().split(':', 1)
try:
user = User.objects.get(username__iexact=username)
if overwrite == True or not user.password:
if password.startswith('{SHA}'):
user.password = "sha1$$%s" % password[len('{SHA}'):]
elif password.startswith('$apr1$'):
user.password = "md5$%s" % password[len('$apr1$'):]
else: # Assume crypt
user.password = "crypt$$%s" % password
user.save()
if verbosity > 0:
sys.stderr.write('.')
if verbosity > 1:
sys.stderr.write(' %s\n' % username)
except User.DoesNotExist:
if verbosity > 1:
sys.stderr.write('\nNo such user: %s\n' % username)
class Command(BaseCommand):
"""
Import passwords from one or more htpasswd files to Django's auth_user table.
This command only imports passwords; it does not import usernames, as that
would leave usernames without associated Person records in the database,
something which is undesirable.
By default the command won't overwrite existing password entries, but
given the --force switch, it will overwrite existing entries too. Without
the --force switch, the command is safe to run repeatedly.
"""
help = dedent(__doc__).strip()
def add_arguments(self, parser):
parser.add_argument('--force',
action='store_true', dest='overwrite', default=False,
help='Overwrite existing passwords in the auth_user table.')
args = '[path [path [...]]]'
def handle(self, *filenames, **options):
overwrite = options.get('overwrite', False)
verbosity = int(options.get('verbosity'))
for fn in filenames:
import_htpasswd_file(fn, verbosity=verbosity, overwrite=overwrite)