[ReviewBoard] Support parallel-installable python-django14 package

Stephen Gallagher sgallagh at fedoraproject.org
Fri Feb 21 21:17:30 UTC 2014


commit 88694758daea62da84393b66aa7f971dbbc0dc8d
Author: Stephen Gallagher <sgallagh at redhat.com>
Date:   Fri Feb 21 16:19:04 2014 -0500

    Support parallel-installable python-django14 package

 0001-Improve-LDAP-user-lookups.patch              |  188 +++++++++++++++++++++
 0002-Support-parallel-installed-Django-eggs.patch |   33 ++++
 FED04-Soften-requires.txt.patch                   |   36 ++++
 ReviewBoard.spec                                  |   32 +++-
 4 files changed, 279 insertions(+), 10 deletions(-)
---
diff --git a/0001-Improve-LDAP-user-lookups.patch b/0001-Improve-LDAP-user-lookups.patch
new file mode 100644
index 0000000..421cea8
--- /dev/null
+++ b/0001-Improve-LDAP-user-lookups.patch
@@ -0,0 +1,188 @@
+From 8ef32849fd94fc0ecd2dfe4acbc987a77de5a254 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Mon, 6 Jan 2014 20:47:52 -0500
+Subject: [PATCH] Improve LDAP user lookups
+
+This patch simplifies configuration of user lookups in LDAP and
+also provides better help text to aid the administrator setting it
+up.
+
+The email thread below has more details:
+https://groups.google.com/forum/#!topic/reviewboard-dev/J6W1o9Eb2IY
+
+Conflicts:
+	reviewboard/accounts/backends.py
+	reviewboard/accounts/forms.py
+---
+ reviewboard/accounts/backends.py | 33 ++++++++++++++++++++-------------
+ reviewboard/accounts/forms.py    | 35 +++++++++++++++++++++++------------
+ reviewboard/admin/siteconfig.py  |  2 ++
+ 3 files changed, 45 insertions(+), 25 deletions(-)
+
+diff --git a/reviewboard/accounts/backends.py b/reviewboard/accounts/backends.py
+index 9cb284dd58368a426386b7c7b58d2531912d353f..392d04c4fbb5a4927401e5c8aa2317a0f303e1cc 100644
+--- a/reviewboard/accounts/backends.py
++++ b/reviewboard/accounts/backends.py
+@@ -277,14 +277,21 @@ class LDAPBackend(AuthBackend):
+ 
+     def authenticate(self, username, password):
+         username = username.strip()
+-        uid = settings.LDAP_UID_MASK % username
++
++        uidattr = "%(userattr)s=%(username)s" % {
++                    'userattr': settings.LDAP_UID,
++                    'username': username}
++        uidfilter = "(%(uidattr)s)" % (uidattr);
++
++        if settings.LDAP_UID_MASK:
++            uidfilter = settings.LDAP_UID_MASK % username
+ 
+         if len(password) == 0:
+             # Don't try to bind using an empty password; the server will
+             # return success, which doesn't mean we have authenticated.
+             # http://tools.ietf.org/html/rfc4513#section-5.1.2
+             # http://tools.ietf.org/html/rfc4513#section-6.3.1
+-            logging.warning("Empty password for: %s" % uid)
++            logging.warning("Empty password for: %s" % username)
+             return None
+ 
+         try:
+@@ -299,13 +306,13 @@ class LDAPBackend(AuthBackend):
+                 # Log in as the anonymous user before searching.
+                 ldapo.simple_bind_s(settings.LDAP_ANON_BIND_UID,
+                                     settings.LDAP_ANON_BIND_PASSWD)
+-                search = ldapo.search_s(settings.LDAP_BASE_DN, ldap.SCOPE_SUBTREE,
+-                                        uid)
++                search = ldapo.search_s(settings.LDAP_BASE_DN,
++                                        ldap.SCOPE_SUBTREE,
++                                        uidfilter)
+                 if not search:
+                     # No such a user, return early, no need for bind attempts
+-                    logging.warning("LDAP error: The specified object does not "
+-                                    "exist in the Directory: %s" %
+-                                    uid)
++                    logging.warning("LDAP error: The specified object does "
++                                    "not exist in the Directory: %s" % username)
+                     return None
+                 else:
+                     # Having found the user anonymously, attempt bind with the password
+@@ -319,11 +326,11 @@ class LDAPBackend(AuthBackend):
+                 ldapo.simple_bind_s()
+                 search = ldapo.search_s(settings.LDAP_BASE_DN,
+                                         ldap.SCOPE_SUBTREE,
+-                                        uid)
++                                        uidfilter)
+                 if (len(search) > 0):
+                     userbinding = search[0][0]
+                 else:
+-                    userbinding = ','.join([uid,settings.LDAP_BASE_DN])
++                    userbinding = ','.join([uidattr, settings.LDAP_BASE_DN])
+                 ldapo.bind_s(userbinding, password)
+ 
+             return self.get_or_create_user(username, None, ldapo)
+@@ -331,10 +338,10 @@ class LDAPBackend(AuthBackend):
+         except ImportError:
+             pass
+         except ldap.INVALID_CREDENTIALS:
+-            logging.warning("LDAP error: The specified object does not "
+-                            "exist in the Directory or provided invalid credentials: %s" %
+-                            uid)
+-        except ldap.LDAPError, e:
++            logging.warning("LDAP error: The specified object does not exist "
++                            "in the Directory or provided invalid "
++                            "credentials: %s" % username)
++        except ldap.LDAPError as e:
+             logging.warning("LDAP error: %s" % e)
+         except:
+             # Fallback exception catch because
+diff --git a/reviewboard/accounts/forms.py b/reviewboard/accounts/forms.py
+index 9d6c9a5349e2c6e0fc123337adf50a398b0f3c10..1478e4f2b6442a89ee512038d78d0bba765d9005 100644
+--- a/reviewboard/accounts/forms.py
++++ b/reviewboard/accounts/forms.py
+@@ -307,6 +307,12 @@ class LDAPSettingsForm(SiteSettingsForm):
+         required=True,
+         widget=forms.TextInput(attrs={'size': '40'}))
+ 
++    auth_ldap_uid = forms.CharField(
++        label=_("Username Attribute"),
++        help_text=_("The attribute in the LDAP server that stores a user's "
++                    "login name"),
++        required=True)
++
+     auth_ldap_given_name_attribute = forms.CharField(
+         label=_("Given Name Attribute"),
+         initial="givenName",
+@@ -347,26 +353,30 @@ class LDAPSettingsForm(SiteSettingsForm):
+         required=False)
+ 
+     auth_ldap_uid_mask = forms.CharField(
+-        label=_("User Mask"),
+-        initial="uid=%s,ou=users,dc=example,dc=com",
+-        help_text=_("The string representing the user. Use \"%(varname)s\" "
+-                    "where the username would normally go. For example: "
+-                    "(uid=%(varname)s) or (sAMAccountName=%(varname)s) "
+-                    "[for active directory LDAP]") %
+-                  {'varname': '%s'},
++        label=_("Custom LDAP User Search Filter"),
++        help_text=_("A custom LDAP search filter, corresponding to RFC 2254. "
++                    "If left unset, this option is equivalent to "
++                    "(usernameattribute=%(varname)s). Use \"%(varname)s\" "
++                    "wherever the username would normally go. "
++                    "Specify this value only if the default cannot locate "
++                    "all users.") % {'varname': '%s'},
++        required=False,
+         widget=forms.TextInput(attrs={'size': '40'}))
+ 
+     auth_ldap_anon_bind_uid = forms.CharField(
+-        label=_("Anonymous User Mask"),
+-        help_text=_("The user mask string for anonymous users. If specified, "
+-                    "this should be in the same format as User Mask."),
++        label=_("Review Board LDAP Bind Account"),
++        help_text=_("The full distinguished name of a user account with "
++                    "sufficient access to perform lookups of users and "
++                    "groups in the LDAP server. If the LDAP server permits "
++                    "such lookups via anonymous bind, you may leave this "
++                    "field blank."),
+         required=False,
+         widget=forms.TextInput(attrs={'size': '40'}))
+ 
+     auth_ldap_anon_bind_passwd = forms.CharField(
+-        label=_("Anonymous User Password"),
++        label=_("Review Board LDAP Bind Password"),
+         widget=forms.PasswordInput(attrs={'size': '30'}),
+-        help_text=_("The optional password for the anonymous user."),
++        help_text=_("The password for the Review Board LDAP Bind Account."),
+         required=False)
+ 
+     def load(self):
+@@ -381,6 +391,7 @@ class LDAPSettingsForm(SiteSettingsForm):
+             self.disabled_fields['auth_ldap_email_attribute'] = True
+             self.disabled_fields['auth_ldap_tls'] = True
+             self.disabled_fields['auth_ldap_base_dn'] = True
++            self.disabled_fields['auth_ldap_uid'] = True
+             self.disabled_fields['auth_ldap_uid_mask'] = True
+             self.disabled_fields['auth_ldap_anon_bind_uid'] = True
+             self.disabled_fields['auth_ldap_anon_bind_password'] = True
+diff --git a/reviewboard/admin/siteconfig.py b/reviewboard/admin/siteconfig.py
+index 0ae5a64d3e05e224055fea80605dbefc28d590ed..cc32032eaa74f07e7c0fd05d47bacb2e8d64540c 100644
+--- a/reviewboard/admin/siteconfig.py
++++ b/reviewboard/admin/siteconfig.py
+@@ -64,6 +64,7 @@ settings_map = {
+     'auth_ldap_email_attribute':      'LDAP_EMAIL_ATTRIBUTE',
+     'auth_ldap_tls':                  'LDAP_TLS',
+     'auth_ldap_base_dn':              'LDAP_BASE_DN',
++    'auth_ldap_uid':                  'LDAP_UID',
+     'auth_ldap_uid_mask':             'LDAP_UID_MASK',
+     'auth_ldap_uri':                  'LDAP_URI',
+     'auth_ad_domain_name':            'AD_DOMAIN_NAME',
+@@ -108,6 +109,7 @@ defaults.update({
+     'auth_ldap_anon_bind_passwd':          '',
+     'auth_ldap_email_domain':              '',
+     'auth_ldap_tls':                       False,
++    'auth_ldap_uid':                       'uid',
+     'auth_ldap_uid_mask':                  '',
+     'auth_ldap_uri':                       '',
+     'auth_nis_email_domain':               '',
+-- 
+1.8.4.2
+
diff --git a/0002-Support-parallel-installed-Django-eggs.patch b/0002-Support-parallel-installed-Django-eggs.patch
new file mode 100644
index 0000000..ee57508
--- /dev/null
+++ b/0002-Support-parallel-installed-Django-eggs.patch
@@ -0,0 +1,33 @@
+From 0b406b50191a2e6930a09e0f1c4533933be31a05 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Fri, 21 Feb 2014 15:57:23 -0500
+Subject: [PATCH 2/4] Support parallel-installed Django eggs
+
+If a system has different versions of Django installed as separate
+eggs (such as having the latest version as well as an older version
+for compatibility with ReviewBoard), this addition to the WSGI
+loader enables it to find the matching supported version on the
+system.
+
+This will also make things easier on distro upgrades, as it will be
+possible to have both the Django 1.4 egg on the system with Review
+Board 1.7.21 and Django 1.6 in place for Review Board 2.0
+---
+ reviewboard/cmdline/conf/reviewboard.wsgi.in | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/reviewboard/cmdline/conf/reviewboard.wsgi.in b/reviewboard/cmdline/conf/reviewboard.wsgi.in
+index cd4b1b888c9b94e20298c035613d3c746bfb41eb..fc3d279ec1b114d1ed07d1f2a4d86ecec74e91a3 100644
+--- a/reviewboard/cmdline/conf/reviewboard.wsgi.in
++++ b/reviewboard/cmdline/conf/reviewboard.wsgi.in
+@@ -1,3 +1,7 @@
++import __main__
++__main__.__requires__=['Reviewboard']
++import pkg_resources
++
+ import os
+ import sys
+ 
+-- 
+1.8.5.3
+
diff --git a/FED04-Soften-requires.txt.patch b/FED04-Soften-requires.txt.patch
new file mode 100644
index 0000000..8e6fa81
--- /dev/null
+++ b/FED04-Soften-requires.txt.patch
@@ -0,0 +1,36 @@
+From ff4f95d3a52ee2231f4d44ff59225fda728aed87 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Fri, 21 Feb 2014 16:10:31 -0500
+Subject: [PATCH 3/4] FEDORA: Soften requires.txt
+
+We don't need hard requirements on the Django version or dateutil
+because RPM handles these gracefully.
+---
+ setup.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/setup.py b/setup.py
+index 6cd6a2fdfcff13fc7b94bf744eb388404ca28750..6dbe3b7ea4c4cc1f35eb6e7b565d0975491f4ec8 100755
+--- a/setup.py
++++ b/setup.py
+@@ -171,7 +171,7 @@ setup(name=PACKAGE_NAME,
+       },
+       cmdclass=cmdclasses,
+       install_requires=[
+-          'Django>=1.4.10,<1.5',
++          'Django>=1.4,<1.5',
+           'django_evolution>=0.6.9,<0.7',
+           'Djblets>=0.7.28,<0.8',
+           'django-pipeline>=1.2.24,<1.3',
+@@ -180,7 +180,7 @@ setup(name=PACKAGE_NAME,
+           'mimeparse>=0.1.3',
+           'paramiko>=1.9.0',
+           'Pygments>=1.5',
+-          'python-dateutil==1.5',
++          'python-dateutil<2.0',
+           'python-memcached',
+           'pytz',
+           'recaptcha-client',
+-- 
+1.8.5.3
+
diff --git a/ReviewBoard.spec b/ReviewBoard.spec
index ee02bfe..9fbfa7c 100644
--- a/ReviewBoard.spec
+++ b/ReviewBoard.spec
@@ -4,7 +4,7 @@
 
 Name:           ReviewBoard
 Version:        1.7.21
-Release:        2%{?dist}
+Release:        3%{?dist}
 Summary:        Web-based code review tool
 Group:          Applications/Internet
 License:        MIT
@@ -68,9 +68,19 @@ Requires:       python-django14
 BuildRequires:  python-django-evolution >= 0.6.9
 Requires:       python-django-evolution >= 0.6.9
 
-Patch1001: 0001-RBSITE-Deploy-correct-Apache-2.4-authorization.patch
+# Upstream patches awaiting the next release
+Patch0001: 0001-RBSITE-Deploy-correct-Apache-2.4-authorization.patch
+Patch0002: 0002-Support-parallel-installed-Django-eggs.patch
+
+# Fedora-specific patches
+
+# The cache file belongs in /var/cache according to guidelines
 Patch1003: FED03-Change-default-cache-file-path.patch
 
+# There are several workarounds for easy_install in the requires.txt
+# that are not needed for (and interfere with) RPM packaging.
+Patch1004: FED04-Soften-requires.txt.patch
+
 %description
 Review Board is a powerful web-based code review tool that offers
 developers an easy way to handle code reviews. It scales well from small
@@ -79,8 +89,14 @@ of the stress and time out of the code review process.
 
 %prep
 %setup -q -n %{name}-%{version}
-%patch1001 -p1
+
+# Upstream patches
+%patch0001 -p1
+%patch0002 -p1
+
+# Fedora patches
 %patch1003 -p1
+%patch1004 -p1
 
 # Remove packaged egg-info so it's regenerated by setup.py
 rm -rf ReviewBoard*.egg-info
@@ -99,13 +115,6 @@ chmod +x $RPM_BUILD_ROOT/%{python_sitelib}/reviewboard/manage.py
 chmod +x $RPM_BUILD_ROOT/%{python_sitelib}/reviewboard/cmdline/rbssh.py
 chmod +x $RPM_BUILD_ROOT/%{python_sitelib}/reviewboard/cmdline/rbsite.py
 
-# The requires.txt file isn't needed, because RPM will guarantee the
-# dependency itself. Furthermore, upstream's requires.txt has workarounds
-# to handle easy_install that cause problems with RPM (notably, an exact
-# version requirement on python-dateutil==1.5 to prevent auto-updating to
-# the python3-only python-dateutil 2.0)
-rm -f $RPM_BUILD_ROOT/%{python_sitelib}/%{name}*.egg-info/requires.txt
-
 # Remove test data from the installed packages
 rm -Rf $RPM_BUILD_ROOT/%{python_sitelib}/reviewboard/diffviewer/testdata \
        $RPM_BUILD_ROOT/%{python_sitelib}/reviewboard/scmtools/testdata
@@ -140,6 +149,9 @@ if [ $1 -eq 2 ] ; then
 fi
 
 %changelog
+* Fri Feb 21 2014 Stephen Gallagher <sgallagh at redhat.com> 1.7.21-3
+- Support parallel-installable python-django14 package
+
 * Mon Jan 27 2014 Stephen Gallagher <sgallagh at redhat.com> 1.7.21-2
 - Fix apache configuration to support new authorization directive
 


More information about the scm-commits mailing list