зеркало из https://github.com/mozilla/pjs.git
Bug 241900: Allow Bugzilla::Auth to have multiple login and validation styles
patch by erik r=joel, kiko a=myk
This commit is contained in:
Родитель
b6b3158289
Коммит
c70d1cf583
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
BEGIN {
|
||||
my $loginmethod = Param("loginmethod");
|
||||
if ($loginmethod =~ /^([A-Za-z0-9_\.\-]+)$/) {
|
||||
$loginmethod = $1;
|
||||
}
|
||||
else {
|
||||
die "Badly-named loginmethod '$loginmethod'";
|
||||
}
|
||||
require "Bugzilla/Auth/" . $loginmethod . ".pm";
|
||||
# The verification method that was successfully used upon login, if any
|
||||
my $current_verify_class = undef;
|
||||
|
||||
our @ISA;
|
||||
push (@ISA, "Bugzilla::Auth::" . $loginmethod);
|
||||
# 'inherit' from the main verify method
|
||||
BEGIN {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
# 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>
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
# -*- 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Login::WWW::CGI;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::Util;
|
||||
|
||||
sub login {
|
||||
my ($class, $type) = @_;
|
||||
|
||||
# 'NORMAL' logins depend on the 'requirelogin' param
|
||||
if ($type == LOGIN_NORMAL) {
|
||||
$type = Param('requirelogin') ? LOGIN_REQUIRED : LOGIN_OPTIONAL;
|
||||
}
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
# First, try the actual login method against form variables
|
||||
my $username = $cgi->param("Bugzilla_login");
|
||||
my $passwd = $cgi->param("Bugzilla_password");
|
||||
|
||||
my $authmethod = Param("user_verify_class");
|
||||
my ($authres, $userid, $extra, $info) =
|
||||
Bugzilla::Auth->authenticate($username, $passwd);
|
||||
|
||||
if ($authres == AUTH_OK) {
|
||||
# Login via username/password was correct and valid, so create
|
||||
# and send out the login cookies
|
||||
my $ipaddr = $cgi->remote_addr;
|
||||
unless ($cgi->param('Bugzilla_restrictlogin') ||
|
||||
Param('loginnetmask') == 32) {
|
||||
$ipaddr = Bugzilla::Auth::get_netaddr($ipaddr);
|
||||
}
|
||||
|
||||
# The IP address is valid, at least for comparing with itself in a
|
||||
# subsequent login
|
||||
trick_taint($ipaddr);
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do("INSERT INTO logincookies (userid, ipaddr) VALUES (?, ?)",
|
||||
undef,
|
||||
$userid, $ipaddr);
|
||||
my $logincookie = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
|
||||
|
||||
# Remember cookie only if admin has told so
|
||||
# or admin didn't forbid it and user told to remember.
|
||||
if ((Param('rememberlogin') eq 'on') ||
|
||||
((Param('rememberlogin') ne 'off') &&
|
||||
($cgi->param('Bugzilla_remember') eq 'on'))) {
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-value => $userid,
|
||||
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-value => $logincookie,
|
||||
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
|
||||
|
||||
}
|
||||
else {
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-value => $userid);
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-value => $logincookie);
|
||||
|
||||
}
|
||||
}
|
||||
elsif ($authres == AUTH_NODATA) {
|
||||
# No data from the form, so try to login via cookies
|
||||
$username = $cgi->cookie("Bugzilla_login");
|
||||
$passwd = $cgi->cookie("Bugzilla_logincookie");
|
||||
|
||||
require Bugzilla::Auth::Login::WWW::CGI::Cookie;
|
||||
my $authmethod = "Cookie";
|
||||
|
||||
($authres, $userid, $extra) =
|
||||
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.
|
||||
# Give them un-loggedin access if allowed (checked below)
|
||||
$authres = AUTH_NODATA if $authres == AUTH_LOGINFAILED;
|
||||
}
|
||||
|
||||
# Now check the result
|
||||
|
||||
# An error may have occurred with the login mechanism
|
||||
if ($authres == AUTH_ERROR) {
|
||||
ThrowCodeError("auth_err",
|
||||
{ authmethod => lc($authmethod),
|
||||
userid => $userid,
|
||||
auth_err_tag => $extra,
|
||||
info => $info
|
||||
});
|
||||
}
|
||||
|
||||
# We can load the page if the login was ok, or there was no data
|
||||
# but a login wasn't required
|
||||
if ($authres == AUTH_OK ||
|
||||
($authres == AUTH_NODATA && $type == LOGIN_OPTIONAL)) {
|
||||
|
||||
# login succeded, so we're done
|
||||
return $userid;
|
||||
}
|
||||
|
||||
# No login details were given, but we require a login if the
|
||||
# page does
|
||||
if ($authres == AUTH_NODATA && $type == LOGIN_REQUIRED) {
|
||||
# Throw up the login page
|
||||
|
||||
print Bugzilla->cgi->header();
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
$template->process("account/auth/login.html.tmpl",
|
||||
{ 'target' => $cgi->url(-relative=>1),
|
||||
'form' => \%::FORM,
|
||||
'mform' => \%::MFORM,
|
||||
'caneditaccount' => Bugzilla::Auth->can_edit('new'),
|
||||
'has_db' => Bugzilla::Auth->has_db,
|
||||
}
|
||||
)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
# This seems like as good as time as any to get rid of old
|
||||
# crufty junk in the logincookies table. Get rid of any entry
|
||||
# that hasn't been used in a month.
|
||||
Bugzilla->dbh->do("DELETE FROM logincookies " .
|
||||
"WHERE TO_DAYS(NOW()) - TO_DAYS(lastused) > 30");
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
# The username/password may be wrong
|
||||
# Don't let the user know whether the username exists or whether
|
||||
# the password was just wrong. (This makes it harder for a cracker
|
||||
# to find account names by brute force)
|
||||
if ($authres == AUTH_LOGINFAILED) {
|
||||
ThrowUserError("invalid_username_or_password");
|
||||
}
|
||||
|
||||
# The account may be disabled
|
||||
if ($authres == AUTH_DISABLED) {
|
||||
# Clear the cookie
|
||||
|
||||
$cgi->send_cookie(-name => 'Bugzilla_login',
|
||||
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
|
||||
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
|
||||
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
|
||||
|
||||
# and throw a user error
|
||||
ThrowUserError("account_disabled",
|
||||
{'disabled_reason' => $extra});
|
||||
}
|
||||
|
||||
# If we get here, then we've run out of options, which shouldn't happen
|
||||
ThrowCodeError("authres_unhandled", { authres => $authres,
|
||||
type => $type, });
|
||||
}
|
||||
|
||||
# Logs user out, according to the option provided; this consists of
|
||||
# removing entries from logincookies for the specified $user.
|
||||
sub logout {
|
||||
my ($class, $user, $option) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$option = LOGOUT_ALL unless defined $option;
|
||||
|
||||
if ($option == LOGOUT_ALL) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE userid = ?",
|
||||
undef, $user->id);
|
||||
return;
|
||||
}
|
||||
|
||||
# The LOGOUT_*_CURRENT options require a cookie
|
||||
my $cookie = Bugzilla->cgi->cookie("Bugzilla_logincookie");
|
||||
detaint_natural($cookie);
|
||||
|
||||
# These queries use both the cookie ID and the user ID as keys. Even
|
||||
# though we know the userid must match, we still check it in the SQL
|
||||
# as a sanity check, since there is no locking here, and if the user
|
||||
# logged out from two machines simultaneously, while someone else
|
||||
# logged in and got the same cookie, we could be logging the other
|
||||
# user out here. Yes, this is very very very unlikely, but why take
|
||||
# chances? - bbaetz
|
||||
if ($option == LOGOUT_KEEP_CURRENT) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE cookie != ? AND userid = ?",
|
||||
undef, $cookie, $user->id);
|
||||
} elsif ($option == LOGOUT_CURRENT) {
|
||||
$dbh->do("DELETE FROM logincookies WHERE cookie = ? AND userid = ?",
|
||||
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 {
|
||||
my $cgi = Bugzilla->cgi;
|
||||
$cgi->send_cookie(-name => "Bugzilla_login",
|
||||
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
|
||||
$cgi->send_cookie(-name => "Bugzilla_logincookie",
|
||||
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Login::WWW::CGI - CGI-based logins for Bugzilla
|
||||
|
||||
=head1 SUMMARY
|
||||
|
||||
This is a L<login module|Bugzilla::Auth/"LOGIN"> for Bugzilla. Users connecting
|
||||
from a CGI script use this module to authenticate. Logouts are also handled here.
|
||||
|
||||
=head1 BEHAVIOUR
|
||||
|
||||
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::Login::WWW::CGI::Cookie>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>
|
|
@ -0,0 +1,115 @@
|
|||
# -*- 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
|
||||
package Bugzilla::Auth::Login::WWW::CGI::Cookie;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Auth;
|
||||
use Bugzilla::Config;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Util;
|
||||
|
||||
sub authenticate {
|
||||
my ($class, $login, $login_cookie) = @_;
|
||||
|
||||
return (AUTH_NODATA) unless defined $login && defined $login_cookie;
|
||||
|
||||
my $cgi = Bugzilla->cgi;
|
||||
|
||||
my $ipaddr = $cgi->remote_addr();
|
||||
my $netaddr = Bugzilla::Auth::get_netaddr($ipaddr);
|
||||
|
||||
# Anything goes for these params - they're just strings which
|
||||
# we're going to verify against the db
|
||||
trick_taint($login);
|
||||
trick_taint($login_cookie);
|
||||
trick_taint($ipaddr);
|
||||
|
||||
my $query = "SELECT profiles.userid, profiles.disabledtext " .
|
||||
"FROM logincookies, profiles " .
|
||||
"WHERE logincookies.cookie=? AND " .
|
||||
" logincookies.userid=profiles.userid AND " .
|
||||
" logincookies.userid=? AND " .
|
||||
" (logincookies.ipaddr=?";
|
||||
if (defined $netaddr) {
|
||||
trick_taint($netaddr);
|
||||
$query .= " OR logincookies.ipaddr=?";
|
||||
}
|
||||
$query .= ")";
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my ($userid, $disabledtext) = $dbh->selectrow_array($query, undef,
|
||||
$login_cookie,
|
||||
$login,
|
||||
$ipaddr,
|
||||
$netaddr);
|
||||
|
||||
return (AUTH_DISABLED, $userid, $disabledtext)
|
||||
if ($disabledtext);
|
||||
|
||||
if ($userid) {
|
||||
# If we logged in successfully, then update the lastused time on the
|
||||
# login cookie
|
||||
$dbh->do("UPDATE logincookies SET lastused=NULL WHERE cookie=?",
|
||||
undef,
|
||||
$login_cookie);
|
||||
|
||||
return (AUTH_OK, $userid);
|
||||
}
|
||||
|
||||
# If we get here, then the login failed.
|
||||
return (AUTH_LOGINFAILED);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Login::WWW::CGI::Cookie - cookie authentication for Bugzilla
|
||||
|
||||
=head1 SUMMARY
|
||||
|
||||
This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
|
||||
Bugzilla, which logs the user in using a persistent cookie stored in the
|
||||
C<logincookies> table.
|
||||
|
||||
The actual password is not stored in the cookie; only the userid and a
|
||||
I<logincookie> (which is used to reverify the login without requiring the
|
||||
password to be sent over the network) are. These I<logincookies> are
|
||||
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::Login::WWW::CGI> handles this.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>, L<Bugzilla::Auth::Login::WWW::CGI>
|
|
@ -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.
|
||||
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
# -*- 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Verify::DB;
|
||||
|
||||
use strict;
|
||||
|
||||
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) = @_;
|
||||
|
||||
return (AUTH_NODATA) unless defined $username && defined $passwd;
|
||||
|
||||
# We're just testing against the db: any value is ok
|
||||
trick_taint($username);
|
||||
|
||||
my $userid = $class->get_id_from_username($username);
|
||||
return (AUTH_LOGINFAILED) unless defined $userid;
|
||||
|
||||
return (AUTH_LOGINFAILED, $userid)
|
||||
unless $class->check_password($userid, $passwd);
|
||||
|
||||
# The user's credentials are okay, so delete any outstanding
|
||||
# password tokens they may have generated.
|
||||
require Bugzilla::Token;
|
||||
Bugzilla::Token::DeletePasswordTokens($userid, "user_logged_in");
|
||||
|
||||
# Account may have been disabled
|
||||
my $disabledtext = $class->get_disabled($userid);
|
||||
return (AUTH_DISABLED, $userid, $disabledtext)
|
||||
if $disabledtext ne '';
|
||||
|
||||
return (AUTH_OK, $userid);
|
||||
}
|
||||
|
||||
sub get_id_from_username {
|
||||
my ($class, $username) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare_cached("SELECT userid FROM profiles " .
|
||||
"WHERE login_name=?");
|
||||
my ($userid) = $dbh->selectrow_array($sth, undef, $username);
|
||||
return $userid;
|
||||
}
|
||||
|
||||
sub get_disabled {
|
||||
my ($class, $userid) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare_cached("SELECT disabledtext FROM profiles " .
|
||||
"WHERE userid=?");
|
||||
my ($text) = $dbh->selectrow_array($sth, undef, $userid);
|
||||
return $text;
|
||||
}
|
||||
|
||||
sub check_password {
|
||||
my ($class, $userid, $passwd) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare_cached("SELECT cryptpassword FROM profiles " .
|
||||
"WHERE userid=?");
|
||||
my ($realcryptpwd) = $dbh->selectrow_array($sth, undef, $userid);
|
||||
|
||||
# Get the salt from the user's crypted password.
|
||||
my $salt = $realcryptpwd;
|
||||
|
||||
# Using the salt, crypt the password the user entered.
|
||||
my $enteredCryptedPassword = crypt($passwd, $salt);
|
||||
|
||||
return $enteredCryptedPassword eq $realcryptpwd;
|
||||
}
|
||||
|
||||
sub change_password {
|
||||
my ($class, $userid, $password) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $cryptpassword = Crypt($password);
|
||||
$dbh->do("UPDATE profiles SET cryptpassword = ? WHERE userid = ?",
|
||||
undef, $cryptpassword, $userid);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Auth::Verify::DB - database authentication for Bugzilla
|
||||
|
||||
=head1 SUMMARY
|
||||
|
||||
This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
|
||||
Bugzilla, which logs the user in using the password stored in the C<profiles>
|
||||
table. This is the most commonly used authentication module.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>
|
|
@ -0,0 +1,196 @@
|
|||
# -*- 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): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Joe Robins <jmrobins@tgix.com>
|
||||
# Dave Miller <justdave@syndicomm.com>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Gervase Markham <gerv@gerv.net>
|
||||
# Christian Reis <kiko@async.com.br>
|
||||
# Bradley Baetz <bbaetz@acm.org>
|
||||
# Erik Stambaugh <erik@dasbistro.com>
|
||||
|
||||
package Bugzilla::Auth::Verify::LDAP;
|
||||
|
||||
use strict;
|
||||
|
||||
use Bugzilla::Config;
|
||||
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) = @_;
|
||||
|
||||
# If no password was provided, then fail the authentication.
|
||||
# While it may be valid to not have an LDAP password, when you
|
||||
# bind without a password (regardless of the binddn value), you
|
||||
# will get an anonymous bind. I do not know of a way to determine
|
||||
# whether a bind is anonymous or not without making changes to the
|
||||
# LDAP access control settings
|
||||
return (AUTH_NODATA) unless $username && $passwd;
|
||||
|
||||
# We need to bind anonymously to the LDAP server. This is
|
||||
# because we need to get the Distinguished Name of the user trying
|
||||
# to log in. Some servers (such as iPlanet) allow you to have unique
|
||||
# uids spread out over a subtree of an area (such as "People"), so
|
||||
# just appending the Base DN to the uid isn't sufficient to get the
|
||||
# user's DN. For servers which don't work this way, there will still
|
||||
# be no harm done.
|
||||
my $LDAPserver = Param("LDAPserver");
|
||||
if ($LDAPserver eq "") {
|
||||
return (AUTH_ERROR, undef, "server_not_defined");
|
||||
}
|
||||
|
||||
my $LDAPport = "389"; # default LDAP port
|
||||
if($LDAPserver =~ /:/) {
|
||||
($LDAPserver, $LDAPport) = split(":",$LDAPserver);
|
||||
}
|
||||
my $LDAPconn = Net::LDAP->new($LDAPserver, port => $LDAPport, version => 3);
|
||||
if(!$LDAPconn) {
|
||||
return (AUTH_ERROR, undef, "connect_failed");
|
||||
}
|
||||
|
||||
my $mesg;
|
||||
if (Param("LDAPbinddn")) {
|
||||
my ($LDAPbinddn,$LDAPbindpass) = split(":",Param("LDAPbinddn"));
|
||||
$mesg = $LDAPconn->bind($LDAPbinddn, password => $LDAPbindpass);
|
||||
}
|
||||
else {
|
||||
$mesg = $LDAPconn->bind();
|
||||
}
|
||||
if($mesg->code) {
|
||||
return (AUTH_ERROR, undef,
|
||||
"connect_failed",
|
||||
{ errstr => $mesg->error });
|
||||
}
|
||||
|
||||
# We've got our anonymous bind; let's look up this user.
|
||||
$mesg = $LDAPconn->search( base => Param("LDAPBaseDN"),
|
||||
scope => "sub",
|
||||
filter => '(&(' . Param("LDAPuidattribute") . "=$username)" . Param("LDAPfilter") . ')',
|
||||
attrs => ['dn'],
|
||||
);
|
||||
return (AUTH_LOGINFAILED, undef, "lookup_failure")
|
||||
unless $mesg->count;
|
||||
|
||||
# Now we get the DN from this search.
|
||||
my $userDN = $mesg->shift_entry->dn;
|
||||
|
||||
# Now we attempt to bind as the specified user.
|
||||
$mesg = $LDAPconn->bind( $userDN, password => $passwd);
|
||||
|
||||
return (AUTH_LOGINFAILED) if $mesg->code;
|
||||
|
||||
# And now we're going to repeat the search, so that we can get the
|
||||
# mail attribute for this user.
|
||||
$mesg = $LDAPconn->search( base => Param("LDAPBaseDN"),
|
||||
scope => "sub",
|
||||
filter => '(&(' . Param("LDAPuidattribute") . "=$username)" . Param("LDAPfilter") . ')',
|
||||
);
|
||||
my $user_entry = $mesg->shift_entry if !$mesg->code && $mesg->count;
|
||||
if(!$user_entry || !$user_entry->exists(Param("LDAPmailattribute"))) {
|
||||
return (AUTH_ERROR, undef,
|
||||
"cannot_retreive_attr",
|
||||
{ attr => Param("LDAPmailattribute") });
|
||||
}
|
||||
|
||||
# get the mail attribute
|
||||
$username = $user_entry->get_value(Param("LDAPmailattribute"));
|
||||
# OK, so now we know that the user is valid. Lets try finding them in the
|
||||
# Bugzilla database
|
||||
|
||||
# XXX - should this part be made more generic, and placed in
|
||||
# Bugzilla::Auth? Lots of login mechanisms may have to do this, although
|
||||
# until we actually get some more, its hard to know - BB
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sth = $dbh->prepare_cached("SELECT userid, disabledtext " .
|
||||
"FROM profiles " .
|
||||
"WHERE login_name=?");
|
||||
my ($userid, $disabledtext) =
|
||||
$dbh->selectrow_array($sth,
|
||||
undef,
|
||||
$username);
|
||||
|
||||
# If the user doesn't exist, then they need to be added
|
||||
unless ($userid) {
|
||||
# We'll want the user's name for this.
|
||||
my $userRealName = $user_entry->get_value("displayName");
|
||||
if($userRealName eq "") {
|
||||
$userRealName = $user_entry->get_value("cn");
|
||||
}
|
||||
&::InsertNewUser($username, $userRealName);
|
||||
|
||||
($userid, $disabledtext) = $dbh->selectrow_array($sth,
|
||||
undef,
|
||||
$username);
|
||||
return (AUTH_ERROR, $userid, "no_userid")
|
||||
unless $userid;
|
||||
}
|
||||
|
||||
# we're done, so disconnect
|
||||
$LDAPconn->unbind;
|
||||
|
||||
# Test for disabled account
|
||||
return (AUTH_DISABLED, $userid, $disabledtext)
|
||||
if $disabledtext ne '';
|
||||
|
||||
# If we get to here, then the user is allowed to login, so we're done!
|
||||
return (AUTH_OK, $userid);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
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.
|
||||
|
||||
=head1 DISCLAIMER
|
||||
|
||||
B<This module is experimental>. It is poorly documented, and not very flexible.
|
||||
Search L<http://bugzilla.mozilla.org/> for a list of known LDAP bugs.
|
||||
|
||||
None of the core Bugzilla developers, nor any of the large installations, use
|
||||
this module, and so it has received less testing. (In fact, this iteration
|
||||
hasn't been tested at all)
|
||||
|
||||
Patches are accepted.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::Auth>
|
|
@ -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) {
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 => 'loginmethod',
|
||||
desc => 'The type of login authentication to use:
|
||||
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 => '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
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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'],
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -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 %]
|
||||
|
|
|
@ -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'));
|
||||
|
|
Загрузка…
Ссылка в новой задаче