]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 241900: Allow Bugzilla::Auth to have multiple login and validation styles
authorbugreport%peshkin.net <>
Wed, 21 Jul 2004 05:41:18 +0000 (05:41 +0000)
committerbugreport%peshkin.net <>
Wed, 21 Jul 2004 05:41:18 +0000 (05:41 +0000)
patch by erik
r=joel, kiko
a=myk

17 files changed:
Bugzilla.pm
Bugzilla/Auth.pm
Bugzilla/Auth/Login/WWW.pm [new file with mode: 0644]
Bugzilla/Auth/Login/WWW/CGI.pm [moved from Bugzilla/Auth/CGI.pm with 93% similarity]
Bugzilla/Auth/Login/WWW/CGI/Cookie.pm [moved from Bugzilla/Auth/Cookie.pm with 93% similarity]
Bugzilla/Auth/README [new file with mode: 0644]
Bugzilla/Auth/Verify/DB.pm [moved from Bugzilla/Auth/DB.pm with 91% similarity]
Bugzilla/Auth/Verify/LDAP.pm [moved from Bugzilla/Auth/LDAP.pm with 95% similarity]
Bugzilla/Config.pm
checksetup.pl
createaccount.cgi
defparams.pl
editusers.cgi
t/Support/Files.pm
template/en/default/account/auth/login.html.tmpl
template/en/default/global/user-error.html.tmpl
token.cgi

index 5cee520c7f2987d083207babb1bdbb8020a3703e..0818fb1d5665feac3dc1f90e1eb2e80d65bb96d5 100644 (file)
@@ -18,6 +18,7 @@
 # Rights Reserved.
 #
 # Contributor(s): Bradley Baetz <bbaetz@student.usyd.edu.au>
+#                 Erik Stambaugh <erik@dasbistro.com>
 #
 
 package Bugzilla;
@@ -25,6 +26,7 @@ package Bugzilla;
 use strict;
 
 use Bugzilla::Auth;
+use Bugzilla::Auth::Login::WWW;
 use Bugzilla::CGI;
 use Bugzilla::Config;
 use Bugzilla::Constants;
@@ -54,39 +56,7 @@ sub user {
 
 sub login {
     my ($class, $type) = @_;
-
-    # Avoid double-logins, which may confuse the auth code
-    # (double cookies, odd compat code settings, etc)
-    # This is particularly important given the munging for
-    # $::COOKIE{'Bugzilla_login'} from a userid to a loginname
-    # (for backwards compat)
-    if (defined $_user) {
-        return $_user;
-    }
-
-    $type = LOGIN_NORMAL unless defined $type;
-
-    # For now, we can only log in from a cgi
-    # One day, we'll be able to log in via apache auth, an email message's
-    # PGP signature, and so on
-
-    use Bugzilla::Auth::CGI;
-    my $userid = Bugzilla::Auth::CGI->login($type);
-    if ($userid) {
-        $_user = new Bugzilla::User($userid);
-
-        # Compat stuff
-        $::userid = $userid;
-
-        # Evil compat hack. The cookie stores the id now, not the name, but
-        # old code still looks at this to get the current user's email
-        # so it needs to be set.
-        $::COOKIE{'Bugzilla_login'} = $_user->login;
-    } else {
-        logout_request();
-    }
-
-    return $_user;
+    $_user = Bugzilla::Auth::Login::WWW->login($type);
 }
 
 sub logout {
@@ -97,20 +67,14 @@ sub logout {
     }
     $option = LOGOUT_CURRENT unless defined $option;
 
-    use Bugzilla::Auth::CGI;
-    Bugzilla::Auth::CGI->logout($_user, $option);
-    if ($option != LOGOUT_KEEP_CURRENT) {
-        Bugzilla::Auth::CGI->clear_browser_cookies();
-        logout_request();
-    }
+    Bugzilla::Auth::Login::WWW->logout($_user, $option);
 }
 
 sub logout_user {
     my ($class, $user) = @_;
     # When we're logging out another user we leave cookies alone, and
-    # therefore avoid calling logout() directly.
-    use Bugzilla::Auth::CGI;
-    Bugzilla::Auth::CGI->logout($user, LOGOUT_ALL);
+    # therefore avoid calling Bugzilla->logout() directly.
+    Bugzilla::Auth::Login::WWW->logout($user, LOGOUT_ALL);
 }
 
 # just a compatibility front-end to logout_user that gets a user by id
@@ -290,7 +254,7 @@ or if the login code has not yet been run.
 =item C<login>
 
 Logs in a user, returning a C<Bugzilla::User> object, or C<undef> if there is
-no logged in user. See L<Bugzilla::Auth|Bugzilla::Auth> and
+no logged in user. See L<Bugzilla::Auth|Bugzilla::Auth>, and
 L<Bugzilla::User|Bugzilla::User>.
 
 =item C<logout($option)>
@@ -315,7 +279,7 @@ Bugzilla::User instance.
 
 Essentially, causes calls to C<Bugzilla->user> to return C<undef>. This has the
 effect of logging out a user for the current request only; cookies and
-database sessions are left intact. 
+database sessions are left intact.
 
 =item C<dbh>
 
index dcea8189a087ad4c47b7fe52f274e4ce21b37a8d..71b125e45db6213c86bf4229f9820e59f65bd444 100644 (file)
@@ -18,6 +18,7 @@
 # Rights Reserved.
 #
 # Contributor(s): Bradley Baetz <bbaetz@acm.org>
+#                 Erik Stambaugh <erik@dasbistro.com>
 
 package Bugzilla::Auth;
 
@@ -26,23 +27,34 @@ use strict;
 use Bugzilla::Config;
 use Bugzilla::Constants;
 
-# 'inherit' from the main loginmethod
+# The verification method that was successfully used upon login, if any
+my $current_verify_class = undef;
+
+# 'inherit' from the main verify method
 BEGIN {
-    my $loginmethod = Param("loginmethod");
-    if ($loginmethod =~ /^([A-Za-z0-9_\.\-]+)$/) {
-        $loginmethod = $1;
-    }
-    else {
-        die "Badly-named loginmethod '$loginmethod'";
+    for my $verifyclass (split /,\s*/, Param("user_verify_class")) {
+        if ($verifyclass =~ /^([A-Za-z0-9_\.\-]+)$/) {
+            $verifyclass = $1;
+        } else {
+            die "Badly-named user_verify_class '$verifyclass'";
+        }
+        require "Bugzilla/Auth/Verify/" . $verifyclass . ".pm";
     }
-    require "Bugzilla/Auth/" . $loginmethod . ".pm";
-
-    our @ISA;
-    push (@ISA, "Bugzilla::Auth::" . $loginmethod);
 }
 
 # PRIVATE
 
+# A number of features, like password change requests, require the DB
+# verification method to be on the list.
+sub has_db {
+    for (split (/[\s,]+/, Param("user_verify_class"))) {
+        if (/^DB$/) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
 # Returns the network address for a given ip
 sub get_netaddr {
     my $ipaddr = shift;
@@ -61,6 +73,53 @@ sub get_netaddr {
     return join(".", unpack("CCCC", pack("N", $addr)));
 }
 
+# This is a replacement for the inherited authenticate function
+# go through each of the available methods for each function
+sub authenticate {
+    my $class = shift;
+    my @args   = @_;
+    my @firstresult = ();
+    my @result = ();
+    for my $method (split /,\s*/, Param("user_verify_class")) {
+        $method = "Bugzilla::Auth::Verify::" . $method;
+        @result = $method->authenticate(@args);
+        @firstresult = @result unless @firstresult;
+
+        if (($result[0] != AUTH_NODATA)&&($result[0] != AUTH_LOGINFAILED)) {
+            $current_verify_class = $method;
+            return @result;
+        }
+    }
+    @result = @firstresult;
+    # no auth match
+
+    # see if we can set $current to the first verify method that
+    # will allow a new login
+
+    for my $method (split /,\s*/, Param("user_verify_class")) {
+        $method = "Bugzilla::Auth::Verify::" . $method;
+        if ($method->can_edit('new')) {
+            $current_verify_class = $method;
+        }
+    }
+
+    return @result;
+}
+
+sub can_edit {
+    my ($class, $type) = @_;
+    if ($current_verify_class) {
+        return $current_verify_class->can_edit($type);
+    }
+    # $current_verify_class will not be set if the user isn't logged in.  That
+    # happens when the user is trying to create a new account, which (for now)
+    # is hard-coded to work with DB.
+    elsif (has_db) {
+        return Bugzilla::Auth::Verify::DB->can_edit($type);
+    }
+    return 0;
+}
+
 1;
 
 __END__
@@ -78,16 +137,8 @@ used to obtain the data (from CGI, email, etc), and the other set uses
 this data to authenticate against the datasource (the Bugzilla DB, LDAP,
 cookies, etc).
 
-The handlers for the various types of authentication
-(DB/LDAP/cookies/etc) provide the actual code for each specific method
-of authentication.
-
-The source modules (currently, only
-L<Bugzilla::Auth::CGI|Bugzilla::Auth::CGI>) then use those methods to do
-the authentication.
-
-I<Bugzilla::Auth> itself inherits from the default authentication handler,
-identified by the I<loginmethod> param.
+Modules for obtaining the data are located under L<Bugzilla::Auth::Login>, and
+modules for authenticating are located in L<Bugzilla::Auth::Verify>.
 
 =head1 METHODS
 
@@ -108,7 +159,9 @@ only some addresses.
 =head1 AUTHENTICATION
 
 Authentication modules check a user's credentials (username, password,
-etc) to verify who the user is.
+etc) to verify who the user is.  The methods that C<Bugzilla::Auth> uses for
+authentication are wrappers that check all configured modules (via the
+C<Param('user_info_class')> and C<Param('user_verify_class')>) in sequence.
 
 =head2 METHODS
 
@@ -175,19 +228,36 @@ Note that this argument is a string, not a tag.
 
 =back
 
+=item C<current_verify_class>
+
+This scalar gets populated with the full name (eg.,
+C<Bugzilla::Auth::Verify::DB>) of the verification method being used by the
+current user.  If no user is logged in, it will contain the name of the first
+method that allows new users, if any.  Otherwise, it carries an undefined
+value.
+
 =item C<can_edit>
 
-This determines if the user's account details can be modified. If this
-method returns a C<true> value, then accounts can be created and
-modified through the Bugzilla user interface. Forgotten passwords can
-also be retrieved through the L<Token interface|Bugzilla::Token>.
+This determines if the user's account details can be modified.  It returns a
+reference to a hash with the keys C<userid>, C<login_name>, and C<realname>,
+which determine whether their respective profile values may be altered, and
+C<new>, which determines if new accounts may be created.
+
+Each user verification method (chosen with C<Param('user_verify_class')> has
+its own set of can_edit values.  Calls to can_edit return the appropriate
+values for the current user's login method.
+
+If a user is not logged in, C<can_edit> will contain the values of the first
+verify method that allows new users to be created, if available.  Otherwise it
+returns an empty hash.
 
 =back
 
 =head1 LOGINS
 
 A login module can be used to try to log in a Bugzilla user in a
-particular way. For example, L<Bugzilla::Auth::CGI|Bugzilla::Auth::CGI>
+particular way. For example,
+L<Bugzilla::Auth::Login::WWW::CGI|Bugzilla::Auth::Login::WWW::CGI>
 logs in users from CGI scripts, first by using form variables, and then
 by trying cookies as a fallback.
 
@@ -250,5 +320,5 @@ user-performed password changes.
 
 =head1 SEE ALSO
 
-L<Bugzilla::Auth::CGI>, L<Bugzilla::Auth::Cookie>, L<Bugzilla::Auth::DB>
+L<Bugzilla::Auth::Login::WWW::CGI>, L<Bugzilla::Auth::Login::WWW::CGI::Cookie>, L<Bugzilla::Auth::Verify::DB>
 
diff --git a/Bugzilla/Auth/Login/WWW.pm b/Bugzilla/Auth/Login/WWW.pm
new file mode 100644 (file)
index 0000000..2c45562
--- /dev/null
@@ -0,0 +1,109 @@
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Bugzilla Bug Tracking System.
+#
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s): Erik Stambaugh <erik@dasbistro.com>
+
+package Bugzilla::Auth::Login::WWW;
+
+use strict;
+
+use Bugzilla::Constants;
+use Bugzilla::Config;
+
+# $current_login_class stores the name of the login style that succeeded.
+my $current_login_class = undef;
+sub login_class {
+    my ($class, $type) = @_;
+    if ($type) {
+        $current_login_class = $type;
+    }
+    return $current_login_class;
+}
+
+sub login {
+    my ($class, $type) = @_;
+
+    my $user = Bugzilla->user;
+
+    # Avoid double-logins, which may confuse the auth code
+    # (double cookies, odd compat code settings, etc)
+    # This is particularly important given the munging for
+    # $::COOKIE{'Bugzilla_login'} from a userid to a loginname
+    # (for backwards compat)
+    if (defined $user) {
+        return $user;
+    }
+
+    $type = LOGIN_NORMAL unless defined $type;
+
+    # Log in using whatever methods are defined in user_info_class.
+    # Please note the particularly strange way require() and the function
+    # calls are being done, because we're calling a module that's named in
+    # a string. I assure you it works, and it avoids the need for an eval().
+    my $userid;
+    for my $login_class (split(/,\s*/, Param('user_info_class'))) {
+        require "Bugzilla/Auth/Login/WWW/" . $login_class . ".pm";
+        $userid = "Bugzilla::Auth::Login::WWW::$login_class"->login($type);
+        if ($userid) {
+            $class->login_class("Bugzilla::Auth::Login::WWW::$login_class");
+            last;
+        }
+    }
+
+    if ($userid) {
+        $user = new Bugzilla::User($userid);
+
+        # Compat stuff
+        $::userid = $userid;
+
+        # Evil compat hack. The cookie stores the id now, not the name, but
+        # old code still looks at this to get the current user's email
+        # so it needs to be set.
+        $::COOKIE{'Bugzilla_login'} = $user->login;
+    } else {
+        Bugzilla->logout_request();
+    }
+    return $user;
+}
+
+sub logout {
+    my ($class, $user, $option) = @_;
+    if ($class->login_class) {
+        $class->login_class->logout($user, $option);
+    }
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Auth::Login::WWW - WWW login information gathering module
+
+=head1 METHODS
+
+=item C<login>
+
+Passes C<login> calls to each class defined in the param C<user_info_class>
+and returns a C<Bugzilla::User> object from the first one that successfully
+gathers user login information.
+
+
similarity index 93%
rename from Bugzilla/Auth/CGI.pm
rename to Bugzilla/Auth/Login/WWW/CGI.pm
index 471e538e944a54970e9d5735de47256fca36d83d..fb00cd01831c2095a9132ca7110ea9e8b6cfb0c7 100644 (file)
@@ -25,8 +25,9 @@
 #                 Gervase Markham <gerv@gerv.net>
 #                 Christian Reis <kiko@async.com.br>
 #                 Bradley Baetz <bbaetz@acm.org>
+#                 Erik Stambaugh <erik@dasbistro.com>
 
-package Bugzilla::Auth::CGI;
+package Bugzilla::Auth::Login::WWW::CGI;
 
 use strict;
 
@@ -49,7 +50,7 @@ sub login {
     my $username = $cgi->param("Bugzilla_login");
     my $passwd = $cgi->param("Bugzilla_password");
 
-    my $authmethod = Param("loginmethod");
+    my $authmethod = Param("user_verify_class");
     my ($authres, $userid, $extra, $info) =
       Bugzilla::Auth->authenticate($username, $passwd);
 
@@ -98,11 +99,11 @@ sub login {
         $username = $cgi->cookie("Bugzilla_login");
         $passwd = $cgi->cookie("Bugzilla_logincookie");
 
-        require Bugzilla::Auth::Cookie;
+        require Bugzilla::Auth::Login::WWW::CGI::Cookie;
         my $authmethod = "Cookie";
 
         ($authres, $userid, $extra) =
-          Bugzilla::Auth::Cookie->authenticate($username, $passwd);
+          Bugzilla::Auth::Login::WWW::CGI::Cookie->authenticate($username, $passwd);
 
         # If the data for the cookie was incorrect, then treat that as
         # NODATA. This could occur if the user's IP changed, for example.
@@ -143,7 +144,8 @@ sub login {
                            { 'target' => $cgi->url(-relative=>1),
                              'form' => \%::FORM,
                              'mform' => \%::MFORM,
-                             'caneditaccount' => Bugzilla::Auth->can_edit,
+                             'caneditaccount' => Bugzilla::Auth->can_edit('new'),
+                             'has_db' => Bugzilla::Auth->has_db,
                            }
                           )
           || ThrowTemplateError($template->error());
@@ -216,7 +218,12 @@ sub logout {
                  undef, $cookie, $user->id);
     } else {
         die("Invalid option $option supplied to logout()");
-  }
+    }
+
+    if ($option != LOGOUT_KEEP_CURRENT) {
+        clear_browser_cookies();
+        Bugzilla->logout_request();
+    }
 }
 
 sub clear_browser_cookies {
@@ -233,7 +240,7 @@ __END__
 
 =head1 NAME
 
-Bugzilla::Auth::CGI - CGI-based logins for Bugzilla
+Bugzilla::Auth::Login::WWW::CGI - CGI-based logins for Bugzilla
 
 =head1 SUMMARY
 
@@ -246,7 +253,7 @@ Users are first authenticated against the default authentication handler,
 using the CGI parameters I<Bugzilla_login> and I<Bugzilla_password>.
 
 If no data is present for that, then cookies are tried, using
-L<Bugzilla::Auth::Cookie>.
+L<Bugzilla::Auth::Login::WWW::CGI::Cookie>.
 
 =head1 SEE ALSO
 
similarity index 93%
rename from Bugzilla/Auth/Cookie.pm
rename to Bugzilla/Auth/Login/WWW/CGI/Cookie.pm
index b50acbe242f68668d8b9d16246b90ba8e93655e4..84f2b27a8d4a07710b64c89954589d48956ed6e7 100644 (file)
@@ -26,7 +26,7 @@
 #                 Christian Reis <kiko@async.com.br>
 #                 Bradley Baetz <bbaetz@acm.org>
 
-package Bugzilla::Auth::Cookie;
+package Bugzilla::Auth::Login::WWW::CGI::Cookie;
 
 use strict;
 
@@ -93,7 +93,7 @@ __END__
 
 =head1 NAME
 
-Bugzilla::Cookie - cookie authentication for Bugzilla
+Bugzilla::Auth::Login::WWW::CGI::Cookie - cookie authentication for Bugzilla
 
 =head1 SUMMARY
 
@@ -108,8 +108,8 @@ restricted to certain IP addresses as a security meaure. The exact
 restriction can be specified by the admin via the C<loginnetmask> parameter.
 
 This module does not ever send a cookie (It has no way of knowing when a user
-is successfully logged in). Instead L<Bugzilla::Auth::CGI> handles this.
+is successfully logged in). Instead L<Bugzilla::Auth::Login::WWW::CGI> handles this.
 
 =head1 SEE ALSO
 
-L<Bugzilla::Auth>, L<Bugzilla::Auth::CGI>
+L<Bugzilla::Auth>, L<Bugzilla::Auth::Login::WWW::CGI>
diff --git a/Bugzilla/Auth/README b/Bugzilla/Auth/README
new file mode 100644 (file)
index 0000000..c765d49
--- /dev/null
@@ -0,0 +1,138 @@
+How Auth Works
+==============
+Christian Reis <kiko@async.com.br>
+
+Overview
+--------
+
+Authentication in Bugzilla is handled by a collection of modules that live in
+the Bugzilla::Auth package.  These modules are organized hierarchically based
+upon their responsibility.
+
+The authentication scheme is divided in two tasks: Login and Verify.  Login
+involves gathering credentials from a user, while Verify validates them
+against an authentication service.
+
+The Bugzilla parameters user_info_class and user_verify_class contain a
+list of Login and Verify modules, respectively.
+
+Task: Login
+-----------
+
+This task obtains user credentials based on a request. Examples of requests
+include CGI access from the Bugzilla web interface, email submissions and
+credentials supplied by standalone scripts.
+
+Each type of Bugzilla front-end should have its own package.  For instance,
+access via the Bugzilla web pages should go through Bugzilla::Auth::WWW.
+These packages would contain modules of their own to perform whatever extra
+functions are needed, like the CGI and Cookie modules in the case of WWW.
+
+Task: Verify
+------------
+
+This task validates user credentials against a user authentication service.
+
+The default service in Bugzilla has been the database, which stores the
+login_name and cryptpasswd fields in the profiles table.  An alternative means
+of validation, LDAP, is already supported, and other contributions would be
+appreciated.
+
+The module layout is similar to the Login package, but there is no need for a
+sub-level as there is with Login request types.
+
+Params
+------
+
+There are two params that define behaviour for each authentication task.  Each
+of them defines a comma-separated list of modules to be tried in order.
+
+    - user_info_class determines the module(s) used to obtain user
+      credentials. This param is specific to the requests from Bugzilla web
+      pages, so all of the listed modules live under
+      Bugzilla::Auth::Login::WWW
+
+    - user_verify_class determines the module(s) used to verify credentials.
+      This param is general and concerns the whole Bugzilla instance, since
+      the same back end should be used regardless of what front end is used.
+
+Responsibilities
+----------------
+
+Bugzilla::Auth
+
+    This module is responsible for abstracting away as much as possible the
+    login and logout tasks in Bugzilla.
+
+    It offers  login() and logout() methods that are proxied to the selected
+    login and verify packages.
+
+Bugzilla::Auth::Login
+
+    This is a container to hold the various modules for each request type.
+
+Bugzilla::Auth::Login::WWW
+
+    This module is responsible for abstracting away details of which web-based
+    login modules exist and are in use. It offers login() and logout() methods
+    that proxy through to whatever specific modules
+
+Bugzilla::Auth::Verify
+
+    This module is responsible for abstracting away details of which
+    credential verification modules exist, and should proxy calls through to
+    them. There is a method that is particularly important, and which should
+    be proxied through to the specific:
+
+        can_edit($type)
+
+            This method takes an argument that specifies what sort of change
+            is being requested; the specific module should return 1 or 0 based
+            on the fact that it implements or not the required change.
+
+            Current values for $type are "new" for new accounts, and "userid",
+            "login_name", "realname" for their respective fields.
+
+Specific Login Modules
+----------------------
+
+    WWW
+
+        The main authentication frontend; regular pages (CGIs) should use only
+        this module. It offers a convenient frontend to the main functionality
+        that CGIs need, using form parameters and cookies.
+
+        - Cookie
+
+            Implements part of the backend code that deals with browser
+            cookies. It's actually tied in to DB.pm, so Cookie logins that use
+            LDAP won't work at all.
+
+    LDAP
+
+        The other authentication module is LDAP-based; it is *only* used for
+        password authentication and not for any other login-related task (it
+        actually relies on the database to handle the profile information).
+
+Legacy
+------
+
+Bugzilla.pm
+
+    There is glue code that currently lives in the top-level module
+    Bugzilla.pm; this module handles backwards-compatibility data that is used
+    in a number of CGIs. This data has been slowly removed from the Bugzilla
+    pages and eventually should go away completely, at which point Bugzilla.pm
+    will be just a wrapper to conveniently offer template, cgi, dbh and user
+    variables.
+
+    This module is meant to be used only by Bugzilla pages, and in the case of
+    a reorganization which moves CGI-specific code to a subdirectory,
+    Bugzilla.pm should go with it.
+
+$::COOKIE
+
+    There are still instances of use of $::COOKIE to obtain Logincookie
+    information; these should be removed as well.
+
+
similarity index 91%
rename from Bugzilla/Auth/DB.pm
rename to Bugzilla/Auth/Verify/DB.pm
index dee3b5db9b173316b2f806d0756b3cfdbe052027..ec13bacf8b02a95a8bbbba40beaebd7e35e92eab 100644 (file)
@@ -25,8 +25,9 @@
 #                 Gervase Markham <gerv@gerv.net>
 #                 Christian Reis <kiko@async.com.br>
 #                 Bradley Baetz <bbaetz@acm.org>
+#                 Erik Stambaugh <erik@dasbistro.com>
 
-package Bugzilla::Auth::DB;
+package Bugzilla::Auth::Verify::DB;
 
 use strict;
 
@@ -34,6 +35,18 @@ use Bugzilla::Config;
 use Bugzilla::Constants;
 use Bugzilla::Util;
 
+my $edit_options = {
+    'new' => 1,
+    'userid' => 0,
+    'login_name' => 1,
+    'realname' => 1,
+};
+
+sub can_edit {
+    my ($class, $type) = @_;
+    return $edit_options->{$type};
+}
+
 sub authenticate {
     my ($class, $username, $passwd) = @_;
 
@@ -61,8 +74,6 @@ sub authenticate {
     return (AUTH_OK, $userid);
 }
 
-sub can_edit { return 1; }
-
 sub get_id_from_username {
     my ($class, $username) = @_;
     my $dbh = Bugzilla->dbh;
@@ -111,7 +122,7 @@ __END__
 
 =head1 NAME
 
-Bugzilla::Auth::DB - database authentication for Bugzilla
+Bugzilla::Auth::Verify::DB - database authentication for Bugzilla
 
 =head1 SUMMARY
 
similarity index 95%
rename from Bugzilla/Auth/LDAP.pm
rename to Bugzilla/Auth/Verify/LDAP.pm
index c34c3698fe3d9d9f9575f05f31dff9ea8e1eb1ea..d5b115ca0ed103d34348fa30736abde214b880f8 100644 (file)
@@ -25,8 +25,9 @@
 #                 Gervase Markham <gerv@gerv.net>
 #                 Christian Reis <kiko@async.com.br>
 #                 Bradley Baetz <bbaetz@acm.org>
+#                 Erik Stambaugh <erik@dasbistro.com>
 
-package Bugzilla::Auth::LDAP;
+package Bugzilla::Auth::Verify::LDAP;
 
 use strict;
 
@@ -35,6 +36,18 @@ use Bugzilla::Constants;
 
 use Net::LDAP;
 
+my $edit_options = {
+    'new' => 0,
+    'userid' => 0,
+    'login_name' => 0,
+    'realname' => 0,
+};
+
+sub can_edit {
+    my ($class, $type) = @_;
+    return $edit_options->{$type};
+}
+
 sub authenticate {
     my ($class, $username, $passwd) = @_;
 
@@ -156,15 +169,13 @@ sub authenticate {
     return (AUTH_OK, $userid);
 }
 
-sub can_edit { return 0; }
-
 1;
 
 __END__
 
 =head1 NAME
 
-Bugzilla::Auth::LDAP - LDAP based authentication for Bugzilla
+Bugzilla::Auth::Verify::LDAP - LDAP based authentication for Bugzilla
 
 This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
 Bugzilla, which logs the user in using an LDAP directory.
index b568918e376b0d602e6c7726210b81577fc58350..71bac4225290b1d43a6d2f7075d4b2d66beb7d15 100644 (file)
@@ -25,6 +25,7 @@
 #                 J. Paul Reed <preed@sigkill.com>
 #                 Bradley Baetz <bbaetz@student.usyd.edu.au>
 #                 Christopher Aillon <christopher@aillon.com>
+#                 Erik Stambaugh <erik@dasbistro.com>
 
 package Bugzilla::Config;
 
@@ -217,6 +218,12 @@ sub UpdateParams {
         $param{'loginmethod'} = $param{'useLDAP'} ? "LDAP" : "DB";
     }
 
+    # set verify method to whatever loginmethod was
+    if (exists $param{'loginmethod'} && !exists $param{'user_verify_class'}) {
+        $param{'user_verify_class'} = $param{'loginmethod'};
+        delete $param{'loginmethod'};
+    }
+
     # --- DEFAULTS FOR NEW PARAMS ---
 
     foreach my $item (@param_list) {
index 5bcc28ef1365a213707d014d603c16258541b2e0..53d9391978333cfa46cb2de26e7ff4b282f10ae6 100755 (executable)
@@ -27,6 +27,7 @@
 #                 Bradley Baetz <bbaetz@student.usyd.edu.au>
 #                 Tobias Burnus <burnus@net-b.de>
 #                 Gervase Markham <gerv@gerv.net>
+#                 Erik Stambaugh <erik@dasbistro.com>
 #
 #
 # Direct any questions on this source code to
@@ -1492,10 +1493,12 @@ END { $dbh->disconnect if $dbh }
 # Check for LDAP
 ###########################################################################
 
-if (Param('loginmethod') eq 'LDAP') {
-    my $netLDAP = have_vers("Net::LDAP", 0);
-    if (!$netLDAP && !$silent) {
-        print "If you wish to use LDAP authentication, then you must install Net::LDAP\n\n";
+for my $verifymethod (split /,\s*/, Param('user_verify_class')) {
+    if ($verifymethod eq 'LDAP') {
+        my $netLDAP = have_vers("Net::LDAP", 0);
+        if (!$netLDAP && !$silent) {
+            print "If you wish to use LDAP authentication, then you must install Net::LDAP\n\n";
+        }
     }
 }
 
index 6364e20bcb1d85c41362674099c16260e657227a..2447c111728a74477521945f59da4cfb8894324e 100755 (executable)
@@ -37,7 +37,7 @@ use vars qw(
 );
 
 # If we're using LDAP for login, then we can't create a new account here.
-unless (Bugzilla::Auth->can_edit) {
+unless (Bugzilla::Auth->can_edit('new')) {
   # Just in case someone already has an account, let them get the correct
   # footer on the error message
   Bugzilla->login();
index 6861d04476944e855f95ebc5102ee89eb656803f..6f8dcf595ac7a5f47b4fad026aaea0af9cd00492 100644 (file)
@@ -25,6 +25,7 @@
 #                 J. Paul Reed <preed@sigkill.com>
 #                 Bradley Baetz <bbaetz@student.usyd.edu.au>
 #                 Joseph Heenan <joseph@heenan.me.uk>
+#                 Erik Stambaugh <erik@dasbistro.com>
 #
 
 # This file defines all the parameters that we have a GUI to edit within
@@ -127,7 +128,7 @@ sub check_netmask {
     return "";
 }
 
-sub check_loginmethod {
+sub check_user_verify_class {
     # doeditparams traverses the list of params, and for each one it checks,
     # then updates. This means that if one param checker wants to look at 
     # other params, it must be below that other one. So you can't have two 
@@ -136,18 +137,20 @@ sub check_loginmethod {
     # the login method as LDAP, we won't notice, but all logins will fail.
     # So don't do that.
 
-    my ($method, $entry) = @_;
-    my $res = check_multi($method, $entry);
-    return $res if $res;
-    if ($method eq 'DB') {
-        # No params
-    } elsif ($method eq 'LDAP') {
-        eval "require Net::LDAP";
-        return "Error requiring Net::LDAP: '$@'" if $@;
-        return "LDAP servername is missing" unless Param("LDAPserver");
-        return "LDAPBaseDN is empty" unless Param("LDAPBaseDN");
-    } else {
-        return "Unknown loginmethod '$method' in check_loginmethod";
+    my ($list, $entry) = @_;
+    for my $class (split /,\s*/, $list) {
+        my $res = check_multi($class, $entry);
+        return $res if $res;
+        if ($class eq 'DB') {
+            # No params
+        } elsif ($class eq 'LDAP') {
+            eval "require Net::LDAP";
+            return "Error requiring Net::LDAP: '$@'" if $@;
+            return "LDAP servername is missing" unless Param("LDAPserver");
+            return "LDAPBaseDN is empty" unless Param("LDAPBaseDN");
+        } else {
+                return "Unknown user_verify_class '$class' in check_user_verify_class";
+        }
     }
     return "";
 }
@@ -432,9 +435,40 @@ sub find_languages {
    default => '',
   },
 
+  # XXX in the future:
+  #
+  # user_verify_class and user_info_class should have choices gathered from
+  # whatever sits in their respective directories
+  #
+  # rather than comma-separated lists, these two should eventually become
+  # arrays, but that requires alterations to editparams first
+
+  {
+   name => 'user_info_class',
+   desc => 'Mechanism(s) to be used for gathering a user\'s login information.
+              <add>
+            More than one may be selected. If the first one returns nothing,
+            the second is tried, and so on.<br />
+            The types are:
+            <dl>
+              <dt>CGI</dt>
+              <dd>
+                Asks for username and password via CGI form interface.
+              </dd>
+             </dl>',
+   type => 's',
+   choices => [ 'CGI' ],
+   default => 'CGI',
+   checker => \&check_multi
+  },
+
   {
-   name => 'loginmethod',
-   desc => 'The type of login authentication to use:
+   name => 'user_verify_class',
+   desc => 'Mechanism(s) to be used for verifying (authenticating) information
+            gathered by user_info_class.
+            More than one may be selected. If the first one cannot find the
+            user, the second is tried, and so on.<br />
+            The types are:
             <dl>
               <dt>DB</dt>
               <dd>
@@ -450,9 +484,9 @@ sub find_languages {
               </dd>
              </dl>',
    type => 's',
-   choices => [ 'DB', 'LDAP' ],
+   choices => [ 'DB', 'LDAP', 'DB,LDAP', 'LDAP,DB' ],
    default => 'DB',
-   checker => \&check_loginmethod
+   checker => \&check_user_verify_class
   },
 
   {
index 826bb4b342a22a13569e0c4f51afa54702772557..fa3efbf8fe68ac8e05e5e979b699efbb7d56a1c2 100755 (executable)
@@ -23,6 +23,7 @@
 #                 Joe Robins <jmrobins@tgix.com>
 #                 Dan Mosedale <dmose@mozilla.org>
 #                 Joel Peshkin <bugreport@peshkin.net>
+#                 Erik Stambaugh <erik@dasbistro.com>
 #
 # Direct any questions on this source code to
 #
@@ -114,15 +115,11 @@ sub EmitFormElements ($$$$)
     if ($editall) {
         print "</TR><TR>\n";
         print "  <TH ALIGN=\"right\">Password:</TH>\n";
-        if(!Bugzilla::Auth->can_edit) {
-          print "  <TD><FONT COLOR=RED>This site's authentication method does not allow password changes through Bugzilla!</FONT></TD>\n";
-        } else {
           print qq|
             <TD><INPUT TYPE="PASSWORD" SIZE="16" MAXLENGTH="16" NAME="password" VALUE=""><br>
                 (enter new password to change)
             </TD>
           |;
-        }
         print "</TR><TR>\n";
 
         print "  <TH ALIGN=\"right\">Disable text:</TH>\n";
@@ -209,7 +206,7 @@ sub EmitFormElements ($$$$)
 sub PutTrailer (@)
 {
     my (@links) = ("Back to the <a href=\"./\">index</a>");
-    if($editall && Bugzilla::Auth->can_edit) {
+    if($editall) {
           push(@links,
               "<a href=\"editusers.cgi?action=add\">add</a> a new user");
     }
@@ -361,7 +358,7 @@ if ($action eq 'list') {
         }
         print "</TR>";
     }
-    if ($editall && Bugzilla::Auth->can_edit) {
+    if ($editall) {
         print "<TR>\n";
         my $span = $candelete ? 3 : 2;
         print qq{
@@ -395,12 +392,6 @@ if ($action eq 'add') {
         exit;
     }
 
-    if(!Bugzilla::Auth->can_edit) {
-      print "The authentication mechanism you are using does not permit accounts to be created from Bugzilla";
-      PutTrailer();
-      exit;
-    }
-
     print "<FORM METHOD=POST ACTION=editusers.cgi>\n";
     print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n";
 
@@ -432,12 +423,6 @@ if ($action eq 'new') {
         exit;
     }
 
-    if (!Bugzilla::Auth->can_edit) {
-      print "This site's authentication mechanism does not allow new users to be added.";
-      PutTrailer();
-      exit;
-    }
-
     # Cleanups and valididy checks
     my $realname = trim($::FORM{realname} || '');
     # We don't trim the password since that could falsely lead the user
@@ -814,7 +799,7 @@ if ($action eq 'update') {
 
 
     # Update the database with the user's new password if they changed it.
-    if ( Bugzilla::Auth->can_edit && $editall && $password ) {
+    if ( $editall && $password ) {
         my $passworderror = ValidatePassword($password);
         if ( !$passworderror ) {
             my $cryptpassword = SqlQuote(Crypt($password));
index ffadc562c43a9fb3fcd75ad36ab540a42e5fd58f..de5b598c58fa88676ee437c8b7979d92b2133764 100644 (file)
@@ -29,7 +29,7 @@ package Support::Files;
 @additional_files = ();
 %exclude_deps = (
     'XML::Parser' => ['importxml.pl'],
-    'Net::LDAP' => ['Bugzilla/Auth/LDAP.pm'],
+    'Net::LDAP' => ['Bugzilla/Auth/Verify/LDAP.pm'],
 );
 
 
index a4757bc1c258edd8548dbe730afc9e8f48bf2043..f8e54f36fd30ffafeffbfc032afc6fc6c7ca43cb 100644 (file)
@@ -25,6 +25,7 @@
   # form: hash; the form values which need to be submitted to the target script
   # mform: hash; the form values with multiple values which need to be
   #   submitted to the target script
+  # has_db: true if DB is one of the available authentication mechanisms
   #%]
 
 [% PROCESS global/variables.none.tmpl %]
   #%]
 
 [% IF caneditaccount %]
-  <hr>
 
   [% IF Param("createemailregexp") %]
+    <hr>
+
     <p>
       If you don't have a [% terms.Bugzilla %] account, you can
       <a href="createaccount.cgi">create a new account</a>.
     </p>
   [% END %]
 
-  <form method="get" action="token.cgi">
-    <input type="hidden" name="a" value="reqpw">
-    If you have an account, but have forgotten your password,
-    enter your login name below and submit a request
-    to change your password.<br>
-    <input size="35" name="loginname">
-    <input type="submit" value="Submit Request">
-  </form>
+  [%# For now, password change requests only apply to the DB
+    # verification method #%]
+
+  [% IF has_db != 0 %]
+    <hr>
+
+    <form method="get" action="token.cgi">
+      <input type="hidden" name="a" value="reqpw">
+      If you have an account, but have forgotten your password,
+      enter your login name below and submit a request
+      to change your password.<br>
+      <input size="35" name="loginname">
+      <input type="submit" value="Submit Request">
+    </form>
+
+  [% END %]
 
   <hr>
 [% END %]
index 97987b786b5687b8740d08c26e08df86c27dc9db..ed95724fb34a31777c970fe663d173f67e9541ca 100644 (file)
     [% title = "Old Password Required" %]
     You must enter your old password to change your email address.
 
+  [% ELSIF error == "password_change_requests_not_allowed" %]
+    [% title = "Password Change Requests Not Allowed" %]
+    The system is not configured to allow password change requests.
+
   [% ELSIF error == "passwords_dont_match" %]
     [% title = "Passwords Don't Match" %]
     The two passwords you entered did not match.
index 36508f0a538a2543a69b5357ecf36cb955c8253c..03d0e8b033a528715786d15436bbd5401c5cee44 100755 (executable)
--- a/token.cgi
+++ b/token.cgi
@@ -95,12 +95,19 @@ if ($cgi->param('t')) {
   }
 }
 
+
 # If the user is requesting a password change, make sure they submitted
-# their login name and it exists in the database.
+# their login name and it exists in the database, and that the DB module is in
+# the list of allowed verification methids.
 if ( $::action eq 'reqpw' ) {
     defined $cgi->param('loginname')
       || ThrowUserError("login_needed_for_password_change");
 
+    # check verification methods
+    unless (Bugzilla::Auth->has_db) {
+        ThrowUserError("password_change_requests_not_allowed");
+    }
+
     # Make sure the login name looks like an email address.  This function
     # displays its own error and stops execution if the login name looks wrong.
     CheckEmailSyntax($cgi->param('loginname'));