diff options
-rw-r--r-- | Bugzilla.pm | 52 | ||||
-rw-r--r-- | Bugzilla/Auth.pm | 126 | ||||
-rw-r--r-- | Bugzilla/Auth/Login/WWW.pm | 109 | ||||
-rw-r--r-- | Bugzilla/Auth/Login/WWW/CGI.pm (renamed from Bugzilla/Auth/CGI.pm) | 23 | ||||
-rw-r--r-- | Bugzilla/Auth/Login/WWW/CGI/Cookie.pm (renamed from Bugzilla/Auth/Cookie.pm) | 8 | ||||
-rw-r--r-- | Bugzilla/Auth/README | 138 | ||||
-rw-r--r-- | Bugzilla/Auth/Verify/DB.pm (renamed from Bugzilla/Auth/DB.pm) | 19 | ||||
-rw-r--r-- | Bugzilla/Auth/Verify/LDAP.pm (renamed from Bugzilla/Auth/LDAP.pm) | 19 | ||||
-rw-r--r-- | Bugzilla/Config.pm | 7 | ||||
-rwxr-xr-x | checksetup.pl | 11 | ||||
-rwxr-xr-x | createaccount.cgi | 2 | ||||
-rw-r--r-- | defparams.pl | 68 | ||||
-rwxr-xr-x | editusers.cgi | 23 | ||||
-rw-r--r-- | t/Support/Files.pm | 2 | ||||
-rw-r--r-- | template/en/default/account/auth/login.html.tmpl | 28 | ||||
-rw-r--r-- | template/en/default/global/user-error.html.tmpl | 4 | ||||
-rwxr-xr-x | token.cgi | 9 |
17 files changed, 504 insertions, 144 deletions
diff --git a/Bugzilla.pm b/Bugzilla.pm index 5cee520c7..0818fb1d5 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -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> diff --git a/Bugzilla/Auth.pm b/Bugzilla/Auth.pm index dcea8189a..71b125e45 100644 --- a/Bugzilla/Auth.pm +++ b/Bugzilla/Auth.pm @@ -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 index 000000000..2c45562d2 --- /dev/null +++ b/Bugzilla/Auth/Login/WWW.pm @@ -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. + + diff --git a/Bugzilla/Auth/CGI.pm b/Bugzilla/Auth/Login/WWW/CGI.pm index 471e538e9..fb00cd018 100644 --- a/Bugzilla/Auth/CGI.pm +++ b/Bugzilla/Auth/Login/WWW/CGI.pm @@ -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 diff --git a/Bugzilla/Auth/Cookie.pm b/Bugzilla/Auth/Login/WWW/CGI/Cookie.pm index b50acbe24..84f2b27a8 100644 --- a/Bugzilla/Auth/Cookie.pm +++ b/Bugzilla/Auth/Login/WWW/CGI/Cookie.pm @@ -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 index 000000000..c765d4971 --- /dev/null +++ b/Bugzilla/Auth/README @@ -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. + + diff --git a/Bugzilla/Auth/DB.pm b/Bugzilla/Auth/Verify/DB.pm index dee3b5db9..ec13bacf8 100644 --- a/Bugzilla/Auth/DB.pm +++ b/Bugzilla/Auth/Verify/DB.pm @@ -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 diff --git a/Bugzilla/Auth/LDAP.pm b/Bugzilla/Auth/Verify/LDAP.pm index c34c3698f..d5b115ca0 100644 --- a/Bugzilla/Auth/LDAP.pm +++ b/Bugzilla/Auth/Verify/LDAP.pm @@ -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. diff --git a/Bugzilla/Config.pm b/Bugzilla/Config.pm index b568918e3..71bac4225 100644 --- a/Bugzilla/Config.pm +++ b/Bugzilla/Config.pm @@ -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) { diff --git a/checksetup.pl b/checksetup.pl index 5bcc28ef1..53d939197 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -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"; + } } } diff --git a/createaccount.cgi b/createaccount.cgi index 6364e20bc..2447c1117 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -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(); diff --git a/defparams.pl b/defparams.pl index 6861d0447..6f8dcf595 100644 --- a/defparams.pl +++ b/defparams.pl @@ -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 }, { diff --git a/editusers.cgi b/editusers.cgi index 826bb4b34..fa3efbf8f 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -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)); diff --git a/t/Support/Files.pm b/t/Support/Files.pm index ffadc562c..de5b598c5 100644 --- a/t/Support/Files.pm +++ b/t/Support/Files.pm @@ -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'], ); diff --git a/template/en/default/account/auth/login.html.tmpl b/template/en/default/account/auth/login.html.tmpl index a4757bc1c..f8e54f36f 100644 --- a/template/en/default/account/auth/login.html.tmpl +++ b/template/en/default/account/auth/login.html.tmpl @@ -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 %] @@ -101,23 +102,32 @@ #%] [% 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 %] diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 97987b786..ed95724fb 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -613,6 +613,10 @@ [% 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. @@ -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')); |